AOP
Aspect oriented programming (AOP) allows you to intercept method calls on objects managed by the container, without modifying the target class.
This implementation uses proxy-based AOP, the same model as Spring Framework's default AOP support.
Rather than modifying class bytecode at compile time (as AspectJ weaving does), the container generates
a decorator class that wraps the target object and delegates to registered aspects at each join point.
This means only objects resolved through the container are subject to interception — direct new
instantiation bypasses it entirely.
Concepts
Aspect — a class that contains the cross-cutting logic to be applied to a target.
Pointcut — the rule that defines which class (or class hierarchy) an aspect applies to.
Advice — a method on the aspect that runs at a specific point relative to the target method call
(Before, After, Around, AfterThrow).
Join point — a JoinPoint object passed to every advice method, giving access to the intercepted
subject, the method name, and the call arguments.
Registering an aspect
Register an aspect class through the DI configurator using the aspect method on the container.
The aspect class itself is resolved through the container, so constructor dependencies are injected normally.
To remove a previously registered aspect by its id:
Defining an aspect class
An aspect class must carry the #[Aspect] attribute and declare one or more advice methods.
- A unique string identifier for this aspect. Used when removing the aspect via
removeAspect. - The pointcut specifies which class this aspect applies to. See Pointcuts below.
- Optional priority. When multiple aspects target the same method, higher priority aspects run first.
- The advice attribute targets the method named
doSomethingon the intercepted class.
Pointcuts
A pointcut is constructed with an AspectEnum strategy and a class name.
| Strategy | Constant | Behavior |
|---|---|---|
| Exact class | AspectEnum::POINTCUT_CLASS |
Applies only to that specific class. |
| Class hierarchy | AspectEnum::POINTCUT_SUBCLASS |
Applies to the class and all its subclasses. |
use Amarant\Framework\Attribute\Aop\PointCut;
use Amarant\Framework\Enum\AspectEnum;
use Vendor\ModuleName\Service\SomeService;
// exact match
new PointCut(AspectEnum::POINTCUT_CLASS, SomeService::class)
// SomeService and any class that extends it
new PointCut(AspectEnum::POINTCUT_SUBCLASS, SomeService::class)
Advice types
Before
Runs before the target method. Receives a MutableArgumentJoinPoint, allowing the advice to modify
the arguments that will be passed to the original method.
Modifications to $joinPoint args are applied before the original method is called.
After
Runs after the target method returns. Receives the immutable JoinPoint and the return value.
Must return the (potentially modified) return value.
Around
Wraps the target method entirely. Receives JoinPoint and a Closure that calls the original method.
Must return a value. If the closure is not called, the original method is skipped and the chain stops.
- Returning without calling
$proceed()skips the original method entirely. - Call
$proceed()to execute the original method and receive its return value.
AfterThrow
Runs when the target method throws an exception. Receives JoinPoint and the Throwable.
The exception is always re-thrown after the advice runs.
The target argument defaults to '*', which matches any method on the intercepted class.
- No argument (defaults to
'*') — intercepts exceptions from any method. - Explicit method name — intercepts exceptions only from
processPayment.
The JoinPoint
Every advice method receives a JoinPoint as its first parameter.
| Method | Description |
|---|---|
getSubject(): object |
The intercepted object instance. |
getMethod(): string |
The method name being intercepted. |
getArgs(): array |
Named argument array (['argName' => $value, ...]). |
getArg(string $key): mixed |
Get a single argument by parameter name. |
MutableArgumentJoinPoint (used exclusively in Before advice) extends JoinPoint with:
| Method | Description |
|---|---|
setArg(string $key, mixed $value): void |
Set a single argument. |
setArgs(array $args): void |
Replace all arguments. |
removeArg(string $key): void |
Remove an argument. |
Execution order
For a single method call, the advice types run in this order:
Before— arguments may be mutatedAround(if present, replaces the original call; otherwise the original method runs directly)After— return value may be replacedAfterThrow— only if an exception was thrown (after steps 1–3)
When multiple aspects target the same method, aspects with higher priority run first in each phase.