3v4l.org

run code in 300+ PHP versions simultaneously
<?php declare(strict_types=1); /** * Shared by all CRUD DTO classes. Understood by service responsible for validation and persistence: CrudEntityManager. */ interface CrudDto { public function apply(): CrudEntity; public static function belongsTo(): string; } abstract class CrudEntity { // Shared stuff such as ID props etc, not important for the question at hand. } /** * Notice a few benefits:. * * 1. No setters * 2. The entity is in charge of updating itself from DTOs it declares it understands * 3. The entity can be created an updated using multiple different DTO objects, as long as the Entity has relevant methods to accept them * 4. No setters means guaranteed read-only properties where a value can be set only during the object creation are possible * 5. Using a static method instead of constructor with a lot of arguments makes inheritance easier */ class Person extends CrudEntity { protected string $name; protected string $writeOnceProperty; protected function __construct() { // Protecting the constructor forces usage of CreatePersonDto } public static function createWith(CreatePersonDto $dto): self { $self = new self(); $self->name = $dto->name; $self->writeOnceProperty = $dto->writeOnceProperty; return $self; } public function updateWith(UpdatePersonDto $dto): self { $this->name = $dto->name; return $this; } public function getName(): string { return $this->name; } public function getWriteOnceProperty(): string { return $this->writeOnceProperty; } } abstract class PersonDto implements CrudDto { public static function belongsTo(): string { return Person::class; } } class CreatePersonDto extends PersonDto { public function __construct(public string $name, public string $writeOnceProperty) { // Void } public function apply(): Person { return Person::createWith($this); } } class UpdatePersonDto extends PersonDto { public string $name; public function __construct(private readonly Person $source) { $this->name = $source->getName(); } public function apply(): Person { return $this->source->updateWith($this); } } class CrudManager { /** * IRL this would validate the DTO and store the $entity in the database. */ public function save(CrudDto $dto): CrudEntity { $entity = $dto->apply(); assert(is_a($entity, $dto::belongsTo())); return $entity; } /** * @template T of CrudEntity * * @param class-string<T> $className * * @return T */ public function saveWithTypeCheck(CrudDto $dto, string $className = CrudEntity::class): CrudEntity { $entity = $this->save($dto); assert($entity instanceof $className); return $entity; } } $manager = new CrudManager(); $dto = new CreatePersonDto(name: 'John Doe', writeOnceProperty: 'Born on 1/1/1970'); $person1 = $manager->save($dto); echo $person1->getName(); echo "\n"; echo get_debug_type($person1); // Prints Person, but static tools such as PHPStan or PHPStorm say it's a CrudEntity echo "\n\n\n"; // We can even use one DTO to create multiple different objects. Almost a prototype pattern, aye?! $person2 = $manager->saveWithTypeCheck($dto, Person::class); // Demo of the update action, just to prove the point $updateDto = new UpdatePersonDto($person2); $updateDto->name = 'New John'; $manager->save($updateDto); echo $person2->getName(); // New John echo "\n"; echo get_debug_type($person2); // Prints Person, but this time PHPStan and PHPStorm know it's CrudEntity|Person. // Conclusion: // // Using $person2 allows my IDE to do autocompletion, and PHPStan check types. // Using $person1 does none of that. // // Question: // // Is there any way to tell CrudManager to get the class-string<T of CrudEntity> from CrudDto::belongsTo()? // It would get rid of the need to pass the class name manually, as demonstrated in CrudManager::saveWithTypeCheck().

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.5.10.0010.00719.45
8.5.00.0120.00920.27
8.4.160.0130.00823.87
8.4.150.0030.00014.05
8.4.140.0130.00817.54
8.4.130.0050.00417.98
8.4.120.0040.00420.64
8.4.110.0120.00818.90
8.4.100.0060.00517.82
8.4.90.0060.01417.69
8.4.80.0080.00717.88
8.4.70.0110.01018.05
8.4.60.0120.00918.67
8.4.50.0140.00518.68
8.4.40.0050.00318.01
8.4.30.0070.01420.34
8.4.20.0070.01122.57
8.4.10.0000.01123.52
8.3.280.0090.01218.45
8.3.270.0100.01016.29
8.3.260.0130.00716.45
8.3.250.0090.01019.02
8.3.240.0100.00616.45
8.3.230.0130.00616.70
8.3.220.0100.00816.56
8.3.210.0080.00616.59
8.3.200.0070.00316.65
8.3.190.0100.00817.17
8.3.180.0050.00317.04
8.3.170.0040.00420.67
8.3.160.0150.00318.38
8.3.150.0100.01017.46
8.3.140.0050.00316.84
8.3.130.0000.00916.73
8.3.120.0030.00618.90
8.3.110.0080.00022.58
8.3.100.0060.00322.58
8.3.90.0090.00922.58
8.3.50.0040.00422.58
8.2.290.0120.00720.48
8.2.280.0130.00618.59
8.2.270.0000.00817.38
8.2.260.0040.00416.61
8.2.250.0030.00518.27
8.2.240.0040.00418.96
8.2.230.0080.00022.58
8.2.220.0030.00622.58
8.2.210.0040.00422.58
8.2.70.0140.00617.38
8.1.330.0130.00721.79
8.1.320.0140.00716.22
8.1.310.0030.00716.15
8.1.300.0030.00620.27

preferences:
36.23 ms | 403 KiB | 5 Q