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

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