【问题标题】:Print name or definition of callable in PHP在 PHP 中打印可调用的名称或定义
【发布时间】:2016-03-23 07:55:45
【问题描述】:

PHP 中的可调用对象可以有多种形式,例如对象、数组或包含函数名称的字符串。

如果我在变量中有这样的可调用对象,我如何在日志中打印一些用户友好的“定义”。

想想这段代码:

call_user_func($callable);
$logger->log("Provided callable " . (string) $callable . " called");

问题是,这会引发错误,例如数组到字符串的转换错误。打印出有关该可调用对象的有用信息的最佳方式是什么?

【问题讨论】:

  • 你考虑过var_export()吗?还是说的太详细了?
  • @dave 如果涉及到一个对象,那就太详细了。理想情况下,它只是类名、函数/方法名或描述性字符串(如果它是匿名函数)。我觉得我可以编写自定义代码来处理所有可能性,但不想重新发明轮子。

标签: php callable


【解决方案1】:

非常老的问题,但由于我只是用它来记录一些信息,我认为它可以使用一些澄清。

@Fabian Picone 的评论有点误导。

类型提示实际上适用于字符串(也适用于数组),但字符串必须是现有的方法或函数(如果您在代码中添加 function foo() {} 它将起作用)。也就是说,它实际上必须是可调用的。错误信息不是那么直观。

也看到这个答案https://stackoverflow.com/a/63289789/7409925

这是我用于日志记录(从 @Seb 扩展而来)、添加对可调用对象的支持并删除不必要的 trim 的观点:

function getCallableName(callable $callable) {
    switch (true) {
        case is_string($callable) && strpos($callable, '::'):
            return '[static] ' . $callable;
        case is_string($callable):
            return '[function] ' . $callable;
        case is_array($callable) && is_object($callable[0]):
            return '[method] ' . get_class($callable[0])  . '->' . $callable[1];
        case is_array($callable):
            return '[static] ' . $callable[0]  . '::' . $callable[1];
        case $callable instanceof Closure:
            return '[closure]';
        case is_object($callable):
            return '[invokable] ' . get_class($callable);
        default:
            return '[unknown]';
    }
}

【讨论】:

    【解决方案2】:

    这样的事情应该可以工作:

    function getCallableName($callable) {
        if (is_string($callable)) {
            return trim($callable);
        } else if (is_array($callable)) {
            if (is_object($callable[0])) {
                return sprintf("%s::%s", get_class($callable[0]), trim($callable[1]));
            } else {
                return sprintf("%s::%s", trim($callable[0]), trim($callable[1]));
            }
        } else if ($callable instanceof Closure) {
            return 'closure';
        } else {
            return 'unknown';
        }
    }
    

    【讨论】:

    • 你的 typehint 已经可以调用了,为什么还要检查 is_string 和 is_array 呢?传递字符串或数组会引发致命错误。
    • 纯字符串和数组也是可调用的。你可以在这里检查一下什么是可调用的:php.net/manual/en/language.types.callable.php
    • 字符串是字符串的类型,不可调用。你测试过吗?我以 1:1 的比例测试了您的代码,请参阅 3v4l.org/YdlTh
    • 你是绝对正确的,那个 typehint 是错误的,我的错。我会编辑答案。谢谢你告诉我!
    【解决方案3】:

    受@Bigdot https://stackoverflow.com/a/68113840/6916271 的回答启发,我创建了 2 个方法,当我们需要检索可调用的上下文时,它们可能很有用。 我将结果与 Monolog 一起使用,但如果您需要将其转换为字符串,也可以将其与 print_r()、json_encode()、var_dump() 或 var_export() 一起使用。 与上面的答案相比,这里的主要区别是关于关闭的扩展信息,在调查期间可能需要这些信息。

    /**
     * Retrieve the context of callable for debugging purposes
     *
     * @param callable $callable
     * @return array
     */
    private function getCallableContext(callable $callable): array
    {
        switch (true) {
            case \is_string($callable) && \strpos($callable, '::'):
                return ['static method' => $callable];
            case \is_string($callable):
                return ['function' => $callable];
            case \is_array($callable) && \is_object($callable[0]):
                return ['class' => \get_class($callable[0]), 'method' => $callable[1]];
            case \is_array($callable):
                return ['class' => $callable[0], 'static method' => $callable[1]];
            case $callable instanceof \Closure:
                try {
                    $reflectedFunction = new \ReflectionFunction($callable);
                    $closureClass = $reflectedFunction->getClosureScopeClass();
                    $closureThis = $reflectedFunction->getClosureThis();
                } catch (\ReflectionException $e) {
                    return ['closure' => 'closure'];
                }
    
                return [
                    'closure this'  => $closureThis ? \get_class($closureThis) : $reflectedFunction->name,
                    'closure scope' => $closureClass ? $closureClass->getName() : $reflectedFunction->name,
                    'static variables' => $this->formatVariablesArray($reflectedFunction->getStaticVariables()),
                ];
            case \is_object($callable):
                return ['invokable' => \get_class($callable)];
            default:
                return ['unknown' => 'unknown'];
        }
    }
    
    /**
     * Format variables array for debugging purposes in order to avoid huge objects dumping
     *
     * @param array $data
     * @return array
     */
    private function formatVariablesArray(array $data): array
    {
        foreach ($data as $key => $value) {
            if (\is_object($value)) {
                $data[$key] = \get_class($value);
            } elseif (\is_array($value)) {
                $data[$key] = $this->formatVariablesArray($value);
            }
        }
    
        return $data;
    }
    

    在我们使用记录器

    try {
        \call_user_func($callable);
    } catch (\Throwable $e) {
        $logger->log(
            'Error occurred',
            ['exception' => $e, 'callable' => $this->getCallableContext($callable)]
        );
        //In case we can use string only
        $logger->log('Error occurred: ' . \print_r($this->getCallableContext($callable), true));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-16
      • 1970-01-01
      相关资源
      最近更新 更多