ReflectionClosurePhp80Test.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. <?php
  2. // Fake
  3. use Some\ClassName as ClassAlias;
  4. use Tests\Fixtures\RegularClass;
  5. test('union types', function () {
  6. $f1 = fn (): string|int|false|Bar|null => 1;
  7. $e1 = 'fn (): string|int|false|\Bar|null => 1';
  8. $f2 = fn (): \Foo|\Bar => 1;
  9. $e2 = 'fn (): \Foo|\Bar => 1';
  10. $f3 = fn (): int|false => false;
  11. $e3 = 'fn (): int|false => false';
  12. $f4 = function (): null|MyClass|ClassAlias|Relative\Ns\ClassName|\Absolute\Ns\ClassName {
  13. return null;
  14. };
  15. $e4 = 'function (): null|\MyClass|\Some\ClassName|\Relative\Ns\ClassName|\Absolute\Ns\ClassName {
  16. return null;
  17. }';
  18. expect($f1)->toBeCode($e1);
  19. expect($f2)->toBeCode($e2);
  20. expect($f3)->toBeCode($e3);
  21. expect($f4)->toBeCode($e4);
  22. });
  23. test('mixed type', function () {
  24. $f1 = function (): mixed {
  25. return 42;
  26. };
  27. $e1 = 'function (): mixed {
  28. return 42;
  29. }';
  30. expect($f1)->toBeCode($e1);
  31. });
  32. test('null safe operator with methods', function () {
  33. $f1 = function () {
  34. $obj = new \stdClass();
  35. return $obj?->invalid();
  36. };
  37. $e1 = 'function () {
  38. $obj = new \stdClass();
  39. return $obj?->invalid();
  40. }';
  41. expect($f1)->toBeCode($e1);
  42. });
  43. test('null safe operator with properties', function () {
  44. $f1 = function () {
  45. $obj = new \stdClass();
  46. return $obj?->invalid;
  47. };
  48. $e1 = 'function () {
  49. $obj = new \stdClass();
  50. return $obj?->invalid;
  51. }';
  52. expect($f1)->toBeCode($e1);
  53. });
  54. test('trailing comma', function () {
  55. $f1 = function (string $param, ) {
  56. };
  57. $e1 = 'function (string $param, ) {
  58. }';
  59. expect($f1)->toBeCode($e1);
  60. });
  61. test('named arguments', function () {
  62. $f1 = function (string $firstName, string $lastName) {
  63. return $firstName.' '.$lastName;
  64. };
  65. $e1 = "function (string \$firstName, string \$lastName) {
  66. return \$firstName.' '.\$lastName;
  67. }";
  68. expect($f1)->toBeCode($e1);
  69. });
  70. test('single named argument within closures', function () {
  71. $f1 = function () {
  72. return (new ReflectionClosurePhp80NamedArguments)->publicMethod(namedArgument: 'string');
  73. };
  74. $e1 = "function () {
  75. return (new \ReflectionClosurePhp80NamedArguments)->publicMethod(namedArgument: 'string');
  76. }";
  77. expect($f1)->toBeCode($e1);
  78. });
  79. test('multiple named arguments within closures', function () {
  80. $f1 = function () {
  81. return (new ReflectionClosurePhp80NamedArguments)->publicMethod(namedArgument: 'string', namedArgumentB: 1);
  82. };
  83. $e1 = "function () {
  84. return (new \ReflectionClosurePhp80NamedArguments)->publicMethod(namedArgument: 'string', namedArgumentB: 1);
  85. }";
  86. expect($f1)->toBeCode($e1);
  87. });
  88. test('named arguments with switch cases and instanceof', function () {
  89. $f1 = function ($a) {
  90. switch (true) {
  91. case (new RegularClass(a2: $a))->a2 instanceof RegularClass:
  92. return (new RegularClass(a2: $a))->a2;
  93. default:
  94. return new RegularClass(a2: RegularClass::C);
  95. }
  96. };
  97. $e1 = 'function ($a) {
  98. switch (true) {
  99. case (new \Tests\Fixtures\RegularClass(a2: $a))->a2 instanceof \Tests\Fixtures\RegularClass:
  100. return (new \Tests\Fixtures\RegularClass(a2: $a))->a2;
  101. default:
  102. return new \Tests\Fixtures\RegularClass(a2: \Tests\Fixtures\RegularClass::C);
  103. }
  104. }';
  105. expect($f1)->toBeCode($e1);
  106. });
  107. test('multiple named arguments within nested closures', function () {
  108. $f1 = function () {
  109. $fn = fn ($namedArgument, $namedArgumentB) => (
  110. new ReflectionClosurePhp80NamedArguments
  111. )->publicMethod(namedArgument: $namedArgument, namedArgumentB: $namedArgumentB);
  112. return $fn(namedArgument: 'string', namedArgumentB: 1);
  113. };
  114. $e1 = "function () {
  115. \$fn = fn (\$namedArgument, \$namedArgumentB) => (
  116. new \ReflectionClosurePhp80NamedArguments
  117. )->publicMethod(namedArgument: \$namedArgument, namedArgumentB: \$namedArgumentB);
  118. return \$fn(namedArgument: 'string', namedArgumentB: 1);
  119. }";
  120. expect($f1)->toBeCode($e1);
  121. })->with('serializers');
  122. class ReflectionClosurePhp80NamedArguments
  123. {
  124. public function publicMethod(string $namedArgument, $namedArgumentB = null)
  125. {
  126. return $namedArgument.(string) $namedArgumentB;
  127. }
  128. }
  129. class PropertyPromotion
  130. {
  131. public function __construct(
  132. public string $public,
  133. protected string $protected,
  134. private string $private,
  135. ) {
  136. }
  137. public function getProtected(): string
  138. {
  139. return $this->protected;
  140. }
  141. public function getPrivate(): string
  142. {
  143. return $this->private;
  144. }
  145. }
  146. function reflection_closure_php_80_switch_statement_test_is_two($a)
  147. {
  148. return $a === 2;
  149. }
  150. class ReflectionClosurePhp80InstanceOfTest
  151. {
  152. };
  153. class ReflectionClosurePhp80SwitchStatementTest
  154. {
  155. }
  156. test('instanceof', function () {
  157. $f1 = function (object $a): array {
  158. $b = $a instanceof DateTime || $a instanceof ReflectionClosurePhp80InstanceOfTest || $a instanceof RegularClass;
  159. return [
  160. $b,
  161. ($a instanceof DateTime || $a instanceof ReflectionClosurePhp80InstanceOfTest || $a instanceof RegularClass),
  162. (function (object $a): bool {
  163. return ($a instanceof DateTime || $a instanceof ReflectionClosurePhp80InstanceOfTest || $a instanceof RegularClass) === true;
  164. })(a: $a),
  165. ];
  166. };
  167. $e1 = 'function (object $a): array {
  168. $b = $a instanceof \DateTime || $a instanceof \ReflectionClosurePhp80InstanceOfTest || $a instanceof \Tests\Fixtures\RegularClass;
  169. return [
  170. $b,
  171. ($a instanceof \DateTime || $a instanceof \ReflectionClosurePhp80InstanceOfTest || $a instanceof \Tests\Fixtures\RegularClass),
  172. (function (object $a): bool {
  173. return ($a instanceof \DateTime || $a instanceof \ReflectionClosurePhp80InstanceOfTest || $a instanceof \Tests\Fixtures\RegularClass) === true;
  174. })(a: $a),
  175. ];
  176. }';
  177. expect($f1)->toBeCode($e1);
  178. });
  179. test('switch statement', function () {
  180. $f1 = function ($a) {
  181. switch (true) {
  182. case $a === 1:
  183. return 'one';
  184. case reflection_closure_php_80_switch_statement_test_is_two(a: $a):
  185. return 'two';
  186. case ReflectionClosurePhp80SwitchStatementTest::isThree(a: $a):
  187. return 'three';
  188. case (new ReflectionClosurePhp80SwitchStatementTest)->isFour(a: $a):
  189. return 'four';
  190. case ($a instanceof ReflectionClosurePhp80SwitchStatementTest):
  191. return 'five';
  192. case ($a instanceof DateTime):
  193. return 'six';
  194. case ($a instanceof RegularClass):
  195. return 'seven';
  196. default:
  197. return 'other';
  198. }
  199. };
  200. $e1 = 'function ($a) {
  201. switch (true) {
  202. case $a === 1:
  203. return \'one\';
  204. case \reflection_closure_php_80_switch_statement_test_is_two(a: $a):
  205. return \'two\';
  206. case \ReflectionClosurePhp80SwitchStatementTest::isThree(a: $a):
  207. return \'three\';
  208. case (new \ReflectionClosurePhp80SwitchStatementTest)->isFour(a: $a):
  209. return \'four\';
  210. case ($a instanceof \ReflectionClosurePhp80SwitchStatementTest):
  211. return \'five\';
  212. case ($a instanceof \DateTime):
  213. return \'six\';
  214. case ($a instanceof \Tests\Fixtures\RegularClass):
  215. return \'seven\';
  216. default:
  217. return \'other\';
  218. }
  219. }';
  220. expect($f1)->toBeCode($e1);
  221. });
  222. function reflection_closure_php_80_match_statement_test_is_two($a)
  223. {
  224. return $a === 2;
  225. }
  226. class ReflectionClosurePhp80MatchStatementTest
  227. {
  228. public static function isThree($a)
  229. {
  230. return $a === 3;
  231. }
  232. public function isFour($a)
  233. {
  234. return $a === 4;
  235. }
  236. }
  237. test('match statement', function () {
  238. $f1 = function ($a) {
  239. return match (true) {
  240. $a === 1 => 'one',
  241. reflection_closure_php_80_match_statement_test_is_two($a) => 'two',
  242. ReflectionClosurePhp80MatchStatementTest::isThree(a: $a) => 'three',
  243. (new ReflectionClosurePhp80MatchStatementTest)->isFour($a) => 'four',
  244. $a instanceof ReflectionClosurePhp80MatchStatementTest => 'five',
  245. $a instanceof DateTime => 'six',
  246. $a instanceof RegularClass => 'seven',
  247. default => 'other',
  248. };
  249. };
  250. $e1 = 'function ($a) {
  251. return match (true) {
  252. $a === 1 => \'one\',
  253. \reflection_closure_php_80_match_statement_test_is_two($a) => \'two\',
  254. \ReflectionClosurePhp80MatchStatementTest::isThree(a: $a) => \'three\',
  255. (new \ReflectionClosurePhp80MatchStatementTest)->isFour($a) => \'four\',
  256. $a instanceof \ReflectionClosurePhp80MatchStatementTest => \'five\',
  257. $a instanceof \DateTime => \'six\',
  258. $a instanceof \Tests\Fixtures\RegularClass => \'seven\',
  259. default => \'other\',
  260. };
  261. }';
  262. expect($f1)->toBeCode($e1);
  263. });