假设我有以下 PHP 代码:
$FooBar = "a string";
我需要一个像这样的函数:
print_var_name($FooBar);
输出结果如下:
FooBar
有什么方法可以实现这个吗?在PHP中是否可能实现?
假设我有以下 PHP 代码:
$FooBar = "a string";
我需要一个像这样的函数:
print_var_name($FooBar);
输出结果如下:
FooBar
有什么方法可以实现这个吗?在PHP中是否可能实现?
我也无法想到一种高效的方法,但我想出了这个。它可以处理以下有限的用途。
耸肩
<?php
function varName( $v ) {
$trace = debug_backtrace();
$vLine = file( __FILE__ );
$fLine = $vLine[ $trace[0]['line'] - 1 ];
preg_match( "#\\$(\w+)#", $fLine, $match );
print_r( $match );
}
$foo = "knight";
$bar = array( 1, 2, 3 );
$baz = 12345;
varName( $foo );
varName( $bar );
varName( $baz );
?>
// Returns
Array
(
[0] => $foo
[1] => foo
)
Array
(
[0] => $bar
[1] => bar
)
Array
(
[0] => $baz
[1] => baz
)
它的工作原理是基于调用该函数的代码行,在那里找到你传递的参数。我想它可以扩展到处理多个参数,但是像其他人说的那样,如果你能更好地解释情况,另一个解决方案可能会更好。
你可以使用get_defined_vars()函数来查找与你要查找名称的变量具有相同值的变量名称。显然,这种方法并不总是有效,因为不同的变量通常具有相同的值,但这是我能想到的唯一方法。
编辑:get_defined_vars()似乎无法正常工作,它返回'var',因为$var在函数本身中被使用。$GLOBALS似乎可以工作,所以我已经将其更改为那个。
function print_var_name($var) {
foreach($GLOBALS as $var_name => $value) {
if ($value === $var) {
return $var_name;
}
}
return false;
}
编辑:明确一点,PHP 中没有很好的方法来完成这个任务,这也许是因为你不应该这样做。可能有更好的方法来实现你要做的事情。
$var_name = "FooBar";
$$var_name = "a string";
print($var_name);
FooBar
$FooBar
,其值为 a string
,请阅读你的手册。在我看来,这太可怕了。你从未给变量 $FooBar
赋值,但它却存在着。痛苦 - Toskan$foo = $bar
不需要立即分配额外的内存),或者因为它们已经被引用赋值(或传递给函数)(例如 $foo =& $bar
)。所以 zval 没有名称。
- 当您将参数传递给函数时,您正在创建一个新变量(即使是引用也是如此)。您可以传递一些匿名内容,比如 "hello"
,但是一旦进入您的函数,它就是您命名的任何变量。这对于代码分离相当基本:如果函数依赖于变量以前的名称,它将更像一个 goto
而不是一个正确的分离函数。
- 全局变量通常被认为是一个坏主意。这里的很多示例假定您要 "反映" 的变量可以在 $GLOBALS
中找到,但是只有当您的代码结构糟糕并且变量没有被限定在某些函数或对象中时,才会是真的。
- 变量名称有助于程序员阅读其代码。重命名变量以更好地适应其目的是一种非常常见的重构实践,整个重点在于这不会有任何区别。debug('$foo + $bar = ' . ($foo + $bar))
)。$backtrace = debug_backtrace();
echo '# Debug function called from ' . $backtrace[0]['file'] . ' at line ' . $backtrace[0]['line'];
这正是您想要的——它是一个即用型的“复制并粘贴”函数,可回显给定变量的名称:
function print_var_name(){
// read backtrace
$bt = debug_backtrace();
// read file
$file = file($bt[0]['file']);
// select exact print_var_name($varname) line
$src = $file[$bt[0]['line']-1];
// search pattern
$pat = '#(.*)'.__FUNCTION__.' *?\( *?(.*) *?\)(.*)#i';
// extract $varname from match no 2
$var = preg_replace($pat, '$2', $src);
// print to browser
echo '<pre>' . trim($var) . ' = ' . print_r(current(func_get_args()), true) . '</pre>';
}
使用方法:print_var_name($FooBar)
打印:FooBar
提示:
现在你可以重新命名该函数,它仍将正常工作,并且可以在一行中多次使用该函数!感谢@Cliffordlife
我还添加了更好的输出!感谢@Blue-Water
$pat = '#(.*)'.__FUNCTION__.' *?\( *?(.*) *?\)(.*)#i';
这样我就不用关心这个调试函数的名字是什么,而且我可以准确地获取传入函数中的参数,比如$hello或者"hello"(在同一行中,我删除了匹配传入变量的$)。 - Cliffordlifeprint_var_name, echo, var_dump
,输出为$variable); echo ' '; var_dump($variable
。 - Brunoecho'<pre>'.trim($var).'='.print_r(current(func_get_args()), true).'</ pre>';
这将输出FooBar = 字符串,并考虑到了数组。 - user2607743在PHP.net上,Lucas提供了一种可靠的方法来检查变量是否存在。他的示例中,他遍历变量的全局变量数组(或作用域数组)的副本,将值更改为随机生成的值,并检查复制的数组中是否存在生成的值。
function variable_name( &$var, $scope=false, $prefix='UNIQUE', $suffix='VARIABLE' ){
if($scope) {
$vals = $scope;
} else {
$vals = $GLOBALS;
}
$old = $var;
$var = $new = $prefix.rand().$suffix;
$vname = FALSE;
foreach($vals as $key => $val) {
if($val === $new) $vname = $key;
}
$var = $old;
return $vname;
}
那么尝试:
$a = 'asdf';
$b = 'asdf';
$c = FALSE;
$d = FALSE;
echo variable_name($a); // a
echo variable_name($b); // b
echo variable_name($c); // c
echo variable_name($d); // d
一定要查看他在PHP.net上的帖子:http://php.net/manual/en/language.variables.php
break;
,并且我创建了可用的基本结构,以便在函数内定义变量时也可以自动获取变量名。如果您经常复制和粘贴或有更好的改进想法,请使用variable_name($variable, (empty(__FUNCTION__) ? false : get_defined_vars()));
。 - odie2function inspect($label, $value = "__undefin_e_d__")
{
if($value == "__undefin_e_d__") {
/* The first argument is not the label but the
variable to inspect itself, so we need a label.
Let's try to find out it's name by peeking at
the source code.
*/
/* The reason for using an exotic string like
"__undefin_e_d__" instead of NULL here is that
inspected variables can also be NULL and I want
to inspect them anyway.
*/
$value = $label;
$bt = debug_backtrace();
$src = file($bt[0]["file"]);
$line = $src[ $bt[0]['line'] - 1 ];
// let's match the function call and the last closing bracket
preg_match( "#inspect\((.+)\)#", $line, $match );
/* let's count brackets to see how many of them actually belongs
to the var name
Eg: die(inspect($this->getUser()->hasCredential("delete")));
We want: $this->getUser()->hasCredential("delete")
*/
$max = strlen($match[1]);
$varname = "";
$c = 0;
for($i = 0; $i < $max; $i++){
if( $match[1]{$i} == "(" ) $c++;
elseif( $match[1]{$i} == ")" ) $c--;
if($c < 0) break;
$varname .= $match[1]{$i};
}
$label = $varname;
}
// $label now holds the name of the passed variable ($ included)
// Eg: inspect($hello)
// => $label = "$hello"
// or the whole expression evaluated
// Eg: inspect($this->getUser()->hasCredential("delete"))
// => $label = "$this->getUser()->hasCredential(\"delete\")"
// now the actual function call to the inspector method,
// passing the var name as the label:
// return dInspect::dump($label, $val);
// UPDATE: I commented this line because people got confused about
// the dInspect class, wich has nothing to do with the issue here.
echo("The label is: ".$label);
echo("The value is: ".$value);
}
下面是inspector函数(以及我的dInspect类)的示例:
该页面上的文本为西班牙语,但代码简洁易懂。
来自php.net
@Alexandre - 简短解决方案
<?php
function vname(&$var, $scope=0)
{
$old = $var;
if (($key = array_search($var = 'unique'.rand().'value', !$scope ? $GLOBALS : $scope)) && $var = $old) return $key;
}
?>
@Lucas - 用法
<?php
//1. Use of a variable contained in the global scope (default):
$my_global_variable = "My global string.";
echo vname($my_global_variable); // Outputs: my_global_variable
//2. Use of a local variable:
function my_local_func()
{
$my_local_variable = "My local string.";
return vname($my_local_variable, get_defined_vars());
}
echo my_local_func(); // Outputs: my_local_variable
//3. Use of an object property:
class myclass
{
public function __constructor()
{
$this->my_object_property = "My object property string.";
}
}
$obj = new myclass;
echo vname($obj->my_object_property, $obj); // Outputs: my_object_property
?>
function getReference(&$var)
{
if(is_object($var))
$var->___uniqid = uniqid();
else
$var = serialize($var);
$name = getReference_traverse($var,$GLOBALS);
if(is_object($var))
unset($var->___uniqid);
else
$var = unserialize($var);
return "\${$name}";
}
function getReference_traverse(&$var,$arr)
{
if($name = array_search($var,$arr,true))
return "{$name}";
foreach($arr as $key=>$value)
if(is_object($value))
if($name = getReference_traverse($var,get_object_vars($value)))
return "{$key}->{$name}";
}
以上是示例:
class A
{
public function whatIs()
{
echo getReference($this);
}
}
$B = 12;
$C = 12;
$D = new A;
echo getReference($B)."<br/>"; //$B
echo getReference($C)."<br/>"; //$C
$D->whatIs(); //$D
function compact_assoc(&$v1='__undefined__', &$v2='__undefined__',&$v3='__undefined__',&$v4='__undefined__',&$v5='__undefined__',&$v6='__undefined__',&$v7='__undefined__',&$v8='__undefined__',&$v9='__undefined__',&$v10='__undefined__',&$v11='__undefined__',&$v12='__undefined__',&$v13='__undefined__',&$v14='__undefined__',&$v15='__undefined__',&$v16='__undefined__',&$v17='__undefined__',&$v18='__undefined__',&$v19='__undefined__'
) {
$defined_vars=get_defined_vars();
$result=Array();
$reverse_key=Array();
$original_value=Array();
foreach( $defined_vars as $source_key => $source_value){
if($source_value==='__undefined__') break;
$original_value[$source_key]=$$source_key;
$new_test_value="PREFIX".rand()."SUFIX";
$reverse_key[$new_test_value]=$source_key;
$$source_key=$new_test_value;
}
foreach($GLOBALS as $key => &$value){
if( is_string($value) && isset($reverse_key[$value]) ) {
$result[$key]=&$value;
}
}
foreach( $original_value as $source_key => $original_value){
$$source_key=$original_value;
}
return $result;
}
$a = 'A';
$b = 'B';
$c = '999';
$myArray=Array ('id'=>'id123','name'=>'Foo');
print_r(compact_assoc($a,$b,$c,$myArray) );
//print
Array
(
[a] => A
[b] => B
[c] => 999
[myArray] => Array
(
[id] => id123
[name] => Foo
)
)
rootName => modelObject
的数组中只会给响应增加不必要的深度。希望这能够被集成到语言的运行时反射能力中。 - Anurag