AttributeTransformerTest.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of PHP CS Fixer.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. namespace PhpCsFixer\Tests\Tokenizer\Transformer;
  13. use PhpCsFixer\Tests\Test\AbstractTransformerTestCase;
  14. use PhpCsFixer\Tokenizer\CT;
  15. use PhpCsFixer\Tokenizer\Tokens;
  16. /**
  17. * @internal
  18. *
  19. * @covers \PhpCsFixer\Tokenizer\Transformer\AttributeTransformer
  20. *
  21. * @phpstan-import-type _TransformerTestExpectedTokens from AbstractTransformerTestCase
  22. */
  23. final class AttributeTransformerTest extends AbstractTransformerTestCase
  24. {
  25. /**
  26. * @param _TransformerTestExpectedTokens $expectedTokens
  27. *
  28. * @dataProvider provideProcessCases
  29. *
  30. * @requires PHP 8.0
  31. */
  32. public function testProcess(string $source, array $expectedTokens): void
  33. {
  34. $this->doTest($source, $expectedTokens);
  35. }
  36. public static function provideProcessCases(): iterable
  37. {
  38. yield ['<?php class Foo {
  39. #[Listens(ProductCreatedEvent::class)]
  40. public $foo;
  41. }
  42. ',
  43. [
  44. 14 => CT::T_ATTRIBUTE_CLOSE,
  45. ],
  46. ];
  47. yield ['<?php class Foo {
  48. #[Required]
  49. public $bar;
  50. }',
  51. [
  52. 9 => CT::T_ATTRIBUTE_CLOSE,
  53. ],
  54. ];
  55. yield [
  56. '<?php function foo(
  57. #[MyAttr([1, 2])] Type $myParam,
  58. ) {}',
  59. [
  60. 16 => CT::T_ATTRIBUTE_CLOSE,
  61. ],
  62. ];
  63. yield [
  64. '<?php class Foo {
  65. #[ORM\Column("string", ORM\Column::UNIQUE)]
  66. #[Assert\Email(["message" => "The email {{ value }} is not a valid email."])]
  67. private $email;
  68. }',
  69. [
  70. 21 => CT::T_ATTRIBUTE_CLOSE,
  71. 36 => CT::T_ATTRIBUTE_CLOSE,
  72. ],
  73. ];
  74. yield [
  75. '<?php
  76. #[ORM\Id]
  77. #[ConditionalDeclare(PHP_VERSION_ID < 70000+1**2-1>>9+foo(a)+foo((bool)$b))] // gets removed from AST when >= 7.0
  78. #[IgnoreRedeclaration] // throws no error when already declared, removes the redeclared thing
  79. function intdiv(int $numerator, int $divisor) {
  80. }
  81. #[
  82. Attr1("foo"),Attr2("bar"),
  83. ]
  84. #[PhpAttribute(self::IS_REPEATABLE)]
  85. class Route
  86. {
  87. }
  88. ',
  89. [
  90. 5 => CT::T_ATTRIBUTE_CLOSE,
  91. 35 => CT::T_ATTRIBUTE_CLOSE,
  92. 41 => CT::T_ATTRIBUTE_CLOSE,
  93. 76 => CT::T_ATTRIBUTE_CLOSE,
  94. 85 => CT::T_ATTRIBUTE_CLOSE,
  95. ],
  96. ];
  97. yield [
  98. '<?php
  99. #[Jit]
  100. function foo() {}
  101. class Foo
  102. {
  103. #[ExampleAttribute]
  104. public const FOO = "foo";
  105. #[ExampleAttribute]
  106. public function foo(#[ExampleAttribute] Type $bar) {}
  107. }
  108. $object = new #[ExampleAttribute] class () {};
  109. $f1 = #[ExampleAttribute] function () {};
  110. $f2 = #[ExampleAttribute] fn() => 1;
  111. ',
  112. [
  113. 3 => CT::T_ATTRIBUTE_CLOSE,
  114. 22 => CT::T_ATTRIBUTE_CLOSE,
  115. 37 => CT::T_ATTRIBUTE_CLOSE,
  116. 47 => CT::T_ATTRIBUTE_CLOSE,
  117. 67 => CT::T_ATTRIBUTE_CLOSE,
  118. 84 => CT::T_ATTRIBUTE_CLOSE,
  119. 101 => CT::T_ATTRIBUTE_CLOSE,
  120. ],
  121. ];
  122. yield [
  123. '<?php
  124. #[
  125. ORM\Entity,
  126. ORM\Table("user")
  127. ]
  128. class User
  129. {
  130. #[ORM\Id, ORM\Column("integer"), ORM\GeneratedValue]
  131. private $id;
  132. #[ORM\Column("string", ORM\Column::UNIQUE)]
  133. #[Assert\Email(["message" => "The email \'{{ value }}\' is not a valid email."])]
  134. private $email;
  135. #[\Doctrine\ORM\ManyToMany(
  136. targetEntity: User::class,
  137. joinColumn: "group_id",
  138. inverseJoinColumn: "user_id",
  139. cascade: array("persist", "remove")
  140. )]
  141. #[Assert\Valid]
  142. #[JMSSerializer\XmlList(inline: true, entry: "user")]
  143. public $users;
  144. }
  145. ',
  146. [
  147. 15 => CT::T_ATTRIBUTE_CLOSE,
  148. 40 => CT::T_ATTRIBUTE_CLOSE,
  149. 61 => CT::T_ATTRIBUTE_CLOSE,
  150. 76 => CT::T_ATTRIBUTE_CLOSE,
  151. 124 => CT::T_ATTRIBUTE_CLOSE,
  152. 130 => CT::T_ATTRIBUTE_CLOSE,
  153. 148 => CT::T_ATTRIBUTE_CLOSE,
  154. ],
  155. ];
  156. }
  157. /**
  158. * @dataProvider provideNotChangeCases
  159. */
  160. public function testNotChange(string $source): void
  161. {
  162. Tokens::clearCache();
  163. foreach (Tokens::fromCode($source) as $token) {
  164. self::assertFalse($token->isGivenKind([
  165. CT::T_ATTRIBUTE_CLOSE,
  166. ]));
  167. }
  168. }
  169. /**
  170. * @return iterable<array{string}>
  171. */
  172. public static function provideNotChangeCases(): iterable
  173. {
  174. yield [
  175. '<?php
  176. $foo = [];
  177. $a[] = $b[1];
  178. $c = $d[2];
  179. // [$e] = $f;',
  180. ];
  181. yield [
  182. '<?php [$e] = $f;',
  183. ];
  184. }
  185. }