You are here

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

Expanded class hierarchy of AbstractQueuedCachedClassLoader

File

src/ClassLoader/AbstractQueuedCachedClassLoader.php, line 25

Namespace

Drupal\xautoload\ClassLoader
View 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);

}

Members

Namesort descending Modifiers Type Description Overrides
AbstractClassLoader::register function Registers this instance as an autoloader. Overrides ClassLoaderInterface::register
AbstractClassLoader::unregister function Unregister from the spl autoload stack. Overrides ClassLoaderInterface::unregister
AbstractClassLoaderDecorator::$finder protected property
AbstractClassLoaderDecorator::setFinder function Replace the finder with another one.
AbstractClassLoaderDecorator::__construct protected function
AbstractQueuedCachedClassLoader::$classFiles private property
AbstractQueuedCachedClassLoader::$n private property
AbstractQueuedCachedClassLoader::$nMax private property
AbstractQueuedCachedClassLoader::$toBeAdded private property
AbstractQueuedCachedClassLoader::$toBeDeleted private property
AbstractQueuedCachedClassLoader::create static function This method has side effects, so it is not the constructor.
AbstractQueuedCachedClassLoader::loadClass function Callback for class loading. This will include ("require") the file found. Overrides AbstractClassLoaderDecorator::loadClass
AbstractQueuedCachedClassLoader::loadClassFiles abstract protected function 2
AbstractQueuedCachedClassLoader::setCachePrefix function Set the new cache prefix after a flush cache. Overrides CacheManagerObserverInterface::setCachePrefix
AbstractQueuedCachedClassLoader::updateClassFiles abstract protected function 2