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:
60: public static function invoke($callable, ...$args)
61: {
62: self::check($callable);
63: return call_user_func_array($callable, $args);
64: }
65:
66:
67: 68: 69: 70: 71:
72: public static function invokeArgs($callable, array $args = [])
73: {
74: self::check($callable);
75: return call_user_func_array($callable, $args);
76: }
77:
78:
79: 80: 81: 82: 83:
84: public static function invokeSafe($function, array $args, $onError)
85: {
86: $prev = set_error_handler(function ($severity, $message, $file) use ($onError, &$prev, $function) {
87: if ($file === '' && defined('HHVM_VERSION')) {
88: $file = func_get_arg(5)[1]['file'];
89: }
90: if ($file === __FILE__) {
91: $msg = preg_replace("#^$function\(.*?\): #", '', $message);
92: if ($onError($msg, $severity) !== false) {
93: return;
94: }
95: }
96: return $prev ? $prev(...func_get_args()) : false;
97: });
98:
99: try {
100: return call_user_func_array($function, $args);
101: } finally {
102: restore_error_handler();
103: }
104: }
105:
106:
107: 108: 109:
110: public static function check($callable, $syntax = false)
111: {
112: if (!is_callable($callable, $syntax)) {
113: throw new Nette\InvalidArgumentException($syntax
114: ? 'Given value is not a callable type.'
115: : sprintf("Callback '%s' is not callable.", self::toString($callable))
116: );
117: }
118: return $callable;
119: }
120:
121:
122: 123: 124:
125: public static function toString($callable)
126: {
127: if ($callable instanceof \Closure) {
128: $inner = self::unwrap($callable);
129: return '{closure' . ($inner instanceof \Closure ? '}' : ' ' . self::toString($inner) . '}');
130: } elseif (is_string($callable) && $callable[0] === "\0") {
131: return '{lambda}';
132: } else {
133: is_callable($callable, true, $textual);
134: return $textual;
135: }
136: }
137:
138:
139: 140: 141:
142: public static function toReflection($callable)
143: {
144: if ($callable instanceof \Closure) {
145: $callable = self::unwrap($callable);
146: }
147:
148: $class = class_exists(Nette\Reflection\Method::class) ? Nette\Reflection\Method::class : 'ReflectionMethod';
149: if (is_string($callable) && strpos($callable, '::')) {
150: return new $class($callable);
151: } elseif (is_array($callable)) {
152: return new $class($callable[0], $callable[1]);
153: } elseif (is_object($callable) && !$callable instanceof \Closure) {
154: return new $class($callable, '__invoke');
155: } else {
156: $class = class_exists(Nette\Reflection\GlobalFunction::class) ? Nette\Reflection\GlobalFunction::class : 'ReflectionFunction';
157: return new $class($callable);
158: }
159: }
160:
161:
162: 163: 164:
165: public static function isStatic($callable)
166: {
167: return is_array($callable) ? is_string($callable[0]) : is_string($callable);
168: }
169:
170:
171: 172: 173: 174: 175:
176: public static function unwrap(\Closure $closure)
177: {
178: $r = new \ReflectionFunction($closure);
179: if (substr($r->getName(), -1) === '}') {
180: $vars = $r->getStaticVariables();
181: return isset($vars['_callable_']) ? $vars['_callable_'] : $closure;
182:
183: } elseif ($obj = $r->getClosureThis()) {
184: return [$obj, $r->getName()];
185:
186: } elseif ($class = $r->getClosureScopeClass()) {
187: return [$class->getName(), $r->getName()];
188:
189: } else {
190: return $r->getName();
191: }
192: }
193: }
194: