3v4l.org

run code in 300+ PHP versions simultaneously
<?php try { $user_data = json_encode( [ 'id' => 'a2aa06d9-b3b6-4f8c-a450-a5a37f401ff4', 'name' => 42, 'username' => (float)'NaN', 'email' => true, 'address' => [ 'street' => false, 'suite' => M_PI, 'city' => 1.5, 'zipcode' => 'The back of beyond', 'geo' => [ 'lat' => 'https://3v4l.org', 'lng' => 'looooong', ], ], 'phone' => 'E.T. Phone Home', 'website' => 0, 'company' => [ 'name' => 1.5, 'catchPhrase' => false, 'bs' => true, ], ], JSON_PRETTY_PRINT ); echo $user_data . PHP_EOL; $user_data = json_decode( $user_data, true ); $user = new User_DTO($user_data); $user_list = new User_List_DTO([ 'id' => 2, 'name' => 'Bar', 'username' => 'bar', ]); echo $user->get_id() . PHP_EOL; echo $user->get_name() . PHP_EOL; echo $user->get_username() . PHP_EOL; echo $user_list->get_id() . PHP_EOL; echo $user_list->get_name() . PHP_EOL; echo $user_list->get_username() . PHP_EOL; } catch(Exception $e) { echo $e->getMessage(); } abstract class DTO { protected $data = []; protected $schema = []; public function __construct( array $data ) { $this->validate_data_against_schema( $data, $this->schema ); $this->data = $data; } private function validate_data_against_schema( array $data, array $schema ) { foreach ( $schema as $key => $value ) { if ( ! array_key_exists( $key, $data ) ) { throw new \UnexpectedValueException( sprintf( 'Required key "%s" does not exist.', $key ) ); } // We don't want to enforce any specific type, but if we // expect a scalar, we don't want an array. if ( is_scalar( $value ) && ! is_scalar( $data[ $key ] ) ) { throw new \UnexpectedValueException( sprintf( 'Key "%s" must be scalar, %s provided.', $key, gettype( $data[ $key ] ) ) ); } // Same the other way around. If we expect an array, // we don't want anything else. if ( is_array( $value ) && ! is_array( $data[ $key ] ) ) { throw new \UnexpectedValueException( sprintf( 'Key "%s" must be an array, %s provided.', $key, gettype( $data[ $key ] ) ) ); } // Traverse down the children, recursively. if ( is_array( $value ) ) { $this->validate_data_against_schema( $data[ $key ], $value ); } } } } class User_DTO extends DTO { protected $schema = [ 'id' => '', 'name' => '', 'username' => '', 'email' => '', 'address' => [ 'street' => '', 'suite' => '', 'city' => '', 'zipcode' => '', 'geo' => [ 'lat' => '', 'lng' => '', ], ], 'phone' => '', 'website' => '', 'company' => [ 'name' => '', 'catchPhrase' => '', 'bs' => '', ], ]; public function get_id(): string { return (string) $this->data['id']; } public function get_name(): string { return (string) $this->data['name']; } public function get_username(): string { return (string) $this->data['username']; } // etc } class User_List_DTO extends DTO { protected $schema = [ 'id' => '', 'name' => '', 'username' => '', ]; public function get_id(): string { return (string) $this->data['id']; } public function get_name(): string { return (string) $this->data['name']; } public function get_username(): string { return (string) $this->data['username']; } }
Output for 7.2.0 - 7.2.33, 7.3.0 - 7.3.33, 7.4.0 - 7.4.33, 8.0.0 - 8.0.30, 8.1.0 - 8.1.28, 8.2.0 - 8.2.18, 8.3.0 - 8.3.4, 8.3.6
{ "id": "a2aa06d9-b3b6-4f8c-a450-a5a37f401ff4", "name": 42, "username": 0, "email": true, "address": { "street": false, "suite": 3.141592653589793, "city": 1.5, "zipcode": "The back of beyond", "geo": { "lat": "https:\/\/3v4l.org", "lng": "looooong" } }, "phone": "E.T. Phone Home", "website": 0, "company": { "name": 1.5, "catchPhrase": false, "bs": true } } a2aa06d9-b3b6-4f8c-a450-a5a37f401ff4 42 0 2 Bar bar
Output for 8.3.5
Warning: PHP Startup: Unable to load dynamic library 'sodium.so' (tried: /usr/lib/php/8.3.5/modules/sodium.so (libsodium.so.23: cannot open shared object file: No such file or directory), /usr/lib/php/8.3.5/modules/sodium.so.so (/usr/lib/php/8.3.5/modules/sodium.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0 { "id": "a2aa06d9-b3b6-4f8c-a450-a5a37f401ff4", "name": 42, "username": 0, "email": true, "address": { "street": false, "suite": 3.141592653589793, "city": 1.5, "zipcode": "The back of beyond", "geo": { "lat": "https:\/\/3v4l.org", "lng": "looooong" } }, "phone": "E.T. Phone Home", "website": 0, "company": { "name": 1.5, "catchPhrase": false, "bs": true } } a2aa06d9-b3b6-4f8c-a450-a5a37f401ff4 42 0 2 Bar bar

preferences:
198.9 ms | 403 KiB | 181 Q