Files
kulakpos_web/vendor/nette/utils/src/Utils/Callback.php

143 lines
4.0 KiB
PHP
Raw Normal View History

2026-03-30 14:54:57 +07:00
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Utils;
use Nette;
use function explode, func_get_args, ini_get, is_array, is_callable, is_object, is_string, preg_replace, restore_error_handler, set_error_handler, sprintf, str_contains, str_ends_with;
/**
* PHP callable tools.
*/
final class Callback
{
use Nette\StaticClass;
/**
* Invokes internal PHP function with own error handler.
2026-04-18 20:32:18 +07:00
* @param callable-string $function
* @param list<mixed> $args
* @param callable(string, int): (bool|void|null) $onError
2026-03-30 14:54:57 +07:00
*/
public static function invokeSafe(string $function, array $args, callable $onError): mixed
{
2026-04-18 20:32:18 +07:00
$prev = set_error_handler(function (int $severity, string $message, string $file, int $line) use ($onError, &$prev, $function): bool {
2026-03-30 14:54:57 +07:00
if ($file === __FILE__) {
$msg = ini_get('html_errors')
? Html::htmlToText($message)
: $message;
2026-04-18 20:32:18 +07:00
$msg = (string) preg_replace("#^$function\\(.*?\\): #", '', $msg);
2026-03-30 14:54:57 +07:00
if ($onError($msg, $severity) !== false) {
2026-04-18 20:32:18 +07:00
return true;
2026-03-30 14:54:57 +07:00
}
}
2026-04-18 20:32:18 +07:00
return $prev ? $prev(...func_get_args()) !== false : false;
2026-03-30 14:54:57 +07:00
});
try {
return $function(...$args);
} finally {
restore_error_handler();
}
}
/**
* Checks that $callable is valid PHP callback. Otherwise throws exception. If the $syntax is set to true, only verifies
* that $callable has a valid structure to be used as a callback, but does not verify if the class or method actually exists.
* @return callable
* @throws Nette\InvalidArgumentException
*/
2026-04-18 20:32:18 +07:00
public static function check(mixed $callable, bool $syntax = false): mixed
2026-03-30 14:54:57 +07:00
{
if (!is_callable($callable, $syntax)) {
throw new Nette\InvalidArgumentException(
$syntax
? 'Given value is not a callable type.'
: sprintf("Callback '%s' is not callable.", self::toString($callable)),
);
}
return $callable;
}
/**
* Converts PHP callback to textual form. Class or method may not exists.
*/
public static function toString(mixed $callable): string
{
if ($callable instanceof \Closure) {
$inner = self::unwrap($callable);
return '{closure' . ($inner instanceof \Closure ? '}' : ' ' . self::toString($inner) . '}');
} else {
is_callable(is_object($callable) ? [$callable, '__invoke'] : $callable, true, $textual);
return $textual;
}
}
/**
* Returns reflection for method or function used in PHP callback.
* @param callable $callable type check is escalated to ReflectionException
* @throws \ReflectionException if callback is not valid
*/
2026-04-18 20:32:18 +07:00
public static function toReflection(mixed $callable): \ReflectionMethod|\ReflectionFunction
2026-03-30 14:54:57 +07:00
{
if ($callable instanceof \Closure) {
$callable = self::unwrap($callable);
}
if (is_string($callable) && str_contains($callable, '::')) {
return new ReflectionMethod(...explode('::', $callable, 2));
} elseif (is_array($callable)) {
return new ReflectionMethod($callable[0], $callable[1]);
} elseif (is_object($callable) && !$callable instanceof \Closure) {
return new ReflectionMethod($callable, '__invoke');
} else {
2026-04-18 20:32:18 +07:00
assert($callable instanceof \Closure || is_string($callable));
2026-03-30 14:54:57 +07:00
return new \ReflectionFunction($callable);
}
}
/**
* Checks whether PHP callback is function or static method.
*/
public static function isStatic(callable $callable): bool
{
return is_string(is_array($callable) ? $callable[0] : $callable);
}
/**
* Unwraps closure created by Closure::fromCallable().
2026-04-18 20:32:18 +07:00
* @return callable|array{object|class-string, string}|string
2026-03-30 14:54:57 +07:00
*/
2026-04-18 20:32:18 +07:00
public static function unwrap(\Closure $closure): callable|array|string
2026-03-30 14:54:57 +07:00
{
$r = new \ReflectionFunction($closure);
$class = $r->getClosureScopeClass()?->name;
if (str_ends_with($r->name, '}')) {
return $closure;
} elseif (($obj = $r->getClosureThis()) && $obj::class === $class) {
return [$obj, $r->name];
} elseif ($class) {
return [$class, $r->name];
} else {
return $r->name;
}
}
}