Getting the name of a variable passed for inspection

Coding Critique is the place to post source code for peer review by other members of DevNetwork. Any kind of code can be posted. Code posted does not have to be limited to PHP. All members are invited to contribute constructive criticism with the goal of improving the code. Posted code should include some background information about it and what areas you specifically would like help with.

Popular code excerpts may be moved to "Code Snippets" by the moderators.

Moderator: General Moderators

Post Reply
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Getting the name of a variable passed for inspection

Post by RobertGonzalez »

So I have been wanting to be able to write my very own variable inspector to blow the lid off of var_dump() or print_r(). These functions, while useful, present data in a very archaic way in my opinion. One of the things that irritates me the most is that when I pass a variable to a dump function, I get the variable data returned, but I can never seem to get the variable name that the data is in. Well, I figured since it wasn't available in PHP core, I'd do it myself.

Side Note: I have a working PHP5 class for this that I am developing. Code for that can be fetched from my blog. As it is in development stage, I would really appreciate no critiquing of it. If you want to suggest features, you may, but it is not ready for critique yet.

The code:

Code: Select all

<?php
/**
 * Gets the name of the variable to be inspected
 * 
 * @param mixed $var A reference to a variable that is to be inspected
 * @return string The string name of the variable, or '[name_not_found]' if not found
 */
function get_var_name(&$var) 
{
    // Before we start, we need to read the data for the var we are checking
    // into a temporary var since we need to pass a reference of the var we
    // want the name for. Without this, we would actually be working with 
    // the original variable, screwing things up royally.
    $passed = $var;
    
    // Initialize the variable name return variable
    $varName = '';
    
    // Now the fun part... we change the value of the variable whose name we 
    // want to something random (not the number 4 onion)
    $var = 'Scarlett_'.rand().'_Johannson';
    
    // Now we loop through the GLOBAL array and see if we find the GLOBAL array 
    // key that matches this value
    foreach ($GLOBALS as $k => $v) {
        // If the value of the GLOBAL is the same as the temp value...
        if ($v === $var) {
            // Then we snag the GLOBAL key and use it as our var name
            $varName = $k;
            
            // And since we have it, we no longer need to loop
            break;
        }
    }
    
    // If there was no name found let the app know
    if (empty($varName)) {
        $varName = '[name_not_found]';
    }
    
    // Now change the value of the variable back to its original state
    $var = $passed;
    
    // And finally, return it to the app
    return $varName;
}
?>
Use:

Code: Select all

<?php
$stringTest = 'Green and Juicy';
echo 'The variable name that was passed was $' . get_var_name($stringTest);
?>
Output:

Code: Select all

The variable name that was passed was $stringTest
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Heres a similar but more compact version of your function

http://www.php.net/manual/en/language.v ... .php#76245
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post by superdezign »

http://www.php.net/manual/en/language.v ... .php#49997

I was trying to make arrays work with your function, and then this popped up. It's somewhat similar. I'm losing all hope on the arrays though. :-p
User avatar
Benjamin
Site Administrator
Posts: 6935
Joined: Sun May 19, 2002 10:24 pm

Post by Benjamin »

I can't imagine ever needing to obtain the name of a variable.
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

You'd be better off comparing "===" to a new instance of an object since "===" only evaluates to true on objects if the objects are references (which they couldn't be if you use the "new" keyword in your function). Comparing by value has a hole, albeit a narrow one.

Code: Select all

<?php 
/** 
 * Gets the name of the variable to be inspected 
 * 
 * @param mixed $var A reference to a variable that is to be inspected 
 * @return string The string name of the variable, or '[name_not_found]' if not found 
 */ 
function get_var_name(&$var) 
{ 
    // Before we start, we need to read the data for the var we are checking 
    // into a temporary var since we need to pass a reference of the var we 
    // want the name for. Without this, we would actually be working with 
    // the original variable, screwing things up royally. 
    $passed = $var;
    
    // Initialize the variable name return variable 
    $varName = ''; 
    
    // Now the fun part... we change the value of the variable whose name we 
    // want to something random (not the number 4 onion) 
    $var =& new stdClass(); 
    
    // Now we loop through the GLOBAL array and see if we find the GLOBAL array 
    // key that matches this value 
    foreach ($GLOBALS as $k => $v) { 
        // If the value of the GLOBAL is the same as the temp value... 
        if ($v === $var) { 
            // Then we snag the GLOBAL key and use it as our var name 
            $varName = $k; 
            
            // And since we have it, we no longer need to loop 
            break; 
        } 
    } 
    
    // If there was no name found let the app know 
    if (empty($varName)) { 
        $varName = '[name_not_found]'; 
    } 
    
    // Now change the value of the variable back to its original state 
    $var = $passed; 
    
    // And finally, return it to the app 
    return $varName; 
} 
?>
Beware of using this method in PHP4 when passing objects to it too. It will break your reference ;)

This won't work neither:

Code: Select all

