123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- <?php
- namespace Doctrine\Common\Annotations;
- use Doctrine\Common\Cache\Cache;
- use ReflectionClass;
- use ReflectionMethod;
- use ReflectionProperty;
- use function array_map;
- use function array_merge;
- use function assert;
- use function filemtime;
- use function max;
- use function time;
- /**
- * A cache aware annotation reader.
- */
- final class CachedReader implements Reader
- {
- /** @var Reader */
- private $delegate;
- /** @var Cache */
- private $cache;
- /** @var bool */
- private $debug;
- /** @var array<string, array<object>> */
- private $loadedAnnotations = [];
- /** @var int[] */
- private $loadedFilemtimes = [];
- /**
- * @param bool $debug
- */
- public function __construct(Reader $reader, Cache $cache, $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);
- if ($annots === false) {
- $annots = $this->delegate->getClassAnnotations($class);
- $this->saveToCache($cacheKey, $annots);
- }
- 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);
- if ($annots === false) {
- $annots = $this->delegate->getPropertyAnnotations($property);
- $this->saveToCache($cacheKey, $annots);
- }
- 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);
- if ($annots === false) {
- $annots = $this->delegate->getMethodAnnotations($method);
- $this->saveToCache($cacheKey, $annots);
- }
- 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;
- }
- /**
- * Clears loaded annotations.
- *
- * @return void
- */
- public function clearLoadedAnnotations()
- {
- $this->loadedAnnotations = [];
- $this->loadedFilemtimes = [];
- }
- /**
- * Fetches a value from the cache.
- *
- * @param string $cacheKey The cache key.
- *
- * @return mixed The cached value or false when the value is not in cache.
- */
- private function fetchFromCache($cacheKey, ReflectionClass $class)
- {
- $data = $this->cache->fetch($cacheKey);
- if ($data !== false) {
- if (! $this->debug || $this->isCacheFresh($cacheKey, $class)) {
- return $data;
- }
- }
- return false;
- }
- /**
- * Saves a value to the cache.
- *
- * @param string $cacheKey The cache key.
- * @param mixed $value The value.
- *
- * @return void
- */
- private function saveToCache($cacheKey, $value)
- {
- $this->cache->save($cacheKey, $value);
- if (! $this->debug) {
- return;
- }
- $this->cache->save('[C]' . $cacheKey, time());
- }
- /**
- * Checks if the cache is fresh.
- *
- * @param string $cacheKey
- *
- * @return bool
- */
- private function isCacheFresh($cacheKey, ReflectionClass $class)
- {
- $lastModification = $this->getLastModification($class);
- if ($lastModification === 0) {
- return true;
- }
- return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification;
- }
- /**
- * 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;
- }
- }
|