1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Utils;
9:
10: use Nette;
11:
12:
13: 14: 15:
16: class Callback
17: {
18: use Nette\StaticClass;
19:
20: 21: 22: 23: 24:
25: public static function closure($callable, $m = null)
26: {
27: if ($m !== null) {
28: $callable = [$callable, $m];
29:
30: } elseif (is_string($callable) && count($tmp = explode('::', $callable)) === 2) {
31: $callable = $tmp;
32:
33: } elseif ($callable instanceof \Closure) {
34: return $callable;
35:
36: } elseif (is_object($callable)) {
37: $callable = [$callable, '__invoke'];
38: }
39:
40: if (is_string($callable) && function_exists($callable)) {
41: return (new \ReflectionFunction($callable))->getClosure();
42:
43: } elseif (is_array($callable) && method_exists($callable[0], $callable[1])) {
44: return (new \ReflectionMethod($callable[0], $callable[1]))->getClosure($callable[0]);
45: }
46:
47: self::check($callable);
48: $_callable_ = $callable;
49: return function (...$args) use ($_callable_) {
50: return $_callable_(...$args);
51: };
52: }
53:
54:
55: 56: 57: 58:
59: public static function invoke($callable, ...$args)
60: {
61: self::check($callable);
62: return call_user_func_array($callable, $args);
63: }
64:
65:
66: 67: 68: 69:
70: public static function invokeArgs($callable, array $args = [])
71: {
72: self::check($callable);
73: return call_user_func_array($callable, $args);
74: }
75:
76:
77: 78: 79: 80: 81:
82: public static function invokeSafe($function, array $args, $onError)
83: {
84: $prev = set_error_handler(function ($severity, $message, $file) use ($onError, &$prev, $function) {
85: if ($file === '' && defined('HHVM_VERSION')) {
86: $file = func_get_arg(5)[1]['file'];
87: }
88: if ($file === __FILE__) {
89: $msg = preg_replace("#^$function\(.*?\): #", '', $message);
90: if ($onError($msg, $severity) !== false) {
91: return;
92: }
93: }
94: return $prev ? $prev(...func_get_args()) : false;
95: });
96:
97: try {
98: return call_user_func_array($function, $args);
99: } finally {
100: restore_error_handler();
101: }
102: }
103:
104:
105: 106: 107:
108: public static function check($callable, $syntax = false)
109: {
110: if (!is_callable($callable, $syntax)) {
111: throw new Nette\InvalidArgumentException($syntax
112: ? 'Given value is not a callable type.'
113: : sprintf("Callback '%s' is not callable.", self::toString($callable))
114: );
115: }
116: return $callable;
117: }
118:
119:
120: 121: 122:
123: public static function toString($callable)
124: {
125: if ($callable instanceof \Closure) {
126: $inner = self::unwrap($callable);
127: return '{closure' . ($inner instanceof \Closure ? '}' : ' ' . self::toString($inner) . '}');
128: } elseif (is_string($callable) && $callable[0] === "\0") {
129: return '{lambda}';
130: } else {
131: is_callable($callable, true, $textual);
132: return $textual;
133: }
134: }
135:
136:
137: 138: 139:
140: public static function toReflection($callable)
141: {
142: if ($callable instanceof \Closure) {
143: $callable = self::unwrap($callable);
144: } elseif ($callable instanceof Nette\Callback) {
145: trigger_error('Nette\Callback is deprecated.', E_USER_DEPRECATED);
146: $callable = $callable->getNative();
147: }
148:
149: $class = class_exists(Nette\Reflection\Method::class) ? Nette\Reflection\Method::class : 'ReflectionMethod';
150: if (is_string($callable) && strpos($callable, '::')) {
151: return new $class($callable);
152: } elseif (is_array($callable)) {
153: return new $class($callable[0], $callable[1]);
154: } elseif (is_object($callable) && !$callable instanceof \Closure) {
155: return new $class($callable, '__invoke');
156: } else {
157: $class = class_exists(Nette\Reflection\GlobalFunction::class) ? Nette\Reflection\GlobalFunction::class : 'ReflectionFunction';
158: return new $class($callable);
159: }
160: }
161:
162:
163: 164: 165:
166: public static function isStatic($callable)
167: {
168: return is_array($callable) ? is_string($callable[0]) : is_string($callable);
169: }
170:
171:
172: 173: 174: 175: 176:
177: public static function unwrap(\Closure $closure)
178: {
179: $r = new \ReflectionFunction($closure);
180: if (substr($r->getName(), -1) === '}') {
181: $vars = $r->getStaticVariables();
182: return isset($vars['_callable_']) ? $vars['_callable_'] : $closure;
183:
184: } elseif ($obj = $r->getClosureThis()) {
185: return [$obj, $r->getName()];
186:
187: } elseif ($class = $r->getClosureScopeClass()) {
188: return [$class->getName(), $r->getName()];
189:
190: } else {
191: return $r->getName();
192: }
193: }
194: }
195: