3v4l.org

run code in 300+ PHP versions simultaneously
<?php declare(strict_types=1); // ─── Identity Package ───────────────────────────────────────────────────────── interface ContextResolver { public function resolve(): Context; } interface Context { } final readonly class IdentityContext implements Context { public function __construct( public readonly UserId $userId, public readonly TeamId $teamId, ) { } } final readonly class IdentityContextResolver implements ContextResolver { public function __construct( private Request $request, private JwtParser $jwt, ) { } public function resolve(): IdentityContext { $payload = $this->jwt->parse($this->request->bearerToken()); return new IdentityContext( userId: new UserId($payload->sub), teamId: new TeamId($payload->teamId), ); } } // ─── Payment Package ────────────────────────────────────────────────────────── final readonly class StripeActor { public function __construct( public readonly string $stripeEventId, public readonly string $stripeAccountId, ) { } } final readonly class PaymentContext implements Context { public function __construct( public readonly IdentityContext $identity, // composed public readonly StripeActor $actor, // composed ) { } } final readonly class PaymentContextResolver implements ContextResolver { public function __construct( private ContextResolver $inner, private Request $request, private StripeSignatureVerifier $verifier, ) { } public function resolve(): PaymentContext { $this->verifier->verify($this->request); $event = json_decode($this->request->getContent()); return new PaymentContext( identity: $this->inner->resolve(), actor: new StripeActor( stripeEventId: $event->id, stripeAccountId: $event->account, ), ); } } // ─── VoiceLead Package ──────────────────────────────────────────────────────── final readonly class VapiActor { public function __construct( public readonly string $botUuid, public readonly string $botId, ) { } } final readonly class VoiceLeadContext implements Context { public function __construct( public readonly IdentityContext $identity, // composed public readonly VapiActor $actor, // composed ) { } } final readonly class VoiceLeadContextResolver implements ContextResolver { public function __construct( private ContextResolver $inner, private Request $request, private HmacVerifier $verifier, private BotRepository $bots, ) { } public function resolve(): VoiceLeadContext { $this->verifier->verify($this->request); $botUuid = $this->request->input('botUuid'); $bot = $this->bots->findByUuid($botUuid); return new VoiceLeadContext( identity: $this->inner->resolve(), actor: new VapiActor( botUuid: $botUuid, botId: $bot->getId(), ), ); } } // ─── Controllers ────────────────────────────────────────────────────────────── final readonly class PaymentWebhookController { public function __construct( private PaymentContextResolver $resolver, ) { } public function __invoke(): void { $context = $this->resolver->resolve(); $context->identity->teamId; // from composed IdentityContext $context->actor->stripeEventId; // from composed StripeActor } } final readonly class VapiWebhookController { public function __construct( private VoiceLeadContextResolver $resolver, ) { } public function __invoke(): void { $context = $this->resolver->resolve(); $context->identity->teamId; // from composed IdentityContext $context->actor->botId; // from composed VapiActor } }
Output for 8.3.0 - 8.3.30, 8.4.1 - 8.4.18, 8.5.0 - 8.5.3

preferences:
51.74 ms | 406 KiB | 5 Q