Alex
Alex

Reputation: 68406

Can I find out if a variable is defined in the global space based on its value and name?

I know the variable name, like $var and its value.

If I check for isset($GLOBALS[$var_name]) && $GLOBALS[$var_name] == $var I may get a positive result even if the variable is not really global, but happens to have the same name and value as an existing global variable :(

I need this for a debug function that I'm building, to display more info about the type of the variable that was passed.

Could I also find out if a variable is static? :)

ps: reflection doesn't help me.. It can only get info for objects or functions...

Upvotes: 2

Views: 262

Answers (3)

DaveRandom
DaveRandom

Reputation: 88647

If the variables are references to each other (which is essentially what you want to check), they will use the same zval. Therefore, here is a slightly nasty, but will work, way to do it:

<?php

    function vars_are_referenced (&$var1, &$var2) {

        // What we are doing *will* throw an error (I said it was nasty ;-) )
        $oldER = error_reporting();
        $oldDE = ini_get('display_errors');
        error_reporting(0);
        ini_set('display_errors', 0);

        // We need to use output buffering, we don't want to break any existing
        // buffering so we cache the contents of the buffer
        $oldBuffer = ob_get_length() ? ob_get_clean() : NULL;

        // If the values are not identical, definitely not a match
        if ($var1 !== $var2) return FALSE;

        // Now we inspect the zval of $var1
        ob_start();
        debug_zval_dump(&$var1);
        preg_match('/\brefcount\((\d+)\)(?:\b|$)/', ob_get_clean(), $matches);
        $var1RefCountBefore = (int) $matches[1];

        // If they are the same, this will increase the refcount
        $temp = &$var2;

        // Inspect the zval of $var1 again
        ob_start();
        debug_zval_dump(&$var1);
        preg_match('/\brefcount\((\d+)\)(?:\b|$)/', ob_get_clean(), $matches);
        $var1RefCountAfter = (int) $matches[1];

        // If refcount is now greater, they are the same
        $result = $var1RefCountAfter > $var1RefCountBefore;

        // Repopulate the output buffer if necessary
        if ($oldBuffer !== NULL) {
            ob_start();
            echo $oldBuffer;
        }

        // Turn error reporting back to correct level
        error_reporting($oldER);
        ini_set('display_errors', $oldDE);

        return $result;

    }

    function test_ref_fail () {
        $a = 1;
        $var_name = 'a';
        var_dump(vars_are_referenced($GLOBALS['a'], $a));
    }

    function test_ref_success_1 () {
        global $a;
        $var_name = 'a';
        var_dump(vars_are_referenced($GLOBALS['a'], $a));
    }

    function test_ref_success_2 (&$a) {
        $var_name = 'a';
        var_dump(vars_are_referenced($GLOBALS['a'], $a));
    }

    $a = 1;
    $b = &$a;
    var_dump(vars_are_referenced($a, $b));
    test_ref_fail();
    test_ref_success_1();
    test_ref_success_2($a);

This would be the best way to do it (by inspecting the zval), but as I'm sure you can see, this is not a pretty method with the functions currently available in PHP. It will raise errors because of the call-time pass-by-reference that is required to make debug_zval_dump() work like we need it to. It will cause a fatal error in PHP >= 5.4 because call-time pass-by-reference has been removed.

So here is the other way to do it - which I don't like, because it involves modifying the variables, but it shouldn't break anything and crucially, wont raise any errors and will work everywhere:

<?php

    function vars_are_referenced (&$var1, &$var2) {

        // If the values are not identical, definitely not a match
        if ($var1 !== $var2) return FALSE;

        // Make a copy of the old value
        $oldVal = $var1;

        // Get a new value we can assign that is different to the old value
        $newVal = ($oldVal === 1) ? 2 : 1;

        // Assign the value to $var1
        $var1 = $newVal;

        // See if $var2 has changed
        $result = $var1 === $var2;

        // Put the value of $var1 right again
        $var1 = $oldVal;

        return $result;

    }

Here is a helper function (which will work with either of the vars_are_referenced() definitions above) to determine specifically if a variable is global, based on the name of the variable as a string and the variable itself:

<?php

    function var_is_global ($name, &$var) {
        return vars_are_referenced($GLOBALS[$name], $var);
    }

I can't think of a way to accurately check if an arbitrary variable is static outside the context of an object.

Upvotes: 7

Kasia Gogolek
Kasia Gogolek

Reputation: 3414

You want to have a look at get_defined_vars() function, which returns an array of all defined variables.

var_dump(get_defined_vars())

But if you want to do it properly, have a look at Xdebug, where you can not only preview set variables, but also trace them. Click here for more info.

Upvotes: 3

PhilMasteG
PhilMasteG

Reputation: 3185

How about eval()'ing the global statement?

function isglobal($name)
{
    return eval('global $'. $name .'; return isset($'. $name .');');
}

However, take good care to properly escape the name to be checked.

Upvotes: 3

Related Questions