The more complex a PHP application becomes, the more often the question arises: How do I route dynamic inputs (e.g., events, commands, types) to the right logic?
Common scenarios include:
Most developers start out with if-/match-blocks. That works - but it doesn’t scale well. In this article, I’ll show you how to build a flexible, decoupled structure using a PHP attribute and an interface - based on automatic discovery, demonstrated through the example of a webhook client integration.
Our PHP developer shows you how to handle webhook events in PHP in an elegant and scalable way – a very technical topic. Perfect for you if you want to free your event logic from rigid if-statements and explore smart discovery patterns.
To assign incoming webhook requests to the appropriate event handler based on the attached event, it’s easy to fall back on using if-statements:
A simpler version of this can, since PHP 8, also be implemented using a match statement:
But this quickly becomes messy:
Instead of linking events to event handlers via if-chains or long match statements, we want to:
1. Annotate handler classes with an attribute (e.g., #[Webhook('event.name')])
2. Implement a common interface
3. Recursively scan a defined directory or namespace
4. Automatically locate and invoke the appropriate handler
This pattern doesn’t just work for webhooks — it can also be applied to:
This allows classes to be tagged with a specific event name. These classes are then responsible for handling that event.
This ensures that all handlers share the same signature and can be invoked in a generic way.
Now let’s look at the actual event handler class that processes a given event. Here’s an example of what such a class might look like:
Each class:
The core is a service that uses reflection to discover the appropriate classes:
Whether only the first matching event handler or all discovered handlers should be executed is up to each developer to decide. In this example, all matching event handlers are executed.
Modular: Each handler is a standalone class
Modular: Each handler is a standalone class
Scalable: New events = new file, no changes needed to the global call
Scalable: New events = new file, no changes needed to the global call
Testable: Handlers can be tested in isolation
Testable: Handlers can be tested in isolation
Decoupled: No centralized routing logic
Decoupled: No centralized routing logic
Dynamic: Events and handlers can be discovered at runtime
Dynamic: Events and handlers can be discovered at runtime
You can apply this principle to any kind of dispatch logic:
The rule of thumb is always the same:
Combine attributes + interface + discovery = maximum flexibility
In the case of the webhook event handler, I added a validation step using Laravel’s validation class. This can be executed before processing and helps decide whether the webhook request should be stored or handled at all.
The first step is to define a 'validate' method in the interface:
Instead of implementing the interface directly in a specific event handler, each event handler extends an abstract event handler that includes the validation method:
In the specific event handler, you additionally define which rules the contents of the payload must follow:
The 'validate' method can, as shown here, be used directly inside the handler method or wherever incoming webhook requests are processed — for example, like this:
This way, requests whose payloads could not be validated can already be filtered out before the webhook request is stored - as intended in the webhook profile of Spatie’s Laravel Webhook Client package.
The combination of PHP attributes, interfaces, and discovery via reflection offers an elegant alternative to traditional if-/match constructs. Especially in dynamic systems such as webhook integrations, plugins, or event handling, this pattern is:
Senior PHP Developer
Phillip Kalusek
Phillip’s open and communicative nature, combined with his PHP expertise, make him a real asset to our team. He contributes his deep Laravel knowledge not only to client projects but also by sharing it with other developers through training sessions.
Become part of our Laravel community
and discover our Meetups.
To the Meetup