|
0
|
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 LGPL. For more information, see |
|
|
17 |
* <http://www.doctrine-project.org>. |
|
|
18 |
*/ |
|
|
19 |
|
|
|
20 |
namespace Doctrine\Common\Annotations; |
|
|
21 |
|
|
|
22 |
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation; |
|
|
23 |
use Closure; |
|
|
24 |
use ReflectionClass; |
|
|
25 |
use ReflectionMethod; |
|
|
26 |
use ReflectionProperty; |
|
|
27 |
|
|
|
28 |
require_once __DIR__ . '/Annotation/IgnoreAnnotation.php'; |
|
|
29 |
|
|
|
30 |
/** |
|
|
31 |
* A reader for docblock annotations. |
|
|
32 |
* |
|
|
33 |
* @author Benjamin Eberlei <kontakt@beberlei.de> |
|
|
34 |
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
|
|
35 |
* @author Jonathan Wage <jonwage@gmail.com> |
|
|
36 |
* @author Roman Borschel <roman@code-factory.org> |
|
|
37 |
* @author Johannes M. Schmitt <schmittjoh@gmail.com> |
|
|
38 |
*/ |
|
|
39 |
final class AnnotationReader implements Reader |
|
|
40 |
{ |
|
|
41 |
/** |
|
|
42 |
* Global map for imports. |
|
|
43 |
* |
|
|
44 |
* @var array |
|
|
45 |
*/ |
|
|
46 |
private static $globalImports = array( |
|
|
47 |
'ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation', |
|
|
48 |
); |
|
|
49 |
|
|
|
50 |
/** |
|
|
51 |
* A list with annotations that are not causing exceptions when not resolved to an annotation class. |
|
|
52 |
* |
|
|
53 |
* The names are case sensitive. |
|
|
54 |
* |
|
|
55 |
* @var array |
|
|
56 |
*/ |
|
|
57 |
private static $globalIgnoredNames = array( |
|
|
58 |
'access'=> true, 'author'=> true, 'copyright'=> true, 'deprecated'=> true, |
|
|
59 |
'example'=> true, 'ignore'=> true, 'internal'=> true, 'link'=> true, 'see'=> true, |
|
|
60 |
'since'=> true, 'tutorial'=> true, 'version'=> true, 'package'=> true, |
|
|
61 |
'subpackage'=> true, 'name'=> true, 'global'=> true, 'param'=> true, |
|
|
62 |
'return'=> true, 'staticvar'=> true, 'category'=> true, 'staticVar'=> true, |
|
|
63 |
'static'=> true, 'var'=> true, 'throws'=> true, 'inheritdoc'=> true, |
|
|
64 |
'inheritDoc'=> true, 'license'=> true, 'todo'=> true, 'deprecated'=> true, |
|
|
65 |
'deprec'=> true, 'author'=> true, 'property' => true, 'method' => true, |
|
|
66 |
'abstract'=> true, 'exception'=> true, 'magic' => true, 'api' => true, |
|
|
67 |
'final'=> true, 'filesource'=> true, 'throw' => true, 'uses' => true, |
|
|
68 |
'usedby'=> true, 'private' => true, 'Annotation' => true, |
|
|
69 |
); |
|
|
70 |
|
|
|
71 |
/** |
|
|
72 |
* Add a new annotation to the globally ignored annotation names with regard to exception handling. |
|
|
73 |
* |
|
|
74 |
* @param string $name |
|
|
75 |
*/ |
|
|
76 |
static public function addGlobalIgnoredName($name) |
|
|
77 |
{ |
|
|
78 |
self::$globalIgnoredNames[$name] = true; |
|
|
79 |
} |
|
|
80 |
|
|
|
81 |
/** |
|
|
82 |
* Annotations Parser |
|
|
83 |
* |
|
|
84 |
* @var Doctrine\Common\Annotations\DocParser |
|
|
85 |
*/ |
|
|
86 |
private $parser; |
|
|
87 |
|
|
|
88 |
/** |
|
|
89 |
* Annotations Parser used to collect parsing metadata |
|
|
90 |
* |
|
|
91 |
* @var Doctrine\Common\Annotations\DocParser |
|
|
92 |
*/ |
|
|
93 |
private $preParser; |
|
|
94 |
|
|
|
95 |
/** |
|
|
96 |
* PHP Parser used to collect imports. |
|
|
97 |
* |
|
|
98 |
* @var Doctrine\Common\Annotations\PhpParser |
|
|
99 |
*/ |
|
|
100 |
private $phpParser; |
|
|
101 |
|
|
|
102 |
/** |
|
|
103 |
* In-memory cache mechanism to store imported annotations per class. |
|
|
104 |
* |
|
|
105 |
* @var array |
|
|
106 |
*/ |
|
|
107 |
private $imports = array(); |
|
|
108 |
|
|
|
109 |
/** |
|
|
110 |
* In-memory cache mechanism to store ignored annotations per class. |
|
|
111 |
* |
|
|
112 |
* @var array |
|
|
113 |
*/ |
|
|
114 |
private $ignoredAnnotationNames = array(); |
|
|
115 |
|
|
|
116 |
/** |
|
|
117 |
* @var string |
|
|
118 |
*/ |
|
|
119 |
private $defaultAnnotationNamespace = false; |
|
|
120 |
|
|
|
121 |
/** |
|
|
122 |
* @var bool |
|
|
123 |
*/ |
|
|
124 |
private $enablePhpImports = true; |
|
|
125 |
|
|
|
126 |
/** |
|
|
127 |
* Constructor. Initializes a new AnnotationReader that uses the given Cache provider. |
|
|
128 |
* |
|
|
129 |
* @param DocParser $parser The parser to use. If none is provided, the default parser is used. |
|
|
130 |
*/ |
|
|
131 |
public function __construct() |
|
|
132 |
{ |
|
|
133 |
AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php'); |
|
|
134 |
|
|
|
135 |
$this->parser = new DocParser; |
|
|
136 |
|
|
|
137 |
$this->preParser = new DocParser; |
|
|
138 |
$this->preParser->setImports(self::$globalImports); |
|
|
139 |
$this->preParser->setIgnoreNotImportedAnnotations(true); |
|
|
140 |
|
|
|
141 |
$this->phpParser = new PhpParser; |
|
|
142 |
} |
|
|
143 |
|
|
|
144 |
/** |
|
|
145 |
* Ignore not imported annotations and not throw an exception. |
|
|
146 |
* |
|
|
147 |
* @param bool $bool |
|
|
148 |
*/ |
|
|
149 |
public function setIgnoreNotImportedAnnotations($bool) |
|
|
150 |
{ |
|
|
151 |
$this->parser->setIgnoreNotImportedAnnotations($bool); |
|
|
152 |
} |
|
|
153 |
|
|
|
154 |
/** |
|
|
155 |
* Detect imports by parsing the use statements of affected files. |
|
|
156 |
* |
|
|
157 |
* @deprecated Will be removed in 3.0, imports will always be enabled. |
|
|
158 |
* @param bool $flag |
|
|
159 |
*/ |
|
|
160 |
public function setEnableParsePhpImports($flag) |
|
|
161 |
{ |
|
|
162 |
$this->enablePhpImports = $flag; |
|
|
163 |
} |
|
|
164 |
|
|
|
165 |
/** |
|
|
166 |
* @deprecated Will be removed in 3.0, imports will always be enabled. |
|
|
167 |
* @return bool |
|
|
168 |
*/ |
|
|
169 |
public function isParsePhpImportsEnabled() |
|
|
170 |
{ |
|
|
171 |
return $this->enablePhpImports; |
|
|
172 |
} |
|
|
173 |
|
|
|
174 |
/** |
|
|
175 |
* Sets the default namespace that the AnnotationReader should assume for annotations |
|
|
176 |
* with not fully qualified names. |
|
|
177 |
* |
|
|
178 |
* @deprecated This method will be removed in Doctrine Common 3.0 |
|
|
179 |
* @param string $defaultNamespace |
|
|
180 |
*/ |
|
|
181 |
public function setDefaultAnnotationNamespace($defaultNamespace) |
|
|
182 |
{ |
|
|
183 |
$this->defaultAnnotationNamespace = $defaultNamespace; |
|
|
184 |
} |
|
|
185 |
|
|
|
186 |
/** |
|
|
187 |
* Sets the custom function to use for creating new annotations on the |
|
|
188 |
* underlying parser. |
|
|
189 |
* |
|
|
190 |
* The function is supplied two arguments. The first argument is the name |
|
|
191 |
* of the annotation and the second argument an array of values for this |
|
|
192 |
* annotation. The function is assumed to return an object or NULL. |
|
|
193 |
* Whenever the function returns NULL for an annotation, the implementation falls |
|
|
194 |
* back to the default annotation creation process of the underlying parser. |
|
|
195 |
* |
|
|
196 |
* @deprecated This method will be removed in Doctrine Common 3.0 |
|
|
197 |
* @param Closure $func |
|
|
198 |
*/ |
|
|
199 |
public function setAnnotationCreationFunction(Closure $func) |
|
|
200 |
{ |
|
|
201 |
$this->parser->setAnnotationCreationFunction($func); |
|
|
202 |
} |
|
|
203 |
|
|
|
204 |
/** |
|
|
205 |
* Sets an alias for an annotation namespace. |
|
|
206 |
* |
|
|
207 |
* @param string $namespace |
|
|
208 |
* @param string $alias |
|
|
209 |
*/ |
|
|
210 |
public function setAnnotationNamespaceAlias($namespace, $alias) |
|
|
211 |
{ |
|
|
212 |
$this->parser->setAnnotationNamespaceAlias($namespace, $alias); |
|
|
213 |
} |
|
|
214 |
|
|
|
215 |
/** |
|
|
216 |
* Gets the annotations applied to a class. |
|
|
217 |
* |
|
|
218 |
* @param string|ReflectionClass $class The name or ReflectionClass of the class from which |
|
|
219 |
* the class annotations should be read. |
|
|
220 |
* @return array An array of Annotations. |
|
|
221 |
*/ |
|
|
222 |
public function getClassAnnotations(ReflectionClass $class) |
|
|
223 |
{ |
|
|
224 |
$this->parser->setImports($this->getImports($class)); |
|
|
225 |
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); |
|
|
226 |
|
|
|
227 |
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); |
|
|
228 |
} |
|
|
229 |
|
|
|
230 |
/** |
|
|
231 |
* Gets a class annotation. |
|
|
232 |
* |
|
|
233 |
* @param ReflectionClass $class The ReflectionClass of the class from which |
|
|
234 |
* the class annotations should be read. |
|
|
235 |
* @param string $annotationName The name of the annotation. |
|
|
236 |
* @return The Annotation or NULL, if the requested annotation does not exist. |
|
|
237 |
*/ |
|
|
238 |
public function getClassAnnotation(ReflectionClass $class, $annotationName) |
|
|
239 |
{ |
|
|
240 |
$annotations = $this->getClassAnnotations($class); |
|
|
241 |
|
|
|
242 |
foreach ($annotations as $annotation) { |
|
|
243 |
if ($annotation instanceof $annotationName) { |
|
|
244 |
return $annotation; |
|
|
245 |
} |
|
|
246 |
} |
|
|
247 |
|
|
|
248 |
return null; |
|
|
249 |
} |
|
|
250 |
|
|
|
251 |
/** |
|
|
252 |
* Gets the annotations applied to a property. |
|
|
253 |
* |
|
|
254 |
* @param string|ReflectionProperty $property The name or ReflectionProperty of the property |
|
|
255 |
* from which the annotations should be read. |
|
|
256 |
* @return array An array of Annotations. |
|
|
257 |
*/ |
|
|
258 |
public function getPropertyAnnotations(ReflectionProperty $property) |
|
|
259 |
{ |
|
|
260 |
$class = $property->getDeclaringClass(); |
|
|
261 |
$context = 'property ' . $class->getName() . "::\$" . $property->getName(); |
|
|
262 |
$this->parser->setImports($this->getImports($class)); |
|
|
263 |
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); |
|
|
264 |
|
|
|
265 |
return $this->parser->parse($property->getDocComment(), $context); |
|
|
266 |
} |
|
|
267 |
|
|
|
268 |
/** |
|
|
269 |
* Gets a property annotation. |
|
|
270 |
* |
|
|
271 |
* @param ReflectionProperty $property |
|
|
272 |
* @param string $annotationName The name of the annotation. |
|
|
273 |
* @return The Annotation or NULL, if the requested annotation does not exist. |
|
|
274 |
*/ |
|
|
275 |
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) |
|
|
276 |
{ |
|
|
277 |
$annotations = $this->getPropertyAnnotations($property); |
|
|
278 |
|
|
|
279 |
foreach ($annotations as $annotation) { |
|
|
280 |
if ($annotation instanceof $annotationName) { |
|
|
281 |
return $annotation; |
|
|
282 |
} |
|
|
283 |
} |
|
|
284 |
|
|
|
285 |
return null; |
|
|
286 |
} |
|
|
287 |
|
|
|
288 |
/** |
|
|
289 |
* Gets the annotations applied to a method. |
|
|
290 |
* |
|
|
291 |
* @param ReflectionMethod $property The name or ReflectionMethod of the method from which |
|
|
292 |
* the annotations should be read. |
|
|
293 |
* @return array An array of Annotations. |
|
|
294 |
*/ |
|
|
295 |
public function getMethodAnnotations(ReflectionMethod $method) |
|
|
296 |
{ |
|
|
297 |
$class = $method->getDeclaringClass(); |
|
|
298 |
$context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; |
|
|
299 |
$this->parser->setImports($this->getImports($class)); |
|
|
300 |
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); |
|
|
301 |
|
|
|
302 |
return $this->parser->parse($method->getDocComment(), $context); |
|
|
303 |
} |
|
|
304 |
|
|
|
305 |
/** |
|
|
306 |
* Gets a method annotation. |
|
|
307 |
* |
|
|
308 |
* @param ReflectionMethod $method |
|
|
309 |
* @param string $annotationName The name of the annotation. |
|
|
310 |
* @return The Annotation or NULL, if the requested annotation does not exist. |
|
|
311 |
*/ |
|
|
312 |
public function getMethodAnnotation(ReflectionMethod $method, $annotationName) |
|
|
313 |
{ |
|
|
314 |
$annotations = $this->getMethodAnnotations($method); |
|
|
315 |
|
|
|
316 |
foreach ($annotations as $annotation) { |
|
|
317 |
if ($annotation instanceof $annotationName) { |
|
|
318 |
return $annotation; |
|
|
319 |
} |
|
|
320 |
} |
|
|
321 |
|
|
|
322 |
return null; |
|
|
323 |
} |
|
|
324 |
|
|
|
325 |
/** |
|
|
326 |
* Returns the ignored annotations for the given class. |
|
|
327 |
* |
|
|
328 |
* @param ReflectionClass $class |
|
|
329 |
* @return array |
|
|
330 |
*/ |
|
|
331 |
private function getIgnoredAnnotationNames(ReflectionClass $class) |
|
|
332 |
{ |
|
|
333 |
if (isset($this->ignoredAnnotationNames[$name = $class->getName()])) { |
|
|
334 |
return $this->ignoredAnnotationNames[$name]; |
|
|
335 |
} |
|
|
336 |
$this->collectParsingMetadata($class); |
|
|
337 |
|
|
|
338 |
return $this->ignoredAnnotationNames[$name]; |
|
|
339 |
} |
|
|
340 |
|
|
|
341 |
private function getImports(ReflectionClass $class) |
|
|
342 |
{ |
|
|
343 |
if (isset($this->imports[$name = $class->getName()])) { |
|
|
344 |
return $this->imports[$name]; |
|
|
345 |
} |
|
|
346 |
$this->collectParsingMetadata($class); |
|
|
347 |
|
|
|
348 |
return $this->imports[$name]; |
|
|
349 |
} |
|
|
350 |
|
|
|
351 |
/** |
|
|
352 |
* Collects parsing metadata for a given class |
|
|
353 |
* |
|
|
354 |
* @param ReflectionClass $class |
|
|
355 |
*/ |
|
|
356 |
private function collectParsingMetadata(ReflectionClass $class) |
|
|
357 |
{ |
|
|
358 |
$imports = self::$globalImports; |
|
|
359 |
$ignoredAnnotationNames = self::$globalIgnoredNames; |
|
|
360 |
|
|
|
361 |
if ($this->enablePhpImports) { |
|
|
362 |
$annotations = $this->preParser->parse($class->getDocComment()); |
|
|
363 |
foreach ($annotations as $annotation) { |
|
|
364 |
if ($annotation instanceof IgnoreAnnotation) { |
|
|
365 |
foreach ($annotation->names AS $annot) { |
|
|
366 |
$ignoredAnnotationNames[$annot] = true; |
|
|
367 |
} |
|
|
368 |
} |
|
|
369 |
} |
|
|
370 |
} |
|
|
371 |
|
|
|
372 |
$name = $class->getName(); |
|
|
373 |
$this->imports[$name] = array_merge( |
|
|
374 |
self::$globalImports, |
|
|
375 |
($this->enablePhpImports) ? $this->phpParser->parseClass($class) : array(), |
|
|
376 |
($this->enablePhpImports) ? array('__NAMESPACE__' => $class->getNamespaceName()) : array() |
|
|
377 |
); |
|
|
378 |
if ($this->defaultAnnotationNamespace) { |
|
|
379 |
$this->imports[$name]['__DEFAULT__'] = $this->defaultAnnotationNamespace; |
|
|
380 |
} |
|
|
381 |
$this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames; |
|
|
382 |
} |
|
|
383 |
} |