AbstractReaderTest.php 28 KB

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