Skip to content

Commit

Permalink
Merge pull request #11 from JonathanO/master
Browse files Browse the repository at this point in the history
Mild refactoring to split configs away from bean factory
  • Loading branch information
JonathanO committed Dec 2, 2015
2 parents 28e29e9 + 7b6feb1 commit 9c8332a
Show file tree
Hide file tree
Showing 9 changed files with 547 additions and 419 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: php
php:
- "5.4"
- "5.3"
before_script:
- echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- echo "apc.enable_cli = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
Expand Down
140 changes: 7 additions & 133 deletions lib/MooDev/Bounce/Context/ApcCachedXmlApplicationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,146 +21,20 @@
* @author steve
*
*/
class ApcCachedXmlApplicationContext extends XmlApplicationContext
class ApcCachedXmlApplicationContext extends ApplicationContext
{
/**
* @var Logger the logging instance
*/
private $_log;
/**
* @var string => array map of stat info returned by stat calls keyed on
* the filename
*/
protected $_statInfoCache = array();

/**
* Creates the caching instance, setting up logging for delegating upwards
* @param string $xmlFilePath
* @param bool $shareBeanCache
* @param $xmlFilePath
* @param bool $shareBeanCache whether to share the bean cache when the context's uniqueID (file path) is the same.
* @param ValueTagProvider[] $customNamespaces
* @param array $logFactory Callback array to call to obtain a logger instance. Will be called with a single param (class name.)
* @param array $logFactory
*/
public function __construct($xmlFilePath, $shareBeanCache = true, $customNamespaces = array(), $logFactory = array('\MooDev\Bounce\Logger\NullLogFactory', 'getLog'))
{
$this->_log = call_user_func($logFactory, get_class($this));
parent::__construct($xmlFilePath, $shareBeanCache, $customNamespaces);
$contextConfig = (new XmlContextParser($xmlFilePath, $customNamespaces))->getContext();
//Create the bean factory
parent::__construct(BeanFactory::getInstance($contextConfig, $shareBeanCache));
}

protected function _parseXmlFile($xmlFilePath)
{
//Do we have caching enabled?
if (function_exists("apc_fetch")) {
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("APC caching is ENABLED");
}
//Do we have a cache entry for this file path?
$cacheKey = "bouncexml~$xmlFilePath";
$cacheContentSerialized = apc_fetch($cacheKey);
//Cache Content structure is:
// array(
// "xmlFiles" => array(
// "/absolute/path/to/parent.xml" => array(
// "dev" => 5, //Device number from the underlying O/S
// "ino" => 1234567, //Inode number of the file on the device
// "mtime" => 124124681274 //Last modified time
// ),
// "/absolute/path/to/child1.xml" => array(
// "dev" => 5,
// "ino" => 345346364,
// "mtime" => 124124681274
// ),
// "/absolute/path/to/child2.xml" => array(
// "dev" => 5,
// "ino" => 345346364,
// "mtime" => 124124681274
// ),
// ),
// "context" => Config_Context
// )
if ($cacheContentSerialized) {
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("Found cached version on $xmlFilePath");
}
$cacheContent = unserialize($cacheContentSerialized);
//Look at all the files from the cache content array to see
//if any of them are different
$filesToCheck = $cacheContent["xmlFiles"];
$cacheUpToDate = true; //Be positive ...
foreach ($filesToCheck as $fileName => $cachedStatInfo) {
//Stat the filename
$onDiskStatInfo = $this->_getStatInfo($fileName);
if ($onDiskStatInfo["mtime"] != $cachedStatInfo["mtime"] ||
$onDiskStatInfo["ino"] != $cachedStatInfo["ino"] ||
$onDiskStatInfo["dev"] != $cachedStatInfo["dev"]
) {
$cacheUpToDate = false;
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("Cache out of date: $fileName has stat: " . print_r($onDiskStatInfo, true) . " but cached " . print_r($cachedStatInfo, true));
}
break;
}
}
if ($cacheUpToDate) {
//Yes, so update the list of processed files for completeness, and
//return the cached context
/*foreach ($filesToCheck as $fileName => $cachedStatInfo) {
$this->_processedFiles[$fileName] = $cacheContent["context"];
}*/ //TODO We can't update this any more since it's a map of child contexts, which we don't have
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("Cache is up to date. Returning cached context");
}
return $cacheContent["context"];
}
}
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("Cache item not found or invalid for $xmlFilePath. Reloading ...");
}
//If we get here, we're basically not up to date one way or another
//so load up the context, and cache it.
$context = parent::_parseXmlFile($xmlFilePath);
//Now create the cache structure. NOTE! It's possible that for a
//nested content structure we're going to include files from the
//processed list that had been processed *BEFORE* we loaded this
//particular XML file. However, we can't tell which ones might
//also be dependencies of this child file, and using another
//XmlApplicationContext instance is impractical as it means exposing
//way more of the internals as public variables purely for the purposes
//of the caching system.
$cacheContent = array(
"xmlFiles" => array(),
"context" => $context
);
foreach (array_keys($this->_processedFiles) as $fileName) {
$cacheContent["xmlFiles"][$fileName] = $this->_getStatInfo($fileName);
}
$cacheContentStr = serialize($cacheContent);
apc_store($cacheKey, $cacheContentStr);
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("Cache item added for $xmlFilePath");
}
//Return it
return $context;
} else {
$this->_log->warn("APC caching is DISABLED");
//No caching possible, so revert to the normal method of
//reading from the filesystem all the time
return parent::_parseXmlFile($xmlFilePath);
}
}

