ReflectionClosure5Test.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <?php
  2. use Foo\Bar as Baz;
  3. use Foo\Baz\Qux;
  4. use Foo\Baz\Qux\Forest;
  5. use Laravel\SerializableClosure\Support\ReflectionClosure;
  6. use Tests\Fixtures\Model;
  7. test('is short closure', function () {
  8. $f1 = fn () => 1;
  9. $f2 = static fn () => 1;
  10. $f3 = function () {
  11. fn () => 1;
  12. };
  13. expect((new ReflectionClosure($f1))->isShortClosure())->toBeTrue();
  14. expect((new ReflectionClosure($f2))->isShortClosure())->toBeTrue();
  15. expect((new ReflectionClosure($f3))->isShortClosure())->toBeFalse();
  16. });
  17. test('basic short closure', function () {
  18. $f1 = fn () => 'hello';
  19. $e1 = 'fn () => \'hello\'';
  20. $f2 = fn &() => 'hello';
  21. $e2 = 'fn &() => \'hello\'';
  22. $f3 = fn ($a) => 'hello';
  23. $e3 = 'fn ($a) => \'hello\'';
  24. $f4 = fn (&$a) => 'hello';
  25. $e4 = 'fn (&$a) => \'hello\'';
  26. $f5 = fn (&$a): string => 'hello';
  27. $e5 = 'fn (&$a): string => \'hello\'';
  28. expect($f1)->toBeCode($e1);
  29. expect($f2)->toBeCode($e2);
  30. expect($f3)->toBeCode($e3);
  31. expect($f4)->toBeCode($e4);
  32. expect($f5)->toBeCode($e5);
  33. });
  34. test('resolve types', function () {
  35. $f1 = fn (Baz $a) => 'hello';
  36. $e1 = 'fn (\Foo\Bar $a) => \'hello\'';
  37. $f2 = fn (Baz $a): Qux => 'hello';
  38. $e2 = 'fn (\Foo\Bar $a): \Foo\Baz\Qux => \'hello\'';
  39. $f3 = fn (Baz $a): int => (function (Qux $x) {
  40. })();
  41. $e3 = 'fn (\Foo\Bar $a): int => (function (\Foo\Baz\Qux $x) {
  42. })()';
  43. $f4 = fn () => new Qux();
  44. $e4 = 'fn () => new \Foo\Baz\Qux()';
  45. $f5 = fn () => new class extends Baz\Qux {};
  46. $e5 = 'fn () => new class extends \Foo\Bar\Qux {}';
  47. $f6 = fn () => new class extends Baz\Qux implements Baz\Qux {};
  48. $e6 = 'fn () => new class extends \Foo\Bar\Qux implements \Foo\Bar\Qux {}';
  49. $f7 = fn () => new class implements Baz\Qux, Baz\Qux {};
  50. $e7 = 'fn () => new class implements \Foo\Bar\Qux, \Foo\Bar\Qux {}';
  51. $f8 = function () {
  52. $a = new class implements Baz\Qux, Baz\Qux {};
  53. $b = new class implements Baz\Qux {};
  54. };
  55. $e8 = 'function () {
  56. $a = new class implements \Foo\Bar\Qux, \Foo\Bar\Qux {};
  57. $b = new class implements \Foo\Bar\Qux {};
  58. }';
  59. $f9 = function () {
  60. $a = new class implements Baz\Qux, Baz\Qux {};
  61. $b = new class extends Forest implements Baz\Qux
  62. {
  63. public Baz\Qux $qux;
  64. public function foo()
  65. {
  66. return new class {};
  67. }
  68. public function qux(Baz\Qux $qux): Baz\Qux
  69. {
  70. return static fn () => new class extends Forest implements Baz\Qux {
  71. //
  72. };
  73. }
  74. };
  75. };
  76. $e9 = 'function () {
  77. $a = new class implements \Foo\Bar\Qux, \Foo\Bar\Qux {};
  78. $b = new class extends \Foo\Baz\Qux\Forest implements \Foo\Bar\Qux
  79. {
  80. public \Foo\Bar\Qux $qux;
  81. public function foo()
  82. {
  83. return new class {};
  84. }
  85. public function qux(\Foo\Bar\Qux $qux): \Foo\Bar\Qux
  86. {
  87. return static fn () => new class extends \Foo\Baz\Qux\Forest implements \Foo\Bar\Qux {
  88. //
  89. };
  90. }
  91. };
  92. }';
  93. expect($f1)->toBeCode($e1);
  94. expect($f2)->toBeCode($e2);
  95. expect($f3)->toBeCode($e3);
  96. expect($f4)->toBeCode($e4);
  97. expect($f5)->toBeCode($e5);
  98. expect($f6)->toBeCode($e6);
  99. expect($f7)->toBeCode($e7);
  100. expect($f8)->toBeCode($e8);
  101. expect($f9)->toBeCode($e9);
  102. });
  103. test('class keywords instantiation', function () {
  104. test()->assertEquals(
  105. 'function () {
  106. return new self();
  107. }',
  108. c(function () {
  109. return new self();
  110. })
  111. );
  112. test()->assertEquals(
  113. 'function () {
  114. return new static();
  115. }',
  116. c(function () {
  117. return new static();
  118. })
  119. );
  120. test()->assertEquals(
  121. 'function () {
  122. return new parent();
  123. }',
  124. c(function () {
  125. return new parent();
  126. })
  127. );
  128. });
  129. test('function inside expressions and arrays', function () {
  130. $f1 = (fn () => 1);
  131. $e1 = 'fn () => 1';
  132. $f2 = [fn () => 1];
  133. $e2 = 'fn () => 1';
  134. $f3 = [fn () => 1, 0];
  135. $e3 = 'fn () => 1';
  136. $f4 = fn () => ($a === true) && (! empty([0, 1]));
  137. $e4 = 'fn () => ($a === true) && (! empty([0, 1]))';
  138. expect($f1)->toBeCode($e1);
  139. expect($f2[0])->toBeCode($e2);
  140. expect($f3[0])->toBeCode($e3);
  141. expect($f4)->toBeCode($e4);
  142. });
  143. test('serialize', function ($e) {
  144. $f1 = fn () => 'hello';
  145. $c1 = s($f1);
  146. $f2 = fn ($a, $b) => $a + $b;
  147. $c2 = s($f2);
  148. $a = 4;
  149. $f3 = fn (int $b, int $c = 5): int => ($a + $b) * $c;
  150. $c3 = s($f3);
  151. expect($c1())->toEqual('hello');
  152. expect($c2(4, 3))->toEqual(7);
  153. expect($c3(4))->toEqual(40);
  154. expect($c3(4, 6))->toEqual(48);
  155. })->with('serializers');
  156. test('typed properties', function () {
  157. $user = new User();
  158. $s = s(function () {
  159. return true;
  160. });
  161. expect($s())->toBeTrue();
  162. $user = new User();
  163. $product = new Product();
  164. $product->name = 'PC';
  165. $user->setProduct($product);
  166. $u = s(function () use ($user) {
  167. return $user->getProduct()->name;
  168. });
  169. expect($u())->toEqual('PC');
  170. })->with('serializers');
  171. test('group namespaces', function () {
  172. $f = fn (): Forest => new Forest();
  173. $e = 'fn (): \Foo\Baz\Qux\Forest => new \Foo\Baz\Qux\Forest()';
  174. expect($f)->toBeCode($e);
  175. });
  176. test('from callable namespaces', function () {
  177. $f = Closure::fromCallable([new Model, 'make']);
  178. $e = 'function (\Tests\Fixtures\Model $model): \Tests\Fixtures\Model
  179. {
  180. return new \Tests\Fixtures\Model();
  181. }';
  182. expect($f)->toBeCode($e);
  183. });
  184. test('from static callable namespaces', function () {
  185. $f = Closure::fromCallable([Model::class, 'staticMake']);
  186. $e = 'static function (\Tests\Fixtures\Model $model): \Tests\Fixtures\Model
  187. {
  188. return new \Tests\Fixtures\Model();
  189. }';
  190. expect($f)->toBeCode($e);
  191. });
  192. // Helpers
  193. function c(Closure $closure)
  194. {
  195. $r = new ReflectionClosure($closure);
  196. return $r->getCode();
  197. }
  198. class Product
  199. {
  200. public string $name;
  201. }
  202. class User
  203. {
  204. protected Product $product;
  205. public function getProduct(): Product
  206. {
  207. return $this->product;
  208. }
  209. public function setProduct(Product $product): void
  210. {
  211. $this->product = $product;
  212. }
  213. }