123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- <?php
- namespace Doctrine\Common\Annotations;
- use Psr\Cache\CacheItemPoolInterface;
- use ReflectionClass;
- use ReflectionMethod;
- use ReflectionProperty;
- use Reflector;
- use function array_map;
- use function array_merge;
- use function assert;
- use function filemtime;
- use function max;
- use function rawurlencode;
- use function time;
- /**
- * A cache aware annotation reader.
- */
- final class PsrCachedReader implements Reader
- {
- /** @var Reader */
- private $delegate;
- /** @var CacheItemPoolInterface */
- private $cache;
- /** @var bool */
- private $debug;
- /** @var array<string, array<object>> */
- private $loadedAnnotations = [];
- /** @var int[] */
- private $loadedFilemtimes = [];
- public function __construct(Reader $reader, CacheItemPoolInterface $cache, bool $debug = false)
- {
- $this->delegate = $reader;
- $this->cache = $cache;
- $this->debug = (bool) $debug;
- }
- /**
- * {@inheritDoc}
- */
- public function getClassAnnotations(ReflectionClass $class)
- {
- $cacheKey = $class->getName();
- if (isset($this->loadedAnnotations[$cacheKey])) {
- return $this->loadedAnnotations[$cacheKey];
- }
- $annots = $this->fetchFromCache($cacheKey, $class, 'getClassAnnotations', $class);
- return $this->loadedAnnotations[$cacheKey] = $annots;
- }
- /**
- * {@inheritDoc}
- */
- public function getClassAnnotation(ReflectionClass $class, $annotationName)
- {
- foreach ($this->getClassAnnotations($class) as $annot) {
- if ($annot instanceof $annotationName) {
- return $annot;
- }
- }
- return null;
- }
- /**
- * {@inheritDoc}
- */
- public function getPropertyAnnotations(ReflectionProperty $property)
- {
- $class = $property->getDeclaringClass();
- $cacheKey = $class->getName() . '$' . $property->getName();
- if (isset($this->loadedAnnotations[$cacheKey])) {
- return $this->loadedAnnotations[$cacheKey];
- }
- $annots = $this->fetchFromCache($cacheKey, $class, 'getPropertyAnnotations', $property);
- return $this->loadedAnnotations[$cacheKey] = $annots;
- }
- /**
- * {@inheritDoc}
- */
- public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
- {
- foreach ($this->getPropertyAnnotations($property) as $annot) {
- if ($annot instanceof $annotationName) {
- return $annot;
- }
- }
- return null;
- }
- /**
- * {@inheritDoc}
- */
- public function getMethodAnnotations(ReflectionMethod $method)
- {
- $class = $method->getDeclaringClass();
- $cacheKey = $class->getName() . '#' . $method->getName();
- if (isset($this->loadedAnnotations[$cacheKey])) {
- return $this->loadedAnnotations[$cacheKey];
- }
- $annots = $this->fetchFromCache($cacheKey, $class, 'getMethodAnnotations', $method);
- return $this->loadedAnnotations[$cacheKey] = $annots;
- }
- /**
- * {@inheritDoc}
- */
- public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
- {
- foreach ($this->getMethodAnnotations($method) as $annot) {
- if ($annot instanceof $annotationName) {
- return $annot;
- }
- }
- return null;
- }
- public function clearLoadedAnnotations(): void
- {
- $this->loadedAnnotations = [];
- $this->loadedFilemtimes = [];
- }
- /** @return mixed[] */
- private function fetchFromCache(
- string $cacheKey,
- ReflectionClass $class,
- string $method,
- Reflector $reflector
- ): array {
- $cacheKey = rawurlencode($cacheKey);
- $item = $this->cache->getItem($cacheKey);
- if (($this->debug && ! $this->refresh($cacheKey, $class)) || ! $item->isHit()) {
- $this->cache->save($item->set($this->delegate->{$method}($reflector)));
- }
- return $item->get();
- }
- /**
- * Used in debug mode to check if the cache is fresh.
- *
- * @return bool Returns true if the cache was fresh, or false if the class
- * being read was modified since writing to the cache.
- */
- private function refresh(string $cacheKey, ReflectionClass $class): bool
- {
- $lastModification = $this->getLastModification($class);
- if ($lastModification === 0) {
- return true;
- }
- $item = $this->cache->getItem('[C]' . $cacheKey);
- if ($item->isHit() && $item->get() >= $lastModification) {
- return true;
- }
- $this->cache->save($item->set(time()));
- return false;
- }
- /**
- * Returns the time the class was last modified, testing traits and parents
- */
- private function getLastModification(ReflectionClass $class): int
- {
- $filename = $class->getFileName();
- if (isset($this->loadedFilemtimes[$filename])) {
- return $this->loadedFilemtimes[$filename];
- }
- $parent = $class->getParentClass();
- $lastModification = max(array_merge(
- [$filename ? filemtime($filename) : 0],
- array_map(function (ReflectionClass $reflectionTrait): int {
- return $this->getTraitLastModificationTime($reflectionTrait);
- }, $class->getTraits()),
- array_map(function (ReflectionClass $class): int {
- return $this->getLastModification($class);
- }, $class->getInterfaces()),
- $parent ? [$this->getLastModification($parent)] : []
- ));
- assert($lastModification !== false);
- return $this->loadedFilemtimes[$filename] = $lastModification;
- }
- private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
- {
- $fileName = $reflectionTrait->getFileName();
- if (isset($this->loadedFilemtimes[$fileName])) {
- return $this->loadedFilemtimes[$fileName];
- }
- $lastModificationTime = max(array_merge(
- [$fileName ? filemtime($fileName) : 0],
- array_map(function (ReflectionClass $reflectionTrait): int {
- return $this->getTraitLastModificationTime($reflectionTrait);
- }, $reflectionTrait->getTraits())
- ));
- assert($lastModificationTime !== false);
- return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
- }
- }
|