【问题标题】:Determine what namespace the function was called in确定调用函数的命名空间
【发布时间】:2012-01-21 01:48:22
【问题描述】:

我想知道在调用函数时是否可以确定当前命名空间是什么。我有这个函数声明:

<?php
namespace Site\Action;
function add ($hook, $function) {
    /**
     * determine the namespace this was called from because
     * __NAMESPACE__ is "site\action" :(
     */
     if (is_callable($function))
         call_user_func($function);
}
?>

在另一个文件上:

<?php
namespace Foo;
function bar () {
}
?>

假设我有这个作为我的程序代码:

<?php
namespace Foo;
Site\Action\add('hookname', 'bar');
?>

假设在这种情况下Bar 旨在解析为Foo\bar,因为这是调用它的命名空间。

这又是一个很长的解释,是否可以确定调用 Site\Action\add() 的活动命名空间?

提前致谢。

【问题讨论】:

  • 您可以将__NAMESPACE__ 作为参数传递。您还可以通过查看来自debug_backtrace 的调用堆栈来获取调用者的命名空间,但我真的不建议这样做。
  • 实际上我怀疑debug_backtrace 会这样做,或者我可能遗漏了什么。我想我可能只需要通过__NAMESPACE__ 或者只是重新考虑整个架构。
  • @GordonM 你建议我们不要使用debug_backtrace() 的动机是什么?请详细说明...
  • @nickl-debug_backtrace 用于调试(线索在名称中)。它会生成大量您不需要的数据,并且由于它是用于调试的,因此它可能没有那么优化。另外,如果您的函数需要知道调用它们的内容,我怀疑您的设计有问题。它们应该关心的是传递给它们的参数。如果您真的需要知道,那么只需将函数名作为参数传递就会容易得多。 $foo = someFunction ($arg1, $arg2, __NAMESPACE__)
  • @GordonM 用它跟踪堆栈的任何其他名称调用它。对于您的论点,我们都应该避免 print_r 但至少它不称为 debug_print =) 有很多选项可以限制生成的数据 DEBUG_BACKTRACE_IGNORE_ARGS 并且 5.4 还引入了 $limit。由于您可以传递完全限定名称,因此您的解决方案变得多余。问题不是我的设计有什么问题,除非您有更好的方法来解决问题,否则无论用例要求如何,您都必须承认所提供的解决方案与宣传的一样。我认为 SO 的目的是回答问题?

标签: php oop namespaces


【解决方案1】:

您正在寻找的是:ReflectionFunctionAbstract::getNamespaceName

如果你想知道你来自哪里debug_backtrace()是你的朋友。

以下内容应该可以解决您的难题:

function backtrace_namespace() 
{
    $trace = array();
    $functions = array_map(
        function ($v) {
            return $v['function'];
        },
        debug_backtrace()
    );
    foreach ($functions as $func) {
        $f = new ReflectionFunction($func);
        $trace[] = array(
            'function' => $func, 
            'namespace' =>  $f->getNamespaceName()
        );
    }
    return $trace;
}

只需从任何地方调用它即可查看回溯。 我修改了你的“程序”代码文件如下:

namespace Foo;

function bar () 
{
    var_export(backtrace_namespace());
}

/** The unasked question: We need to use the fully qualified name currently. */
function go() 
{
    \Site\Action\add('hookname', 'Foo\\bar');
}

go();

包含此文件的结果将在标准输出上如下:

array (
    0 =>
    array (
        'function' => 'backtrace_namespace',
        'namespace' => '',
    ),
    1 =>
    array (
        'function' => 'Foo\\bar',
        'namespace' => 'Foo',
    ),
    2 =>
    array (
        'function' => 'call_user_func',
        'namespace' => '',
    ),
    3 =>
    array (
        'function' => 'Site\\Action\\add',
        'namespace' => 'Site\\Action',
    ),
    4 =>
    array (
        'function' => 'Foo\\go',
        'namespace' => 'Foo',
    ),
)

现在回答隐藏问题的加分:

如何解析调用命名空间以避免使用完全限定的函数名作为参数?

以下将允许您按预期调用该函数:

 Site\Action\add('hookname', 'bar');

不用担心:

警告:call_user_func() 期望参数 1 是有效回调,未找到函数 'bar' 或无效函数名

所以在你重新设计之前先试试这个尺寸:

namespace Site\Action;

function add($hook, $function) 
{
    $trace = backtrace_namespace();
    $prev = (object) end($trace);

    $function = "$prev->namespace\\$function";

    if (is_callable($function))
        call_user_func($function);
}

我看不出为什么不应该使用debug_backtrace,这就是它的用途。

开心!

【讨论】:

  • 您也可以直接使用 debug back_trace 中的完全限定函数名。类似:$trace = debug_backtrace(); $trace = (object) end($trace); call_user_func(substr($trace-&gt;function, 0, strrpos($trace-&gt;function, '\\')+1).$function); 应该可以。 /me 交叉手指...
  • 您的代码仅适用于函数,不适用于静态函数或方法。要使其与静态函数一起使用,您可能可以使用return $v['class'].$v['type'].$v['function']; 而不是return $v['function']; 对于全局对象,甚至可以使用$v['object'].$v['type'].$v['function']; 来访问方法。见php.net/manual/en/…
  • 编辑:好吧,忘记我写的关于方法的内容。 $v['object'] 不是字符串,而是对象指针,这实际上更有意义。如果您想包含它,则必须以更复杂的方式进行。但是,您的代码至少应该更加安全,并且可以排除方法或以不同的方式格式化跟踪。
【解决方案2】:

如果您使用的是闭包而不是静态函数,您可以随时询问反射 API

namespace A;
$closure = function($word = 'hello')
{
    return $word;
};

...

$r = new ReflectionFunction($closure);
print $r->getNamespaceName();

【讨论】:

  • 适用于任何可解析或有效的回调函数名称。
  • 需要使用new \ReflectionFunction 因为A\ReflectionFunction 不存在
【解决方案3】:

我不知道我是否遗漏了什么,但有了这个:

http://php.net/manual/en/reflectionclass.getnamespacename.php,我认为你可以得到你正在使用的对象的命名空间。

【讨论】:

  • 关闭,你想使用 ReflectionFunction 而不是 ReflectionClass。注意:我们还有 ReflectionMethod、ReflectionParameter、ReflectionProperty、ReflectionObject 的用法都非常相似。
猜你喜欢
  • 2018-10-28
  • 2011-09-19
  • 1970-01-01
  • 2011-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多