Skip to content

AOP Sample Application

Build an app that guards a REST endpoint with a custom AOP attribute. You annotate the endpoint method with #[RequireCustomHeader] and the framework runs your interceptor before the method body: requests carrying X-Custom-Foo-Bar: foo-bar go through, everything else is denied with 403 without the method executing. This example applies the concepts from the main AOP documentation — attributes, interceptors, and the advice lifecycle — to a complete runnable app.

You need PHP 8.5 or later with the swoole and pcntl extensions. No external services are needed.

The sample uses this layout:

aop-guard/
├── bin/
│ └── application.php # Application entry point
├── config/
│ └── application.yml # Server and app identity
├── src/
│ ├── AopSampleApplication.php # Main application class
│ ├── aop/
│ │ ├── RequireCustomHeader.php # The AOP attribute
│ │ └── RequireCustomHeaderInterceptor.php # The advice
│ └── rest/
│ └── AopDemoController.php # Guarded endpoint
└── composer.json # Dependencies

Require the framework package:

Terminal window
composer require suvera/winter-boot

Switch between the source files. Each tab shows the exact file from the sample.

The single entry point. No extra #[Enable*] attribute is needed — AOP support is always on.

<?php
namespace dev\example;
use dev\winterframework\stereotype\WinterBootApplication;
#[WinterBootApplication(
configDirectory: [__DIR__ . "/../config"],
scanNamespaces: [
['dev\\example', __DIR__ . '']
]
)]
class AopSampleApplication {
public static function main(): void {
$winterApp = new \dev\winterframework\core\app\WinterWebSwooleApplication();
$winterApp->run(self::class);
}
}

AOP needs no module config. The full sample application.yml sets the server and app identity:

server:
port: 8080
address: 0.0.0.0
context-path: /
winter:
application:
name: AOP Guard Sample Application
id: aop-guard-sample-app
version: 1.0.0

See Configuration for every application.yml key.

Start the application, then call the endpoint with and without the header.

1. Start the application:

Terminal window
composer install
php bin/application.php

2. Call without the header — expect 403:

Terminal window
curl -i http://127.0.0.1:8080/aop-demo/secure-greeting
HTTP/1.1 403 Forbidden
{"success": false, "data": null, "error": "Forbidden"}

The interceptor stopped execution, so the endpoint body never ran.

3. Call with a wrong value — expect 403:

Terminal window
curl -i -H "X-Custom-Foo-Bar: wrong" http://127.0.0.1:8080/aop-demo/secure-greeting

4. Call with the right value — expect 200:

Terminal window
curl -i -H "X-Custom-Foo-Bar: foo-bar" http://127.0.0.1:8080/aop-demo/secure-greeting
HTTP/1.1 200 OK
{"success": true, "data": "Hello from the AOP-guarded endpoint!"}
  • Read AOP for the advice lifecycle (begin/commit/failed), stopExecution, and execution variables.
  • Put reusable advice on #[Service] beans the same way — bean-to-bean calls run through proxies, so the same attribute works there unchanged.