/**
* Returns the stat info for a given filename. This will make reading and
* processing for putting into the cache rather more efficient.
*
* @param $fileName string the name of the file we want to get stat information
* for
* @return array result of the stat call for this filename
*/
private function _getStatInfo($fileName)
{
if (!array_key_exists($fileName, $this->_statInfoCache)) {
$this->_statInfoCache[$fileName] = stat($fileName);
}
return $this->_statInfoCache[$fileName];
}
}
165 changes: 165 additions & 0 deletions lib/MooDev/Bounce/Context/ApcCachedXmlContextParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<?php
/**
* @author Steve Storey <[email protected]>
* @copyright Copyright (c) 2012, MOO Print Ltd.
* @license ISC
*/

namespace MooDev\Bounce\Context;

use MooDev\Bounce\Config;
use MooDev\Bounce\Logger\Logger;

/**
* Sub-class of the XmlApplicationContext which caches the parsed configuration
* objects into the APC cache.
*
* It keeps track of both modification times, and the inodes of all the files
* which have been read in to create the configuration. If any files are newer
* or have different inodes, then reload everything and re-cache it.
*
* @author steve
*
*/
class ApcCachedXmlContextProvider extends XmlContextParser
{
/**
* @var Logger the logging instance
*/
private $_log;
/**
* @var string => array map of stat info returned by stat calls keyed on
* the filename
*/
protected $_statInfoCache = array();

/**
* Creates the caching instance, setting up logging for delegating upwards
* @param string $xmlFilePath
* @param ValueTagProvider[] $customNamespaces
* @param array $logFactory Callback array to call to obtain a logger instance. Will be called with a single param (class name.)
*/
public function __construct($xmlFilePath, $customNamespaces = array(), $logFactory = array('\MooDev\Bounce\Logger\NullLogFactory', 'getLog'))
{
$this->_log = call_user_func($logFactory, get_class($this));
parent::__construct($xmlFilePath, $customNamespaces);
}

protected function _parseXmlFile($xmlFilePath)
{
//Do we have caching enabled?
if (function_exists("apc_fetch")) {
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("APC caching is ENABLED");
}
//Do we have a cache entry for this file path?
$cacheKey = "bouncexml~$xmlFilePath";
$cacheContentSerialized = apc_fetch($cacheKey);
//Cache Content structure is:
// array(
// "xmlFiles" => array(
// "/absolute/path/to/parent.xml" => array(
// "dev" => 5, //Device number from the underlying O/S
// "ino" => 1234567, //Inode number of the file on the device
// "mtime" => 124124681274 //Last modified time
// ),
// "/absolute/path/to/child1.xml" => array(
// "dev" => 5,
// "ino" => 345346364,
// "mtime" => 124124681274
// ),
// "/absolute/path/to/child2.xml" => array(
// "dev" => 5,
// "ino" => 345346364,
// "mtime" => 124124681274
// ),
// ),
// "context" => Config_Context
// )
if ($cacheContentSerialized) {
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("Found cached version on $xmlFilePath");
}
$cacheContent = unserialize($cacheContentSerialized);
//Look at all the files from the cache content array to see
//if any of them are different
$filesToCheck = $cacheContent["xmlFiles"];
$cacheUpToDate = true; //Be positive ...
foreach ($filesToCheck as $fileName => $cachedStatInfo) {
//Stat the filename
$onDiskStatInfo = $this->_getStatInfo($fileName);
if ($onDiskStatInfo["mtime"] != $cachedStatInfo["mtime"] ||
$onDiskStatInfo["ino"] != $cachedStatInfo["ino"] ||
$onDiskStatInfo["dev"] != $cachedStatInfo["dev"]
) {
$cacheUpToDate = false;
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("Cache out of date: $fileName has stat: " . print_r($onDiskStatInfo, true) . " but cached " . print_r($cachedStatInfo, true));
}
break;
}
}
if ($cacheUpToDate) {
//Yes, so update the list of processed files for completeness, and
//return the cached context
/*foreach ($filesToCheck as $fileName => $cachedStatInfo) {
$this->_processedFiles[$fileName] = $cacheContent["context"];
}*/ //TODO We can't update this any more since it's a map of child contexts, which we don't have
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("Cache is up to date. Returning cached context");
}
return $cacheContent["context"];
}
}
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("Cache item not found or invalid for $xmlFilePath. Reloading ...");
}
//If we get here, we're basically not up to date one way or another
//so load up the context, and cache it.
$context = parent::_parseXmlFile($xmlFilePath);
//Now create the cache structure. NOTE! It's possible that for a
//nested content structure we're going to include files from the
//processed list that had been processed *BEFORE* we loaded this
//particular XML file. However, we can't tell which ones might
//also be dependencies of this child file, and using another
//XmlApplicationContext instance is impractical as it means exposing
//way more of the internals as public variables purely for the purposes
//of the caching system.
$cacheContent = array(
"xmlFiles" => array(),
"context" => $context
);
foreach (array_keys($this->_processedFiles) as $fileName) {
$cacheContent["xmlFiles"][$fileName] = $this->_getStatInfo($fileName);
}
$cacheContentStr = serialize($cacheContent);
apc_store($cacheKey, $cacheContentStr);
if ($this->_log->isDebugEnabled()) {
$this->_log->debug("Cache item added for $xmlFilePath");
}
//Return it
return $context;
} else {
$this->_log->warn("APC caching is DISABLED");
//No caching possible, so revert to the normal method of
//reading from the filesystem all the time
return parent::_parseXmlFile($xmlFilePath);
}
}

