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 = $message;
92: if (ini_get('html_errors')) {
93: $msg = html_entity_decode(strip_tags($msg));
94: }
95: $msg = preg_replace("#^$function\(.*?\): #", '', $msg);
96: if ($onError($msg, $severity) !== false) {
97: return;
98: }
99: }
100: return $prev ? $prev(...func_get_args()) : false;
101: });
102:
103: try {
104: return call_user_func_array($function, $args);
105: } finally {
106: restore_error_handler();
107: }
108: }
109:
110:
111: 112: 113:
114: public static function check($callable, $syntax = false)
115: {
116: if (!is_callable($callable, $syntax)) {
117: throw new Nette\InvalidArgumentException($syntax
118: ? 'Given value is not a callable type.'
119: : sprintf("Callback '%s' is not callable.", self::toString($callable))
120: );
121: }
122: return $callable;
123: }
124:
125:
126: 127: 128:
129: public static function toString($callable)
130: {
131: if ($callable instanceof \Closure) {
132: $inner = self::unwrap($callable);
133: return '{closure' . ($inner instanceof \Closure ? '}' : ' ' . self::toString($inner) . '}');
134: } elseif (is_string($callable) && $callable[0] === "\0") {
135: return '{lambda}';
136: } else {
137: is_callable(is_object($callable) ? [$callable, '__invoke'] : $callable, true, $textual);
138: return $textual;
139: }
140: }
141:
142:
143: 144: 145:
146: public static function toReflection($callable)
147: {
148: if ($callable instanceof \Closure) {
149: $callable = self::unwrap($callable);
150: }
151:
152: $class = class_exists(Nette\Reflection\Method::class) ? Nette\Reflection\Method::class : 'ReflectionMethod';
153: if (is_string($callable) && strpos($callable, '::')) {
154: return new $class($callable);
155: } elseif (is_array($callable)) {
156: return new $class($callable[0], $callable[1]);
157: } elseif (is_object($callable) && !$callable instanceof \Closure) {
158: return new $class($callable, '__invoke');
159: } else {
160: $class = class_exists(Nette\Reflection\GlobalFunction::class) ? Nette\Reflection\GlobalFunction::class : 'ReflectionFunction';
161: return new $class($callable);
162: }
163: }
164:
165:
166: 167: 168:
169: public static function isStatic($callable)
170: {
171: return is_array($callable) ? is_string($callable[0]) : is_string($callable);
172: }
173:
174:
175: 176: 177: 178: 179:
180: public static function unwrap(\Closure $closure)
181: {
182: $r = new \ReflectionFunction($closure);
183: if (substr($r->getName(), -1) === '}') {
184: $vars = $r->getStaticVariables();
185: return isset($vars['_callable_']) ? $vars['_callable_'] : $closure;
186:
187: } elseif ($obj = $r->getClosureThis()) {
188: return [$obj, $r->getName()];
189:
190: } elseif ($class = $r->getClosureScopeClass()) {
191: return [$class->getName(), $r->getName()];
192:
193: } else {
194: return $r->getName();
195: }
196: }
197: }
198: