NativeFunctionInvocationFixerTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. <?php
  2. /*
  3. * This file is part of PHP CS Fixer.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  7. *
  8. * This source file is subject to the MIT license that is bundled
  9. * with this source code in the file LICENSE.
  10. */
  11. namespace PhpCsFixer\Tests\Fixer\FunctionNotation;
  12. use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
  13. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  14. /**
  15. * @author Andreas Möller <am@localheinz.com>
  16. * @author SpacePossum
  17. *
  18. * @internal
  19. *
  20. * @covers \PhpCsFixer\Fixer\FunctionNotation\NativeFunctionInvocationFixer
  21. */
  22. final class NativeFunctionInvocationFixerTest extends AbstractFixerTestCase
  23. {
  24. public function testConfigureRejectsUnknownConfigurationKey()
  25. {
  26. $key = 'foo';
  27. $this->expectException(\PhpCsFixer\ConfigurationException\InvalidConfigurationException::class);
  28. $this->expectExceptionMessage(sprintf(
  29. '[native_function_invocation] Invalid configuration: The option "%s" does not exist.',
  30. $key
  31. ));
  32. $this->fixer->configure([
  33. $key => 'bar',
  34. ]);
  35. }
  36. /**
  37. * @dataProvider provideInvalidConfigurationElementCases
  38. *
  39. * @param mixed $element
  40. */
  41. public function testConfigureRejectsInvalidConfigurationElement($element)
  42. {
  43. $this->expectException(\PhpCsFixer\ConfigurationException\InvalidConfigurationException::class);
  44. $this->expectExceptionMessage(sprintf(
  45. 'Each element must be a non-empty, trimmed string, got "%s" instead.',
  46. \is_object($element) ? \get_class($element) : \gettype($element)
  47. ));
  48. $this->fixer->configure([
  49. 'exclude' => [
  50. $element,
  51. ],
  52. ]);
  53. }
  54. /**
  55. * @return array
  56. */
  57. public function provideInvalidConfigurationElementCases()
  58. {
  59. return [
  60. 'null' => [null],
  61. 'false' => [false],
  62. 'true' => [false],
  63. 'int' => [1],
  64. 'array' => [[]],
  65. 'float' => [0.1],
  66. 'object' => [new \stdClass()],
  67. 'not-trimmed' => [' json_encode '],
  68. ];
  69. }
  70. /**
  71. * @param string[] $include
  72. * @param null|string $expectedExceptionClass
  73. * @param null|string $expectedExceptionMessage
  74. *
  75. * @dataProvider provideConfigureIncludeSetsCases
  76. */
  77. public function testConfigureIncludeSets(
  78. array $include,
  79. $expectedExceptionClass = null,
  80. $expectedExceptionMessage = null
  81. ) {
  82. if (null !== $expectedExceptionClass) {
  83. $this->expectException($expectedExceptionClass);
  84. $this->expectExceptionMessageMatches(sprintf('#^%s$#', preg_quote($expectedExceptionMessage, '#')));
  85. }
  86. $this->fixer->configure(['include' => $include]);
  87. if (null === $expectedExceptionClass) {
  88. $this->addToAssertionCount(1);
  89. }
  90. }
  91. public function provideConfigureIncludeSetsCases()
  92. {
  93. return [
  94. [['foo', 'bar']],
  95. [['@all']],
  96. [['@all', 'bar']],
  97. [
  98. ['@xxx'],
  99. InvalidFixerConfigurationException::class,
  100. '[native_function_invocation] Invalid configuration: Unknown set "@xxx", known sets are "@all", "@internal", "@compiler_optimized".',
  101. ],
  102. [
  103. [' x '],
  104. InvalidFixerConfigurationException::class,
  105. '[native_function_invocation] Invalid configuration: Each element must be a non-empty, trimmed string, got "string" instead.',
  106. ],
  107. ];
  108. }
  109. public function testConfigureResetsExclude()
  110. {
  111. $this->fixer->configure([
  112. 'exclude' => [
  113. 'json_encode',
  114. ],
  115. ]);
  116. $before = <<<'PHP'
  117. <?php
  118. namespace WithClassNotPrefixed;
  119. class Bar
  120. {
  121. public function baz($foo)
  122. {
  123. if (isset($foo)) {
  124. json_encode($foo);
  125. }
  126. }
  127. }
  128. PHP;
  129. $after = <<<'PHP'
  130. <?php
  131. namespace WithClassNotPrefixed;
  132. class Bar
  133. {
  134. public function baz($foo)
  135. {
  136. if (isset($foo)) {
  137. \json_encode($foo);
  138. }
  139. }
  140. }
  141. PHP;
  142. $this->doTest($before);
  143. $this->fixer->configure([]);
  144. $this->doTest($after, $before);
  145. }
  146. /**
  147. * @dataProvider provideFixWithDefaultConfigurationCases
  148. *
  149. * @param string $expected
  150. * @param null|string $input
  151. */
  152. public function testFixWithDefaultConfiguration($expected, $input = null)
  153. {
  154. $this->doTest($expected, $input);
  155. }
  156. /**
  157. * @return array
  158. */
  159. public function provideFixWithDefaultConfigurationCases()
  160. {
  161. return [
  162. [
  163. '<?php
  164. \json_encode($foo);
  165. ',
  166. ],
  167. [
  168. '<?php
  169. \json_encode($foo);
  170. ',
  171. '<?php
  172. json_encode($foo);
  173. ',
  174. ],
  175. [
  176. '<?php
  177. class Foo
  178. {
  179. public function bar($foo)
  180. {
  181. return \json_encode($foo);
  182. }
  183. }
  184. ',
  185. ],
  186. [
  187. '<?php
  188. class Foo
  189. {
  190. public function bar($foo)
  191. {
  192. return \JSON_ENCODE($foo);
  193. }
  194. }
  195. ',
  196. '<?php
  197. class Foo
  198. {
  199. public function bar($foo)
  200. {
  201. return JSON_ENCODE($foo);
  202. }
  203. }
  204. ',
  205. ],
  206. ];
  207. }
  208. /**
  209. * @dataProvider provideFixWithConfiguredExcludeCases
  210. *
  211. * @param string $expected
  212. * @param null|string $input
  213. */
  214. public function testFixWithConfiguredExclude($expected, $input = null)
  215. {
  216. $this->fixer->configure([
  217. 'exclude' => [
  218. 'json_encode',
  219. ],
  220. ]);
  221. $this->doTest($expected, $input);
  222. }
  223. /**
  224. * @return array
  225. */
  226. public function provideFixWithConfiguredExcludeCases()
  227. {
  228. return [
  229. [
  230. '<?php
  231. json_encode($foo);
  232. ',
  233. ],
  234. [
  235. '<?php
  236. class Foo
  237. {
  238. public function bar($foo)
  239. {
  240. return json_encode($foo);
  241. }
  242. }
  243. ',
  244. ],
  245. ];
  246. }
  247. /**
  248. * @dataProvider provideFixWithNamespaceConfigurationCases
  249. *
  250. * @param string $expected
  251. * @param null|string $input
  252. * @param string $scope
  253. */
  254. public function testFixWithNamespaceConfiguration($expected, $input = null, $scope = 'namespaced')
  255. {
  256. $this->fixer->configure(['scope' => $scope]);
  257. $this->doTest($expected, $input);
  258. }
  259. public function provideFixWithNamespaceConfigurationCases()
  260. {
  261. return [
  262. [
  263. '<?php echo count([1]);',
  264. ],
  265. [
  266. '<?php
  267. namespace space1 { ?>
  268. <?php echo \count([2]) ?>
  269. <?php }namespace {echo count([1]);}
  270. ',
  271. '<?php
  272. namespace space1 { ?>
  273. <?php echo count([2]) ?>
  274. <?php }namespace {echo count([1]);}
  275. ',
  276. ],
  277. [
  278. '<?php
  279. namespace Bar {
  280. echo \strtoLOWER("in 1");
  281. }
  282. namespace {
  283. echo strtolower("out 1");
  284. }
  285. namespace {
  286. echo strtolower("out 2");
  287. }
  288. namespace Bar{
  289. echo \strtolower("in 2");
  290. }
  291. namespace {
  292. echo strtolower("out 3");
  293. }
  294. ',
  295. '<?php
  296. namespace Bar {
  297. echo strtoLOWER("in 1");
  298. }
  299. namespace {
  300. echo strtolower("out 1");
  301. }
  302. namespace {
  303. echo strtolower("out 2");
  304. }
  305. namespace Bar{
  306. echo strtolower("in 2");
  307. }
  308. namespace {
  309. echo strtolower("out 3");
  310. }
  311. ',
  312. ],
  313. [
  314. '<?php
  315. namespace space11 ?>
  316. <?php
  317. echo \strtolower(__NAMESPACE__);
  318. namespace space2;
  319. echo \strtolower(__NAMESPACE__);
  320. ',
  321. '<?php
  322. namespace space11 ?>
  323. <?php
  324. echo strtolower(__NAMESPACE__);
  325. namespace space2;
  326. echo strtolower(__NAMESPACE__);
  327. ',
  328. ],
  329. [
  330. '<?php namespace PhpCsFixer\Tests\Fixer\Casing;\count([1]);',
  331. '<?php namespace PhpCsFixer\Tests\Fixer\Casing;count([1]);',
  332. ],
  333. [
  334. '<?php
  335. namespace Space12;
  336. echo \count([1]);
  337. namespace Space2;
  338. echo \count([1]);
  339. ?>
  340. ',
  341. '<?php
  342. namespace Space12;
  343. echo count([1]);
  344. namespace Space2;
  345. echo count([1]);
  346. ?>
  347. ',
  348. ],
  349. [
  350. '<?php namespace {echo strtolower("out 2");}',
  351. ],
  352. [
  353. '<?php
  354. namespace space13 {
  355. echo \strtolower("in 1");
  356. }
  357. namespace space2 {
  358. echo \strtolower("in 2");
  359. }
  360. namespace { // global
  361. echo strtolower("global 1");
  362. }
  363. ',
  364. '<?php
  365. namespace space13 {
  366. echo strtolower("in 1");
  367. }
  368. namespace space2 {
  369. echo strtolower("in 2");
  370. }
  371. namespace { // global
  372. echo strtolower("global 1");
  373. }
  374. ',
  375. ],
  376. [
  377. '<?php
  378. namespace space1 {
  379. echo \count([1]);
  380. }
  381. namespace {
  382. echo \count([1]);
  383. }
  384. ',
  385. '<?php
  386. namespace space1 {
  387. echo count([1]);
  388. }
  389. namespace {
  390. echo count([1]);
  391. }
  392. ',
  393. 'all',
  394. ],
  395. ];
  396. }
  397. /**
  398. * @dataProvider provideFixWithConfiguredIncludeCases
  399. *
  400. * @param string $expected
  401. * @param null|string $input
  402. */
  403. public function testFixWithConfiguredInclude($expected, $input = null, array $configuration = [])
  404. {
  405. $this->fixer->configure($configuration);
  406. $this->doTest($expected, $input);
  407. }
  408. public function provideFixWithConfiguredIncludeCases()
  409. {
  410. $tests = [
  411. 'include set + 1, exclude 1' => [
  412. '<?php
  413. echo \count([1]);
  414. \some_other($a, 3);
  415. echo strlen($a);
  416. not_me();
  417. ',
  418. '<?php
  419. echo count([1]);
  420. some_other($a, 3);
  421. echo strlen($a);
  422. not_me();
  423. ',
  424. [
  425. 'include' => ['@internal', 'some_other'],
  426. 'exclude' => ['strlen'],
  427. ],
  428. ],
  429. 'include @all' => [
  430. '<?php
  431. echo \count([1]);
  432. \some_other($a, 3);
  433. echo \strlen($a);
  434. \me_as_well();
  435. ',
  436. '<?php
  437. echo count([1]);
  438. some_other($a, 3);
  439. echo strlen($a);
  440. me_as_well();
  441. ',
  442. [
  443. 'include' => ['@all'],
  444. ],
  445. ],
  446. 'include @compiler_optimized' => [
  447. '<?php
  448. // do not fix
  449. $a = strrev($a);
  450. $a .= str_repeat($a, 4);
  451. $b = \already_prefixed_function();
  452. // fix
  453. $c = \get_class($d);
  454. $e = \intval($f);
  455. ',
  456. '<?php
  457. // do not fix
  458. $a = strrev($a);
  459. $a .= str_repeat($a, 4);
  460. $b = \already_prefixed_function();
  461. // fix
  462. $c = get_class($d);
  463. $e = intval($f);
  464. ',
  465. [
  466. 'include' => ['@compiler_optimized'],
  467. ],
  468. ],
  469. [
  470. '<?php class Foo {
  471. public function & strlen($name) {
  472. }
  473. }
  474. ',
  475. ],
  476. 'scope namespaced and strict enabled' => [
  477. '<?php
  478. $a = not_compiler_optimized_function();
  479. $b = intval($c);
  480. ',
  481. '<?php
  482. $a = \not_compiler_optimized_function();
  483. $b = \intval($c);
  484. ',
  485. [
  486. 'scope' => 'namespaced',
  487. 'strict' => true,
  488. ],
  489. ],
  490. [
  491. '<?php
  492. use function foo\json_decode;
  493. json_decode($base);
  494. ',
  495. null,
  496. [
  497. 'include' => ['@all'],
  498. ],
  499. ],
  500. ];
  501. foreach ($tests as $index => $test) {
  502. yield $index => $test;
  503. }
  504. if (\PHP_VERSION_ID < 80000) {
  505. yield 'include @compiler_optimized with strict enabled' => [
  506. '<?php
  507. $a = not_compiler_optimized_function();
  508. $b = not_compiler_optimized_function();
  509. $c = \intval($d);
  510. ',
  511. '<?php
  512. $a = \not_compiler_optimized_function();
  513. $b = \ not_compiler_optimized_function();
  514. $c = intval($d);
  515. ',
  516. [
  517. 'include' => ['@compiler_optimized'],
  518. 'strict' => true,
  519. ],
  520. ];
  521. }
  522. }
  523. /**
  524. * @requires PHP 7.3
  525. */
  526. public function testFix73()
  527. {
  528. $this->doTest(
  529. '<?php $name = \get_class($foo, );',
  530. '<?php $name = get_class($foo, );'
  531. );
  532. }
  533. /**
  534. * @requires PHP <8.0
  535. */
  536. public function testFixPrePHP80()
  537. {
  538. $this->doTest(
  539. '<?php
  540. echo \/**/strlen($a);
  541. echo \ strlen($a);
  542. echo \#
  543. #
  544. strlen($a);
  545. echo \strlen($a);
  546. ',
  547. '<?php
  548. echo \/**/strlen($a);
  549. echo \ strlen($a);
  550. echo \#
  551. #
  552. strlen($a);
  553. echo strlen($a);
  554. '
  555. );
  556. }
  557. }