Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7
 
8
declare(strict_types=1);
9
 
10
namespace Nette\Utils;
11
 
12
use Nette;
13
use function is_array, is_object, is_string;
14
 
15
 
16
/**
17
 * PHP callable tools.
18
 */
19
final class Callback
20
{
21
	use Nette\StaticClass;
22
 
23
	/**
24
	 * @param  string|object|callable  $callable  class, object, callable
25
	 * @deprecated use Closure::fromCallable()
26
	 */
27
	public static function closure($callable, ?string $method = null): \Closure
28
	{
29
		trigger_error(__METHOD__ . '() is deprecated, use Closure::fromCallable().', E_USER_DEPRECATED);
30
		try {
31
			return \Closure::fromCallable($method === null ? $callable : [$callable, $method]);
32
		} catch (\TypeError $e) {
33
			throw new Nette\InvalidArgumentException($e->getMessage());
34
		}
35
	}
36
 
37
 
38
	/**
39
	 * Invokes callback.
40
	 * @return mixed
41
	 * @deprecated
42
	 */
43
	public static function invoke($callable, ...$args)
44
	{
45
		trigger_error(__METHOD__ . '() is deprecated, use native invoking.', E_USER_DEPRECATED);
46
		self::check($callable);
47
		return $callable(...$args);
48
	}
49
 
50
 
51
	/**
52
	 * Invokes callback with an array of parameters.
53
	 * @return mixed
54
	 * @deprecated
55
	 */
56
	public static function invokeArgs($callable, array $args = [])
57
	{
58
		trigger_error(__METHOD__ . '() is deprecated, use native invoking.', E_USER_DEPRECATED);
59
		self::check($callable);
60
		return $callable(...$args);
61
	}
62
 
63
 
64
	/**
65
	 * Invokes internal PHP function with own error handler.
66
	 * @return mixed
67
	 */
68
	public static function invokeSafe(string $function, array $args, callable $onError)
69
	{
70
		$prev = set_error_handler(function ($severity, $message, $file) use ($onError, &$prev, $function): ?bool {
71
			if ($file === __FILE__) {
72
				$msg = ini_get('html_errors')
73
					? Html::htmlToText($message)
74
					: $message;
75
				$msg = preg_replace("#^$function\\(.*?\\): #", '', $msg);
76
				if ($onError($msg, $severity) !== false) {
77
					return null;
78
				}
79
			}
80
 
81
			return $prev ? $prev(...func_get_args()) : false;
82
		});
83
 
84
		try {
85
			return $function(...$args);
86
		} finally {
87
			restore_error_handler();
88
		}
89
	}
90
 
91
 
92
	/**
93
	 * Checks that $callable is valid PHP callback. Otherwise throws exception. If the $syntax is set to true, only verifies
94
	 * that $callable has a valid structure to be used as a callback, but does not verify if the class or method actually exists.
95
	 * @param  mixed  $callable
96
	 * @return callable
97
	 * @throws Nette\InvalidArgumentException
98
	 */
99
	public static function check($callable, bool $syntax = false)
100
	{
101
		if (!is_callable($callable, $syntax)) {
102
			throw new Nette\InvalidArgumentException(
103
				$syntax
104
				? 'Given value is not a callable type.'
105
				: sprintf("Callback '%s' is not callable.", self::toString($callable))
106
			);
107
		}
108
 
109
		return $callable;
110
	}
111
 
112
 
113
	/**
114
	 * Converts PHP callback to textual form. Class or method may not exists.
115
	 * @param  mixed  $callable
116
	 */
117
	public static function toString($callable): string
118
	{
119
		if ($callable instanceof \Closure) {
120
			$inner = self::unwrap($callable);
121
			return '{closure' . ($inner instanceof \Closure ? '}' : ' ' . self::toString($inner) . '}');
122
		} elseif (is_string($callable) && $callable[0] === "\0") {
123
			return '{lambda}';
124
		} else {
125
			is_callable(is_object($callable) ? [$callable, '__invoke'] : $callable, true, $textual);
126
			return $textual;
127
		}
128
	}
129
 
130
 
131
	/**
132
	 * Returns reflection for method or function used in PHP callback.
133
	 * @param  callable  $callable  type check is escalated to ReflectionException
134
	 * @return \ReflectionMethod|\ReflectionFunction
135
	 * @throws \ReflectionException  if callback is not valid
136
	 */
137
	public static function toReflection($callable): \ReflectionFunctionAbstract
138
	{
139
		if ($callable instanceof \Closure) {
140
			$callable = self::unwrap($callable);
141
		}
142
 
143
		if (is_string($callable) && strpos($callable, '::')) {
144
			return new \ReflectionMethod($callable);
145
		} elseif (is_array($callable)) {
146
			return new \ReflectionMethod($callable[0], $callable[1]);
147
		} elseif (is_object($callable) && !$callable instanceof \Closure) {
148
			return new \ReflectionMethod($callable, '__invoke');
149
		} else {
150
			return new \ReflectionFunction($callable);
151
		}
152
	}
153
 
154
 
155
	/**
156
	 * Checks whether PHP callback is function or static method.
157
	 */
158
	public static function isStatic(callable $callable): bool
159
	{
150 lars 160
		return is_string(is_array($callable) ? $callable[0] : $callable);
148 lars 161
	}
162
 
163
 
164
	/**
165
	 * Unwraps closure created by Closure::fromCallable().
166
	 * @return callable|array
167
	 */
168
	public static function unwrap(\Closure $closure)
169
	{
170
		$r = new \ReflectionFunction($closure);
171
		if (substr($r->name, -1) === '}') {
172
			return $closure;
173
 
174
		} elseif ($obj = $r->getClosureThis()) {
175
			return [$obj, $r->name];
176
 
177
		} elseif ($class = $r->getClosureScopeClass()) {
178
			return [$class->name, $r->name];
179
 
180
		} else {
181
			return $r->name;
182
		}
183
	}
184
}