FileCacheReader.php 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the MIT license. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\Common\Annotations;
  20. /**
  21. * File cache reader for annotations.
  22. *
  23. * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  24. * @author Benjamin Eberlei <kontakt@beberlei.de>
  25. */
  26. class FileCacheReader implements Reader
  27. {
  28. /**
  29. * @var Reader
  30. */
  31. private $reader;
  32. /**
  33. * @var string
  34. */
  35. private $dir;
  36. /**
  37. * @var bool
  38. */
  39. private $debug;
  40. /**
  41. * @var array
  42. */
  43. private $loadedAnnotations = array();
  44. /**
  45. * Constructor
  46. *
  47. * @param Reader $reader
  48. * @param string $cacheDir
  49. * @param bool $debug
  50. *
  51. * @throws \InvalidArgumentException
  52. */
  53. public function __construct(Reader $reader, $cacheDir, $debug = false)
  54. {
  55. $this->reader = $reader;
  56. if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true)) {
  57. throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir));
  58. }
  59. if (!is_writable($cacheDir)) {
  60. throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable. Both, the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package.', $cacheDir));
  61. }
  62. $this->dir = rtrim($cacheDir, '\\/');
  63. $this->debug = $debug;
  64. }
  65. /**
  66. * Retrieve annotations for class
  67. *
  68. * @param \ReflectionClass $class
  69. * @return array
  70. */
  71. public function getClassAnnotations(\ReflectionClass $class)
  72. {
  73. $key = $class->getName();
  74. if (isset($this->loadedAnnotations[$key])) {
  75. return $this->loadedAnnotations[$key];
  76. }
  77. $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
  78. if (!file_exists($path)) {
  79. $annot = $this->reader->getClassAnnotations($class);
  80. $this->saveCacheFile($path, $annot);
  81. return $this->loadedAnnotations[$key] = $annot;
  82. }
  83. if ($this->debug
  84. && (false !== $filename = $class->getFilename())
  85. && filemtime($path) < filemtime($filename)) {
  86. @unlink($path);
  87. $annot = $this->reader->getClassAnnotations($class);
  88. $this->saveCacheFile($path, $annot);
  89. return $this->loadedAnnotations[$key] = $annot;
  90. }
  91. return $this->loadedAnnotations[$key] = include $path;
  92. }
  93. /**
  94. * Get annotations for property
  95. *
  96. * @param \ReflectionProperty $property
  97. * @return array
  98. */
  99. public function getPropertyAnnotations(\ReflectionProperty $property)
  100. {
  101. $class = $property->getDeclaringClass();
  102. $key = $class->getName().'$'.$property->getName();
  103. if (isset($this->loadedAnnotations[$key])) {
  104. return $this->loadedAnnotations[$key];
  105. }
  106. $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
  107. if (!file_exists($path)) {
  108. $annot = $this->reader->getPropertyAnnotations($property);
  109. $this->saveCacheFile($path, $annot);
  110. return $this->loadedAnnotations[$key] = $annot;
  111. }
  112. if ($this->debug
  113. && (false !== $filename = $class->getFilename())
  114. && filemtime($path) < filemtime($filename)) {
  115. unlink($path);
  116. $annot = $this->reader->getPropertyAnnotations($property);
  117. $this->saveCacheFile($path, $annot);
  118. return $this->loadedAnnotations[$key] = $annot;
  119. }
  120. return $this->loadedAnnotations[$key] = include $path;
  121. }
  122. /**
  123. * Retrieve annotations for method
  124. *
  125. * @param \ReflectionMethod $method
  126. * @return array
  127. */
  128. public function getMethodAnnotations(\ReflectionMethod $method)
  129. {
  130. $class = $method->getDeclaringClass();
  131. $key = $class->getName().'#'.$method->getName();
  132. if (isset($this->loadedAnnotations[$key])) {
  133. return $this->loadedAnnotations[$key];
  134. }
  135. $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
  136. if (!file_exists($path)) {
  137. $annot = $this->reader->getMethodAnnotations($method);
  138. $this->saveCacheFile($path, $annot);
  139. return $this->loadedAnnotations[$key] = $annot;
  140. }
  141. if ($this->debug
  142. && (false !== $filename = $class->getFilename())
  143. && filemtime($path) < filemtime($filename)) {
  144. unlink($path);
  145. $annot = $this->reader->getMethodAnnotations($method);
  146. $this->saveCacheFile($path, $annot);
  147. return $this->loadedAnnotations[$key] = $annot;
  148. }
  149. return $this->loadedAnnotations[$key] = include $path;
  150. }
  151. /**
  152. * Save cache file
  153. *
  154. * @param string $path
  155. * @param mixed $data
  156. */
  157. private function saveCacheFile($path, $data)
  158. {
  159. file_put_contents($path, '<?php return unserialize('.var_export(serialize($data), true).');');
  160. }
  161. /**
  162. * Gets a class annotation.
  163. *
  164. * @param \ReflectionClass $class The ReflectionClass of the class from which
  165. * the class annotations should be read.
  166. * @param string $annotationName The name of the annotation.
  167. *
  168. * @return mixed The Annotation or NULL, if the requested annotation does not exist.
  169. */
  170. public function getClassAnnotation(\ReflectionClass $class, $annotationName)
  171. {
  172. $annotations = $this->getClassAnnotations($class);
  173. foreach ($annotations as $annotation) {
  174. if ($annotation instanceof $annotationName) {
  175. return $annotation;
  176. }
  177. }
  178. return null;
  179. }
  180. /**
  181. * Gets a method annotation.
  182. *
  183. * @param \ReflectionMethod $method
  184. * @param string $annotationName The name of the annotation.
  185. * @return mixed The Annotation or NULL, if the requested annotation does not exist.
  186. */
  187. public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
  188. {
  189. $annotations = $this->getMethodAnnotations($method);
  190. foreach ($annotations as $annotation) {
  191. if ($annotation instanceof $annotationName) {
  192. return $annotation;
  193. }
  194. }
  195. return null;
  196. }
  197. /**
  198. * Gets a property annotation.
  199. *
  200. * @param \ReflectionProperty $property
  201. * @param string $annotationName The name of the annotation.
  202. * @return mixed The Annotation or NULL, if the requested annotation does not exist.
  203. */
  204. public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
  205. {
  206. $annotations = $this->getPropertyAnnotations($property);
  207. foreach ($annotations as $annotation) {
  208. if ($annotation instanceof $annotationName) {
  209. return $annotation;
  210. }
  211. }
  212. return null;
  213. }
  214. /**
  215. * Clear stores annotations
  216. */
  217. public function clearLoadedAnnotations()
  218. {
  219. $this->loadedAnnotations = array();
  220. }
  221. }