Callback.closure.phpt 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php
  2. /**
  3. * Test: Nette\Utils\Callback closures tests.
  4. */
  5. declare(strict_types=1);
  6. use Nette\Utils\Callback;
  7. use Tester\Assert;
  8. require __DIR__ . '/../bootstrap.php';
  9. class Test
  10. {
  11. public function __invoke($a)
  12. {
  13. return __METHOD__ . $a;
  14. }
  15. public function publicFun($a)
  16. {
  17. return __METHOD__ . $a;
  18. }
  19. private function privateFun($a)
  20. {
  21. return __METHOD__ . $a;
  22. }
  23. public static function publicStatic($a)
  24. {
  25. return __METHOD__ . $a;
  26. }
  27. private static function privateStatic($a)
  28. {
  29. return __METHOD__ . $a;
  30. }
  31. public function createPrivateClosure(): Closure
  32. {
  33. return Closure::fromCallable([$this, 'privateFun']);
  34. }
  35. public static function createPrivateStaticClosure(): Closure
  36. {
  37. return Closure::fromCallable([self::class, 'privateStatic']);
  38. }
  39. public function ref(&$a)
  40. {
  41. $a = __METHOD__;
  42. return $a;
  43. }
  44. }
  45. class TestDynamic
  46. {
  47. public function __call($nm, $args)
  48. {
  49. return __METHOD__ . " $nm $args[0]";
  50. }
  51. public static function __callStatic($nm, $args)
  52. {
  53. return __METHOD__ . " $nm $args[0]";
  54. }
  55. }
  56. class TestChild extends Test
  57. {
  58. }
  59. function getName($ref)
  60. {
  61. if ($ref instanceof ReflectionFunction) {
  62. return $ref->getName();
  63. } elseif ($ref instanceof ReflectionMethod) {
  64. return $ref->getDeclaringClass()->getName() . '::' . $ref->getName();
  65. }
  66. }
  67. test('global function', function () {
  68. Assert::same('trim', Callback::unwrap(Closure::fromCallable('trim')));
  69. Assert::same('trim', Callback::toString('trim'));
  70. Assert::same('{closure trim}', Callback::toString(Closure::fromCallable('trim')));
  71. Assert::same('trim', getName(Callback::toReflection('trim')));
  72. Assert::same('trim', getName(Callback::toReflection(Closure::fromCallable('trim'))));
  73. Assert::same('x', Closure::fromCallable('trim')->__invoke(' x '));
  74. Assert::same('undefined', Callback::toString('undefined'));
  75. Assert::exception(
  76. fn() => Callback::toReflection('undefined'),
  77. ReflectionException::class,
  78. 'Function undefined() does not exist',
  79. );
  80. });
  81. test('closure', function () {
  82. $closure = function (&$a) {
  83. $a = __FUNCTION__;
  84. return $a;
  85. };
  86. Assert::same($closure, Closure::fromCallable($closure));
  87. Assert::same($closure, Callback::unwrap($closure));
  88. Assert::same('{closure}', Callback::toString($closure));
  89. Assert::same('{closure}', getName(Callback::toReflection($closure)));
  90. Assert::same('{closure}', Closure::fromCallable($closure)(...[&$res]));
  91. Assert::same('{closure}', $res);
  92. });
  93. test('invokable object', function () {
  94. $test = new Test;
  95. Assert::same([$test, '__invoke'], Callback::unwrap(Closure::fromCallable($test)));
  96. Assert::same('Test::__invoke', Callback::toString($test));
  97. Assert::same('{closure Test::__invoke}', Callback::toString(Closure::fromCallable($test)));
  98. Assert::same('Test::__invoke', getName(Callback::toReflection($test)));
  99. Assert::same('Test::__invoke', getName(Callback::toReflection(Closure::fromCallable($test))));
  100. Assert::same('Test::__invoke*', Closure::fromCallable($test)->__invoke('*'));
  101. });
  102. test('object methods', function () {
  103. $test = new Test;
  104. Assert::same([$test, 'publicFun'], Callback::unwrap(Closure::fromCallable([$test, 'publicFun'])));
  105. Assert::same('Test::publicFun', Callback::toString([$test, 'publicFun']));
  106. Assert::same('{closure Test::publicFun}', Callback::toString(Closure::fromCallable([$test, 'publicFun'])));
  107. Assert::same('Test::publicFun', getName(Callback::toReflection([$test, 'publicFun'])));
  108. Assert::same('Test::publicFun', getName(Callback::toReflection(Closure::fromCallable([$test, 'publicFun']))));
  109. Assert::same('Test::publicFun*', Closure::fromCallable([$test, 'publicFun'])->__invoke('*'));
  110. Assert::same([$test, 'privateFun'], Callback::unwrap($test->createPrivateClosure()));
  111. Assert::same('Test::privateFun', Callback::toString([$test, 'privateFun']));
  112. Assert::same('{closure Test::privateFun}', Callback::toString($test->createPrivateClosure()));
  113. Assert::same('Test::privateFun', getName(Callback::toReflection([$test, 'privateFun'])));
  114. Assert::same('Test::privateFun', getName(Callback::toReflection($test->createPrivateClosure())));
  115. Assert::same(['Test', 'privateFun'], Callback::unwrap((new TestChild)->createPrivateClosure()));
  116. Assert::same('Test::privateFun', getName(Callback::toReflection((new TestChild)->createPrivateClosure())));
  117. Assert::same('Test::privateFun*', $test->createPrivateClosure()->__invoke('*'));
  118. Assert::same('Test::ref', Closure::fromCallable([$test, 'ref'])(...[&$res]));
  119. Assert::same('Test::ref', $res);
  120. });
  121. test('static methods', function () {
  122. $test = new Test;
  123. Assert::same(['Test', 'publicStatic'], Callback::unwrap(Closure::fromCallable(['Test', 'publicStatic'])));
  124. Assert::same(['Test', 'publicStatic'], Callback::unwrap(Closure::fromCallable('Test::publicStatic')));
  125. Assert::same('Test::publicStatic', Callback::toString(['Test', 'publicStatic']));
  126. Assert::same('Test::publicStatic', Callback::toString([$test, 'publicStatic']));
  127. Assert::same('Test::publicStatic', Callback::toString('Test::publicStatic'));
  128. Assert::same('{closure Test::publicStatic}', Callback::toString(Closure::fromCallable('Test::publicStatic')));
  129. Assert::same('Test::publicStatic', getName(Callback::toReflection(['Test', 'publicStatic'])));
  130. Assert::same('Test::publicStatic', getName(Callback::toReflection([$test, 'publicStatic'])));
  131. Assert::same('Test::publicStatic', getName(Callback::toReflection('Test::publicStatic')));
  132. Assert::same('Test::publicStatic', getName(Callback::toReflection(Closure::fromCallable('Test::publicStatic'))));
  133. Assert::same('Test::publicStatic*', Closure::fromCallable(['Test', 'publicStatic'])->__invoke('*'));
  134. Assert::same('Test::publicStatic*', Closure::fromCallable([$test, 'publicStatic'])->__invoke('*'));
  135. Assert::same(['Test', 'privateStatic'], Callback::unwrap(Test::createPrivateStaticClosure()));
  136. Assert::same('Test::privateStatic', Callback::toString('Test::privateStatic'));
  137. Assert::same('{closure Test::privateStatic}', Callback::toString(Test::createPrivateStaticClosure()));
  138. Assert::same('Test::privateStatic', getName(Callback::toReflection('Test::privateStatic')));
  139. Assert::same('Test::privateStatic', getName(Callback::toReflection(Test::createPrivateStaticClosure())));
  140. Assert::same('Test::privateStatic', getName(Callback::toReflection(TestChild::createPrivateStaticClosure())));
  141. Assert::same('Test::privateStatic*', Test::createPrivateStaticClosure()->__invoke('*'));
  142. });
  143. test('magic methods', function () {
  144. $test = new TestDynamic;
  145. Assert::same([$test, 'magic'], Callback::unwrap(Closure::fromCallable([$test, 'magic'])));
  146. Assert::same('TestDynamic::magic', Callback::toString([$test, 'magic']));
  147. Assert::same('{closure TestDynamic::magic}', Callback::toString(Closure::fromCallable([$test, 'magic'])));
  148. Assert::same('TestDynamic::__call magic *', Closure::fromCallable([$test, 'magic'])->__invoke('*'));
  149. Assert::same(['TestDynamic', 'magic'], Callback::unwrap(Closure::fromCallable('TestDynamic::magic')));
  150. Assert::same('TestDynamic::magic', Callback::toString('TestDynamic::magic'));
  151. Assert::same('{closure TestDynamic::magic}', Callback::toString(Closure::fromCallable('TestDynamic::magic')));
  152. Assert::same('TestDynamic::__callStatic magic *', Closure::fromCallable('TestDynamic::magic')->__invoke('*'));
  153. Assert::exception(
  154. fn() => Callback::toReflection([new TestDynamic, 'magic']),
  155. ReflectionException::class,
  156. 'Method TestDynamic::magic() does not exist',
  157. );
  158. Assert::exception(
  159. fn() => Callback::toReflection(Closure::fromCallable([new TestDynamic, 'magic'])),
  160. ReflectionException::class,
  161. 'Method TestDynamic::magic() does not exist',
  162. );
  163. });
  164. test('PHP bugs - is_callable($object, true) fails', function () {
  165. Assert::same('stdClass::__invoke', Callback::toString(new stdClass));
  166. Assert::exception(
  167. fn() => Callback::toReflection(new stdClass),
  168. ReflectionException::class,
  169. 'Method stdClass::__invoke() does not exist',
  170. );
  171. });