Detecting whether a PHP variable is a reference / referenced

后端 未结 5 1004
情书的邮戳
情书的邮戳 2020-11-27 05:33

Is there a way in PHP to determine whether a given variable is a reference to another variable and / or referenced by another variable? I appreciate that it might not be po

5条回答
  •  一整个雨季
    2020-11-27 05:46

    Take a peak at xdebug_debug_zval(). Right now, that's the only way to really know if you can determine everything about the variable's zval.

    So here are a couple of helper functions to determine some helpful information:

    function isRef($var) {
        $info = getZvalRefCountInfo($var);
        return (boolean) $info['is_ref'];
    }
    function getRefCount($var) {
        $info = getZvalRefCountInfo($var);
        return $info['refcount'];
    }
    function canCopyOnWrite($var) {
        $info = getZvalRefCountInfo($var);
        return $info['is_ref'] == 0;
    }
    function canReferenceWithoutCopy($var) {
        $info = getZvalRefCountInfo($var);
        return $info['is_ref'] == 1 || $info['refcount'] == 1;
    }
    
    function getZvalRefCountInfo($var) {
        ob_start();
        xdebug_debug_zval($var);
        $info = ob_get_clean();
        preg_match('(: \(refcount=(\d+), is_ref=(\d+)\))', $info, $match);
        return array('refcount' => $match[1], 'is_ref' => $match[2]);
    }
    

    So with some sample variables:

    $a = 'test';
    $b = $a;
    $c = $b;
    $d =& $c;
    $e = 'foo';
    

    We can test if a variable is a reference:

    isRef('a'); // false
    isRef('c'); // true
    isRef('e'); // false
    

    We can get the number of variables linked to the zval (not necessarily a reference, can be for copy-on-write):

    getRefCount('a'); // 2
    getRefCount('c'); // 2
    getRefCount('e'); // 1
    

    We can test if we can copy-on-write (copy without performing a memory copy):

    canCopyOnWrite('a'); // true
    canCopyOnWrite('c'); // false
    canCopyOnWrite('e'); // true
    

    And we can test if we can make a reference without copying the zval:

    canReferenceWithoutCopy('a'); // false
    canReferenceWithoutCopy('c'); // true
    canReferenceWithoutCopy('e'); // true
    

    And now, we can check if a variable references itself through some black magic:

    function isReferenceOf(&$a, &$b) {
        if (!isRef('a') || getZvalRefCountInfo('a') != getZvalRefCountInfo('b')) {
            return false;
        }
        $tmp = $a;
        if (is_object($a) || is_array($a)) {
            $a = 'test';
            $ret = $b === 'test';
            $a = $tmp;
        } else {
            $a = array();
            $ret = $b === array();
            $a = $tmp;
        }
        return $tmp;
    }
    

    It's a bit hacky since we can't determine what other symbols reference the same zval (only that other symbols reference). So this basically checks to see if $a is a reference, and if $a and $b both have the same refcount and reference flag set. Then, it changes one to check if the other changes (indicating they are the same reference).

提交回复
热议问题