3v4l.org

run code in 300+ PHP versions simultaneously
<?php interface DomainEventInterface { } trait EventRecordingTrait { /** * @var array<DomainEventInterface> */ protected array $domainEvents = []; protected function recordThat(DomainEventInterface $domainEvent): void { $this->domainEvents[] = $domainEvent; } /** * @return array<DomainEventInterface> */ public function releaseEvents(): array { $domainEvents = $this->domainEvents; $this->domainEvents = []; return $domainEvents; } } readonly class Created implements DomainEventInterface { public function __construct( public string $name, public string $email ) { } } readonly class NameUpdated implements DomainEventInterface { public function __construct(public string $name) { } } class User { use EventRecordingTrait; // No intention of throwing events, it's just a simple object hydration! public function __construct( private string $name, private string $email, ) { } // intent of creating an instance and recording that specific event public static function create(string $name, string $email): self { $self = new User($name, $email); $self->recordThat(new Created($name, $email)); return $self; } // Wouldn't be necessary with some kind of asymetric visibility, but it's not a big hassle public function getName(): string { return $this->name; } // Here I would update the same $name attribute but throwing a different event public function updateName(string $name): void { if ($this->name === $name) { return; } $this->recordThat(new NameUpdated($name)); $this->name = $name; } // Wouldn't be necessary with some kind of asymetric visibility, but it's not a big hassle public function getEmail(): string { return $this->email; } } $user1 = new User('Hydrated from database', 'user1@example.com'); var_dump($user1); $user1->updateName('Mutation intent'); var_dump($user1); $user2 = User::create('Created from controller', 'user2@example.com'); var_dump($user2); $user2->updateName('Another mutation intent'); var_dump($user2);
Output for 8.2.11 - 8.2.25, 8.3.5 - 8.3.14
object(User)#1 (3) { ["name":"User":private]=> string(22) "Hydrated from database" ["email":"User":private]=> string(17) "user1@example.com" ["domainEvents":protected]=> array(0) { } } object(User)#1 (3) { ["name":"User":private]=> string(15) "Mutation intent" ["email":"User":private]=> string(17) "user1@example.com" ["domainEvents":protected]=> array(1) { [0]=> object(NameUpdated)#2 (1) { ["name"]=> string(15) "Mutation intent" } } } object(User)#3 (3) { ["name":"User":private]=> string(23) "Created from controller" ["email":"User":private]=> string(17) "user2@example.com" ["domainEvents":protected]=> array(1) { [0]=> object(Created)#4 (2) { ["name"]=> string(23) "Created from controller" ["email"]=> string(17) "user2@example.com" } } } object(User)#3 (3) { ["name":"User":private]=> string(23) "Another mutation intent" ["email":"User":private]=> string(17) "user2@example.com" ["domainEvents":protected]=> array(2) { [0]=> object(Created)#4 (2) { ["name"]=> string(23) "Created from controller" ["email"]=> string(17) "user2@example.com" } [1]=> object(NameUpdated)#5 (1) { ["name"]=> string(23) "Another mutation intent" } } }
Output for 8.1.30
Parse error: syntax error, unexpected token "readonly", expecting end of file in /in/ZCVpG on line 32
Process exited with code 255.

preferences:
30.06 ms | 408 KiB | 5 Q