3v4l.org

run code in 300+ PHP versions simultaneously
<?php /** * Example of dynamic property that is initialized only when the property is invoked, * i.e. only on get. */ class DynamicPropInitOnGet { public $prop1; public $prop2; public $prop3; public function __construct( $props ) { foreach ( $props as $prop => $value ) { $this->$prop = $value; } } public function __get( $key ) { switch ( $key ) { case 'known': $known = new stdClass(); foreach ( array( 'prop1', 'prop2' ) as $column ) { $known->{$column} = isset( $this->{$column} ) ? $this->{$column} : null; } return $known; } } } class TestMagicMethods { private $obj; private $is_known_prop = false; public function __construct( $obj ) { $this->obj = $obj; } public function test( $prop ) { $this->is_known_prop = ( 'known' === $prop ); // __isset. $this->test_isset( $prop ); // __get(). $this->test_get( $prop ); // __isset() after __get(). $this->test_isset( $prop, true ); // __unset(). $this->test_unset( $prop ); } private function test_isset( $prop, $after_get = false ) { $actual = isset( $this->obj->$prop ); if ( ! $after_get ) { printf( "__isset() results should be false %s\n", $this->test_results( false === $actual ) ); } else { $expected = $this->is_known_prop; printf( "\n\n__isset() after __get() results should be %s %s\n", $this->is_known_prop ? 'true' : 'false', $this->test_results( $expected == $actual ) ); } var_dump( $actual ); } private function test_get( $prop ) { $actual = $this->obj->$prop; $message = "\n\n__get() results should be %s %s\n"; if ( $this->is_known_prop ) { printf( $message, 'an object with prop1 and prop2', $this->test_results( $actual instanceof stdClass ) ); } else { printf( $message, 'null', $this->test_results( null === $actual ) ); } var_dump( $actual ); } private function test_set( $prop ) { $value = $this->is_known_prop ? (array) $this->$obj->known : 'I am an unknown dynamic property'; $obj->$prop = $value; $actual = $this->obj->$prop; if ( $this->is_known_prop ) { printf( "\n\n" . '__set() results should set the value %s and not throw a deprecation on PHP 8.2+.' . 'However, once the __get() is called again, it will set reset it to an object with prop1 and prop2.' . "\n", $this->test_results( $actual instanceof stdClass ) ); } else { printf( "\n\n" . '__set() results should set the value %s and not throw a deprecation on PHP 8.2+.' . 'The set value should remain set when invoking __get()' . "\n", $this->test_results( $value === $actual ) ); } var_dump( $actual ); } private function test_unset( $prop ) {// unset. unset( $this->obj->$prop ); $actual = $this->obj->$prop; $message = "\n\n__unset() + __get() results should be %s %s\n"; if ( $this->is_known_prop ) { printf( $message, 're-init the object with prop1 and prop2', $this->test_results( $actual instanceof stdClass ) ); } else { printf( $message, 'null', $this->test_results( null === $actual ) ); } var_dump( $actual ); } private function test_results( $actual ) { return $actual ? '✅' : '❌'; } } $obj = new DynamicPropInitOnGet( array( 'prop1' => 'foo', 'prop2' => 'bar', 'prop3' => 'baz', ) ); $tester = new TestMagicMethods( $obj ); echo "***** Test the 'known' property *****\n\n"; $tester->test( 'known' ); echo "\n\n***** Unknown, unexpected dynamic property *****\n\n"; $tester->test( 'unknown' );
Output for 8.1.0 - 8.1.29, 8.2.0 - 8.2.21, 8.3.0 - 8.3.9
***** Test the 'known' property ***** __isset() results should be false ✅ bool(false) __get() results should be an object with prop1 and prop2 ✅ object(stdClass)#3 (2) { ["prop1"]=> string(3) "foo" ["prop2"]=> string(3) "bar" } __isset() after __get() results should be true ❌ bool(false) __unset() + __get() results should be re-init the object with prop1 and prop2 ✅ object(stdClass)#3 (2) { ["prop1"]=> string(3) "foo" ["prop2"]=> string(3) "bar" } ***** Unknown, unexpected dynamic property ***** __isset() results should be false ✅ bool(false) __get() results should be null ✅ NULL __isset() after __get() results should be false ✅ bool(false) __unset() + __get() results should be null ✅ NULL

preferences:
85.5 ms | 405 KiB | 67 Q