/**
* Returns the stat info for a given filename. This will make reading and
* processing for putting into the cache rather more efficient.
*
* @param $fileName string the name of the file we want to get stat information
* for
* @return array result of the stat call for this filename
*/
private function _getStatInfo($fileName)
{
if (!array_key_exists($fileName, $this->_statInfoCache)) {
$this->_statInfoCache[$fileName] = stat($fileName);
}
return $this->_statInfoCache[$fileName];
}
}
8 changes: 7 additions & 1 deletion lib/MooDev/Bounce/Context/ApplicationContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@
*/
class ApplicationContext
{

public function __construct(IBeanFactory $beanFactory)
{
$this->_beanFactory = $beanFactory;
}

/**
* @var $_beanFactory BeanFactory a bean factory instance
* @var $_beanFactory IBeanFactory a bean factory instance
* to retrieve objects from.
*/
protected $_beanFactory;
Expand Down
2 changes: 1 addition & 1 deletion lib/MooDev/Bounce/Context/BeanFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @property Config\Context contextConfig
*/
class BeanFactory
class BeanFactory implements IBeanFactory
{

/**
Expand Down
27 changes: 27 additions & 0 deletions lib/MooDev/Bounce/Context/IBeanFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
/**
* @author Jonathan Oddy <[email protected]>
* @copyright Copyright (c) 2015, MOO Print Ltd.
* @license ISC
*/
namespace MooDev\Bounce\Context;

/**
* Thing that creates beans.
*/
interface IBeanFactory
{

/**
* Create/retrieve an instance of a named bean.
* @param string $name
* @return mixed
*/
public function createByName($name);

/**
* @return string[] A map of bean names to class names.
*/
public function getAllBeanClasses();

}
Loading

0 comments on commit 9c8332a

Please sign in to comment.