Aspect-oriented programming sounds more academic than it needs to.
The version that matters for practical PHP work is not complicated: AOP is a way to add behavior around method calls without hard-wiring that behavior into every class. That is the whole idea. The reason I became interested in it was a specific recurring problem — I wanted to add behavior before or after a method, I did not want to rewrite the class to include it, and I did not want to create the brittle inheritance that comes from subclassing just to override one method. aspect-me is my attempt to give that problem a cleaner answer in PHP.
Cross-cutting concerns and why they keep coming up
The textbook motivation for AOP is cross-cutting concerns: behaviors like logging, caching, authorization, and validation that appear in many places but are not the core business logic of any of those places. If you implement those behaviors manually in every class, the code gets repetitive and the concerns get tangled with the logic they are supposed to be wrapping.
The concrete version is something like: a service method that performs an expensive database query, where the result should be cached and the execution should be logged, and neither caching nor logging should require the method to know about the cache layer or the logging infrastructure. In an ideal world, those concerns live around the method call, not inside it. The method does the business thing it is supposed to do. Something else handles the plumbing.
In languages with strong meta-programming support or built-in proxying, this is easier to implement cleanly. PHP is not that language. The natural extension mechanisms are subclassing, decorating, and composition — all useful, but none of which always fit the shape of “I want to intercept this method call without redesigning the dependency path around it.”
What Magento’s plugin system taught me
Magento is an underappreciated starting point for thinking about method interception in PHP, because its plugin system essentially implements AOP without calling it that.
Magento plugins let you hook before, after, or around method execution on most classes in the framework. A before plugin receives the arguments and can modify them. An after plugin receives the return value and can modify it. An around plugin wraps the entire call and can change both sides. That mechanism is how most behavioral customizations in Magento are done without modifying the original class.
This showed me that method interception is a viable, practical extension mechanism in PHP applications, not just a theoretical concept. It also showed me the failure mode: Magento’s plugin system is deeply coupled to Magento’s own DI container and code generation pipeline. You cannot apply the same idea outside the Magento context without bringing the whole framework with you.
That coupling was the gap I wanted to close. The general idea of before/after/around interception is useful enough to deserve a smaller, framework-independent form.
What aspect-me provides
aspect-me provides controlled method interception through a proxy layer. Three modes: behavior before method execution, behavior after, and behavior that wraps the entire call.
Before interception receives the method arguments and can observe or modify them before the underlying method runs. After interception receives the return value and can observe or modify it. Around interception wraps the call and controls when or whether the underlying method executes at all — useful for caching, where you may not want to call the method at all if a cached result exists.
Those three modes cover a practical range of use cases. Logging around an expensive operation. Authorization before a sensitive method. Caching around a method whose output can be safely reused. Validation before a method that should not run with invalid inputs. All of these are patterns where the cross-cutting behavior should live around the method rather than inside it, and where method-by-method manual wrapping or inheritance creates either repetition or coupling that interception avoids.
The mechanism underneath is proxy generation — an object that sits in front of the real class and mediates the method call. I like that this is visible and understandable rather than magical. Once you see the proxy pattern, AOP feels like a disciplined application of familiar design ideas rather than a black box. The interception layer exists as a real object; the indirection is explicit. That makes it debuggable in a way that truly opaque meta-programming is not.
When to reach for it and when not to
The constraint I think about most when considering interception is whether the team can maintain it safely.
AOP is powerful precisely because it hides indirection. A method that appears to do X is actually doing X plus whatever is attached to it, and that relationship is not visible at the call site. That is an acceptable tradeoff when the behavior is genuinely cross-cutting, when it belongs conceptually around the logic rather than inside it, and when everyone who works in the codebase understands the interception model well enough to reason about it.
It is not an acceptable tradeoff when the interception becomes a clever workaround for bad design, when the behavior being attached is actually core business logic rather than infrastructure concern, or when the team does not have enough shared understanding to know where to look when something is behaving unexpectedly. A codebase full of invisible before/after hooks that modify behavior in ways that are not surfaced at the call site is a debugging nightmare.
Cases where I would reach for aspect-me: instrumenting an existing service with logging or metrics without modifying it, applying authorization policy around an entry point without embedding it in the implementation, caching an expensive computation result in a way that the computation method does not need to know about.
Cases where I would not: anything where the behavior being added is business logic, anything where it would be simpler to just compose the behavior directly, anything where the added indirection layer would make someone’s debugging session significantly harder without a clear payoff.
Why this project connects to the broader work
aspect-me came from the same thread as the layout-core and mods-framework work: how to extend framework and application behavior without creating upgrade-brittle inheritance or copy-paste fragmentation.
Method interception is one piece of that answer. It gives PHP applications a mechanism that Magento has used effectively for years but that does not otherwise exist in a general form. Whether that earns its way into production codebases depends on the team and the specific problem. But having the capability available — a well-tested, understandable proxy-based interception model that does not require a full framework — is the thing I wanted to build.
Some problems fit this pattern exactly, and when they do, having a clean implementation available is much better than either writing it from scratch or reaching for inheritance as the default.