phpphp-7late-static-binding

Assigning static properties by reference behaves differently between PHP5 and PHP7


After upgrading from PHP 5 to PHP 7, the following code is producing different results:

abstract class TheParent {
    public static $prop;

    public function __construct( $val ) {
        static::set_prop($val);
    }

    public static function set_prop( $val ) {
        $ref_reset = $val;
        static::$prop =& $ref_reset;
    }
}

class Child extends TheParent {
    // public static $prop;     //<-- not declared on purpose
}

class Child2 extends TheParent {
    // public static $prop;     //<-- not declared on purpose
}

$c  = new Child('do');
$c2 = new Child2('re');

echo 'Child: ' . Child::$prop . '<br>';
echo 'Child2: ' . Child2::$prop . '<br>';
echo 'TheParent: ' . TheParent::$prop;

In PHP5:

Child: do
Child2: re
TheParent:

In PHP7:

Child: re
Child2: re
TheParent: re

My desired output is the PHP5 output, as I want to be able to reference a single property name in all classes that extend the base (parent) class, but I do not want to redeclare that property or the method that sets it in every single child class (mainly to avoid the maintenance overhead of adding the same property/method to dozens of classes).

It seems the magic in PHP5 was in the assignment by reference (after much hunting on SO, helpful answerers have mentioned that setting by reference "breaks" the "reference set", which allows the properties in each child class to hold separate values). I found this to be a pretty elegant solution in PHP5.

Is there any way to achieve the same result with the same or similar code in PHP7?

Workaround:

It looks like this is the result of a breaking change between PHP 7.2 and 7.3 and probably doesn't have a similarly elegant two-liner alternative. After refactoring my code, I found this slightly more verbose workaround to be effective (and meeting my main goal of not having to re-declare properties in sub-classes):

abstract class TheParent {
    public static $props = [];

    public function __construct( $val ) {
        static::set_prop($val);
    }

    public static function set_prop( $val ) {
        self::$props[static::class] = $val;
    }

    public static function get_prop() {
        if( isset(self::$props[static::class]) )
            return self::$props[static::class];
    }
}

class Child extends TheParent {
    // public static $prop;        //<-- not declared on purpose
}

class Child2 extends TheParent {
    // public static $prop;        //<-- not declared on purpose
}

$c  = new Child('do');
$c2 = new Child2('re');

echo 'Child: ' . Child::get_prop(). '<br>';     // 'do' in PHP 7.3
echo 'Child2: ' . Child2::get_prop() . '<br>';  // 're' in PHP 7.3
echo 'TheParent: ' . TheParent::get_prop();     // ''   in PHP 7.3

Solution

  • This was changed in PHP 7.2 => 7.3

    Static Properties no longer separated by Reference Assignment

    In PHP, static properties are shared between inheriting classes, unless the static property is explicitly overridden in a child class. However, due to an implementation artifact it was possible to separate the static properties by assigning a reference. This loophole has been fixed.

    https://www.php.net/manual/en/migration73.incompatible.php