Как получить имя переменной в виде строки в PHP?
Скажем, у меня есть этот код PHP:
$FooBar = "a string";
тогда мне нужна такая функция:
print_var_name($FooBar);
который печатает:
FooBar
Есть идеи как этого добиться? Это вообще возможно в PHP?
27 ответов
Вы можете использовать http://php.net/get_defined_vars, чтобы найти имя переменной, значение которой совпадает со значением переменной, имя которой вы пытаетесь найти. Очевидно, что это не всегда будет работать, поскольку разные переменные часто имеют одинаковые значения, но это единственный способ, которым я могу придумать.
Edit: 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, что, вероятно, потому, что вам не нужно это делать. Возможно, есть лучшие способы сделать то, что вы пытаетесь сделать.
Я не мог придумать способ сделать это эффективно, но я придумал это. Это работает, для ограниченного использования ниже.
пожимание плечами
<?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
)
Он работает на основе строки, вызвавшей функцию, где находит аргумент, который вы передали. Я полагаю, его можно расширить для работы с несколькими аргументами, но, как говорили другие, если бы вы могли объяснить ситуацию лучше, возможно, было бы другое решение. работать лучше
Вы могли бы подумать об изменении вашего подхода и использовании имени переменной переменной?
$var_name = "FooBar";
$$var_name = "a string";
тогда вы могли бы просто
print($var_name);
получить
FooBar
Вот ссылка на руководство PHP по переменным переменным
Никто, кажется, не упомянул фундаментальные причины, почему это а) сложно и б) неразумно:
- "Переменная" - это просто символ, указывающий на что-то еще. В PHP он внутренне указывает на то, что называется "zval", который может фактически использоваться для нескольких переменных одновременно, либо потому, что они имеют одинаковое значение (PHP реализует нечто, называемое "копирование при записи", так что
$foo = $bar
не нужно сразу выделять дополнительную память) или потому, что они были назначены (или переданы функции) по ссылке (например,$foo =& $bar
). Так что у звала нет имени. - Когда вы передаете параметр в функцию, вы создаете новую переменную (даже если это ссылка). Вы можете передать что-то анонимное, как
"hello"
, но когда-то внутри вашей функции, это любая переменная, которую вы называете. Это довольно фундаментально для разделения кода: если функция полагается на то, что используется для вызова переменной, она будет больше похожа наgoto
чем правильно отдельная функция. - Глобальные переменные обычно считаются плохой идеей. Многие примеры здесь предполагают, что переменная, которую вы хотите "отразить", находится в
$GLOBALS
, но это будет верно только в том случае, если вы плохо структурировали свой код и переменные не ограничены какой-либо функцией или объектом. - Имена переменных помогают программистам читать их код. Переименование переменных для лучшего соответствия их назначению является очень распространенной практикой рефакторинга, и весь смысл в том, что это не имеет никакого значения.
Теперь я понимаю желание этого для отладки (хотя некоторые из предложенных способов использования выходят далеко за рамки этого), но в качестве обобщенного решения это на самом деле не так полезно, как вы думаете: если ваша функция отладки говорит, что ваша переменная называется "$ file ", это может быть любая из десятков переменных" $ file "в вашем коде или переменная, которую вы назвали" $ filename ", но передаете функции, параметр которой называется" $ file ".
Гораздо более полезная информация - откуда в вашем коде была вызвана функция отладки. Поскольку вы можете быстро найти это в своем редакторе, вы можете увидеть, какую переменную вы выводили для себя, и даже можете передать целые выражения в нее за один раз (например, 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 debug($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 trim($var);
}
ИСПОЛЬЗОВАНИЕ: print_var_name($FooBar)
ПЕЧАТЬ: FooBar
СОВЕТ Теперь вы можете переименовать функцию, и она все равно будет работать, а также использовать функцию несколько раз в одной строке! Благодаря @Cliffordlife
Lucas на PHP.net предоставил надежный способ проверить, существует ли переменная. В своем примере он перебирает копию массива глобальных переменных (или массива с областями видимости) переменных, изменяет значение на случайно сгенерированное значение и проверяет сгенерированное значение в скопированном массиве.
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
Я сделал функцию проверки по причинам отладки. Это похоже на print_r() на стероидах, очень похоже на Krumo, но немного более эффективно на объектах. Я хотел добавить определение имени var и получил это, вдохновленный постом Ника Престы на этой странице. Он обнаруживает любое выражение, переданное в качестве аргумента, а не только имена переменных.
Это только функция-обертка, которая обнаруживает переданное выражение. Работает в большинстве случаев. Это не будет работать, если вы вызываете функцию более одного раза в одной и той же строке кода.
Это работает нормально: die (inspect ( $ this-> getUser () -> hasCredential ("delete") ));
inspect () - это функция, которая обнаружит переданное выражение.
Мы получаем: $ this-> getUser () -> hasCredential ("delete")
function 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);
}
Вот пример функции инспектора (и моего класса 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
?>
Многие ответы ставят под сомнение полезность этого. Однако получение ссылки на переменную может быть очень полезным. Особенно в случаях с объектами и $ this. Мое решение работает как с объектами, так и с объектами, определенными свойствами:
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
Адаптировано из ответов выше для многих переменных, с хорошей производительностью, всего лишь одно сканирование $GLOBALS для многих
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
)
)
Вы можете использовать compact() для достижения этой цели.
$FooBar = "a string";
$newArray = compact('FooBar');
Это создаст ассоциативный массив с именем переменной в качестве ключа. Затем вы можете перебрать массив, используя имя ключа, где вам это нужно.
foreach($newarray as $key => $value) {
echo $key;
}
У меня есть действительный вариант использования для этого.
У меня есть функция cacheVariable($var) (хорошо, у меня есть кэш функции ($key, $value), но я хотел бы иметь функцию, как уже упоминалось).
Цель состоит в том, чтобы сделать:
$colour = 'blue';
cacheVariable($colour);
...
// another session
...
$myColour = getCachedVariable('colour');
Я пробовал с
function cacheVariable($variable) {
$key = ${$variable}; // This doesn't help! It only gives 'variable'.
// do some caching using suitable backend such as apc, memcache or ramdisk
}
Я также пытался с
function varName(&$var) {
$definedVariables = get_defined_vars();
$copyOfDefinedVariables = array();
foreach ($definedVariables as $variable=>$value) {
$copyOfDefinedVariables[$variable] = $value;
}
$oldVar = $var;
$var = !$var;
$difference = array_diff_assoc($definedVariables, $copyOfDefinedVariables);
$var = $oldVar;
return key(array_slice($difference, 0, 1, true));
}
Но и это не помогает...:(
Конечно, я мог бы продолжать делать кэш ('color', $color), но я ленивый, вы знаете...;)
Итак, мне нужна функция, которая получает ОРИГИНАЛЬНОЕ имя переменной, как оно было передано функции. Внутри функции я никак не могу это знать, как кажется. Передача get_defined_vars() по ссылке во втором примере выше мне очень помогла (спасибо Жан-Жаку Гугану за эту идею). Последняя функция начала работать, но она все еще продолжала возвращать локальную переменную ("переменная", а не "цвет").
Я еще не пробовал использовать get_func_args() и get_func_arg(), ${}- конструкции и key () вместе, но я полагаю, что это тоже не удастся.
Я думаю, что вы хотите знать имя переменной с ее значением. Вы можете использовать ассоциативный массив для достижения этой цели.
используйте имена переменных для ключей массива:
$vars = array('FooBar' => 'a string');
Если вы хотите получить имена переменных, используйте array_keys($vars)
, он вернет массив тех имен переменных, которые использовались в вашем $vars
массив как это ключи.
Существует в настоящее время обсуждается RFC для
функцияnameof()
, которая будет делать то, о чем вы просите. Предлагается стать частью PHP 8.3, однако я не уверен, смогут ли они уложиться в этот срок.
Если переменная взаимозаменяема, у вас должна быть логика, определяющая, какая переменная будет использоваться. Все, что вам нужно сделать, это поместить имя переменной в $variable
в рамках этой логики, пока вы делаете все остальное.
Я думаю, что нам всем трудно понять, зачем тебе это нужно. Пример кода или объяснение того, что вы на самом деле пытаетесь сделать, может помочь, но я подозреваю, что вы слишком, слишком обдумываете это.
Вот мое решение, основанное на Jeremy Ruten
class DebugHelper {
function printVarNames($systemDefinedVars, $varNames) {
foreach ($systemDefinedVars as $var=>$value) {
if (in_array($var, $varNames )) {
var_dump($var);
var_dump($value);
}
}
}
}
используй это
DebugHelper::printVarNames(
$systemDefinedVars = get_defined_vars(),
$varNames=array('yourVar00', 'yourVar01')
);
У меня есть это:
debug_echo(array('$query'=>$query, '$nrUsers'=>$nrUsers, '$hdr'=>$hdr));
Я бы предпочел это:
debug_echo($query, $nrUsers, $hdr);
Существующая функция отображает желтое поле с красным контуром и показывает каждую переменную по имени и значению. Решение для массива работает, но немного запутано для ввода, когда это необходимо.
Это мой вариант использования, и да, это связано с отладкой. Я согласен с теми, кто ставит под сомнение его использование в противном случае.
В PHP нет предопределенной функции, которая могла бы выводить имя переменной. Однако вы можете использовать результат
get_defined_vars()
, который возвращает все переменные, определенные в области, включая имя и значение. Вот пример:
<?php
// Function for determining the name of a variable
function getVarName(&$var, $definedVars=null) {
$definedVars = (!is_array($definedVars) ? $GLOBALS : $definedVars);
$val = $var;
$rand = 1;
while (in_array($rand, $definedVars, true)) {
$rand = md5(mt_rand(10000, 1000000));
}
$var = $rand;
foreach ($definedVars as $dvName=>$dvVal) {
if ($dvVal === $rand) {
$var = $val;
return $dvName;
}
}
return null;
}
// the name of $a is to be determined.
$a = 1;
// Determine the name of $a
echo getVarName($a);
?>
Подробнее читайте в разделе Как получить имя переменной в виде строки в PHP?
Так я и сделал
function getVar(&$var) {
$tmp = $var; // store the variable value
$var = '_$_%&33xc$%^*7_r4'; // give the variable a new unique value
$name = array_search($var, $GLOBALS); // search $GLOBALS for that unique value and return the key(variable)
$var = $tmp; // restore the variable old value
return $name;
}
Применение
$city = "San Francisco";
echo getVar($city); // city
Примечание: некоторые версии PHP 7 не будут работать должным образом из-за ошибки вarray_search
с $GLOBALS
, однако все другие версии будут работать.
Смотрите это https://3v4l.org/UMW7V
Я знаю, что это старый и уже ответил, но я действительно искал это. Я отправляю этот ответ, чтобы сэкономить людям немного времени на уточнение некоторых ответов.
Опция 1:
$data = array('$FooBar');
$vars = [];
$vars = preg_replace('/^\\$/', '', $data);
$varname = key(compact($vars));
echo $varname;
Печать:
FooBar
По какой бы причине вы ни оказались в подобной ситуации, это действительно работает.
.
Вариант 2:
$FooBar = "a string";
$varname = trim(array_search($FooBar, $GLOBALS), " \t.");
echo $varname;
Если $FooBar
содержит уникальное значение, он напечатает "FooBar". Если$FooBar
пусто или пусто, он напечатает имя первой найденной пустой или нулевой строки.
Его можно использовать как таковое:
if (isset($FooBar) && !is_null($FooBar) && !empty($FooBar)) {
$FooBar = "a string";
$varname = trim(array_search($FooBar, $GLOBALS), " \t.");
}
Я искал это, но просто решил ввести имя, у меня все равно обычно есть имя в буфере обмена.
function VarTest($my_var,$my_var_name){
echo '$'.$my_var_name.': '.$my_var.'<br />';
}
$fruit='apple';
VarTest($fruit,'fruit');
Почему бы вам просто не создать простую функцию и НЕ СКАЗАТЬ?
/**
* Prints out $obj for debug
*
* @param any_type $obj
* @param (string) $title
*/
function print_all( $obj, $title = false )
{
print "\n<div style=\"font-family:Arial;\">\n";
if( $title ) print "<div style=\"background-color:red; color:white; font-size:16px; font-weight:bold; margin:0; padding:10px; text-align:center;\">$title</div>\n";
print "<pre style=\"background-color:yellow; border:2px solid red; color:black; margin:0; padding:10px;\">\n\n";
var_export( $obj );
print "\n\n</pre>\n</div>\n";
}
print_all( $aUser, '$aUser' );
другое использование:
пожимать плечами
function varsToArrayAssoc(...$arguments){
$bt = debug_backtrace();
$file = file($bt[0]['file']);
$src = $file[$bt[0]['line']-1];
$pat = '#(.*)'.__FUNCTION__.' *?\( *?(.*) *?\)(.*)#i';
$vars =explode(',',substr_replace(trim(preg_replace($pat, '$2', $src)) ,"", -1));
$result=[];
foreach(func_get_args() as $key=>$v){
$index=trim(explode('$',$vars[$key])[1]);
$result[$index]=$v;
}
return $result;
}
$a=12;
$b=13;
$c=123;
$d='aa';
var_dump(varsToArrayAssoc($a,$b,$c,$d));
Используйте это, чтобы отсоединить пользовательские переменные от глобальных, чтобы проверить переменные в данный момент.
function get_user_var_defined ()
{
return array_slice($GLOBALS,8,count($GLOBALS)-8);
}
function get_var_name ($var)
{
$vuser = get_user_var_defined();
foreach($vuser as $key=>$value)
{
if($var===$value) return $key ;
}
}
Это может считаться быстрым и грязным, но я лично предпочитаю использовать такую функцию / метод:
public function getVarName($var) {
$tmp = array($var => '');
$keys = array_keys($tmp);
return trim($keys[0]);
}
в основном он просто создает ассоциативный массив, содержащий один пустой / нулевой элемент, используя в качестве ключа переменную, имя которой вы хотите получить.
затем мы получаем значение этого ключа с помощью array_keys и возвращаем его.
очевидно, что это быстро запутывается и нежелательно в производственной среде, но это работает для представленной проблемы.
Почему мы должны использовать глобальные переменные, чтобы получить имя переменной... мы можем использовать просто, как показано ниже.
$variableName = "ajaxmint";
echo getVarName('$variableName');
function getVarName($name) {
return str_replace('$','',$name);
}
Я действительно не вижу варианта использования... Если вы введете print_var_name($foobar), что такого сложного (и другого) в наборе print("foobar")?
Потому что даже если бы вы использовали это в функции, вы бы получили локальное имя переменной...
В любом случае, вот руководство по рефлексии на случай, если там есть что-то, что вам нужно.