3v4l.org

run code in 300+ PHP versions simultaneously
<?php /** * Strategy for converting a dynamic property that is initialized only when the property is invoked. * * This strategy declares the property but sets its visibility to private. By setting it to * private (opr protected), the original intent still works, meaning it's only init and set when * code invokes it (which invokes the __get()). */ class DynamicPropInitOnGet { public $prop1; public $prop2; public $prop3; private $known; public function __construct( $props ) { foreach ( $props as $prop => $value ) { $this->$prop = $value; } } public function __get( $prop ) { if ( 'known' !== $prop ) { trigger_error( sprintf( 'Getting the dynamic property "%s" on %s is deprecated', $prop, __CLASS__ ), E_USER_DEPRECATED ); return null; } $this->known = new stdClass(); foreach ( array( 'prop1', 'prop2' ) as $column ) { $this->known->{$column} = isset( $this->{$column} ) ? $this->{$column} : null; } return $this->known; } public function __isset( $prop ) { if ( 'known' === $prop ) { return isset( $this->known ); } return false; } public function __set( $prop, $value ) { if ( 'known' === $prop ) { $this->known = $value; return; } trigger_error( sprintf( 'Setting the dynamic property "%s" on %s is deprecated', $prop, __CLASS__ ), E_USER_DEPRECATED ); } public function __unset( $prop ) { if ( 'known' === $prop ) { unset( $this->known ); return; } trigger_error( sprintf( 'Unsetting the dynamic property "%s" on %s is deprecated', $prop, __CLASS__ ), E_USER_DEPRECATED ); } } 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 git.master_jit, git.master, rfc.property-hooks
***** 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(true) __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) Deprecated: Getting the dynamic property "unknown" on DynamicPropInitOnGet is deprecated in /in/6gXXU on line 25 __get() results should be null ✅ NULL __isset() after __get() results should be false ✅ bool(false) Deprecated: Unsetting the dynamic property "unknown" on DynamicPropInitOnGet is deprecated in /in/6gXXU on line 66 Deprecated: Getting the dynamic property "unknown" on DynamicPropInitOnGet is deprecated in /in/6gXXU on line 25 __unset() + __get() results should be null ✅ NULL

This tab shows result from various feature-branches currently under review by the php developers. Contact me to have additional branches featured.

Active branches

Archived branches

Once feature-branches are merged or declined, they are no longer available. Their functionality (when merged) can be viewed from the main output page


preferences:
44.67 ms | 408 KiB | 5 Q