Как получить имя переменной в виде строки в PHP? - PullRequest
185 голосов
/ 01 ноября 2008

Скажите, у меня есть этот код PHP:

$FooBar = "a string";

тогда мне нужна такая функция:

print_var_name($FooBar);

который печатает:

FooBar

Есть идеи, как этого добиться? Возможно ли это даже в PHP?

Ответы [ 22 ]

42 голосов
/ 01 января 2009

Я тоже не мог придумать, как сделать это эффективно, но я придумал это. Это работает, для ограниченного использования ниже.

пожав плечами

<?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
)

Он работает на основе строки, которая вызвала функцию, где он находит аргумент, который вы передали. Я полагаю, его можно расширить для работы с несколькими аргументами, но, как сказали другие, если бы вы могли объяснить ситуацию лучше, другой решение, вероятно, будет работать лучше.

35 голосов
/ 01 ноября 2008

Вы можете использовать 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 нет хорошего способа сделать это, вероятно потому, что вам не нужно это делать. Возможно, есть лучшие способы сделать то, что вы пытаетесь сделать.

26 голосов
/ 01 января 2009

Возможно, вы захотите изменить свой подход и использовать имя переменной переменной?

$var_name = "FooBar";
$$var_name = "a string";

тогда вы могли бы просто

print($var_name);

чтобы получить

FooBar

Вот ссылка на руководство по PHP для переменных

18 голосов
/ 11 апреля 2013

Никто, кажется, не упомянул фундаментальные причины , почему это а) сложно и б) неразумно:

  • «Переменная» - это просто символ, указывающий на что-то еще. В PHP он внутренне указывает на то, что называется «zval», который может фактически использоваться для нескольких переменных одновременно, либо потому, что они имеют одинаковое значение (PHP реализует что-то, называемое «копирование при записи», так что $foo = $bar не делает « необходимо сразу выделить дополнительную память) или потому, что они были назначены (или переданы функции) по ссылке (например, $foo =& $bar). Таким образом, у zval нет имени.
  • Когда вы передаете параметр в функцию, вы создаете новую переменную (даже если это ссылка). Вы можете передать что-то анонимное, например "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'];
13 голосов
/ 10 марта 2010

Я сделал функцию проверки по причинам отладки. Это похоже на print_r () на стероидах, очень похоже на Krumo, но немного более эффективно на объектах. Я хотел добавить определение имени var и предложил это, вдохновленный постом Ника Престы на этой странице. Он обнаруживает любое выражение, переданное в качестве аргумента, а не только имена переменных.

Это только функция-обертка, которая обнаруживает переданное выражение. Работает в большинстве случаев. Это не будет работать, если вы вызываете функцию более одного раза в одной и той же строке кода.

Это прекрасно работает: умереть ( осмотр ( $ this-> GetUser () -> hasCredential ( "Удалить") ) );

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) в действии:

http://inspect.ip1.cc

Тексты на этой странице на испанском, но код лаконичен и действительно прост для понимания.

10 голосов
/ 27 октября 2010

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

8 голосов
/ 28 апреля 2016

Это именно то, что вы хотите - это готовая к использованию функция «копировать и вставить», которая отображает имя заданного var:

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 trim($var);
}

ИСПОЛЬЗОВАНИЕ: print_var_name ($ FooBar)

ПЕЧАТЬ: FooBar

ПОДСКАЗКА Теперь вы можете переименовать функцию, и она все равно будет работать, а также использовать функцию несколько раз в одной строке! Благодаря @ Cliffordlife

5 голосов
/ 13 августа 2011

С php.net

@ Александр - короткое решение

<?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
?>
4 голосов
/ 16 марта 2012

Многие ответы ставят под сомнение полезность этого. Однако получение ссылки на переменную может быть очень полезным. Особенно в случаях с объектами и $ это . Мое решение работает как с объектами, так и с объектами, определенными свойствами:

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
2 голосов
/ 28 декабря 2012

Адаптировано из приведенных выше ответов для многих переменных, с хорошей производительностью, всего одно сканирование $ 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
        )

)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...