Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 150 | 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
	 * Invokes internal PHP function with own error handler.
25
	 */
399 lars 26
	public static function invokeSafe(string $function, array $args, callable $onError): mixed
148 lars 27
	{
28
		$prev = set_error_handler(function ($severity, $message, $file) use ($onError, &$prev, $function): ?bool {
29
			if ($file === __FILE__) {
30
				$msg = ini_get('html_errors')
31
					? Html::htmlToText($message)
32
					: $message;
33
				$msg = preg_replace("#^$function\\(.*?\\): #", '', $msg);
34
				if ($onError($msg, $severity) !== false) {
35
					return null;
36
				}
37
			}
38
 
39
			return $prev ? $prev(...func_get_args()) : false;
40
		});
41
 
42
		try {
43
			return $function(...$args);
44
		} finally {
45
			restore_error_handler();
46
		}
47
	}
48
 
49
 
50
	/**
51
	 * Checks that $callable is valid PHP callback. Otherwise throws exception. If the $syntax is set to true, only verifies
52
	 * that $callable has a valid structure to be used as a callback, but does not verify if the class or method actually exists.
53
	 * @return callable
54
	 * @throws Nette\InvalidArgumentException
55
	 */
399 lars 56
	public static function check(mixed $callable, bool $syntax = false)
148 lars 57
	{
58
		if (!is_callable($callable, $syntax)) {
59
			throw new Nette\InvalidArgumentException(
60
				$syntax
61
				? 'Given value is not a callable type.'
399 lars 62
				: sprintf("Callback '%s' is not callable.", self::toString($callable)),
148 lars 63
			);
64
		}
65
 
66
		return $callable;
67
	}
68
 
69
 
70
	/**
71
	 * Converts PHP callback to textual form. Class or method may not exists.
72
	 */
399 lars 73
	public static function toString(mixed $callable): string
148 lars 74
	{
75
		if ($callable instanceof \Closure) {
76
			$inner = self::unwrap($callable);
77
			return '{closure' . ($inner instanceof \Closure ? '}' : ' ' . self::toString($inner) . '}');
78
		} else {
79
			is_callable(is_object($callable) ? [$callable, '__invoke'] : $callable, true, $textual);
80
			return $textual;
81
		}
82
	}
83
 
84
 
85
	/**
86
	 * Returns reflection for method or function used in PHP callback.
87
	 * @param  callable  $callable  type check is escalated to ReflectionException
88
	 * @throws \ReflectionException  if callback is not valid
89
	 */
399 lars 90
	public static function toReflection($callable): \ReflectionMethod|\ReflectionFunction
148 lars 91
	{
92
		if ($callable instanceof \Closure) {
93
			$callable = self::unwrap($callable);
94
		}
95
 
399 lars 96
		if (is_string($callable) && str_contains($callable, '::')) {
148 lars 97
			return new \ReflectionMethod($callable);
98
		} elseif (is_array($callable)) {
99
			return new \ReflectionMethod($callable[0], $callable[1]);
100
		} elseif (is_object($callable) && !$callable instanceof \Closure) {
101
			return new \ReflectionMethod($callable, '__invoke');
102
		} else {
103
			return new \ReflectionFunction($callable);
104
		}
105
	}
106
 
107
 
108
	/**
109
	 * Checks whether PHP callback is function or static method.
110
	 */
111
	public static function isStatic(callable $callable): bool
112
	{
150 lars 113
		return is_string(is_array($callable) ? $callable[0] : $callable);
148 lars 114
	}
115
 
116
 
117
	/**
118
	 * Unwraps closure created by Closure::fromCallable().
119
	 */
399 lars 120
	public static function unwrap(\Closure $closure): callable|array
148 lars 121
	{
122
		$r = new \ReflectionFunction($closure);
399 lars 123
		if (str_ends_with($r->name, '}')) {
148 lars 124
			return $closure;
125
 
126
		} elseif ($obj = $r->getClosureThis()) {
127
			return [$obj, $r->name];
128
 
129
		} elseif ($class = $r->getClosureScopeClass()) {
130
			return [$class->name, $r->name];
131
 
132
		} else {
133
			return $r->name;
134
		}
135
	}
136
}