BraceTransformerTest.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  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. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  18. *
  19. * @internal
  20. *
  21. * @covers \PhpCsFixer\Tokenizer\Transformer\BraceTransformer
  22. *
  23. * @phpstan-import-type _TransformerTestExpectedTokens from AbstractTransformerTestCase
  24. */
  25. final class BraceTransformerTest extends AbstractTransformerTestCase
  26. {
  27. /**
  28. * @param _TransformerTestExpectedTokens $expectedTokens
  29. *
  30. * @dataProvider provideProcessCases
  31. */
  32. public function testProcess(string $source, array $expectedTokens = []): void
  33. {
  34. $this->doTest(
  35. $source,
  36. $expectedTokens,
  37. [
  38. T_CURLY_OPEN,
  39. CT::T_CURLY_CLOSE,
  40. T_DOLLAR_OPEN_CURLY_BRACES,
  41. CT::T_DOLLAR_CLOSE_CURLY_BRACES,
  42. CT::T_DYNAMIC_PROP_BRACE_OPEN,
  43. CT::T_DYNAMIC_PROP_BRACE_CLOSE,
  44. CT::T_DYNAMIC_VAR_BRACE_OPEN,
  45. CT::T_DYNAMIC_VAR_BRACE_CLOSE,
  46. CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  47. CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  48. CT::T_GROUP_IMPORT_BRACE_OPEN,
  49. CT::T_GROUP_IMPORT_BRACE_CLOSE,
  50. CT::T_PROPERTY_HOOK_BRACE_OPEN,
  51. CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  52. ]
  53. );
  54. }
  55. public static function provideProcessCases(): iterable
  56. {
  57. yield 'curly open/close I' => [
  58. '<?php echo "This is {$great}";',
  59. [
  60. 5 => T_CURLY_OPEN,
  61. 7 => CT::T_CURLY_CLOSE,
  62. ],
  63. ];
  64. yield 'curly open/close II' => [
  65. '<?php $a = "a{$b->c()}d";',
  66. [
  67. 7 => T_CURLY_OPEN,
  68. 13 => CT::T_CURLY_CLOSE,
  69. ],
  70. ];
  71. yield 'dynamic var brace open/close' => [
  72. '<?php echo "I\'d like an {${beers::$ale}}\n";',
  73. [
  74. 5 => T_CURLY_OPEN,
  75. 7 => CT::T_DYNAMIC_VAR_BRACE_OPEN,
  76. 11 => CT::T_DYNAMIC_VAR_BRACE_CLOSE,
  77. 12 => CT::T_CURLY_CLOSE,
  78. ],
  79. ];
  80. yield 'dollar curly brace open/close' => [
  81. '<?php echo "This is ${great}";',
  82. [
  83. 5 => T_DOLLAR_OPEN_CURLY_BRACES,
  84. 7 => CT::T_DOLLAR_CLOSE_CURLY_BRACES,
  85. ],
  86. ];
  87. yield 'dynamic property brace open/close' => [
  88. '<?php $foo->{$bar};',
  89. [
  90. 3 => CT::T_DYNAMIC_PROP_BRACE_OPEN,
  91. 5 => CT::T_DYNAMIC_PROP_BRACE_CLOSE,
  92. ],
  93. ];
  94. yield 'dynamic variable brace open/close' => [
  95. '<?php ${$bar};',
  96. [
  97. 2 => CT::T_DYNAMIC_VAR_BRACE_OPEN,
  98. 4 => CT::T_DYNAMIC_VAR_BRACE_CLOSE,
  99. ],
  100. ];
  101. yield 'mixed' => [
  102. '<?php echo "This is {$great}";
  103. $a = "a{$b->c()}d";
  104. echo "I\'d like an {${beers::$ale}}\n";
  105. ',
  106. [
  107. 5 => T_CURLY_OPEN,
  108. 7 => CT::T_CURLY_CLOSE,
  109. 17 => T_CURLY_OPEN,
  110. 23 => CT::T_CURLY_CLOSE,
  111. 32 => T_CURLY_OPEN,
  112. 34 => CT::T_DYNAMIC_VAR_BRACE_OPEN,
  113. 38 => CT::T_DYNAMIC_VAR_BRACE_CLOSE,
  114. 39 => CT::T_CURLY_CLOSE,
  115. ],
  116. ];
  117. yield 'do not touch' => [
  118. '<?php if (1) {} class Foo{ } function bar(){ }',
  119. ];
  120. yield 'dynamic property with string with variable' => [
  121. '<?php $object->{"set_{$name}"}(42);',
  122. [
  123. 3 => CT::T_DYNAMIC_PROP_BRACE_OPEN,
  124. 6 => T_CURLY_OPEN,
  125. 8 => CT::T_CURLY_CLOSE,
  126. 10 => CT::T_DYNAMIC_PROP_BRACE_CLOSE,
  127. ],
  128. ];
  129. yield 'group import' => [
  130. '<?php use some\a\{ClassA, ClassB, ClassC as C};',
  131. [
  132. 7 => CT::T_GROUP_IMPORT_BRACE_OPEN,
  133. 19 => CT::T_GROUP_IMPORT_BRACE_CLOSE,
  134. ],
  135. ];
  136. yield 'nested curly open + close' => [
  137. '<?php echo "{$foo->{"{$bar}"}}";',
  138. [
  139. 4 => T_CURLY_OPEN,
  140. 7 => CT::T_DYNAMIC_PROP_BRACE_OPEN,
  141. 9 => T_CURLY_OPEN,
  142. 11 => CT::T_CURLY_CLOSE,
  143. 13 => CT::T_DYNAMIC_PROP_BRACE_CLOSE,
  144. 14 => CT::T_CURLY_CLOSE,
  145. ],
  146. ];
  147. }
  148. /**
  149. * @param _TransformerTestExpectedTokens $expectedTokens
  150. *
  151. * @dataProvider provideProcess80Cases
  152. *
  153. * @requires PHP 8.0
  154. */
  155. public function testProcess80(string $source, array $expectedTokens = []): void
  156. {
  157. $this->doTest(
  158. $source,
  159. $expectedTokens,
  160. [
  161. T_CURLY_OPEN,
  162. CT::T_CURLY_CLOSE,
  163. T_DOLLAR_OPEN_CURLY_BRACES,
  164. CT::T_DOLLAR_CLOSE_CURLY_BRACES,
  165. CT::T_DYNAMIC_PROP_BRACE_OPEN,
  166. CT::T_DYNAMIC_PROP_BRACE_CLOSE,
  167. CT::T_DYNAMIC_VAR_BRACE_OPEN,
  168. CT::T_DYNAMIC_VAR_BRACE_CLOSE,
  169. CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  170. CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  171. CT::T_GROUP_IMPORT_BRACE_OPEN,
  172. CT::T_GROUP_IMPORT_BRACE_CLOSE,
  173. CT::T_PROPERTY_HOOK_BRACE_OPEN,
  174. CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  175. ]
  176. );
  177. }
  178. public static function provideProcess80Cases(): iterable
  179. {
  180. yield 'dynamic nullable property brace open/close' => [
  181. '<?php $foo?->{$bar};',
  182. [
  183. 3 => CT::T_DYNAMIC_PROP_BRACE_OPEN,
  184. 5 => CT::T_DYNAMIC_PROP_BRACE_CLOSE,
  185. ],
  186. ];
  187. }
  188. /**
  189. * @param _TransformerTestExpectedTokens $expectedTokens
  190. *
  191. * @dataProvider providePre84ProcessCases
  192. *
  193. * @requires PHP <8.4
  194. */
  195. public function testPre84Process(string $source, array $expectedTokens = []): void
  196. {
  197. $this->doTest(
  198. $source,
  199. $expectedTokens,
  200. [
  201. T_CURLY_OPEN,
  202. CT::T_CURLY_CLOSE,
  203. T_DOLLAR_OPEN_CURLY_BRACES,
  204. CT::T_DOLLAR_CLOSE_CURLY_BRACES,
  205. CT::T_DYNAMIC_PROP_BRACE_OPEN,
  206. CT::T_DYNAMIC_PROP_BRACE_CLOSE,
  207. CT::T_DYNAMIC_VAR_BRACE_OPEN,
  208. CT::T_DYNAMIC_VAR_BRACE_CLOSE,
  209. CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  210. CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  211. CT::T_GROUP_IMPORT_BRACE_OPEN,
  212. CT::T_GROUP_IMPORT_BRACE_CLOSE,
  213. CT::T_PROPERTY_HOOK_BRACE_OPEN,
  214. CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  215. ]
  216. );
  217. }
  218. /**
  219. * @return iterable<array{string, array<int, int>}>
  220. */
  221. public static function providePre84ProcessCases(): iterable
  222. {
  223. yield 'array index curly brace open/close' => [
  224. '<?php
  225. echo $arr{$index};
  226. echo $arr[$index];
  227. if (1) {}
  228. ',
  229. [
  230. 5 => CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  231. 7 => CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  232. ],
  233. ];
  234. yield 'array index curly brace open/close, after square index' => [
  235. '<?php $b = [1]{0};
  236. ',
  237. [
  238. 8 => CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  239. 10 => CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  240. ],
  241. ];
  242. yield 'array index curly brace open/close, nested' => [
  243. '<?php
  244. echo $nestedArray{$index}{$index2}[$index3]{$index4};
  245. ',
  246. [
  247. 5 => CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  248. 7 => CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  249. 8 => CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  250. 10 => CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  251. 14 => CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  252. 16 => CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  253. ],
  254. ];
  255. yield 'array index curly brace open/close, repeated' => [
  256. '<?php
  257. echo $array{0}->foo;
  258. echo $collection->items{1}->property;
  259. ',
  260. [
  261. 5 => CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  262. 7 => CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  263. 17 => CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  264. 19 => CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  265. ],
  266. ];
  267. yield 'array index curly brace open/close, minimal' => [
  268. '<?php
  269. echo [1]{0};
  270. echo array(1){0};
  271. ',
  272. [
  273. 7 => CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  274. 9 => CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  275. 18 => CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  276. 20 => CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  277. ],
  278. ];
  279. }
  280. /**
  281. * @param _TransformerTestExpectedTokens $expectedTokens
  282. *
  283. * @dataProvider provideStarting84ProcessCases
  284. *
  285. * @requires PHP 8.4
  286. */
  287. public function testStarting84Process(string $source, array $expectedTokens = []): void
  288. {
  289. $this->doTest(
  290. $source,
  291. $expectedTokens,
  292. [
  293. T_CURLY_OPEN,
  294. CT::T_CURLY_CLOSE,
  295. T_DOLLAR_OPEN_CURLY_BRACES,
  296. CT::T_DOLLAR_CLOSE_CURLY_BRACES,
  297. CT::T_DYNAMIC_PROP_BRACE_OPEN,
  298. CT::T_DYNAMIC_PROP_BRACE_CLOSE,
  299. CT::T_DYNAMIC_VAR_BRACE_OPEN,
  300. CT::T_DYNAMIC_VAR_BRACE_CLOSE,
  301. CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
  302. CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  303. CT::T_GROUP_IMPORT_BRACE_OPEN,
  304. CT::T_GROUP_IMPORT_BRACE_CLOSE,
  305. CT::T_PROPERTY_HOOK_BRACE_OPEN,
  306. CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  307. ]
  308. );
  309. }
  310. /**
  311. * @return iterable<array{string, array<int, int>}>
  312. */
  313. public static function provideStarting84ProcessCases(): iterable
  314. {
  315. yield 'property hooks: property without default value' => [
  316. <<<'PHP'
  317. <?php
  318. class PropertyHooks
  319. {
  320. public string $bar { // << this one
  321. set(string $value) {
  322. $this->bar = strtolower($value);
  323. }
  324. } // << this one
  325. }
  326. PHP,
  327. [
  328. 13 => CT::T_PROPERTY_HOOK_BRACE_OPEN,
  329. 40 => CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  330. ],
  331. ];
  332. yield 'property hooks: property with default value (string)' => [
  333. <<<'PHP'
  334. <?php
  335. class PropertyHooks
  336. {
  337. public string $bar = "example" { // << this one
  338. set(string $value) {
  339. $this->bar = strtolower($value);
  340. }
  341. } // << this one
  342. }
  343. PHP,
  344. [
  345. 17 => CT::T_PROPERTY_HOOK_BRACE_OPEN,
  346. 44 => CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  347. ],
  348. ];
  349. yield 'property hooks: property with default value (array)' => [
  350. <<<'PHP'
  351. <?php
  352. class PropertyHooks
  353. {
  354. public $bar = [1,2,3] { // << this one
  355. set($value) {
  356. $this->bar = $value;
  357. }
  358. } // << this one
  359. }
  360. PHP,
  361. [
  362. 21 => CT::T_PROPERTY_HOOK_BRACE_OPEN,
  363. 43 => CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  364. ],
  365. ];
  366. yield 'property hooks: property with default value (namespaced)' => [
  367. <<<'PHP'
  368. <?php
  369. class PropertyHooks
  370. {
  371. public $bar = DateTimeInterface::ISO8601 { // << this one
  372. set($value) {
  373. $this->bar = $value;
  374. }
  375. } // << this one
  376. }
  377. PHP,
  378. [
  379. 17 => CT::T_PROPERTY_HOOK_BRACE_OPEN,
  380. 39 => CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  381. ],
  382. ];
  383. yield 'property hooks: property with setter attributes' => [
  384. <<<'PHP'
  385. <?php
  386. class PropertyHooks
  387. {
  388. public string $bar { // << this one
  389. #[A]
  390. #[B]
  391. set(string $value) {
  392. $this->bar = strtolower($value);
  393. }
  394. } // << this one
  395. }
  396. PHP,
  397. [
  398. 13 => CT::T_PROPERTY_HOOK_BRACE_OPEN,
  399. 48 => CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  400. ],
  401. ];
  402. yield 'property hooks: property with short setter' => [
  403. <<<'PHP'
  404. <?php
  405. class PropertyHooks
  406. {
  407. public string $bar { // << this one
  408. set {
  409. $this->bar = strtolower($value);
  410. }
  411. } // << this one
  412. }
  413. PHP,
  414. [
  415. 13 => CT::T_PROPERTY_HOOK_BRACE_OPEN,
  416. 35 => CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  417. ],
  418. ];
  419. yield 'property hooks: property with short getter' => [
  420. <<<'PHP'
  421. <?php
  422. class PropertyHooks
  423. {
  424. public string $bar { // << this one
  425. get => ucwords(mb_strtolower($this->bar));
  426. } // << this one
  427. }
  428. PHP,
  429. [
  430. 13 => CT::T_PROPERTY_HOOK_BRACE_OPEN,
  431. 32 => CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  432. ],
  433. ];
  434. yield 'property hooks: some more curly braces within hook' => [
  435. <<<'PHP'
  436. <?php
  437. class PropertyHooks
  438. {
  439. public $callable { // << this one
  440. set($value) {
  441. if (is_callable($value)) {
  442. $this->callable = $value;
  443. } else {
  444. $this->callable = static function (): void {
  445. $foo = new class implements \Stringable {
  446. public function __toString(): string {
  447. echo 'Na';
  448. }
  449. };
  450. for ($i = 0; $i < 8; $i++) {
  451. echo (string) $foo;
  452. }
  453. };
  454. }
  455. }
  456. } // << this one
  457. }
  458. PHP,
  459. [
  460. 11 => CT::T_PROPERTY_HOOK_BRACE_OPEN,
  461. 143 => CT::T_PROPERTY_HOOK_BRACE_CLOSE,
  462. ],
  463. ];
  464. }
  465. /**
  466. * @dataProvider provideNotDynamicClassConstantFetchCases
  467. */
  468. public function testNotDynamicClassConstantFetch(string $source): void
  469. {
  470. Tokens::clearCache();
  471. $tokens = Tokens::fromCode($source);
  472. self::assertFalse(
  473. $tokens->isAnyTokenKindsFound(
  474. [
  475. CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  476. CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  477. ]
  478. )
  479. );
  480. }
  481. /**
  482. * @return iterable<string, array{string}>
  483. */
  484. public static function provideNotDynamicClassConstantFetchCases(): iterable
  485. {
  486. yield 'negatives' => [
  487. '<?php
  488. namespace B {$b = Z::B;};
  489. echo $c::{$static_method}();
  490. echo Foo::{$static_method}();
  491. echo Foo::${static_property};
  492. echo Foo::${$static_property};
  493. echo Foo::class;
  494. echo $foo::$bar;
  495. echo $foo::bar();
  496. echo foo()::A();
  497. {$z = A::C;}
  498. ',
  499. ];
  500. }
  501. /**
  502. * @param _TransformerTestExpectedTokens $expectedTokens
  503. *
  504. * @dataProvider provideDynamicClassConstantFetchCases
  505. *
  506. * @requires PHP 8.3
  507. */
  508. public function testDynamicClassConstantFetch(array $expectedTokens, string $source): void
  509. {
  510. $this->doTest(
  511. $source,
  512. $expectedTokens,
  513. [
  514. CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  515. CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  516. ],
  517. );
  518. }
  519. public static function provideDynamicClassConstantFetchCases(): iterable
  520. {
  521. yield 'simple' => [
  522. [
  523. 5 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  524. 7 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  525. ],
  526. '<?php echo Foo::{$bar};',
  527. ];
  528. yield 'long way of writing `Bar::class`' => [
  529. [
  530. 5 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  531. 7 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  532. ],
  533. "<?php echo Bar::{'class'};",
  534. ];
  535. yield 'variable variable wrapped, close tag' => [
  536. [
  537. 5 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  538. 10 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  539. ],
  540. '<?php echo Foo::{${$var}}?>',
  541. ];
  542. yield 'variable variable, comment' => [
  543. [
  544. 5 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  545. 8 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  546. ],
  547. '<?php echo Foo::{$$var}/* */;?>',
  548. ];
  549. yield 'static, self' => [
  550. [
  551. 37 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  552. 39 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  553. 46 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  554. 48 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  555. 55 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  556. 57 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  557. ],
  558. '<?php
  559. class Foo
  560. {
  561. private const X = 1;
  562. public function Bar($var): void
  563. {
  564. echo self::{$var};
  565. echo static::{$var};
  566. echo static::{"X"};
  567. }
  568. }
  569. ',
  570. ];
  571. yield 'chained' => [
  572. [
  573. 5 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  574. 7 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  575. 9 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  576. 11 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  577. ],
  578. "<?php echo Foo::{'BAR'}::{'BLA'}::{static_method}(1,2) ?>",
  579. ];
  580. yield 'mixed chain' => [
  581. [
  582. 21 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  583. 23 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  584. 25 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  585. 27 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  586. ],
  587. '<?php echo Foo::{\'static_method\'}()::{$$a}()["const"]::{some_const}::{$other_const}::{$last_static_method}();',
  588. ];
  589. }
  590. /**
  591. * @param _TransformerTestExpectedTokens $expectedTokens
  592. *
  593. * @dataProvider provideDynamicClassConstantFetchPhp83Cases
  594. *
  595. * @requires PHP ~8.3.0
  596. */
  597. public function testDynamicClassConstantFetchPhp83(array $expectedTokens, string $source): void
  598. {
  599. $this->doTest(
  600. $source,
  601. $expectedTokens,
  602. [
  603. CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  604. CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  605. ],
  606. );
  607. }
  608. /**
  609. * @return iterable<array{array<int, int>, string}>
  610. */
  611. public static function provideDynamicClassConstantFetchPhp83Cases(): iterable
  612. {
  613. yield 'static method var, string' => [
  614. [
  615. 10 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  616. 12 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  617. ],
  618. "<?php echo Foo::{\$static_method}(){'XYZ'};",
  619. ];
  620. yield 'mixed chain' => [
  621. [
  622. 17 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  623. 19 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  624. 21 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  625. 23 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  626. 25 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_OPEN,
  627. 27 => CT::T_DYNAMIC_CLASS_CONSTANT_FETCH_CURLY_BRACE_CLOSE,
  628. ],
  629. '<?php echo Foo::{\'static_method\'}()::{$$a}(){"const"}::{some_const}::{$other_const}::{$last_static_method}();',
  630. ];
  631. }
  632. }