function myFunction()
{
  $myvar = "foo";
  echo get_var_name($myvar);
}
I had to pick the flaws in this :P The object one will catch people out though. Try this (PHP4):

Code: Select all

$o =& new stdClass();
$o->foo = "bar";

function injectThing(&$obj)
{
  if (get_var_name($obj) == "o") $obj->thing = "bleh";
}

inject_thing($o);

var_dump($o);
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post by superdezign »

astions wrote:I can't imagine ever needing to obtain the name of a variable.
Good thing too, as writing a function that will always get the variable's name is a near impossible task in PHP. Plus, GLOBALS has a reference of itself! Who's idea was that? Nesting level too deep my ass...
User avatar
Ollie Saunders
DevNet Master
Posts: 3179
Joined: Tue May 24, 2005 6:01 pm
Location: UK

Post by Ollie Saunders »

Code: Select all

function getVarName($var)
{
    static $files = array();
    list($frame) = debug_backtrace();
    $fileName = $frame['file'];
    if (!isset($files[$fileName])) {
        $files[$fileName] = $file = file($fileName);
    } else {
        $file = $files[$fileName];
    }
    $line = $file[$frame['line'] - 1];
    $matches = array();
    $pattern = '~' . preg_quote(__FUNCTION__, '~') . '\(\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*?)\)~i';
	if (!preg_match($pattern, $line, $matches)) {
	   return false;
	}
    list(, $match) = $matches;
    return $match;
}
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Post by Chris Corbyn »

ole wrote:

Code: Select all

function getVarName($var)
{
    static $files = array();
    list($frame) = debug_backtrace();
    $fileName = $frame['file'];
    if (!isset($files[$fileName])) {
        $files[$fileName] = $file = file($fileName);
    } else {
        $file = $files[$fileName];
    }
    $line = $file[$frame['line'] - 1];
    $matches = array();
    $pattern = '~' . preg_quote(__FUNCTION__, '~') . '\(\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*?)\)~i';
	if (!preg_match($pattern, $line, $matches)) {
	   return false;
	}
    list(, $match) = $matches;
    return $match;
}
Getting sillier, but still....

Code: Select all

<?php

eval('

$foo = "something";
echo getVarName($foo);

');
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post by superdezign »

I'm beginning to think this is more trouble than it's worth... :P
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

Quick note, nothing I code for at the moment is for PHP4. I should have said that. I have no interest in supporting PHP4 code.

I tested that single line snippet from the manual and it wouldn't work correctly in a PHP 5 class. I also tested other versions of name fetching code and either none of them worked in PHP 5, none of them worked in constructors of an object being instantiated from within another object or they always returned null (empty). The code I posted works as a stand alone function and also works in objects. That, to me, sets it apart from the other snips.

There are several times when I have dumped data throughout a script at various points and would rather have had the variable name than the line number (it also helps you see how a variable's values changes).

I tested the code on PHP 5.1.6. At the moment I make no claims to functionality on other PHP distros as I have not been able to test them under these platforms.

Thanks for going over this guys. I truly appreciate the feedback.
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

superdezign wrote:http://www.php.net/manual/en/language.v ... .php#49997

I was trying to make arrays work with your function, and then this popped up. It's somewhat similar. I'm losing all hope on the arrays though. :-p
Well crap, I thought I was special. That code is almost exactly the same as what I put out (honestly I hadn't seen that one before). I had seen the snips on print_r() and var_dump(), but that code is almost identical to what I put out. Oh well, I guess my originality is crap :( .
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post by superdezign »

Everah wrote:There are several times when I have dumped data throughout a script at various points and would rather have had the variable name than the line number (it also helps you see how a variable's values changes).
I agree. I hate having to break if-statements up into different lines
Everah wrote:I tested the code on PHP 5.1.6. At the moment I make no claims to functionality on other PHP distros as I have not been able to test them under these platforms.
There are a lot of documents online regarding recursive dependency (specifically the reference to GLOBALS in GLOBALS) that seem to start at PHP 5.2, which makes in extremely difficult to traverse deeper into GLOBALS.

array_slice() / array_shift() work when the globals array is ordered starting with G, but not every installation needs to do so. array_search() can't find GLOBALS in GLOBALS, in_array() can't do it, and simple '===' comparison can't either. Otherwise, I would have had an addition to your snippet that could document the exact position in the GLOBALS array (i.e. GLOBALS=>FOO=>BAR=>varName). Sadly, I couldn't find a way to make it a dynamic fix.
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Post by superdezign »

Everah wrote:
superdezign wrote:http://www.php.net/manual/en/language.v ... .php#49997

I was trying to make arrays work with your function, and then this popped up. It's somewhat similar. I'm losing all hope on the arrays though. :-p
Well crap, I thought I was special. That code is almost exactly the same as what I put out (honestly I hadn't seen that one before). I had seen the snips on print_r() and var_dump(), but that code is almost identical to what I put out. Oh well, I guess my originality is crap :( .
Hehe. Look at it this way: Yours has a more unique name than 'unique'. ;)
Post Reply