如何在函数内部找到变量的名称?

3
我有一个函数。
function my_dump($a,$name){
    echo '<pre>'.$name.":\n";
    var_export($a);
    echo '</pre>';
}

我该如何使用只有一个参数的方式实现这一点,以便函数能够识别所调用变量本身的名称?
例如:
my_dump($cool_variable_name);

输出结果为:

名称:"酷炫的变量名"
内容:数组(...


5
为什么你需要知道?这些函数只知道它们内部的东西,不多也不少。 - Mark Baker
1
如果你真的需要知道,你可以使用 debug_backtrace(),但它速度慢且效率低。 - Mark Baker
看看这个主题,也许会对你有所帮助: https://dev59.com/OXVC5IYBdhLWcg3wjx5d - El Abogato
5个回答

10

如果你需要调试代码,那么使用像xdebug这样的工具比自己编写变量转储要灵活和高效得多;但是debug_backtrace()(虽然开销很大)可能会给你所需的东西。提取调用调试转储函数的文件名和行号,然后解析该行以提取在调用该函数时使用的变量名。

function my_dump($a) {
    $backtrace = debug_backtrace()[0];
    $fh = fopen($backtrace['file'], 'r');
    $line = 0;
    while (++$line <= $backtrace['line']) {
        $code = fgets($fh);
    }
    fclose($fh);
    preg_match('/' . __FUNCTION__ . '\s*\((.*)\)\s*;/u', $code, $name);
    echo '<pre>'.trim($name[1]).":\n";
    var_export($a);
    echo '</pre>';
}


$foo = 'bar';

$baz = array(
    'Hello',
    'World'
);

my_dump($foo);
my_dump(
    $baz
);
如果您的 PHP 版本不支持数组解引用,请更改。
$backtrace = debug_backtrace()[0];

$backtrace = debug_backtrace();
$backtrace = $backtrace[0];

如果你的 my_dump() 函数调用跨越多行(就像我的 $baz 示例一样),你需要一个稍微复杂一些的解析器来从你的代码中提取变量名。


如果my_dump是从另一个包含文件中调用的,这会起作用吗? - rubo77
回溯跟踪保留了调用 my_dump() 的文件和行的详细信息,这就是 $backtrace['file'] 和 $backtrace['line'] 包含的内容。 - Mark Baker
这太完美了!它像魔法一样运行着 ;) 当然你不应该在生产环境中使用它,因为它很慢。 - rubo77
即使在开发环境中,最好使用像xdebug这样的适当调试工具,它还提供了许多其他额外的好处。 - Mark Baker

1
function my_dump($a) {
$backtrace = debug_backtrace()[0];
$fh = fopen($backtrace['file'], 'r');
$line = 0;
while (++$line <= $backtrace['line']) {
    $code = fgets($fh);
}
fclose($fh);
preg_match('/' . __FUNCTION__ . '\s*\((.*)\)\s*;/u', $code, $name);
echo '<pre>'.trim($name[1]).":\n";
var_export($a);
echo '</pre>';
}

它不能正常运行 - 因为使用的表达式没有给出正确的结果。
在函数名称和带参数的括号之间使用\s*是没有意义的,因为中间什么也没有。
在表达式中使用;也没有意义。至少因为它只允许将此函数的结果作为独立变量赋值给其他变量。但有时需要将此函数用作参数。
而使用(.*)还将允许所有以参数形式给出的变量名后面的内容,即使其中有分号。
因此,需要排除结束括号符号)的结果。有很多方法可以做到这一点。
您可以使用下面函数代码中的形式或使用以下代码(或其他代码)。
'/'.__FUNCTION__.'\((\w+)\)/'

整个函数可以被简化为以下形式(例如,这是类函数的情况):
protected function Get_VariableNameAsText($Variable="")
{
    $File = file(debug_backtrace()[0]['file']);

    for($Line = 1; $Line < count($File); $Line++)
    {
        if($Line == debug_backtrace()[0]['line']-1)
        {
            preg_match('/'.__FUNCTION__.'\(([^\)]*)\)/', $File[$Line], $VariableName);
            return trim($VariableName[1]);
        }
    }
}

