FunctionToConstantFixerTest.php 9.1 KB

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