3v4l.org

run code in 300+ PHP versions simultaneously
<?php # Part 1: Prepare the classes. There should me lots of more code to make it robust, but I'm trying the PoC simple. class Repository { public array $data; /** * Works out the namespaced key and stores the value twice. * * If we're storing objects, they're stored as a reference anyway, so it doesn't matter that the same thing is there twice. * * If we store a scalar, then yes, we take up 2x the memory, but I think it's not a common use case and it's not a big deal. */ public function setReference(string $key, mixed $identity): void { // Stores the last $identity set with $key. If you don't re-use keys, this always keeps your object. Previous behaviour. $this->data[$key] = $identity; // Stores the namespaced identity. If you re-use keys and use the type as discriminator, this is what you'll be getting things back from. if (is_object($identity)) { $nsKey = $key.$this->getRealClass($identity::class); } else { $nsKey = $key.(get_debug_type($identity) ?? ''); } $this->data[$nsKey] = $identity; } /** * The @template notation below tells static analysers what value is returned when $type is provided. I tested this with PHPStorm - $boss = $repository->getReference('foo', Boss::class) correctly identifies $boss as Boss::class object. * * @template T * @param string $key * @param class-string<T> $type Could be a class name, or even a 'string' or 'int' value. * @return T */ public function getReference(string $key, string $type = null) { // If the $key doesn't exist, it doesn't matter what $type we might be asking for: the $key was never used when setting a value if (!array_key_exists($key, $this->data)) { throw new \Exception('Fixture not found'); } // No $type provided or not found using the namespaced key if (!$type) { return $this->data[$key]; } // The correct $key and $type combination exist - all good! if (array_key_exuists($key.$type, $this->data)) { // I'd assert the type here too, but for sake of example simplicity of this PoC, let's just not return $this->data[$key.$type]; } // If we made it here, the $key was used to set a value, but the $key we are asking for is not what the object was determined to be when it was being set. // The not-namespaced key happens to be of the correct type - this covers storing the Employee::class and asking for Person::class if (class_exists($type) && $this->data[$key] instanceof $type) { return $this->data[$key]; } // This means the last object stored using $key was not of $type. Since we're clearly saying we want a $type, we can't return anything throw new \Exception(sprintf( 'Fixture %s was found, but is type %s, not %s. Please ask for the correct type.', $key, get_debug_type($this->data[$key]), $type, )); } protected function getRealClass($className) { if (substr($className, -5) === 'Proxy') { return substr($className, 0, -5); } return $className; } } abstract class Person { public function __construct(public string $name) {} } class Boss extends Person {} class Employee extends Person {} # Part 2: Basic use case PoC $repository = new Repository(); $bossJohn = new Boss('John'); $bossJane = new Boss('Jane'); $employeeJim = new Employee('Jim'); $repository->setReference('boss', $bossJohn); $repository->setReference('boss', $bossJane); // overwrites 'boss' key with Jane because John and Jane are the same class assert($repository->getReference('boss', Boss::class) === $bossJane); // Retrieves last set boss that overwrites previous bosses assert($repository->getReference('boss') === $bossJane); // retrieves the non-namespaced value $repository->setReference('person', $bossJohn); $repository->setReference('person', $employeeJim); // adds Jim under duplicate 'person' key because of different classes assert($repository->getReference('person', Boss::class) === $bossJohn); // retrieves the correct person assert($repository->getReference('person', Employee::class) === $employeeJim); // retrieves the correct person assert($repository->getReference('person') === $employeeJim); # Part 3: PoC of things that I don't think are possible if https://github.com/doctrine/data-fixtures/pull/409 gets merged as is on 8th Feb 2023: // This is where things get intereesting, you can store other things than objects, if you want. Think of testing encryption keys generated on the fly etc. $repository->setReference('number', 31337); assert($repository->getReference('number') === 31337); $repository->setReference('number', 31337.0); assert($repository->getReference('number', 'float') === 31337.0); // Using this method we can use any parent in the inheritance structure assert($repository->getReference('person', Person::class) === $employeeJim); // Returns the last set 'person' even thought we're using a parent Person::class, not Employee::class // Using this method we can use any parent in the inheritance structure assert($repository->getReference('person', Boss::class) === $bossJohn); // Returns the correct fixture that duplicates keys, as long as we know what is the exact class we're after echo 'Success.';

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.3.110.0120.00320.94
8.3.100.0080.00424.06
8.3.90.0080.00026.77
8.3.80.0040.00718.81
8.3.70.0170.00718.56
8.3.60.0100.01618.43
8.3.50.0040.01116.82
8.3.40.0110.00320.46
8.3.30.0120.00618.82
8.3.20.0060.00324.18
8.3.10.0000.00824.66
8.3.00.0110.00026.16
8.2.230.0060.00322.58
8.2.220.0060.00337.54
8.2.210.0040.00426.77
8.2.200.0040.00716.63
8.2.190.0180.00316.75
8.2.180.0070.01116.63
8.2.170.0120.00318.99
8.2.160.0070.00722.96
8.2.150.0090.00025.66
8.2.140.0060.00324.66
8.2.130.0000.00826.16
8.2.120.0040.00421.05
8.2.110.0030.00620.52
8.2.100.0000.00920.52
8.2.20.0060.00418.25
8.1.290.0110.00430.84
8.1.280.0040.01525.92
8.1.270.0030.00722.18
8.1.260.0040.00426.35
8.1.250.0080.00028.09
8.1.240.0000.01118.96
8.1.230.0070.00318.78
8.1.200.0030.01279.33

preferences:
28.47 ms | 403 KiB | 5 Q