abstract class AbstractQueuedCachedClassLoader in X Autoload 7.5
Bass class for cached class loader decorators where cache entries cannot be written one by one, but have to be written all at once instead.
Saving the cache immediately on every cache miss would be too expensive. On the other hand, saving only at the end of the request might fail if the request does not end properly, or if some classes are still loaded after the end-of-process callback.
The solution is an exponentially growing queue. Cache writing happens not on every cache miss, but only on the 1st, 3rd, 7th, 15th, 31st, 63rd etc.
This will result in a "hot" cache after a limited number of requests, and with a limited number of cache write operations.
Hierarchy
- class \Drupal\xautoload\ClassLoader\AbstractClassLoader implements ClassLoaderInterface
- class \Drupal\xautoload\ClassLoader\AbstractClassLoaderDecorator
- class \Drupal\xautoload\ClassLoader\AbstractQueuedCachedClassLoader implements CacheManagerObserverInterface
- class \Drupal\xautoload\ClassLoader\AbstractClassLoaderDecorator
Expanded class hierarchy of AbstractQueuedCachedClassLoader
File
- src/
ClassLoader/ AbstractQueuedCachedClassLoader.php, line 25
Namespace
Drupal\xautoload\ClassLoaderView source
abstract class AbstractQueuedCachedClassLoader extends AbstractClassLoaderDecorator implements CacheManagerObserverInterface {
/**
* @var int
*/
private $nMax = 1;
/**
* @var int
*/
private $n = 0;
/**
* @var string[]
*/
private $toBeDeleted = array();
/**
* @var string[]
*/
private $toBeAdded = array();
/**
* @var string[]
*/
private $classFiles;
/**
* This method has side effects, so it is not the constructor.
*
* @param ClassFinderInterface $finder
* @param CacheManager $cacheManager
*
* @return self
*
* @throws \Exception
*/
static function create($finder, $cacheManager) {
/** @var self $loader */
$loader = new static($finder);
$cacheManager
->observeCachePrefix($loader);
return $loader;
}
/**
* {@inheritdoc}
*/
function loadClass($class) {
// Look if the cache has anything for this class.
if (isset($this->classFiles[$class])) {
$file = $this->classFiles[$class];
// The is_file() check may cost around 0.0045 ms per class file, but this
// depends on your system of course.
if (is_file($file)) {
require $file;
return;
}
$this->toBeDeleted[$class] = $file;
unset($this->classFiles[$class]);
++$this->n;
}
// Resolve cache miss.
$api = new LoadClassGetFileInjectedApi($class);
if ($this->finder
->apiFindFile($api, $class)) {
// Queue the result for the cache.
$this->toBeAdded[$class] = $this->classFiles[$class] = $api
->getFile();
++$this->n;
}
// Save the cache if enough has been queued up.
if ($this->n >= $this->nMax) {
$this->classFiles = $this
->updateClassFiles($this->toBeAdded, $this->toBeDeleted);
$this->toBeDeleted = array();
$this->toBeAdded = array();
$this->nMax *= 2;
$this->n = 0;
}
}
/**
* Set the new cache prefix after a flush cache.
*
* @param string $prefix
* A prefix for the storage key in APC.
*/
function setCachePrefix($prefix) {
$this->classFiles = $this
->loadClassFiles($prefix);
}
/**
* @param string $prefix
*
* @return string[]
*/
protected abstract function loadClassFiles($prefix);
/**
* @param string[] $toBeAdded
* @param string[] $toBeRemoved
*
* @return string[]
*/
protected abstract function updateClassFiles($toBeAdded, $toBeRemoved);
}