Chris@0: . Chris@0: */ Chris@0: Chris@0: namespace Doctrine\Common\Annotations; Chris@0: Chris@0: use Doctrine\Common\Cache\Cache; Chris@0: Chris@0: /** Chris@0: * A cache aware annotation reader. Chris@0: * Chris@0: * @author Johannes M. Schmitt Chris@0: * @author Benjamin Eberlei Chris@0: */ Chris@0: final class CachedReader implements Reader Chris@0: { Chris@0: /** Chris@0: * @var string Chris@0: */ Chris@0: private static $CACHE_SALT = '@[Annot]'; Chris@0: Chris@0: /** Chris@0: * @var Reader Chris@0: */ Chris@0: private $delegate; Chris@0: Chris@0: /** Chris@0: * @var Cache Chris@0: */ Chris@0: private $cache; Chris@0: Chris@0: /** Chris@0: * @var boolean Chris@0: */ Chris@0: private $debug; Chris@0: Chris@0: /** Chris@0: * @var array Chris@0: */ Chris@0: private $loadedAnnotations = array(); Chris@0: Chris@0: /** Chris@0: * Constructor. Chris@0: * Chris@0: * @param Reader $reader Chris@0: * @param Cache $cache Chris@0: * @param bool $debug Chris@0: */ Chris@0: public function __construct(Reader $reader, Cache $cache, $debug = false) Chris@0: { Chris@0: $this->delegate = $reader; Chris@0: $this->cache = $cache; Chris@0: $this->debug = (boolean) $debug; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritDoc} Chris@0: */ Chris@0: public function getClassAnnotations(\ReflectionClass $class) Chris@0: { Chris@0: $cacheKey = $class->getName(); Chris@0: Chris@0: if (isset($this->loadedAnnotations[$cacheKey])) { Chris@0: return $this->loadedAnnotations[$cacheKey]; Chris@0: } Chris@0: Chris@0: if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { Chris@0: $annots = $this->delegate->getClassAnnotations($class); Chris@0: $this->saveToCache($cacheKey, $annots); Chris@0: } Chris@0: Chris@0: return $this->loadedAnnotations[$cacheKey] = $annots; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritDoc} Chris@0: */ Chris@0: public function getClassAnnotation(\ReflectionClass $class, $annotationName) Chris@0: { Chris@0: foreach ($this->getClassAnnotations($class) as $annot) { Chris@0: if ($annot instanceof $annotationName) { Chris@0: return $annot; Chris@0: } Chris@0: } Chris@0: Chris@0: return null; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritDoc} Chris@0: */ Chris@0: public function getPropertyAnnotations(\ReflectionProperty $property) Chris@0: { Chris@0: $class = $property->getDeclaringClass(); Chris@0: $cacheKey = $class->getName().'$'.$property->getName(); Chris@0: Chris@0: if (isset($this->loadedAnnotations[$cacheKey])) { Chris@0: return $this->loadedAnnotations[$cacheKey]; Chris@0: } Chris@0: Chris@0: if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { Chris@0: $annots = $this->delegate->getPropertyAnnotations($property); Chris@0: $this->saveToCache($cacheKey, $annots); Chris@0: } Chris@0: Chris@0: return $this->loadedAnnotations[$cacheKey] = $annots; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritDoc} Chris@0: */ Chris@0: public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) Chris@0: { Chris@0: foreach ($this->getPropertyAnnotations($property) as $annot) { Chris@0: if ($annot instanceof $annotationName) { Chris@0: return $annot; Chris@0: } Chris@0: } Chris@0: Chris@0: return null; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritDoc} Chris@0: */ Chris@0: public function getMethodAnnotations(\ReflectionMethod $method) Chris@0: { Chris@0: $class = $method->getDeclaringClass(); Chris@0: $cacheKey = $class->getName().'#'.$method->getName(); Chris@0: Chris@0: if (isset($this->loadedAnnotations[$cacheKey])) { Chris@0: return $this->loadedAnnotations[$cacheKey]; Chris@0: } Chris@0: Chris@0: if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { Chris@0: $annots = $this->delegate->getMethodAnnotations($method); Chris@0: $this->saveToCache($cacheKey, $annots); Chris@0: } Chris@0: Chris@0: return $this->loadedAnnotations[$cacheKey] = $annots; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritDoc} Chris@0: */ Chris@0: public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) Chris@0: { Chris@0: foreach ($this->getMethodAnnotations($method) as $annot) { Chris@0: if ($annot instanceof $annotationName) { Chris@0: return $annot; Chris@0: } Chris@0: } Chris@0: Chris@0: return null; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Clears loaded annotations. Chris@0: * Chris@0: * @return void Chris@0: */ Chris@0: public function clearLoadedAnnotations() Chris@0: { Chris@0: $this->loadedAnnotations = array(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Fetches a value from the cache. Chris@0: * Chris@0: * @param string $rawCacheKey The cache key. Chris@0: * @param \ReflectionClass $class The related class. Chris@0: * Chris@0: * @return mixed The cached value or false when the value is not in cache. Chris@0: */ Chris@0: private function fetchFromCache($rawCacheKey, \ReflectionClass $class) Chris@0: { Chris@0: $cacheKey = $rawCacheKey . self::$CACHE_SALT; Chris@0: if (($data = $this->cache->fetch($cacheKey)) !== false) { Chris@0: if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) { Chris@0: return $data; Chris@0: } Chris@0: } Chris@0: Chris@0: return false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Saves a value to the cache. Chris@0: * Chris@0: * @param string $rawCacheKey The cache key. Chris@0: * @param mixed $value The value. Chris@0: * Chris@0: * @return void Chris@0: */ Chris@0: private function saveToCache($rawCacheKey, $value) Chris@0: { Chris@0: $cacheKey = $rawCacheKey . self::$CACHE_SALT; Chris@0: $this->cache->save($cacheKey, $value); Chris@0: if ($this->debug) { Chris@0: $this->cache->save('[C]'.$cacheKey, time()); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks if the cache is fresh. Chris@0: * Chris@0: * @param string $cacheKey Chris@0: * @param \ReflectionClass $class Chris@0: * Chris@0: * @return boolean Chris@0: */ Chris@0: private function isCacheFresh($cacheKey, \ReflectionClass $class) Chris@0: { Chris@0: if (false === $filename = $class->getFilename()) { Chris@0: return true; Chris@0: } Chris@0: Chris@0: return $this->cache->fetch('[C]'.$cacheKey) >= filemtime($filename); Chris@0: } Chris@0: }