|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Doctrine\Tests\Annotations; |
|
4
|
|
|
|
|
5
|
|
|
use Doctrine\Annotations\Annotation; |
|
6
|
|
|
use Doctrine\Annotations\AnnotationException; |
|
7
|
|
|
use PHPUnit\Framework\TestCase; |
|
8
|
|
|
use ReflectionClass, Doctrine\Annotations\AnnotationReader; |
|
9
|
|
|
|
|
10
|
|
|
require_once __DIR__ . '/TopLevelAnnotation.php'; |
|
11
|
|
|
|
|
12
|
|
|
abstract class AbstractReaderTest extends TestCase |
|
13
|
|
|
{ |
|
14
|
|
|
public function getReflectionClass() |
|
15
|
|
|
{ |
|
16
|
|
|
return new ReflectionClass(DummyClass::class); |
|
17
|
|
|
} |
|
18
|
|
|
|
|
19
|
|
|
public function testAnnotations() |
|
20
|
|
|
{ |
|
21
|
|
|
$class = $this->getReflectionClass(); |
|
22
|
|
|
$reader = $this->getReader(); |
|
23
|
|
|
|
|
24
|
|
|
self::assertCount(1, $reader->getClassAnnotations($class)); |
|
25
|
|
|
self::assertInstanceOf($annotName = DummyAnnotation::class, $annot = $reader->getClassAnnotation($class, $annotName)); |
|
26
|
|
|
self::assertEquals('hello', $annot->dummyValue); |
|
27
|
|
|
|
|
28
|
|
|
$field1Prop = $class->getProperty('field1'); |
|
29
|
|
|
$propAnnots = $reader->getPropertyAnnotations($field1Prop); |
|
30
|
|
|
self::assertCount(1, $propAnnots); |
|
31
|
|
|
self::assertInstanceOf($annotName, $annot = $reader->getPropertyAnnotation($field1Prop, $annotName)); |
|
32
|
|
|
self::assertEquals('fieldHello', $annot->dummyValue); |
|
33
|
|
|
|
|
34
|
|
|
$getField1Method = $class->getMethod('getField1'); |
|
35
|
|
|
$methodAnnots = $reader->getMethodAnnotations($getField1Method); |
|
36
|
|
|
self::assertCount(1, $methodAnnots); |
|
37
|
|
|
self::assertInstanceOf($annotName, $annot = $reader->getMethodAnnotation($getField1Method, $annotName)); |
|
38
|
|
|
self::assertEquals([1, 2, 'three'], $annot->value); |
|
39
|
|
|
|
|
40
|
|
|
$field2Prop = $class->getProperty('field2'); |
|
41
|
|
|
$propAnnots = $reader->getPropertyAnnotations($field2Prop); |
|
42
|
|
|
self::assertCount(1, $propAnnots); |
|
43
|
|
|
self::assertInstanceOf($annotName = DummyJoinTable::class, $joinTableAnnot = $reader->getPropertyAnnotation($field2Prop, $annotName)); |
|
44
|
|
|
self::assertCount(1, $joinTableAnnot->joinColumns); |
|
45
|
|
|
self::assertCount(1, $joinTableAnnot->inverseJoinColumns); |
|
46
|
|
|
self::assertInstanceOf(DummyJoinColumn::class, $joinTableAnnot->joinColumns[0]); |
|
47
|
|
|
self::assertInstanceOf(DummyJoinColumn::class, $joinTableAnnot->inverseJoinColumns[0]); |
|
48
|
|
|
self::assertEquals('col1', $joinTableAnnot->joinColumns[0]->name); |
|
49
|
|
|
self::assertEquals('col2', $joinTableAnnot->joinColumns[0]->referencedColumnName); |
|
50
|
|
|
self::assertEquals('col3', $joinTableAnnot->inverseJoinColumns[0]->name); |
|
51
|
|
|
self::assertEquals('col4', $joinTableAnnot->inverseJoinColumns[0]->referencedColumnName); |
|
52
|
|
|
|
|
53
|
|
|
$dummyAnnot = $reader->getMethodAnnotation($class->getMethod('getField1'), DummyAnnotation::class); |
|
54
|
|
|
self::assertEquals('', $dummyAnnot->dummyValue); |
|
55
|
|
|
self::assertEquals([1, 2, 'three'], $dummyAnnot->value); |
|
56
|
|
|
|
|
57
|
|
|
$dummyAnnot = $reader->getMethodAnnotation($class->getMethod('getField3'), DummyAnnotation::class); |
|
58
|
|
|
self::assertEquals('\d{4}-[01]\d-[0-3]\d [0-2]\d:[0-5]\d:[0-5]\d', $dummyAnnot->value); |
|
59
|
|
|
|
|
60
|
|
|
$dummyAnnot = $reader->getPropertyAnnotation($class->getProperty('field1'), DummyAnnotation::class); |
|
61
|
|
|
self::assertEquals('fieldHello', $dummyAnnot->dummyValue); |
|
62
|
|
|
|
|
63
|
|
|
$classAnnot = $reader->getClassAnnotation($class, DummyAnnotation::class); |
|
64
|
|
|
self::assertEquals('hello', $classAnnot->dummyValue); |
|
65
|
|
|
} |
|
66
|
|
|
|
|
67
|
|
|
public function testAnnotationsWithValidTargets() |
|
68
|
|
|
{ |
|
69
|
|
|
$reader = $this->getReader(); |
|
70
|
|
|
$class = new ReflectionClass(Fixtures\ClassWithValidAnnotationTarget::class); |
|
71
|
|
|
|
|
72
|
|
|
self::assertCount(1, $reader->getClassAnnotations($class)); |
|
73
|
|
|
self::assertCount(1, $reader->getPropertyAnnotations($class->getProperty('foo'))); |
|
74
|
|
|
self::assertCount(1, $reader->getMethodAnnotations($class->getMethod('someFunction'))); |
|
75
|
|
|
self::assertCount(1, $reader->getPropertyAnnotations($class->getProperty('nested'))); |
|
76
|
|
|
} |
|
77
|
|
|
|
|
78
|
|
|
public function testAnnotationsWithVarType() |
|
79
|
|
|
{ |
|
80
|
|
|
$reader = $this->getReader(); |
|
81
|
|
|
$class = new ReflectionClass(Fixtures\ClassWithAnnotationWithVarType::class); |
|
82
|
|
|
|
|
83
|
|
|
self::assertCount(1, $fooAnnot = $reader->getPropertyAnnotations($class->getProperty('foo'))); |
|
84
|
|
|
self::assertCount(1, $barAnnot = $reader->getMethodAnnotations($class->getMethod('bar'))); |
|
85
|
|
|
|
|
86
|
|
|
self::assertInternalType('string', $fooAnnot[0]->string); |
|
|
|
|
|
|
87
|
|
|
self::assertInstanceOf(Fixtures\AnnotationTargetAll::class, $barAnnot[0]->annotation); |
|
88
|
|
|
} |
|
89
|
|
|
|
|
90
|
|
|
public function testAtInDescription() |
|
91
|
|
|
{ |
|
92
|
|
|
$reader = $this->getReader(); |
|
93
|
|
|
$class = new ReflectionClass(Fixtures\ClassWithAtInDescriptionAndAnnotation::class); |
|
94
|
|
|
|
|
95
|
|
|
self::assertCount(1, $fooAnnot = $reader->getPropertyAnnotations($class->getProperty('foo'))); |
|
96
|
|
|
self::assertCount(1, $barAnnot = $reader->getPropertyAnnotations($class->getProperty('bar'))); |
|
97
|
|
|
|
|
98
|
|
|
self::assertInstanceOf(Fixtures\AnnotationTargetPropertyMethod::class, $fooAnnot[0]); |
|
99
|
|
|
self::assertInstanceOf(Fixtures\AnnotationTargetPropertyMethod::class, $barAnnot[0]); |
|
100
|
|
|
} |
|
101
|
|
|
|
|
102
|
|
|
public function testClassWithWithDanglingComma() |
|
103
|
|
|
{ |
|
104
|
|
|
$reader = $this->getReader(); |
|
105
|
|
|
$annots = $reader->getClassAnnotations(new \ReflectionClass(DummyClassWithDanglingComma::class)); |
|
106
|
|
|
|
|
107
|
|
|
self::assertCount(1, $annots); |
|
108
|
|
|
} |
|
109
|
|
|
|
|
110
|
|
|
/** |
|
111
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
112
|
|
|
* @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetPropertyMethod is not allowed to be declared on class Doctrine\Tests\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtClass. You may only use this annotation on these code elements: METHOD, PROPERTY |
|
113
|
|
|
*/ |
|
114
|
|
|
public function testClassWithInvalidAnnotationTargetAtClassDocBlock() |
|
115
|
|
|
{ |
|
116
|
|
|
$reader = $this->getReader(); |
|
117
|
|
|
$reader->getClassAnnotations(new \ReflectionClass(Fixtures\ClassWithInvalidAnnotationTargetAtClass::class)); |
|
118
|
|
|
} |
|
119
|
|
|
|
|
120
|
|
|
public function testClassWithWithInclude() |
|
121
|
|
|
{ |
|
122
|
|
|
$reader = $this->getReader(); |
|
123
|
|
|
$annots = $reader->getClassAnnotations(new \ReflectionClass(Fixtures\ClassWithRequire::class)); |
|
124
|
|
|
self::assertCount(1, $annots); |
|
125
|
|
|
} |
|
126
|
|
|
|
|
127
|
|
|
/** |
|
128
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
129
|
|
|
* @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetClass is not allowed to be declared on property Doctrine\Tests\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty::$foo. You may only use this annotation on these code elements: CLASS |
|
130
|
|
|
*/ |
|
131
|
|
|
public function testClassWithInvalidAnnotationTargetAtPropertyDocBlock() |
|
132
|
|
|
{ |
|
133
|
|
|
$reader = $this->getReader(); |
|
134
|
|
|
$reader->getPropertyAnnotations(new \ReflectionProperty(Fixtures\ClassWithInvalidAnnotationTargetAtProperty::class, 'foo')); |
|
135
|
|
|
} |
|
136
|
|
|
|
|
137
|
|
|
/** |
|
138
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
139
|
|
|
* @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetAnnotation is not allowed to be declared on property Doctrine\Tests\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty::$bar. You may only use this annotation on these code elements: ANNOTATION |
|
140
|
|
|
*/ |
|
141
|
|
|
public function testClassWithInvalidNestedAnnotationTargetAtPropertyDocBlock() |
|
142
|
|
|
{ |
|
143
|
|
|
$reader = $this->getReader(); |
|
144
|
|
|
$reader->getPropertyAnnotations(new \ReflectionProperty(Fixtures\ClassWithInvalidAnnotationTargetAtProperty::class, 'bar')); |
|
145
|
|
|
} |
|
146
|
|
|
|
|
147
|
|
|
/** |
|
148
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
149
|
|
|
* @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetClass is not allowed to be declared on method Doctrine\Tests\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtMethod::functionName(). You may only use this annotation on these code elements: CLASS |
|
150
|
|
|
*/ |
|
151
|
|
|
public function testClassWithInvalidAnnotationTargetAtMethodDocBlock() |
|
152
|
|
|
{ |
|
153
|
|
|
$reader = $this->getReader(); |
|
154
|
|
|
$reader->getMethodAnnotations(new \ReflectionMethod(Fixtures\ClassWithInvalidAnnotationTargetAtMethod::class, 'functionName')); |
|
155
|
|
|
} |
|
156
|
|
|
|
|
157
|
|
|
/** |
|
158
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
159
|
|
|
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Annotations\Fixtures\AnnotationWithTargetSyntaxError. |
|
160
|
|
|
*/ |
|
161
|
|
|
public function testClassWithAnnotationWithTargetSyntaxErrorAtClassDocBlock() |
|
162
|
|
|
{ |
|
163
|
|
|
$reader = $this->getReader(); |
|
164
|
|
|
$reader->getClassAnnotations(new \ReflectionClass(Fixtures\ClassWithAnnotationWithTargetSyntaxError::class)); |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
/** |
|
168
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
169
|
|
|
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Annotations\Fixtures\AnnotationWithTargetSyntaxError. |
|
170
|
|
|
*/ |
|
171
|
|
|
public function testClassWithAnnotationWithTargetSyntaxErrorAtPropertyDocBlock() |
|
172
|
|
|
{ |
|
173
|
|
|
$reader = $this->getReader(); |
|
174
|
|
|
$reader->getPropertyAnnotations(new \ReflectionProperty(Fixtures\ClassWithAnnotationWithTargetSyntaxError::class,'foo')); |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
/** |
|
178
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
179
|
|
|
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Annotations\Fixtures\AnnotationWithTargetSyntaxError. |
|
180
|
|
|
*/ |
|
181
|
|
|
public function testClassWithAnnotationWithTargetSyntaxErrorAtMethodDocBlock() |
|
182
|
|
|
{ |
|
183
|
|
|
$reader = $this->getReader(); |
|
184
|
|
|
$reader->getMethodAnnotations(new \ReflectionMethod(Fixtures\ClassWithAnnotationWithTargetSyntaxError::class,'bar')); |
|
185
|
|
|
} |
|
186
|
|
|
|
|
187
|
|
|
/** |
|
188
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
189
|
|
|
* @expectedExceptionMessage [Type Error] Attribute "string" of @AnnotationWithVarType declared on property Doctrine\Tests\Annotations\Fixtures\ClassWithAnnotationWithVarType::$invalidProperty expects a(n) string, but got integer. |
|
190
|
|
|
*/ |
|
191
|
|
|
public function testClassWithPropertyInvalidVarTypeError() |
|
192
|
|
|
{ |
|
193
|
|
|
$reader = $this->getReader(); |
|
194
|
|
|
$class = new ReflectionClass(Fixtures\ClassWithAnnotationWithVarType::class); |
|
195
|
|
|
|
|
196
|
|
|
$reader->getPropertyAnnotations($class->getProperty('invalidProperty')); |
|
197
|
|
|
} |
|
198
|
|
|
|
|
199
|
|
|
/** |
|
200
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
201
|
|
|
* @expectedExceptionMessage [Type Error] Attribute "annotation" of @AnnotationWithVarType declared on method Doctrine\Tests\Annotations\Fixtures\ClassWithAnnotationWithVarType::invalidMethod() expects a(n) \Doctrine\Tests\Annotations\Fixtures\AnnotationTargetAll, but got an instance of Doctrine\Tests\Annotations\Fixtures\AnnotationTargetAnnotation. |
|
202
|
|
|
*/ |
|
203
|
|
|
public function testClassWithMethodInvalidVarTypeError() |
|
204
|
|
|
{ |
|
205
|
|
|
$reader = $this->getReader(); |
|
206
|
|
|
$class = new ReflectionClass(Fixtures\ClassWithAnnotationWithVarType::class); |
|
207
|
|
|
|
|
208
|
|
|
$reader->getMethodAnnotations($class->getMethod('invalidMethod')); |
|
209
|
|
|
} |
|
210
|
|
|
|
|
211
|
|
|
/** |
|
212
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
213
|
|
|
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in class Doctrine\Tests\Annotations\DummyClassSyntaxError. |
|
214
|
|
|
*/ |
|
215
|
|
|
public function testClassSyntaxErrorContext() |
|
216
|
|
|
{ |
|
217
|
|
|
$reader = $this->getReader(); |
|
218
|
|
|
$reader->getClassAnnotations(new \ReflectionClass(DummyClassSyntaxError::class)); |
|
219
|
|
|
} |
|
220
|
|
|
|
|
221
|
|
|
/** |
|
222
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
223
|
|
|
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in method Doctrine\Tests\Annotations\DummyClassMethodSyntaxError::foo(). |
|
224
|
|
|
*/ |
|
225
|
|
|
public function testMethodSyntaxErrorContext() |
|
226
|
|
|
{ |
|
227
|
|
|
$reader = $this->getReader(); |
|
228
|
|
|
$reader->getMethodAnnotations(new \ReflectionMethod(DummyClassMethodSyntaxError::class, 'foo')); |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
/** |
|
232
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
233
|
|
|
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in property Doctrine\Tests\Annotations\DummyClassPropertySyntaxError::$foo. |
|
234
|
|
|
*/ |
|
235
|
|
|
public function testPropertySyntaxErrorContext() |
|
236
|
|
|
{ |
|
237
|
|
|
$reader = $this->getReader(); |
|
238
|
|
|
$reader->getPropertyAnnotations(new \ReflectionProperty(DummyClassPropertySyntaxError::class, 'foo')); |
|
239
|
|
|
} |
|
240
|
|
|
|
|
241
|
|
|
/** |
|
242
|
|
|
* @group regression |
|
243
|
|
|
*/ |
|
244
|
|
|
public function testMultipleAnnotationsOnSameLine() |
|
245
|
|
|
{ |
|
246
|
|
|
$reader = $this->getReader(); |
|
247
|
|
|
$annots = $reader->getPropertyAnnotations(new \ReflectionProperty(DummyClass2::class, 'id')); |
|
248
|
|
|
self::assertCount(3, $annots); |
|
249
|
|
|
} |
|
250
|
|
|
|
|
251
|
|
|
public function testNonAnnotationProblem() |
|
252
|
|
|
{ |
|
253
|
|
|
$reader = $this->getReader(); |
|
254
|
|
|
|
|
255
|
|
|
self::assertNotNull($annot = $reader->getPropertyAnnotation(new \ReflectionProperty(DummyClassNonAnnotationProblem::class, 'foo'), $name = DummyAnnotation::class)); |
|
256
|
|
|
self::assertInstanceOf($name, $annot); |
|
257
|
|
|
} |
|
258
|
|
|
|
|
259
|
|
|
public function testIncludeIgnoreAnnotation() |
|
260
|
|
|
{ |
|
261
|
|
|
$reader = $this->getReader(); |
|
262
|
|
|
|
|
263
|
|
|
$reader->getPropertyAnnotations(new \ReflectionProperty(Fixtures\ClassWithIgnoreAnnotation::class, 'foo')); |
|
264
|
|
|
self::assertFalse(class_exists(Fixtures\IgnoreAnnotationClass::class, false)); |
|
265
|
|
|
} |
|
266
|
|
|
|
|
267
|
|
|
public function testImportWithConcreteAnnotation() |
|
268
|
|
|
{ |
|
269
|
|
|
$reader = $this->getReader(); |
|
270
|
|
|
$property = new \ReflectionProperty(TestImportWithConcreteAnnotation::class, 'field'); |
|
271
|
|
|
$annotations = $reader->getPropertyAnnotations($property); |
|
272
|
|
|
self::assertCount(1, $annotations); |
|
273
|
|
|
self::assertNotNull($reader->getPropertyAnnotation($property, DummyAnnotation::class)); |
|
274
|
|
|
} |
|
275
|
|
|
|
|
276
|
|
|
public function testImportWithInheritance() |
|
277
|
|
|
{ |
|
278
|
|
|
$reader = $this->getReader(); |
|
279
|
|
|
|
|
280
|
|
|
$class = new TestParentClass(); |
|
281
|
|
|
$ref = new \ReflectionClass($class); |
|
282
|
|
|
|
|
283
|
|
|
$childAnnotations = $reader->getPropertyAnnotations($ref->getProperty('child')); |
|
284
|
|
|
self::assertCount(1, $childAnnotations); |
|
285
|
|
|
self::assertInstanceOf(Foo\Name::class, reset($childAnnotations)); |
|
286
|
|
|
|
|
287
|
|
|
$parentAnnotations = $reader->getPropertyAnnotations($ref->getProperty('parent')); |
|
288
|
|
|
self::assertCount(1, $parentAnnotations); |
|
289
|
|
|
self::assertInstanceOf(Bar\Name::class, reset($parentAnnotations)); |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
/** |
|
293
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
294
|
|
|
* @expectedExceptionMessage The annotation "@NameFoo" in property Doctrine\Tests\Annotations\TestAnnotationNotImportedClass::$field was never imported. |
|
295
|
|
|
*/ |
|
296
|
|
|
public function testImportDetectsNotImportedAnnotation() |
|
297
|
|
|
{ |
|
298
|
|
|
$reader = $this->getReader(); |
|
299
|
|
|
$reader->getPropertyAnnotations(new \ReflectionProperty(TestAnnotationNotImportedClass::class, 'field')); |
|
300
|
|
|
} |
|
301
|
|
|
|
|
302
|
|
|
/** |
|
303
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
304
|
|
|
* @expectedExceptionMessage The annotation "@Foo\Bar\Name" in property Doctrine\Tests\Annotations\TestNonExistentAnnotationClass::$field was never imported. |
|
305
|
|
|
*/ |
|
306
|
|
|
public function testImportDetectsNonExistentAnnotation() |
|
307
|
|
|
{ |
|
308
|
|
|
$reader = $this->getReader(); |
|
309
|
|
|
$reader->getPropertyAnnotations(new \ReflectionProperty(TestNonExistentAnnotationClass::class, 'field')); |
|
310
|
|
|
} |
|
311
|
|
|
|
|
312
|
|
|
public function testTopLevelAnnotation() |
|
313
|
|
|
{ |
|
314
|
|
|
$reader = $this->getReader(); |
|
315
|
|
|
$annotations = $reader->getPropertyAnnotations(new \ReflectionProperty(TestTopLevelAnnotationClass::class, 'field')); |
|
316
|
|
|
|
|
317
|
|
|
self::assertCount(1, $annotations); |
|
318
|
|
|
self::assertInstanceOf(\TopLevelAnnotation::class, reset($annotations)); |
|
319
|
|
|
} |
|
320
|
|
|
|
|
321
|
|
|
public function testIgnoresAnnotationsNotPrefixedWithWhitespace() |
|
322
|
|
|
{ |
|
323
|
|
|
$reader = $this->getReader(); |
|
324
|
|
|
|
|
325
|
|
|
$annotation = $reader->getClassAnnotation(new \ReflectionClass(new TestIgnoresNonAnnotationsClass()), Name::class); |
|
326
|
|
|
self::assertInstanceOf(Name::class, $annotation); |
|
327
|
|
|
} |
|
328
|
|
|
|
|
329
|
|
|
private static $testResetsPhpParserAfterUseRun = false; |
|
330
|
|
|
|
|
331
|
|
|
/** |
|
332
|
|
|
* When getUseStatements isn't available on ReflectionClass the PhpParser has to use token_get_all(). If that |
|
333
|
|
|
* happens various PHP compiler globals get set, and these can have seriously bad effects on the next file to be |
|
334
|
|
|
* parsed. |
|
335
|
|
|
* Notably the doc_comment compiler global can end up containing a docblock comment. The next file to be parsed |
|
336
|
|
|
* on an include() will have this docblock comment attached to the first thing in the file that the compiler |
|
337
|
|
|
* considers to own comments. If this is a class then any later calls to getDocComment() for that class will have |
|
338
|
|
|
* undesirable effects. *sigh* |
|
339
|
|
|
*/ |
|
340
|
|
|
public function testResetsPhpParserAfterUse() |
|
341
|
|
|
{ |
|
342
|
|
|
// If someone has already included our main test fixture this test is invalid. It's important that our require |
|
343
|
|
|
// causes this file to be parsed and compiled at a certain point. |
|
344
|
|
|
self::assertFalse(!self::$testResetsPhpParserAfterUseRun && class_exists(\Doctrine_Tests_Annotations_Fixtures_ClassNoNamespaceNoComment::class), 'Test invalid if class has already been compiled'); |
|
345
|
|
|
self::$testResetsPhpParserAfterUseRun = true; |
|
346
|
|
|
|
|
347
|
|
|
$reader = $this->getReader(); |
|
348
|
|
|
|
|
349
|
|
|
// First make sure the annotation cache knows about the annotations we want to use. |
|
350
|
|
|
// If we don't do this then loading of annotations into the cache will cause the parser to get out of the bad |
|
351
|
|
|
// state we want to test. |
|
352
|
|
|
$class = new ReflectionClass(Fixtures\ClassWithValidAnnotationTarget::class); |
|
353
|
|
|
$reader->getClassAnnotations($class); |
|
354
|
|
|
|
|
355
|
|
|
// Now import an incredibly dull class which makes use of the same class level annotation that the previous class does. |
|
356
|
|
|
$class = new ReflectionClass(Fixtures\ClassWithClassAnnotationOnly::class); |
|
357
|
|
|
$annotations = $reader->getClassAnnotations($class); |
|
358
|
|
|
|
|
359
|
|
|
// This include needs to be here since we need the PHP compiler to run over it as the next thing the PHP |
|
360
|
|
|
// parser sees since PhpParser called token_get_all() on the intro to ClassWithClassAnnotationOnly. |
|
361
|
|
|
// Our test class cannot be in a namespace (some versions of PHP reset the doc_comment compiler global when |
|
362
|
|
|
// you hit a namespace declaration), so cannot be autoloaded. |
|
363
|
|
|
require_once __DIR__ . '/Fixtures/ClassNoNamespaceNoComment.php'; |
|
364
|
|
|
|
|
365
|
|
|
// So, hopefully, if all has gone OK, our class with class annotations should actually have them. |
|
366
|
|
|
// If this fails then something is quite badly wrong elsewhere. |
|
367
|
|
|
// Note that if this happens before the require it can cause other PHP files to be included, resetting the |
|
368
|
|
|
// compiler global state, and invalidating this test case. |
|
369
|
|
|
self::assertNotEmpty($annotations); |
|
370
|
|
|
|
|
371
|
|
|
$annotations = $reader->getClassAnnotations(new \ReflectionClass(new \Doctrine_Tests_Annotations_Fixtures_ClassNoNamespaceNoComment())); |
|
372
|
|
|
// And if our workaround for this bug is OK, our class with no doc comment should not have any class annotations. |
|
373
|
|
|
self::assertEmpty($annotations); |
|
374
|
|
|
} |
|
375
|
|
|
|
|
376
|
|
|
/** |
|
377
|
|
|
* @expectedException \Doctrine\Annotations\AnnotationException |
|
378
|
|
|
* @expectedExceptionMessage The class "Doctrine\Tests\Annotations\Fixtures\NoAnnotation" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "Doctrine\Tests\Annotations\Fixtures\NoAnnotation". If it is indeed no annotation, then you need to add @IgnoreAnnotation("NoAnnotation") to the _class_ doc comment of class Doctrine\Tests\Annotations\Fixtures\InvalidAnnotationUsageClass. |
|
379
|
|
|
*/ |
|
380
|
|
|
public function testErrorWhenInvalidAnnotationIsUsed() |
|
381
|
|
|
{ |
|
382
|
|
|
$reader = $this->getReader(); |
|
383
|
|
|
$ref = new \ReflectionClass(Fixtures\InvalidAnnotationUsageClass::class); |
|
384
|
|
|
$reader->getClassAnnotations($ref); |
|
385
|
|
|
} |
|
386
|
|
|
|
|
387
|
|
|
public function testInvalidAnnotationUsageButIgnoredClass() |
|
388
|
|
|
{ |
|
389
|
|
|
$reader = $this->getReader(); |
|
390
|
|
|
$ref = new \ReflectionClass(Fixtures\InvalidAnnotationUsageButIgnoredClass::class); |
|
391
|
|
|
$annots = $reader->getClassAnnotations($ref); |
|
392
|
|
|
|
|
393
|
|
|
self::assertCount(2, $annots); |
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
/** |
|
397
|
|
|
* @group DDC-1660 |
|
398
|
|
|
* @group regression |
|
399
|
|
|
*/ |
|
400
|
|
|
public function testInvalidAnnotationButIgnored() |
|
401
|
|
|
{ |
|
402
|
|
|
$reader = $this->getReader(); |
|
403
|
|
|
$class = new \ReflectionClass(Fixtures\ClassDDC1660::class); |
|
404
|
|
|
|
|
405
|
|
|
self::assertTrue(class_exists(Fixtures\Annotation\Version::class)); |
|
406
|
|
|
self::assertEmpty($reader->getClassAnnotations($class)); |
|
407
|
|
|
self::assertEmpty($reader->getMethodAnnotations($class->getMethod('bar'))); |
|
408
|
|
|
self::assertEmpty($reader->getPropertyAnnotations($class->getProperty('foo'))); |
|
409
|
|
|
} |
|
410
|
|
|
|
|
411
|
|
|
public function testGloballyIgnoredAnnotationNotIgnored() : void |
|
412
|
|
|
{ |
|
413
|
|
|
$reader = $this->getReader(); |
|
414
|
|
|
$class = new \ReflectionClass(Fixtures\ClassDDC1660::class); |
|
415
|
|
|
|
|
416
|
|
|
$testLoader = static function (string $className) : bool { |
|
417
|
|
|
if ($className === 'since') { |
|
418
|
|
|
throw new \InvalidArgumentException('Globally ignored annotation names should never be passed to an autoloader.'); |
|
419
|
|
|
} |
|
420
|
|
|
|
|
421
|
|
|
return false; |
|
422
|
|
|
}; |
|
423
|
|
|
|
|
424
|
|
|
spl_autoload_register($testLoader, true, true); |
|
425
|
|
|
|
|
426
|
|
|
try { |
|
427
|
|
|
self::assertEmpty($reader->getClassAnnotations($class)); |
|
428
|
|
|
} finally { |
|
429
|
|
|
spl_autoload_unregister($testLoader); |
|
430
|
|
|
} |
|
431
|
|
|
} |
|
432
|
|
|
|
|
433
|
|
|
public function testAnnotationEnumeratorException() |
|
434
|
|
|
{ |
|
435
|
|
|
$reader = $this->getReader(); |
|
436
|
|
|
$class = new \ReflectionClass(Fixtures\ClassWithAnnotationEnum::class); |
|
437
|
|
|
|
|
438
|
|
|
self::assertCount(1, $bar = $reader->getMethodAnnotations($class->getMethod('bar'))); |
|
439
|
|
|
self::assertCount(1, $foo = $reader->getPropertyAnnotations($class->getProperty('foo'))); |
|
440
|
|
|
|
|
441
|
|
|
self::assertInstanceOf(Fixtures\AnnotationEnum::class, $bar[0]); |
|
442
|
|
|
self::assertInstanceOf(Fixtures\AnnotationEnum::class, $foo[0]); |
|
443
|
|
|
|
|
444
|
|
|
try { |
|
445
|
|
|
$reader->getPropertyAnnotations($class->getProperty('invalidProperty')); |
|
446
|
|
|
$this->fail(); |
|
447
|
|
|
} catch (AnnotationException $exc) { |
|
448
|
|
|
self::assertEquals('[Enum Error] Attribute "value" of @Doctrine\Tests\Annotations\Fixtures\AnnotationEnum declared on property Doctrine\Tests\Annotations\Fixtures\ClassWithAnnotationEnum::$invalidProperty accept only [ONE, TWO, THREE], but got FOUR.', $exc->getMessage()); |
|
449
|
|
|
} |
|
450
|
|
|
|
|
451
|
|
|
try { |
|
452
|
|
|
$reader->getMethodAnnotations($class->getMethod('invalidMethod')); |
|
453
|
|
|
$this->fail(); |
|
454
|
|
|
} catch (AnnotationException $exc) { |
|
455
|
|
|
self::assertEquals('[Enum Error] Attribute "value" of @Doctrine\Tests\Annotations\Fixtures\AnnotationEnum declared on method Doctrine\Tests\Annotations\Fixtures\ClassWithAnnotationEnum::invalidMethod() accept only [ONE, TWO, THREE], but got 5.', $exc->getMessage()); |
|
456
|
|
|
} |
|
457
|
|
|
} |
|
458
|
|
|
|
|
459
|
|
|
/** |
|
460
|
|
|
* @group DCOM-106 |
|
461
|
|
|
*/ |
|
462
|
|
|
public function testIgnoreFixMeAndUpperCaseToDo() |
|
463
|
|
|
{ |
|
464
|
|
|
$reader = $this->getReader(); |
|
465
|
|
|
$ref = new \ReflectionClass(DCOM106::class); |
|
466
|
|
|
|
|
467
|
|
|
self::assertEmpty($reader->getClassAnnotations($ref)); |
|
468
|
|
|
} |
|
469
|
|
|
|
|
470
|
|
|
/** |
|
471
|
|
|
* @return AnnotationReader |
|
472
|
|
|
*/ |
|
473
|
|
|
abstract protected function getReader(); |
|
474
|
|
|
} |
|
475
|
|
|
|
|
476
|
|
|
/** |
|
477
|
|
|
* @parseAnnotation("var") |
|
478
|
|
|
* @author Johannes M. Schmitt <[email protected]> |
|
479
|
|
|
* |
|
480
|
|
|
*/ |
|
481
|
|
|
class TestParseAnnotationClass |
|
482
|
|
|
{ |
|
483
|
|
|
/** |
|
484
|
|
|
* @var |
|
485
|
|
|
*/ |
|
486
|
|
|
private $field; |
|
|
|
|
|
|
487
|
|
|
} |
|
488
|
|
|
|
|
489
|
|
|
/** |
|
490
|
|
|
* @Name |
|
491
|
|
|
* @author Johannes M. Schmitt <[email protected]> |
|
492
|
|
|
*/ |
|
493
|
|
|
class TestIgnoresNonAnnotationsClass |
|
494
|
|
|
{ |
|
495
|
|
|
} |
|
496
|
|
|
|
|
497
|
|
|
class TestTopLevelAnnotationClass |
|
498
|
|
|
{ |
|
499
|
|
|
/** |
|
500
|
|
|
* @\TopLevelAnnotation |
|
501
|
|
|
*/ |
|
502
|
|
|
private $field; |
|
503
|
|
|
} |
|
504
|
|
|
|
|
505
|
|
|
class TestNonExistentAnnotationClass |
|
506
|
|
|
{ |
|
507
|
|
|
/** |
|
508
|
|
|
* @Foo\Bar\Name |
|
509
|
|
|
*/ |
|
510
|
|
|
private $field; |
|
511
|
|
|
} |
|
512
|
|
|
|
|
513
|
|
|
class TestAnnotationNotImportedClass |
|
514
|
|
|
{ |
|
515
|
|
|
/** |
|
516
|
|
|
* @NameFoo |
|
517
|
|
|
*/ |
|
518
|
|
|
private $field; |
|
519
|
|
|
} |
|
520
|
|
|
|
|
521
|
|
|
class TestChildClass |
|
522
|
|
|
{ |
|
523
|
|
|
/** |
|
524
|
|
|
* @\Doctrine\Tests\Annotations\Foo\Name(name = "foo") |
|
525
|
|
|
*/ |
|
526
|
|
|
protected $child; |
|
527
|
|
|
} |
|
528
|
|
|
|
|
529
|
|
|
class TestParentClass extends TestChildClass |
|
530
|
|
|
{ |
|
531
|
|
|
/** |
|
532
|
|
|
* @\Doctrine\Tests\Annotations\Bar\Name(name = "bar") |
|
533
|
|
|
*/ |
|
534
|
|
|
private $parent; |
|
|
|
|
|
|
535
|
|
|
} |
|
536
|
|
|
|
|
537
|
|
|
class TestImportWithConcreteAnnotation |
|
538
|
|
|
{ |
|
539
|
|
|
/** |
|
540
|
|
|
* @DummyAnnotation(dummyValue = "bar") |
|
541
|
|
|
*/ |
|
542
|
|
|
private $field; |
|
543
|
|
|
} |
|
544
|
|
|
|
|
545
|
|
|
/** |
|
546
|
|
|
* @ignoreAnnotation("var") |
|
547
|
|
|
*/ |
|
548
|
|
|
class DummyClass2 { |
|
549
|
|
|
/** |
|
550
|
|
|
* @DummyId @DummyColumn(type="integer") @DummyGeneratedValue |
|
551
|
|
|
* @var integer |
|
552
|
|
|
*/ |
|
553
|
|
|
private $id; |
|
|
|
|
|
|
554
|
|
|
} |
|
555
|
|
|
|
|
556
|
|
|
/** @Annotation */ |
|
557
|
|
|
class DummyId extends Annotation {} |
|
558
|
|
|
/** @Annotation */ |
|
559
|
|
|
class DummyColumn extends Annotation { |
|
560
|
|
|
public $type; |
|
561
|
|
|
} |
|
562
|
|
|
/** @Annotation */ |
|
563
|
|
|
class DummyGeneratedValue extends Annotation {} |
|
564
|
|
|
/** @Annotation */ |
|
565
|
|
|
class DummyAnnotation extends Annotation { |
|
566
|
|
|
public $dummyValue; |
|
567
|
|
|
} |
|
568
|
|
|
|
|
569
|
|
|
/** |
|
570
|
|
|
* @api |
|
571
|
|
|
* @Annotation |
|
572
|
|
|
*/ |
|
573
|
|
|
class DummyAnnotationWithIgnoredAnnotation extends Annotation { |
|
574
|
|
|
public $dummyValue; |
|
575
|
|
|
} |
|
576
|
|
|
|
|
577
|
|
|
/** @Annotation */ |
|
578
|
|
|
class DummyJoinColumn extends Annotation { |
|
579
|
|
|
public $name; |
|
580
|
|
|
public $referencedColumnName; |
|
581
|
|
|
} |
|
582
|
|
|
/** @Annotation */ |
|
583
|
|
|
class DummyJoinTable extends Annotation { |
|
584
|
|
|
public $name; |
|
585
|
|
|
public $joinColumns; |
|
586
|
|
|
public $inverseJoinColumns; |
|
587
|
|
|
} |
|
588
|
|
|
|
|
589
|
|
|
/** |
|
590
|
|
|
* @DummyAnnotation(dummyValue = "bar",) |
|
591
|
|
|
*/ |
|
592
|
|
|
class DummyClassWithDanglingComma |
|
593
|
|
|
{ |
|
594
|
|
|
} |
|
595
|
|
|
|
|
596
|
|
|
/** |
|
597
|
|
|
* @DummyAnnotation(@) |
|
598
|
|
|
*/ |
|
599
|
|
|
class DummyClassSyntaxError |
|
600
|
|
|
{ |
|
601
|
|
|
|
|
602
|
|
|
} |
|
603
|
|
|
|
|
604
|
|
|
class DummyClassMethodSyntaxError |
|
605
|
|
|
{ |
|
606
|
|
|
/** |
|
607
|
|
|
* @DummyAnnotation(@) |
|
608
|
|
|
*/ |
|
609
|
|
|
public function foo() |
|
610
|
|
|
{ |
|
611
|
|
|
|
|
612
|
|
|
} |
|
613
|
|
|
} |
|
614
|
|
|
|
|
615
|
|
|
class DummyClassPropertySyntaxError |
|
616
|
|
|
{ |
|
617
|
|
|
/** |
|
618
|
|
|
* @DummyAnnotation(@) |
|
619
|
|
|
*/ |
|
620
|
|
|
public $foo; |
|
621
|
|
|
} |
|
622
|
|
|
|
|
623
|
|
|
/** |
|
624
|
|
|
* @ignoreAnnotation({"since", "var"}) |
|
625
|
|
|
*/ |
|
626
|
|
|
class DummyClassNonAnnotationProblem |
|
627
|
|
|
{ |
|
628
|
|
|
/** |
|
629
|
|
|
* @DummyAnnotation |
|
630
|
|
|
* |
|
631
|
|
|
* @var \Test |
|
632
|
|
|
* @since 0.1 |
|
633
|
|
|
*/ |
|
634
|
|
|
public $foo; |
|
635
|
|
|
} |
|
636
|
|
|
|
|
637
|
|
|
|
|
638
|
|
|
/** |
|
639
|
|
|
* @DummyAnnotation Foo bar <[email protected]> |
|
640
|
|
|
*/ |
|
641
|
|
|
class DummyClassWithEmail |
|
642
|
|
|
{ |
|
643
|
|
|
|
|
644
|
|
|
} |
|
645
|
|
|
|
|
646
|
|
|
|
|
647
|
|
|
/** |
|
648
|
|
|
* @fixme public |
|
649
|
|
|
* @TODO |
|
650
|
|
|
*/ |
|
651
|
|
|
class DCOM106 |
|
652
|
|
|
{ |
|
653
|
|
|
|
|
654
|
|
|
} |
|
655
|
|
|
|
|
656
|
|
|
namespace Doctrine\Tests\Annotations\Foo; |
|
657
|
|
|
|
|
658
|
|
|
use Doctrine\Annotations\Annotation; |
|
659
|
|
|
|
|
660
|
|
|
/** @Annotation */ |
|
661
|
|
|
class Name extends Annotation |
|
662
|
|
|
{ |
|
663
|
|
|
public $name; |
|
664
|
|
|
} |
|
665
|
|
|
|
|
666
|
|
|
namespace Doctrine\Tests\Annotations\Bar; |
|
667
|
|
|
|
|
668
|
|
|
use Doctrine\Annotations\Annotation; |
|
669
|
|
|
|
|
670
|
|
|
/** @Annotation */ |
|
671
|
|
|
class Name extends Annotation |
|
672
|
|
|
{ |
|
673
|
|
|
public $name; |
|
674
|
|
|
} |
|
675
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.