FunctionToConstantFixerTest.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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\LanguageConstruct;
  13. use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
  14. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  15. /**
  16. * @internal
  17. *
  18. * @covers \PhpCsFixer\Fixer\LanguageConstruct\FunctionToConstantFixer
  19. *
  20. * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\LanguageConstruct\FunctionToConstantFixer>
  21. *
  22. * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\LanguageConstruct\FunctionToConstantFixer
  23. */
  24. final class FunctionToConstantFixerTest extends AbstractFixerTestCase
  25. {
  26. /**
  27. * @param _AutogeneratedInputConfiguration $config
  28. *
  29. * @dataProvider provideFixCases
  30. */
  31. public function testFix(string $expected, ?string $input = null, array $config = []): void
  32. {
  33. $this->fixer->configure($config);
  34. $this->doTest($expected, $input);
  35. }
  36. public static function provideFixCases(): iterable
  37. {
  38. yield 'Minimal case, alternative casing, alternative statement end.' => [
  39. '<?php echo PHP_VERSION?>',
  40. '<?php echo PHPversion()?>',
  41. ];
  42. yield 'With embedded comment.' => [
  43. '<?php echo PHP_VERSION/**/?>',
  44. '<?php echo phpversion(/**/)?>',
  45. ];
  46. yield 'With white space.' => [
  47. '<?php echo PHP_VERSION ;',
  48. '<?php echo phpversion ( ) ;',
  49. ];
  50. yield 'With multi line whitespace.' => [
  51. '<?php echo
  52. PHP_VERSION
  53. '.'
  54. '.'
  55. ;',
  56. '<?php echo
  57. phpversion
  58. (
  59. )
  60. ;',
  61. ];
  62. yield 'Global namespaced.' => [
  63. '<?php echo \PHP_VERSION;',
  64. '<?php echo \phpversion();',
  65. ];
  66. yield 'Wrong number of arguments.' => [
  67. '<?php phpversion($a);',
  68. ];
  69. yield 'Wrong namespace.' => [
  70. '<?php A\B\phpversion();',
  71. ];
  72. yield 'Class creating.' => [
  73. '<?php new phpversion();',
  74. ];
  75. yield 'Class static method call.' => [
  76. '<?php A::phpversion();',
  77. ];
  78. yield 'Class method call.' => [
  79. '<?php $a->phpversion();',
  80. ];
  81. yield 'Overridden function.' => [
  82. '<?php if (!function_exists("phpversion")){function phpversion(){}}?>',
  83. ];
  84. yield 'phpversion only' => [
  85. '<?php echo PHP_VERSION; echo php_sapi_name(); echo pi();',
  86. '<?php echo phpversion(); echo php_sapi_name(); echo pi();',
  87. ['functions' => ['phpversion']],
  88. ];
  89. yield 'php_sapi_name only' => [
  90. '<?php echo phpversion(); echo PHP_SAPI; echo pi();',
  91. '<?php echo phpversion(); echo php_sapi_name(); echo pi();',
  92. ['functions' => ['php_sapi_name']],
  93. ];
  94. yield 'php_sapi_name in conditional' => [
  95. '<?php if ("cli" === PHP_SAPI && $a){ echo 123;}',
  96. '<?php if ("cli" === php_sapi_name() && $a){ echo 123;}',
  97. ['functions' => ['php_sapi_name']],
  98. ];
  99. yield 'pi only' => [
  100. '<?php echo phpversion(); echo php_sapi_name(); echo M_PI;',
  101. '<?php echo phpversion(); echo php_sapi_name(); echo pi();',
  102. ['functions' => ['pi']],
  103. ];
  104. yield 'multi line pi' => [
  105. '<?php
  106. $a =
  107. $b
  108. || $c < M_PI
  109. ;',
  110. '<?php
  111. $a =
  112. $b
  113. || $c < pi()
  114. ;',
  115. ['functions' => ['pi']],
  116. ];
  117. yield 'phpversion and pi' => [
  118. '<?php echo PHP_VERSION; echo php_sapi_name(); echo M_PI;',
  119. '<?php echo phpversion(); echo php_sapi_name(); echo M_PI;',
  120. ['functions' => ['pi', 'phpversion']],
  121. ];
  122. yield 'diff argument count than native allows' => [
  123. '<?php
  124. echo phpversion(1);
  125. echo php_sapi_name(1,2);
  126. echo pi(1);
  127. ',
  128. ];
  129. yield 'get_class => T_CLASS' => [
  130. '<?php
  131. class A
  132. {
  133. public function echoClassName($notMe)
  134. {
  135. echo get_class($notMe);
  136. echo self::class/** 1 *//* 2 */;
  137. echo self::class;
  138. }
  139. }
  140. class B
  141. {
  142. use A;
  143. }
  144. ',
  145. '<?php
  146. class A
  147. {
  148. public function echoClassName($notMe)
  149. {
  150. echo get_class($notMe);
  151. echo get_class(/** 1 *//* 2 */);
  152. echo GET_Class();
  153. }
  154. }
  155. class B
  156. {
  157. use A;
  158. }
  159. ',
  160. ];
  161. yield 'get_class with leading backslash' => [
  162. '<?php self::class;',
  163. '<?php \get_class();',
  164. ];
  165. yield [
  166. '<?php class A { function B(){ echo static::class; }}',
  167. '<?php class A { function B(){ echo get_called_class(); }}',
  168. ['functions' => ['get_called_class']],
  169. ];
  170. yield [
  171. '<?php class A { function B(){
  172. echo#.
  173. #0
  174. static::class#1
  175. #2
  176. #3
  177. #4
  178. #5
  179. #6
  180. ;#7
  181. }}
  182. ',
  183. '<?php class A { function B(){
  184. echo#.
  185. #0
  186. get_called_class#1
  187. #2
  188. (#3
  189. #4
  190. )#5
  191. #6
  192. ;#7
  193. }}
  194. ',
  195. ['functions' => ['get_called_class']],
  196. ];
  197. yield 'get_called_class with leading backslash' => [
  198. '<?php class A { function B(){echo static::class; }}',
  199. '<?php class A { function B(){echo \get_called_class(); }}',
  200. ['functions' => ['get_called_class']],
  201. ];
  202. yield 'get_called_class overridden' => [
  203. '<?php echo get_called_class(1);',
  204. null,
  205. ['functions' => ['get_called_class']],
  206. ];
  207. yield [
  208. '<?php class Foo{ public function Bar(){ echo static::class ; }}',
  209. '<?php class Foo{ public function Bar(){ echo get_class( $This ); }}',
  210. ['functions' => ['get_class_this']],
  211. ];
  212. yield [
  213. '<?php class Foo{ public function Bar(){ echo static::class; get_class(1, 2); get_class($a); get_class($a, $b);}}',
  214. '<?php class Foo{ public function Bar(){ echo get_class($this); get_class(1, 2); get_class($a); get_class($a, $b);}}',
  215. ['functions' => ['get_class_this']],
  216. ];
  217. yield [
  218. '<?php class Foo{ public function Bar(){ echo static::class /* 0 */ /* 1 */ ;}}',
  219. '<?php class Foo{ public function Bar(){ echo \get_class( /* 0 */ $this /* 1 */ );}}',
  220. ['functions' => ['get_class_this']],
  221. ];
  222. yield [
  223. '<?php class Foo{ public function Bar(){ echo static::class; echo self::class; }}',
  224. '<?php class Foo{ public function Bar(){ echo \get_class((($this))); echo get_class(); }}',
  225. ['functions' => ['get_class_this', 'get_class']],
  226. ];
  227. yield [
  228. '<?php
  229. class Foo{ public function Bar(){ echo $reflection = new \ReflectionClass(get_class($this->extension)); }}
  230. class Foo{ public function Bar(){ echo $reflection = new \ReflectionClass(get_class($this() )); }}
  231. ',
  232. null,
  233. ['functions' => ['get_class_this']],
  234. ];
  235. yield [
  236. "<?php namespace Foo;\nfunction &PHPversion(){}",
  237. ];
  238. }
  239. /**
  240. * @param _AutogeneratedInputConfiguration $config
  241. *
  242. * @dataProvider provideInvalidConfigurationKeysCases
  243. */
  244. public function testInvalidConfigurationKeys(array $config, string $expectedExceptionMessage): void
  245. {
  246. $this->expectException(InvalidFixerConfigurationException::class);
  247. $this->expectExceptionMessage($expectedExceptionMessage);
  248. $this->fixer->configure($config);
  249. }
  250. public static function provideInvalidConfigurationKeysCases(): iterable
  251. {
  252. yield [
  253. ['functions' => ['a']],
  254. '[function_to_constant] Invalid configuration: The option "functions" with value array is invalid.',
  255. ];
  256. yield [
  257. ['functions' => [false => 1]],
  258. '[function_to_constant] Invalid configuration: The option "functions" with value array is expected to be of type "string[]", but one of the elements is of type "int".',
  259. ];
  260. yield [
  261. ['functions' => ['abc' => true]],
  262. '[function_to_constant] Invalid configuration: The option "functions" with value array is expected to be of type "string[]", but one of the elements is of type "bool".',
  263. ];
  264. }
  265. public function testInvalidConfigurationValue(): void
  266. {
  267. $this->expectException(InvalidFixerConfigurationException::class);
  268. $this->expectExceptionMessageMatches('#^\[function_to_constant\] Invalid configuration: The option "0" does not exist\. Defined options are: "functions"\.$#');
  269. $this->fixer->configure(['pi123']);
  270. }
  271. /**
  272. * @dataProvider provideFix81Cases
  273. *
  274. * @requires PHP 8.1
  275. */
  276. public function testFix81(string $expected, ?string $input = null): void
  277. {
  278. $this->doTest($expected, $input);
  279. }
  280. /**
  281. * @return iterable<string, array{string}>
  282. */
  283. public static function provideFix81Cases(): iterable
  284. {
  285. yield 'first callable class' => [
  286. '<?php $a = get_class(...);',
  287. ];
  288. }
  289. }