OrderedAttributesFixerTest.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905
  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\Fixer\AttributeNotation;
  13. use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
  14. use PhpCsFixer\Fixer\AttributeNotation\OrderedAttributesFixer;
  15. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  16. /**
  17. * @author HypeMC <hypemc@gmail.com>
  18. *
  19. * @internal
  20. *
  21. * @covers \PhpCsFixer\Fixer\AttributeNotation\OrderedAttributesFixer
  22. *
  23. * @requires PHP 8.0
  24. *
  25. * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\AttributeNotation\OrderedAttributesFixer>
  26. *
  27. * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\AttributeNotation\OrderedAttributesFixer
  28. */
  29. final class OrderedAttributesFixerTest extends AbstractFixerTestCase
  30. {
  31. /**
  32. * @dataProvider provideInvalidConfigurationCases
  33. *
  34. * @param _AutogeneratedInputConfiguration $configuration
  35. */
  36. public function testInvalidConfiguration(array $configuration, string $expectedExceptionMessage): void
  37. {
  38. self::expectException(InvalidFixerConfigurationException::class);
  39. self::expectExceptionMessage($expectedExceptionMessage);
  40. $this->fixer->configure($configuration);
  41. }
  42. /**
  43. * @return iterable<array{0: array{sort_algorithm: OrderedAttributesFixer::ORDER_*, order?: list<string>}, 1: string}>
  44. */
  45. public static function provideInvalidConfigurationCases(): iterable
  46. {
  47. yield 'Custom order strategy without `order` option' => [
  48. ['sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM],
  49. 'The custom order strategy requires providing `order` option with a list of attributes\'s FQNs.',
  50. ];
  51. yield 'Custom order strategy with empty `order` option' => [
  52. [
  53. 'sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM,
  54. 'order' => [],
  55. ],
  56. 'The custom order strategy requires providing `order` option with a list of attributes\'s FQNs.',
  57. ];
  58. yield 'Non unique attributes throw an exception' => [
  59. [
  60. 'sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM,
  61. 'order' => ['A\B\Bar', 'Test\Corge', 'A\B\Bar'],
  62. ],
  63. 'The list includes attributes that are not unique.',
  64. ];
  65. }
  66. /**
  67. * @param _AutogeneratedInputConfiguration $configuration
  68. *
  69. * @dataProvider provideFixCases
  70. */
  71. public function testFix(string $expected, ?string $input = null, array $configuration = []): void
  72. {
  73. $this->fixer->configure($configuration);
  74. $this->doTest($expected, $input);
  75. }
  76. /**
  77. * @return iterable<array{0: string, 1?: string, 2?: array<string, mixed>}>
  78. */
  79. public static function provideFixCases(): iterable
  80. {
  81. yield 'Alpha on various declarations' => [
  82. '<?php
  83. namespace Test;
  84. use A\B\Foo;
  85. use A\B\Bar as BarAlias;
  86. use A\B as AB;
  87. #[AB\Baz(prop: \'baz\')]
  88. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  89. #[\A\B\Qux()]
  90. #[BarAlias(3)]
  91. #[Corge]
  92. #[Foo(4, \'baz qux\')]
  93. class X
  94. {
  95. #[AB\Baz(prop: \'baz\')]
  96. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  97. #[\A\B\Qux()]
  98. #[BarAlias(3)]
  99. #[Corge]
  100. #[Foo(4, \'baz qux\')]
  101. const Y = 1;
  102. #[AB\Baz(prop: \'baz\')]
  103. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  104. #[\A\B\Qux()]
  105. #[BarAlias(3)]
  106. #[Corge]
  107. #[Foo(4, \'baz qux\')]
  108. public $y;
  109. #[AB\Baz(prop: \'baz\')]
  110. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  111. #[\A\B\Qux()]
  112. #[BarAlias(3)]
  113. #[Corge]
  114. #[Foo(4, \'baz qux\')]
  115. public function y() {}
  116. }
  117. #[AB\Baz(prop: \'baz\')]
  118. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  119. #[\A\B\Qux()]
  120. #[BarAlias(3)]
  121. #[Corge]
  122. #[Foo(4, \'baz qux\')]
  123. function f(
  124. #[AB\Baz(prop: \'baz\')]
  125. #[BarAlias(3)]
  126. #[Foo(4, \'baz qux\')]
  127. string $param1,
  128. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  129. #[\A\B\Qux()]
  130. #[Corge]
  131. string $param2
  132. ) {}
  133. function f2(#[BarAlias(3)] #[Foo(4, \'baz qux\')] string $param1, #[\A\B\Qux()] #[Corge] string $param2) {}
  134. $anon = #[AB\Baz(prop: \'baz\')] #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')] #[\A\B\Qux()] #[BarAlias(3)] #[Corge] #[Foo(4, \'baz qux\')] function () {};
  135. $short = #[AB\Baz(prop: \'baz\')] #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')] #[\A\B\Qux()] #[BarAlias(3)] #[Corge] #[Foo(4, \'baz qux\')] fn () => null;
  136. ',
  137. '<?php
  138. namespace Test;
  139. use A\B\Foo;
  140. use A\B\Bar as BarAlias;
  141. use A\B as AB;
  142. #[Foo(4, \'baz qux\')]
  143. #[BarAlias(3)]
  144. #[AB\Baz(prop: \'baz\')]
  145. #[\A\B\Qux()]
  146. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  147. #[Corge]
  148. class X
  149. {
  150. #[Foo(4, \'baz qux\')]
  151. #[BarAlias(3)]
  152. #[AB\Baz(prop: \'baz\')]
  153. #[\A\B\Qux()]
  154. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  155. #[Corge]
  156. const Y = 1;
  157. #[Foo(4, \'baz qux\')]
  158. #[BarAlias(3)]
  159. #[AB\Baz(prop: \'baz\')]
  160. #[\A\B\Qux()]
  161. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  162. #[Corge]
  163. public $y;
  164. #[Foo(4, \'baz qux\')]
  165. #[BarAlias(3)]
  166. #[AB\Baz(prop: \'baz\')]
  167. #[\A\B\Qux()]
  168. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  169. #[Corge]
  170. public function y() {}
  171. }
  172. #[Foo(4, \'baz qux\')]
  173. #[BarAlias(3)]
  174. #[AB\Baz(prop: \'baz\')]
  175. #[\A\B\Qux()]
  176. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  177. #[Corge]
  178. function f(
  179. #[Foo(4, \'baz qux\')]
  180. #[BarAlias(3)]
  181. #[AB\Baz(prop: \'baz\')]
  182. string $param1,
  183. #[\A\B\Qux()]
  184. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  185. #[Corge]
  186. string $param2
  187. ) {}
  188. function f2(#[Foo(4, \'baz qux\')] #[BarAlias(3)] string $param1, #[\A\B\Qux()] #[Corge] string $param2) {}
  189. $anon = #[Foo(4, \'baz qux\')] #[BarAlias(3)] #[AB\Baz(prop: \'baz\')] #[\A\B\Qux()] #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')] #[Corge] function () {};
  190. $short = #[Foo(4, \'baz qux\')] #[BarAlias(3)] #[AB\Baz(prop: \'baz\')] #[\A\B\Qux()] #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')] #[Corge] fn () => null;
  191. ',
  192. ];
  193. yield 'Explicit in namespace' => [
  194. '<?php
  195. namespace Test;
  196. use A\B\Foo;
  197. use A\B\Bar as BarAlias;
  198. use A\B as AB;
  199. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  200. #[BarAlias(3)]
  201. #[Corge]
  202. #[AB\Baz(prop: \'baz\')]
  203. #[Foo(4, \'baz qux\')]
  204. #[\A\B\Qux()]
  205. function f() {}
  206. ',
  207. '<?php
  208. namespace Test;
  209. use A\B\Foo;
  210. use A\B\Bar as BarAlias;
  211. use A\B as AB;
  212. #[Foo(4, \'baz qux\')]
  213. #[BarAlias(3)]
  214. #[AB\Baz(prop: \'baz\')]
  215. #[\A\B\Qux()]
  216. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  217. #[Corge]
  218. function f() {}
  219. ',
  220. [
  221. 'sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM,
  222. 'order' => ['Test\A\B\Quux', 'A\B\Bar', 'Test\Corge', 'A\B\Baz', 'A\B\Foo', 'A\B\Qux'],
  223. ],
  224. ];
  225. yield 'Explicit in global namespace' => [
  226. '<?php
  227. use A\B\Foo;
  228. use A\B\Bar as BarAlias;
  229. use A\B as AB;
  230. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  231. #[BarAlias(3)]
  232. #[Corge]
  233. #[AB\Baz(prop: \'baz\')]
  234. #[Foo(4, \'baz qux\')]
  235. #[\A\B\Qux()]
  236. function f() {}
  237. ',
  238. '<?php
  239. use A\B\Foo;
  240. use A\B\Bar as BarAlias;
  241. use A\B as AB;
  242. #[Foo(4, \'baz qux\')]
  243. #[BarAlias(3)]
  244. #[AB\Baz(prop: \'baz\')]
  245. #[\A\B\Qux()]
  246. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  247. #[Corge]
  248. function f() {}
  249. ',
  250. [
  251. 'sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM,
  252. 'order' => ['A\B\Quux', 'A\B\Bar', 'Corge', 'A\B\Baz', 'A\B\Foo', 'A\B\Qux'],
  253. ],
  254. ];
  255. yield 'Explicit with unconfigured attributes' => [
  256. '<?php
  257. namespace Test;
  258. use A\B\Foo;
  259. use A\B\Bar as BarAlias;
  260. use A\B as AB;
  261. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  262. #[AB\Baz(prop: \'baz\')]
  263. #[\A\B\Qux()]
  264. #[Foo(4, \'baz qux\')]
  265. #[BarAlias(3)]
  266. #[Corge]
  267. function f() {}
  268. ',
  269. '<?php
  270. namespace Test;
  271. use A\B\Foo;
  272. use A\B\Bar as BarAlias;
  273. use A\B as AB;
  274. #[Foo(4, \'baz qux\')]
  275. #[BarAlias(3)]
  276. #[AB\Baz(prop: \'baz\')]
  277. #[\A\B\Qux()]
  278. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  279. #[Corge]
  280. function f() {}
  281. ',
  282. [
  283. 'sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM,
  284. 'order' => ['Test\A\B\Quux', 'A\B\Baz', 'A\B\Qux'],
  285. ],
  286. ];
  287. yield 'Multiple namespaces' => [
  288. '<?php
  289. namespace Test
  290. {
  291. use A\B\Foo;
  292. use A\B\Bar as BarAlias;
  293. #[BarAlias(3)]
  294. #[AB\Baz(prop: \'baz\')]
  295. #[Foo(4, \'baz qux\')]
  296. function f() {}
  297. }
  298. namespace Test2
  299. {
  300. use A\B\Bar as BarAlias;
  301. use A\B as AB;
  302. #[BarAlias(3)]
  303. #[\A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  304. #[AB\Baz(prop: \'baz\')]
  305. function f() {}
  306. }
  307. namespace Test
  308. {
  309. use A\B\Foo;
  310. use A\B\Bar as BarAlias2;
  311. #[BarAlias2(3)]
  312. #[AB\Baz(prop: \'baz\')]
  313. #[Foo(4, \'baz qux\')]
  314. function f2() {}
  315. }
  316. namespace
  317. {
  318. use A\B\Foo;
  319. use A\B\Bar as BarAlias3;
  320. #[BarAlias3(3)]
  321. #[Foo(4, \'baz qux\')]
  322. #[AB\Baz(prop: \'baz\')]
  323. function f() {}
  324. }
  325. ',
  326. '<?php
  327. namespace Test
  328. {
  329. use A\B\Foo;
  330. use A\B\Bar as BarAlias;
  331. #[AB\Baz(prop: \'baz\')]
  332. #[Foo(4, \'baz qux\')]
  333. #[BarAlias(3)]
  334. function f() {}
  335. }
  336. namespace Test2
  337. {
  338. use A\B\Bar as BarAlias;
  339. use A\B as AB;
  340. #[BarAlias(3)]
  341. #[AB\Baz(prop: \'baz\')]
  342. #[\A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  343. function f() {}
  344. }
  345. namespace Test
  346. {
  347. use A\B\Foo;
  348. use A\B\Bar as BarAlias2;
  349. #[AB\Baz(prop: \'baz\')]
  350. #[Foo(4, \'baz qux\')]
  351. #[BarAlias2(3)]
  352. function f2() {}
  353. }
  354. namespace
  355. {
  356. use A\B\Foo;
  357. use A\B\Bar as BarAlias3;
  358. #[AB\Baz(prop: \'baz\')]
  359. #[Foo(4, \'baz qux\')]
  360. #[BarAlias3(3)]
  361. function f() {}
  362. }
  363. ',
  364. [
  365. 'sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM,
  366. 'order' => ['A\B\Bar', 'Test\AB\Baz', 'A\B\Quux', 'A\B\Baz', 'A\B\Foo', 'AB\Baz'],
  367. ],
  368. ];
  369. yield 'With whitespaces' => [
  370. '<?php
  371. namespace Test;
  372. use A\B\Foo;
  373. use A\B\Bar as BarAlias;
  374. use A\B as AB;
  375. #[ AB\Baz (prop: \'baz\') ]
  376. #[ A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\') ]
  377. #[ \A\B\Qux() ]
  378. #[ BarAlias (3) ]
  379. #[ Corge ]
  380. #[ Foo (4, \'baz qux\') ]
  381. function f() {}
  382. ',
  383. '<?php
  384. namespace Test;
  385. use A\B\Foo;
  386. use A\B\Bar as BarAlias;
  387. use A\B as AB;
  388. #[ Foo (4, \'baz qux\') ]
  389. #[ BarAlias (3) ]
  390. #[ AB\Baz (prop: \'baz\') ]
  391. #[ \A\B\Qux() ]
  392. #[ A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\') ]
  393. #[ Corge ]
  394. function f() {}
  395. ',
  396. ];
  397. yield 'With docblock' => [
  398. '<?php
  399. namespace Test;
  400. use A\B\Foo;
  401. use A\B\Bar as BarAlias;
  402. use A\B as AB;
  403. /**
  404. * Start docblock
  405. */
  406. /**
  407. * AB\Baz docblock
  408. */
  409. #[AB\Baz(prop: \'baz\')]
  410. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  411. #[\A\B\Qux()]
  412. #[BarAlias(3)]
  413. /**
  414. * Corge docblock
  415. */
  416. #[Corge]
  417. #[Foo(4, \'baz qux\')]
  418. /**
  419. * End docblock
  420. */
  421. class X
  422. {}
  423. function f2(/** Start docblock */#[\A\B\Qux()] #[BarAlias(3)] /** Corge docblock */#[Corge] #[Foo(4, \'baz qux\')] /** End docblock */string $param) {}
  424. ',
  425. '<?php
  426. namespace Test;
  427. use A\B\Foo;
  428. use A\B\Bar as BarAlias;
  429. use A\B as AB;
  430. /**
  431. * Start docblock
  432. */
  433. #[Foo(4, \'baz qux\')]
  434. #[BarAlias(3)]
  435. /**
  436. * AB\Baz docblock
  437. */
  438. #[AB\Baz(prop: \'baz\')]
  439. #[\A\B\Qux()]
  440. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  441. /**
  442. * Corge docblock
  443. */
  444. #[Corge]
  445. /**
  446. * End docblock
  447. */
  448. class X
  449. {}
  450. function f2(/** Start docblock */#[Foo(4, \'baz qux\')] #[BarAlias(3)] #[\A\B\Qux()] /** Corge docblock */#[Corge] /** End docblock */string $param) {}
  451. ',
  452. ];
  453. yield 'With comments' => [
  454. '<?php
  455. namespace Test;
  456. use A\B\Foo;
  457. use A\B\Bar as BarAlias;
  458. use A\B as AB;
  459. #[/* comment */A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\') /* comment */]
  460. #[ /* comment */ BarAlias/* comment */(3)/* comment */]
  461. #[/* comment */ Corge/* comment */]
  462. #[/* comment */AB\Baz /* comment */ (prop: \'baz\') /* comment */ ]
  463. #[/* comment */Foo/* comment */(4, \'baz qux\') /* comment */ ]
  464. #[ /* comment */ \A\B\Qux()/* comment */]
  465. function f() {}
  466. ',
  467. '<?php
  468. namespace Test;
  469. use A\B\Foo;
  470. use A\B\Bar as BarAlias;
  471. use A\B as AB;
  472. #[/* comment */Foo/* comment */(4, \'baz qux\') /* comment */ ]
  473. #[ /* comment */ BarAlias/* comment */(3)/* comment */]
  474. #[/* comment */AB\Baz /* comment */ (prop: \'baz\') /* comment */ ]
  475. #[ /* comment */ \A\B\Qux()/* comment */]
  476. #[/* comment */A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\') /* comment */]
  477. #[/* comment */ Corge/* comment */]
  478. function f() {}
  479. ',
  480. [
  481. 'sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM,
  482. 'order' => ['Test\A\B\Quux', 'A\B\Bar', 'Test\Corge', 'A\B\Baz', 'A\B\Foo', 'A\B\Qux'],
  483. ],
  484. ];
  485. yield 'Alpha with multiple attributes' => [
  486. '<?php
  487. namespace Test;
  488. use A\B\Foo;
  489. use A\B\Bar as BarAlias;
  490. use A\B as AB;
  491. #[
  492. AB\Baz(prop: \'baz\'),
  493. A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),
  494. \A\B\Qux(),
  495. BarAlias(3),
  496. Corge,
  497. Foo(4, \'baz qux\'),
  498. ]
  499. class X
  500. {
  501. #[ AB\Baz(prop: \'baz\'), A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'), \A\B\Qux(), BarAlias(3), Corge,Foo(4, \'baz qux\')]
  502. public function y() {}
  503. }
  504. ',
  505. '<?php
  506. namespace Test;
  507. use A\B\Foo;
  508. use A\B\Bar as BarAlias;
  509. use A\B as AB;
  510. #[
  511. Foo(4, \'baz qux\'),
  512. BarAlias(3),
  513. AB\Baz(prop: \'baz\'),
  514. \A\B\Qux(),
  515. A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),
  516. Corge,
  517. ]
  518. class X
  519. {
  520. #[Foo(4, \'baz qux\'), BarAlias(3), AB\Baz(prop: \'baz\'), \A\B\Qux(), A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'), Corge]
  521. public function y() {}
  522. }
  523. ',
  524. ];
  525. yield 'Explicit with multiple attributes' => [
  526. '<?php
  527. namespace Test;
  528. use A\B\Foo;
  529. use A\B\Bar as BarAlias;
  530. use A\B as AB;
  531. #[
  532. A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),
  533. BarAlias(3),
  534. Corge,
  535. AB\Baz(prop: \'baz\'),
  536. \A\B\Qux(),
  537. Foo(4, \'baz qux\'),
  538. ]
  539. class X
  540. {
  541. #[ A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'), BarAlias(3), Corge, AB\Baz(prop: \'baz\'), \A\B\Qux(),Foo(4, \'baz qux\')]
  542. public function y() {}
  543. }
  544. ',
  545. '<?php
  546. namespace Test;
  547. use A\B\Foo;
  548. use A\B\Bar as BarAlias;
  549. use A\B as AB;
  550. #[
  551. Foo(4, \'baz qux\'),
  552. BarAlias(3),
  553. AB\Baz(prop: \'baz\'),
  554. \A\B\Qux(),
  555. A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),
  556. Corge,
  557. ]
  558. class X
  559. {
  560. #[Foo(4, \'baz qux\'), BarAlias(3), AB\Baz(prop: \'baz\'), \A\B\Qux(), A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'), Corge]
  561. public function y() {}
  562. }
  563. ',
  564. [
  565. 'sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM,
  566. 'order' => ['Test\A\B\Quux', 'A\B\Bar', 'Test\Corge', 'A\B\Baz', 'A\B\Qux'],
  567. ],
  568. ];
  569. yield 'Multiline with no trailing comma' => [
  570. '<?php
  571. namespace Test;
  572. use A\B\Foo;
  573. use A\B\Bar as BarAlias;
  574. use A\B as AB;
  575. #[
  576. AB\Baz(prop: \'baz\'),
  577. A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),
  578. \A\B\Qux(),
  579. BarAlias(3),
  580. Corge,
  581. Foo(4, \'baz qux\')
  582. ]
  583. class X
  584. {}
  585. ',
  586. '<?php
  587. namespace Test;
  588. use A\B\Foo;
  589. use A\B\Bar as BarAlias;
  590. use A\B as AB;
  591. #[
  592. Foo(4, \'baz qux\'),
  593. BarAlias(3),
  594. AB\Baz(prop: \'baz\'),
  595. \A\B\Qux(),
  596. A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),
  597. Corge
  598. ]
  599. class X
  600. {}
  601. ',
  602. ];
  603. yield 'Multiple with comments' => [
  604. '<?php
  605. namespace Test;
  606. use A\B\Foo;
  607. use A\B\Bar as BarAlias;
  608. use A\B as AB;
  609. #[
  610. /*
  611. * AB\Baz comment
  612. */
  613. AB\Baz(prop: \'baz\'),
  614. A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),
  615. \A\B\Qux(),
  616. BarAlias(3),
  617. /*
  618. * Corge comment
  619. */
  620. Corge,
  621. /**
  622. * Foo docblock
  623. */
  624. Foo(4, \'baz qux\'),
  625. ]
  626. class X
  627. {
  628. #[ /* AB\Baz comment */AB\Baz(prop: \'baz\'), A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'), \A\B\Qux(), BarAlias(3), /* Corge comment */Corge,/** Foo docblock */Foo(4, \'baz qux\')]
  629. public function y() {}
  630. }
  631. ',
  632. '<?php
  633. namespace Test;
  634. use A\B\Foo;
  635. use A\B\Bar as BarAlias;
  636. use A\B as AB;
  637. #[
  638. /**
  639. * Foo docblock
  640. */
  641. Foo(4, \'baz qux\'),
  642. BarAlias(3),
  643. /*
  644. * AB\Baz comment
  645. */
  646. AB\Baz(prop: \'baz\'),
  647. \A\B\Qux(),
  648. A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),
  649. /*
  650. * Corge comment
  651. */
  652. Corge,
  653. ]
  654. class X
  655. {
  656. #[/** Foo docblock */Foo(4, \'baz qux\'), BarAlias(3), /* AB\Baz comment */AB\Baz(prop: \'baz\'), \A\B\Qux(), A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'), /* Corge comment */Corge]
  657. public function y() {}
  658. }
  659. ',
  660. ];
  661. yield 'Alpha with mixed multiple attribute declarations and attributes' => [
  662. '<?php
  663. namespace Test;
  664. use A\B\Foo;
  665. use A\B\Bar as BarAlias;
  666. use A\B as AB;
  667. #[AB\Baz(prop: \'baz\')]
  668. #[ A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),Corge]
  669. #[ \A\B\Qux(), BarAlias(3),Foo(4, \'baz qux\')]
  670. function f() {}
  671. ',
  672. '<?php
  673. namespace Test;
  674. use A\B\Foo;
  675. use A\B\Bar as BarAlias;
  676. use A\B as AB;
  677. #[Foo(4, \'baz qux\'), \A\B\Qux(), BarAlias(3)]
  678. #[AB\Baz(prop: \'baz\')]
  679. #[Corge, A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  680. function f() {}
  681. ',
  682. ];
  683. yield 'Explicit with mixed multiple attribute declarations and attributes' => [
  684. '<?php
  685. namespace Test;
  686. use A\B\Foo;
  687. use A\B\Bar as BarAlias;
  688. use A\B as AB;
  689. #[ A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),Corge]
  690. #[ BarAlias(3), \A\B\Qux(),Foo(4, \'baz qux\')]
  691. #[AB\Baz(prop: \'baz\')]
  692. function f() {}
  693. ',
  694. '<?php
  695. namespace Test;
  696. use A\B\Foo;
  697. use A\B\Bar as BarAlias;
  698. use A\B as AB;
  699. #[Foo(4, \'baz qux\'), \A\B\Qux(), BarAlias(3)]
  700. #[AB\Baz(prop: \'baz\')]
  701. #[Corge, A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  702. function f() {}
  703. ',
  704. [
  705. 'sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM,
  706. 'order' => ['Test\A\B\Quux', 'A\B\Bar', 'Test\Corge', 'A\B\Baz', 'A\B\Qux'],
  707. ],
  708. ];
  709. }
  710. /**
  711. * @param _AutogeneratedInputConfiguration $configuration
  712. *
  713. * @dataProvider provideFix81Cases
  714. *
  715. * @requires PHP 8.1
  716. */
  717. public function testFix81(string $expected, ?string $input = null, array $configuration = []): void
  718. {
  719. $this->fixer->configure($configuration);
  720. $this->doTest($expected, $input);
  721. }
  722. /**
  723. * @return iterable<array{0: string, 1?: string, 2?: array<string, mixed>}>
  724. */
  725. public static function provideFix81Cases(): iterable
  726. {
  727. yield 'Alpha with nested attribute' => [
  728. '<?php
  729. namespace Test;
  730. use A\B\Foo;
  731. use A\B\Bar as BarAlias;
  732. use A\B as AB;
  733. #[AB\Baz(prop: new P\R())]
  734. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  735. #[\A\B\Qux()]
  736. #[BarAlias(3)]
  737. #[Corge]
  738. #[Foo(4, \'baz qux\')]
  739. function f() {}
  740. ',
  741. '<?php
  742. namespace Test;
  743. use A\B\Foo;
  744. use A\B\Bar as BarAlias;
  745. use A\B as AB;
  746. #[Foo(4, \'baz qux\')]
  747. #[BarAlias(3)]
  748. #[AB\Baz(prop: new P\R())]
  749. #[\A\B\Qux()]
  750. #[A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\')]
  751. #[Corge]
  752. function f() {}
  753. ',
  754. ];
  755. yield 'Explicit multiple with nested attribute' => [
  756. '<?php
  757. namespace Test;
  758. use A\B\Foo;
  759. use A\B\Bar as BarAlias;
  760. use A\B as AB;
  761. #[
  762. A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),
  763. BarAlias(3),
  764. Corge,
  765. AB\Baz(prop: new P\R()),
  766. Foo(4, \'baz qux\'),
  767. \A\B\Qux(),
  768. ]
  769. function f() {}
  770. ',
  771. '<?php
  772. namespace Test;
  773. use A\B\Foo;
  774. use A\B\Bar as BarAlias;
  775. use A\B as AB;
  776. #[
  777. Foo(4, \'baz qux\'),
  778. BarAlias(3),
  779. AB\Baz(prop: new P\R()),
  780. \A\B\Qux(),
  781. A\B\Quux(prop1: [1, 2, 4], prop2: true, prop3: \'foo bar\'),
  782. Corge,
  783. ]
  784. function f() {}
  785. ',
  786. [
  787. 'sort_algorithm' => OrderedAttributesFixer::ORDER_CUSTOM,
  788. 'order' => ['Test\A\B\Quux', 'A\B\Bar', 'Test\Corge', 'A\B\Baz', 'A\B\Foo', 'A\B\Qux'],
  789. ],
  790. ];
  791. }
  792. }