vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PsrCachedReader.php line 50

Open in your IDE?
  1. <?php
  2. namespace Doctrine\Common\Annotations;
  3. use Psr\Cache\CacheItemPoolInterface;
  4. use ReflectionClass;
  5. use ReflectionMethod;
  6. use ReflectionProperty;
  7. use Reflector;
  8. use function array_map;
  9. use function array_merge;
  10. use function assert;
  11. use function filemtime;
  12. use function is_file;
  13. use function max;
  14. use function rawurlencode;
  15. use function time;
  16. /**
  17.  * A cache aware annotation reader.
  18.  */
  19. final class PsrCachedReader implements Reader
  20. {
  21.     /** @var Reader */
  22.     private $delegate;
  23.     /** @var CacheItemPoolInterface */
  24.     private $cache;
  25.     /** @var bool */
  26.     private $debug;
  27.     /** @var array<string, array<object>> */
  28.     private $loadedAnnotations = [];
  29.     /** @var int[] */
  30.     private $loadedFilemtimes = [];
  31.     public function __construct(Reader $readerCacheItemPoolInterface $cachebool $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'getClassAnnotations'$class);
  47.         return $this->loadedAnnotations[$cacheKey] = $annots;
  48.     }
  49.     /**
  50.      * {@inheritDoc}
  51.      */
  52.     public function getClassAnnotation(ReflectionClass $class$annotationName)
  53.     {
  54.         foreach ($this->getClassAnnotations($class) as $annot) {
  55.             if ($annot instanceof $annotationName) {
  56.                 return $annot;
  57.             }
  58.         }
  59.         return null;
  60.     }
  61.     /**
  62.      * {@inheritDoc}
  63.      */
  64.     public function getPropertyAnnotations(ReflectionProperty $property)
  65.     {
  66.         $class    $property->getDeclaringClass();
  67.         $cacheKey $class->getName() . '$' $property->getName();
  68.         if (isset($this->loadedAnnotations[$cacheKey])) {
  69.             return $this->loadedAnnotations[$cacheKey];
  70.         }
  71.         $annots $this->fetchFromCache($cacheKey$class'getPropertyAnnotations'$property);
  72.         return $this->loadedAnnotations[$cacheKey] = $annots;
  73.     }
  74.     /**
  75.      * {@inheritDoc}
  76.      */
  77.     public function getPropertyAnnotation(ReflectionProperty $property$annotationName)
  78.     {
  79.         foreach ($this->getPropertyAnnotations($property) as $annot) {
  80.             if ($annot instanceof $annotationName) {
  81.                 return $annot;
  82.             }
  83.         }
  84.         return null;
  85.     }
  86.     /**
  87.      * {@inheritDoc}
  88.      */
  89.     public function getMethodAnnotations(ReflectionMethod $method)
  90.     {
  91.         $class    $method->getDeclaringClass();
  92.         $cacheKey $class->getName() . '#' $method->getName();
  93.         if (isset($this->loadedAnnotations[$cacheKey])) {
  94.             return $this->loadedAnnotations[$cacheKey];
  95.         }
  96.         $annots $this->fetchFromCache($cacheKey$class'getMethodAnnotations'$method);
  97.         return $this->loadedAnnotations[$cacheKey] = $annots;
  98.     }
  99.     /**
  100.      * {@inheritDoc}
  101.      */
  102.     public function getMethodAnnotation(ReflectionMethod $method$annotationName)
  103.     {
  104.         foreach ($this->getMethodAnnotations($method) as $annot) {
  105.             if ($annot instanceof $annotationName) {
  106.                 return $annot;
  107.             }
  108.         }
  109.         return null;
  110.     }
  111.     public function clearLoadedAnnotations(): void
  112.     {
  113.         $this->loadedAnnotations = [];
  114.         $this->loadedFilemtimes  = [];
  115.     }
  116.     /** @return mixed[] */
  117.     private function fetchFromCache(
  118.         string $cacheKey,
  119.         ReflectionClass $class,
  120.         string $method,
  121.         Reflector $reflector
  122.     ): array {
  123.         $cacheKey rawurlencode($cacheKey);
  124.         $item $this->cache->getItem($cacheKey);
  125.         if (($this->debug && ! $this->refresh($cacheKey$class)) || ! $item->isHit()) {
  126.             $this->cache->save($item->set($this->delegate->{$method}($reflector)));
  127.         }
  128.         return $item->get();
  129.     }
  130.     /**
  131.      * Used in debug mode to check if the cache is fresh.
  132.      *
  133.      * @return bool Returns true if the cache was fresh, or false if the class
  134.      * being read was modified since writing to the cache.
  135.      */
  136.     private function refresh(string $cacheKeyReflectionClass $class): bool
  137.     {
  138.         $lastModification $this->getLastModification($class);
  139.         if ($lastModification === 0) {
  140.             return true;
  141.         }
  142.         $item $this->cache->getItem('[C]' $cacheKey);
  143.         if ($item->isHit() && $item->get() >= $lastModification) {
  144.             return true;
  145.         }
  146.         $this->cache->save($item->set(time()));
  147.         return false;
  148.     }
  149.     /**
  150.      * Returns the time the class was last modified, testing traits and parents
  151.      */
  152.     private function getLastModification(ReflectionClass $class): int
  153.     {
  154.         $filename $class->getFileName();
  155.         if (isset($this->loadedFilemtimes[$filename])) {
  156.             return $this->loadedFilemtimes[$filename];
  157.         }
  158.         $parent $class->getParentClass();
  159.         $lastModification =  max(array_merge(
  160.             [$filename !== false && is_file($filename) ? filemtime($filename) : 0],
  161.             array_map(function (ReflectionClass $reflectionTrait): int {
  162.                 return $this->getTraitLastModificationTime($reflectionTrait);
  163.             }, $class->getTraits()),
  164.             array_map(function (ReflectionClass $class): int {
  165.                 return $this->getLastModification($class);
  166.             }, $class->getInterfaces()),
  167.             $parent ? [$this->getLastModification($parent)] : []
  168.         ));
  169.         assert($lastModification !== false);
  170.         return $this->loadedFilemtimes[$filename] = $lastModification;
  171.     }
  172.     private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
  173.     {
  174.         $fileName $reflectionTrait->getFileName();
  175.         if (isset($this->loadedFilemtimes[$fileName])) {
  176.             return $this->loadedFilemtimes[$fileName];
  177.         }
  178.         $lastModificationTime max(array_merge(
  179.             [$fileName !== false && is_file($fileName) ? filemtime($fileName) : 0],
  180.             array_map(function (ReflectionClass $reflectionTrait): int {
  181.                 return $this->getTraitLastModificationTime($reflectionTrait);
  182.             }, $reflectionTrait->getTraits())
  183.         ));
  184.         assert($lastModificationTime !== false);
  185.         return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
  186.     }
  187. }