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

preferences:
39.61 ms | 404 KiB | 5 Q