Вход Регистрация
Файл: vendor/nette/utils/src/Utils/Callback.php
Строк: 177
<?php declare(strict_types=1);

/**
 * This file is part of the Nette Framework (https://nette.org)
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
 */

namespace NetteUtils;

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 
NetteStaticClass;

    
/**
     * Invokes internal PHP function with own error handler.
     * @param  callable-string  $function
     * @param  list<mixed>  $args
     * @param  callable(string, int): (bool|void|null)  $onError
     */
    
public static function invokeSafe(string $function, array $args, callable $onError): mixed
    
{
        
$prev = set_error_handler(function (int $severity, string $message, string $file, int $line) use ($onError, &$prev, $function): bool {
            if (
$file === __FILE__) {
                
$msg = ini_get('html_errors')
                    ? 
Html::htmlToText($message)
                    : 
$message;
                
$msg = (string) preg_replace("#^$function\(.*?\): #", '', $msg);
                if (
$onError($msg, $severity) !== false) {
                    return 
true;
                }
            }

            return 
$prev ? $prev(...func_get_args()) !== false : false;
        });

        try {
            return 
$function(...$args);
        } finally {
            
restore_error_handler();
        }
    }


    
/**
     * Checks that $callable is a valid PHP callback and returns it. With $syntax set to true, only verifies
     * the structural validity without checking whether the class or method actually exists.
     * @return callable
     * @throws NetteInvalidArgumentException
     */
    
public static function check(mixed $callable, bool $syntax = false): mixed
    
{
        if (!
is_callable($callable, $syntax)) {
            throw new 
NetteInvalidArgumentException(
                
$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
     */
    
public static function toReflection(mixed $callable): ReflectionMethod|ReflectionFunction
    
{
        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 {
            
assert($callable instanceof Closure || is_string($callable));
            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().
     * @return callable|array{object|class-string, string}|string
     */
    
public static function unwrap(Closure $closure): callable|array|string
    
{
        
$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;
        }
    }
}
Онлайн: 1
Реклама