vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php line 119

Open in your IDE?
  1. <?php
  2. namespace Doctrine\Common\Annotations;
  3. use Doctrine\Common\Cache\Cache;
  4. use ReflectionClass;
  5. use ReflectionMethod;
  6. use ReflectionProperty;
  7. use function array_map;
  8. use function array_merge;
  9. use function assert;
  10. use function filemtime;
  11. use function max;
  12. use function time;
  13. /**
  14.  * A cache aware annotation reader.
  15.  */
  16. final class CachedReader implements Reader
  17. {
  18.     /** @var Reader */
  19.     private $delegate;
  20.     /** @var Cache */
  21.     private $cache;
  22.     /** @var bool */
  23.     private $debug;
  24.     /** @var array<string, array<object>> */
  25.     private $loadedAnnotations = [];
  26.     /** @var int[] */
  27.     private $loadedFilemtimes = [];
  28.     /**
  29.      * @param bool $debug
  30.      */
  31.     public function __construct(Reader $readerCache $cache$debug false)
  32.     {
  33.         $this->delegate $reader;
  34.         $this->cache    $cache;
  35.         $this->debug    = (bool) $debug;
  36.     }
  37.     /**
  38.      * {@inheritDoc}
  39.      */
  40.     public function getClassAnnotations(ReflectionClass $class)
  41.     {
  42.         $cacheKey $class->getName();
  43.         if (isset($this->loadedAnnotations[$cacheKey])) {
  44.             return $this->loadedAnnotations[$cacheKey];
  45.         }
  46.         $annots $this->fetchFromCache($cacheKey$class);
  47.         if ($annots === false) {
  48.             $annots $this->delegate->getClassAnnotations($class);
  49.             $this->saveToCache($cacheKey$annots);
  50.         }
  51.         return $this->loadedAnnotations[$cacheKey] = $annots;
  52.     }
  53.     /**
  54.      * {@inheritDoc}
  55.      */
  56.     public function getClassAnnotation(ReflectionClass $class$annotationName)
  57.     {
  58.         foreach ($this->getClassAnnotations($class) as $annot) {
  59.             if ($annot instanceof $annotationName) {
  60.                 return $annot;
  61.             }
  62.         }
  63.         return null;
  64.     }
  65.     /**
  66.      * {@inheritDoc}
  67.      */
  68.     public function getPropertyAnnotations(ReflectionProperty $property)
  69.     {
  70.         $class    $property->getDeclaringClass();
  71.         $cacheKey $class->getName() . '$' $property->getName();
  72.         if (isset($this->loadedAnnotations[$cacheKey])) {
  73.             return $this->loadedAnnotations[$cacheKey];
  74.         }
  75.         $annots $this->fetchFromCache($cacheKey$class);
  76.         if ($annots === false) {
  77.             $annots $this->delegate->getPropertyAnnotations($property);
  78.             $this->saveToCache($cacheKey$annots);
  79.         }
  80.         return $this->loadedAnnotations[$cacheKey] = $annots;
  81.     }
  82.     /**
  83.      * {@inheritDoc}
  84.      */
  85.     public function getPropertyAnnotation(ReflectionProperty $property$annotationName)
  86.     {
  87.         foreach ($this->getPropertyAnnotations($property) as $annot) {
  88.             if ($annot instanceof $annotationName) {
  89.                 return $annot;
  90.             }
  91.         }
  92.         return null;
  93.     }
  94.     /**
  95.      * {@inheritDoc}
  96.      */
  97.     public function getMethodAnnotations(ReflectionMethod $method)
  98.     {
  99.         $class    $method->getDeclaringClass();
  100.         $cacheKey $class->getName() . '#' $method->getName();
  101.         if (isset($this->loadedAnnotations[$cacheKey])) {
  102.             return $this->loadedAnnotations[$cacheKey];
  103.         }
  104.         $annots $this->fetchFromCache($cacheKey$class);
  105.         if ($annots === false) {
  106.             $annots $this->delegate->getMethodAnnotations($method);
  107.             $this->saveToCache($cacheKey$annots);
  108.         }
  109.         return $this->loadedAnnotations[$cacheKey] = $annots;
  110.     }
  111.     /**
  112.      * {@inheritDoc}
  113.      */
  114.     public function getMethodAnnotation(ReflectionMethod $method$annotationName)
  115.     {
  116.         foreach ($this->getMethodAnnotations($method) as $annot) {
  117.             if ($annot instanceof $annotationName) {
  118.                 return $annot;
  119.             }
  120.         }
  121.         return null;
  122.     }
  123.     /**
  124.      * Clears loaded annotations.
  125.      *
  126.      * @return void
  127.      */
  128.     public function clearLoadedAnnotations()
  129.     {
  130.         $this->loadedAnnotations = [];
  131.         $this->loadedFilemtimes  = [];
  132.     }
  133.     /**
  134.      * Fetches a value from the cache.
  135.      *
  136.      * @param string $cacheKey The cache key.
  137.      *
  138.      * @return mixed The cached value or false when the value is not in cache.
  139.      */
  140.     private function fetchFromCache($cacheKeyReflectionClass $class)
  141.     {
  142.         $data $this->cache->fetch($cacheKey);
  143.         if ($data !== false) {
  144.             if (! $this->debug || $this->isCacheFresh($cacheKey$class)) {
  145.                 return $data;
  146.             }
  147.         }
  148.         return false;
  149.     }
  150.     /**
  151.      * Saves a value to the cache.
  152.      *
  153.      * @param string $cacheKey The cache key.
  154.      * @param mixed  $value    The value.
  155.      *
  156.      * @return void
  157.      */
  158.     private function saveToCache($cacheKey$value)
  159.     {
  160.         $this->cache->save($cacheKey$value);
  161.         if (! $this->debug) {
  162.             return;
  163.         }
  164.         $this->cache->save('[C]' $cacheKeytime());
  165.     }
  166.     /**
  167.      * Checks if the cache is fresh.
  168.      *
  169.      * @param string $cacheKey
  170.      *
  171.      * @return bool
  172.      */
  173.     private function isCacheFresh($cacheKeyReflectionClass $class)
  174.     {
  175.         $lastModification $this->getLastModification($class);
  176.         if ($lastModification === 0) {
  177.             return true;
  178.         }
  179.         return $this->cache->fetch('[C]' $cacheKey) >= $lastModification;
  180.     }
  181.     /**
  182.      * Returns the time the class was last modified, testing traits and parents
  183.      */
  184.     private function getLastModification(ReflectionClass $class): int
  185.     {
  186.         $filename $class->getFileName();
  187.         if (isset($this->loadedFilemtimes[$filename])) {
  188.             return $this->loadedFilemtimes[$filename];
  189.         }
  190.         $parent $class->getParentClass();
  191.         $lastModification =  max(array_merge(
  192.             [$filename filemtime($filename) : 0],
  193.             array_map(function (ReflectionClass $reflectionTrait): int {
  194.                 return $this->getTraitLastModificationTime($reflectionTrait);
  195.             }, $class->getTraits()),
  196.             array_map(function (ReflectionClass $class): int {
  197.                 return $this->getLastModification($class);
  198.             }, $class->getInterfaces()),
  199.             $parent ? [$this->getLastModification($parent)] : []
  200.         ));
  201.         assert($lastModification !== false);
  202.         return $this->loadedFilemtimes[$filename] = $lastModification;
  203.     }
  204.     private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
  205.     {
  206.         $fileName $reflectionTrait->getFileName();
  207.         if (isset($this->loadedFilemtimes[$fileName])) {
  208.             return $this->loadedFilemtimes[$fileName];
  209.         }
  210.         $lastModificationTime max(array_merge(
  211.             [$fileName filemtime($fileName) : 0],
  212.             array_map(function (ReflectionClass $reflectionTrait): int {
  213.                 return $this->getTraitLastModificationTime($reflectionTrait);
  214.             }, $reflectionTrait->getTraits())
  215.         ));
  216.         assert($lastModificationTime !== false);
  217.         return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
  218.     }
  219. }