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' );

Here you find the average performance (time & memory) of each version. A grayed out version indicates it didn't complete successfully (based on exit-code).

VersionSystem time (s)User time (s)Memory (MiB)
8.4.10.0050.00519.60
8.3.140.0130.00617.14
8.3.130.0090.00918.52
8.3.120.0000.00919.30
8.3.110.0050.00320.94
8.3.100.0090.00024.06
8.3.90.0050.00326.77
8.3.80.0080.00417.97
8.3.70.0070.01516.75
8.3.60.0100.00716.72
8.3.50.0200.00316.73
8.3.40.0100.01721.59
8.3.30.0100.01019.14
8.3.20.0140.00321.82
8.3.10.0170.00021.77
8.3.00.0120.00318.89
8.2.250.0060.00316.63
8.2.240.0000.00917.21
8.2.230.0000.00922.58
8.2.220.0000.01137.54
8.2.210.0030.00626.77
8.2.200.0060.00318.41
8.2.190.0070.01516.75
8.2.180.0100.01025.92
8.2.170.0070.00719.09
8.2.160.0120.00620.05
8.2.150.0070.00719.10
8.2.140.0090.00620.55
8.2.130.0120.00620.35
8.2.120.0040.01119.22
8.2.110.0060.01619.22
8.2.100.0190.00319.28
8.2.90.0110.01120.69
8.2.80.0070.01119.45
8.2.70.0120.00618.68
8.2.60.0060.00918.88
8.2.50.0070.01119.25
8.2.40.0100.01018.82
8.2.30.0100.00718.69
8.2.20.0130.00720.61
8.2.10.0160.00318.72
8.2.00.0000.01818.56
8.1.300.0050.00518.13
8.1.290.0060.00330.84
8.1.280.0130.00625.92
8.1.270.0110.00722.22
8.1.260.0080.00822.15
8.1.250.0090.00622.30
8.1.240.0110.01118.70
8.1.230.0100.00718.66
8.1.220.0060.01218.95
8.1.210.0100.01018.76
8.1.200.0120.00318.95
8.1.190.0100.00718.59
8.1.180.0140.01018.49
8.1.170.0080.01218.81
8.1.160.0100.01018.43
8.1.150.0130.00618.86
8.1.140.0060.01318.77
8.1.130.0130.01018.85
8.1.120.0130.00718.75
8.1.110.0120.00618.66
8.1.100.0030.01519.89
8.1.90.0080.00818.81
8.1.80.0030.01219.86
8.1.70.0120.00319.07
8.1.60.0080.00818.94
8.1.50.0090.00618.63
8.1.40.0100.00718.88
8.1.30.0090.00618.77
8.1.20.0000.01620.09
8.1.10.0060.00918.77
8.1.00.0130.00318.52

preferences:
46.16 ms | 403 KiB | 5 Q