我删除了使用var_export的部分,因为我不明白为什么要使用它,当我只想获取变量名作为字符串(文本)时。而给定字符串的打印最好在此函数之外。
如果您没有5.4或更高版本,则需要更改代码。
$File = file(debug_backtrace()[0]['file']);
if($Line == debug_backtrace()[0]['line']-1)

在。
$Trace = debug_backtrace();
$File = file($Trace[0]['file']);
if($Line == $Trace[0]['line']-1)

谁接受了这个?不是说它不正确,我只是想知道因为我没有这样做,而且我认为只有我可以标记正确答案。 - rubo77
我有点困惑你的话,@rubo77。什么不完全正确? - Václav
好像有人更改了这里正确答案的投票。也许是某个管理员。 - rubo77

1

我很抱歉之前回答有误,修改起来会太长(不方便阅读),请见谅。

以下是改进过的函数,可以多次使用。这个版本能够在一行中获取正确变量的名称。

它无法获取多个变量的名称 - 至少因为我认为这没有什么用处。

区别:

  • 添加整数参数$Index
  • 新的正则表达式函数
  • 新的正则表达式
  • 新处理正则表达式结果

  • 参数$Index并非必需,但是除了第一次使用该函数外忽略它将会导致错误结果

  • \x20在表达式中表示空格

  • try-catch块中的异常类和异常消息可以根据需要重写

  • 可以更改或删除函数的protected状态

    protected function Get_VariableNameAsText($Variable="", $Index="")
    {
        $File = file(debug_backtrace()[0]['file']);
    
        try
        {
            if(!empty($Index) && !is_integer($Index))
            {
                throw new UniT_Exception(UniT_Exception::UNIT_EXCEPTIONS_MAIN_PARAM, UniT_Exception::UNIT_EXCEPTIONS_PARAM_VALTYPE);
            }
        }
        catch(UniT_Exception $Exception)
        {
            $Exception->ExceptionWarning(__CLASS__, __FUNCTION__, $this->Get_Parameters(__CLASS__, __FUNCTION__)[1], gettype($Index), 'integer');
        }
    
        for($Line = 1; $Line < count($File); $Line++)
        {
            if($Line == debug_backtrace()[0]['line']-1)
            {
                preg_match_all('/'.__FUNCTION__.'\((?<type>[a-z]{1,}\:{2}\${1}|\$this\x20{0,1}\-\>{1}\x20{0,1}|\${1})(?<variable>[A-Za-z0-9_]{1,})\x20{0,1}\,{0,1}\x20{0,1}(?<index>[0-9]{0,})\x20{0,}\)/', $File[$Line], $VariableName, PREG_SET_ORDER);
    
                if(empty($Index))
                {
                    return $VariableName[0]['type'].$VariableName[0]['variable'];
                }
                else
                {
                    return $VariableName[$Index]['type'].$VariableName[$Index]['variable'];
                }
            }
        }
    }
    
我希望这个版本能够对你有所帮助,甚至比之前的版本更好。

编辑:新表达式允许使用更多类型的变量(不仅限于常规变量)。

0

如果它是一个简单的脚本,其中所有变量都在全局作用域中定义,那么你可以采用 $GLOBALS 的解决方案:

function my_dump($a){
    if(is_string($a) and isset($GLOBALS[$a])) {
        echo "varname: $a\n";
        $a=&$GLOBALS[$a];
    }
    echo '<pre>'.$name.":\n";
    var_export($a);
    echo '</pre>';
}

这样你就可以使用变量名调用dump函数了

my_dump("cool_variable_name");

而不是

my_dump($cool_variable_name);

只有在所有变量都定义在全局作用域中时才能正常工作,这通常不被视为良好的编码实践。 - Mark Baker

-1

这是无法做到的。

当函数被调用时,参数的值会被粘贴到您的 $a 中,因此任何尝试获取其名称的操作都将返回“a”。

http://us3.php.net/get_defined_vars 将失败,因为它只返回函数内部的变量(也就是“a”)。

您可以使用一个包含一个元素的数组,但我猜这不是您想要做的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接