Skip to main content
Aspect-Oriented Programming (AOP) lets you attach behaviour to method calls without modifying the method itself. In Winter Boot, AOP is expressed entirely through PHP 8 attributes. When the container detects an AOP attribute on a managed bean’s method, it wraps that method with an interceptor chain. The original method body only runs if every interceptor in the chain allows it — making it trivial to enforce security checks, manage transactions, or add caching with a single attribute. All AOP contracts live in the dev\winterframework\stereotype\aop\ namespace.

Built-in AOP attributes

Winter Boot ships several ready-to-use AOP attributes. Enable the corresponding module in your #[WinterBootApplication] class before using any of them.
Wraps the annotated method in a database transaction. The transaction commits when the method returns normally, or rolls back if an exception is thrown.
OrderService.php
Requires #[EnableTransactionManagement] on your application class. See the Transactions guide for full configuration.
Caches the method’s return value. Subsequent calls with the same arguments return the cached result without executing the method body.
ProductCatalog.php
Requires #[EnableCaching] on your application class. See the Caching guide for full configuration.
Acquires a distributed lock before the method body executes and releases it afterwards, preventing concurrent duplicate execution.
InventoryService.php
See the Locking guide for configuration details.
Schedules the method to run asynchronously in a worker process. The caller returns immediately without waiting for the result.
EmailService.php
Requires #[EnableAsync] on your application class. See the Async Tasks guide for full configuration.
Marks a method to be called on a fixed schedule. Supports fixed delay, fixed rate, and initial delay in milliseconds.
ReportGenerator.php
Requires #[EnableScheduling] on your application class. See the Scheduling guide for full configuration.
Instruments the method with distributed tracing spans, recording start time, duration, and any exceptions for observability tooling.
CheckoutService.php
See the Telemetry guide for full configuration.

Custom AOP attributes

You can create your own AOP attributes using two components: an attribute class that implements AopStereoType, and an interceptor class that implements WinterAspect. Once both classes live in a scanned namespace, apply the attribute to any managed bean method.
1

Create the attribute class

Declare a PHP attribute class, annotate it with #[StereoTyped], and implement the AopStereoType interface. The attribute class must implement two methods:
  • isPerInstance(): bool — return true if a new interceptor instance should be created per bean instance, or false for a shared stateless interceptor.
  • getAspect(): WinterAspect — return the interceptor object (lazily constructed).
TimedLog.php
When isPerInstance() returns false, the single interceptor instance is shared across all beans that use this attribute. Return true only when the interceptor needs to hold per-bean state.
2

Create the interceptor class

Implement WinterAspect with all five lifecycle methods. AopContext carries method reflection and the application context. AopExecutionContext carries the target object, arguments, and execution control handles.
TimedLogInterceptor.php
3

Apply your custom attribute

Once both classes are in a scanned namespace, apply the attribute to any managed bean method:
OrderService.php
AOP attributes are validated at startup: they are not allowed on constructors, destructors, abstract, private, static, or final methods — a TypeError is raised otherwise.

AOP on controllers

In a #[RestController], put AOP attributes only on endpoint methods — the methods marked with #[GetMapping], #[PostMapping], or the other mapping attributes. Those methods run when someone calls their URL, and your advice runs along with them. Do not put AOP attributes on plain helper methods inside a controller. Helpers run as ordinary method calls, so your advice will simply never run for them — the attribute sits there doing nothing, with no error to tell you. If a helper needs guarding, move that logic into a #[Service] bean instead, where AOP works on every public method.

WinterAspect lifecycle reference

The five methods of WinterAspect form the interceptor pipeline around every method call. Each method fires at a distinct point in the execution flow.

AopExecutionContext — execution control

AopExecutionContext gives an interceptor full control over whether the method body runs and what value the caller receives.
AopExecutionContext usage
Use setVariable / getVariable to pass state between begin() and commit() or failed() — for example, storing a transaction ID in begin() and committing or rolling it back in commit() / failed().

AopContext — method reflection

AopContext carries the reflection metadata for the intercepted method call, giving you access to the method name, the attribute instance, and the full application context.
AopContext usage

Next steps

See the AOP guard example for a complete runnable app: a custom #[RequireCustomHeader] attribute protecting a REST endpoint with a 403 deny path.