Integration bridge for the Go! AOP framework and Laravel. It boots the AOP engine early in the application lifecycle so aspects are woven into your classes transparently — no code generation steps, no manual proxies.
Some logic doesn't belong to any single class, yet ends up copied into hundreds of them: logging, caching, metrics, transactions, authorization checks, rate limiting, retry policies. Laravel's middleware solves this for HTTP requests — AOP solves it for any method in your codebase. Define the behavior once as an aspect, declare where it applies with a pointcut expression, and the engine weaves it in automatically. Your services stay pure business logic.
- 🪡 Transparent weaving — aspects are applied by transforming classes at load time. No base classes to extend, no interfaces to implement, no
Proxywrappers to maintain, no changes to how younewor inject your services. - 🏷️ Modern PHP 8 attributes — advices are declared with
#[Before],#[After],#[Around]and#[AfterThrowing]right on your aspect methods, with a powerful regexp-like pointcut syntax (execution(public App\Services\**->*(*))). - 🧩 Laravel-native integration — package auto-discovery, publishable config, aspects resolved through the container (constructor DI works), registration via a simple config list or the
goaop.aspectservice tag,php artisan aboutsupport. - 🚀 Production-friendly — pre-generate all proxies at deploy time with
php artisan aop:warmup; withdebugoff the woven code is served straight from cache and plays nicely with opcache. - 🔬 Pure PHP, no magic runtime — built on Go! AOP 4.x: no PECL extensions, no
eval(), all transformations produce reviewable static PHP files. - ✅ Proven by tests — the bridge ships with end-to-end weaving tests (advice execution,
Aroundreturn rewriting, cache generation) running against Laravel 12 and 13 on PHP 8.4.
- PHP >= 8.4
- Laravel 12 or 13
- goaop/framework 4.x (attribute-based aspects)
composer require goaop/goaop-laravel-bridgeThe service provider is registered automatically via package discovery.
Note Until goaop/framework 4.0 is tagged, the bridge depends on the unreleased
4.0-devline, so your application'scomposer.jsonneeds:"minimum-stability": "dev", "prefer-stable": true
Publish the configuration if you want to tweak it:
php artisan vendor:publish --tag=goaop-configAspects are plain classes implementing Go\Aop\Aspect whose advice methods are declared with PHP 8 attributes (#[Before], #[After], #[Around], #[AfterThrowing]):
<?php
namespace App\Aspects;
use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Attribute\Around;
use Go\Lang\Attribute\Before;
use Illuminate\Support\Facades\Log;
class LoggingAspect implements Aspect
{
#[Before('execution(public App\Services\**->*(*))')]
public function logMethodCall(MethodInvocation $invocation): void
{
Log::debug('Calling ' . $invocation->getMethod()->getName());
}
#[Around('execution(public App\Services\PaymentService->charge(*))')]
public function measureCharge(MethodInvocation $invocation): mixed
{
$start = hrtime(true);
try {
return $invocation->proceed();
} finally {
Log::info('charge() took ' . (hrtime(true) - $start) / 1e6 . 'ms');
}
}
}See the Go! AOP documentation on core concepts and creating aspects for the full pointcut expression syntax.
You never register aspects with the AOP engine manually. During the application's boot phase — after all service providers have registered, before your application code runs — the bridge collects aspects from two sources and hands each of them to the engine's aspect container:
-
The
go_aop.aspectsconfig list. Every class listed here is resolved through the Laravel service container, so constructor dependency injection works out of the box:// config/go_aop.php 'aspects' => [ App\Aspects\LoggingAspect::class, ],
-
The
goaop.aspectcontainer tag. Any service tagged withgoaop.aspectin one of your service providers is picked up automatically — useful when an aspect needs non-trivial construction logic, or when a package wants to contribute aspects without touching your config:// app/Providers/AppServiceProvider.php — in register() $this->app->singleton(LoggingAspect::class, function ($app) { return new LoggingAspect($app->make(LoggerInterface::class)); }); $this->app->tag([LoggingAspect::class], 'goaop.aspect');
Both sources can be combined; duplicates are registered only once. Every discovered class must implement Go\Aop\Aspect, otherwise the bridge fails fast with a descriptive exception. Once registered, the engine reads the #[Before]/#[After]/#[Around]/#[AfterThrowing] attributes from the aspect's methods and weaves the advices into every class matched by their pointcut expressions as it is loaded (or ahead of time via aop:warmup).
Key options in config/go_aop.php (all overridable via env):
| Option | Default | Purpose |
|---|---|---|
debug |
GOAOP_DEBUG → APP_DEBUG |
Re-weave when sources change; keep off in production |
appDir |
base_path() |
Root directory the weaver may touch |
cacheDir |
storage_path('framework/aop') (GOAOP_CACHE_DIR) |
Where woven sources and proxies are cached |
cacheFileMode |
0770 (GOAOP_CACHE_PERMISSIONS, octal digits, e.g. "770") |
chmod for cache files |
includePaths |
[app_path()] |
Only these directories are woven |
excludePaths |
[] |
Never weave these |
features |
0 (GOAOP_FEATURES) |
Bitmask of Go\Aop\Features engine features |
aspects |
[] |
Aspect classes to auto-register |
Weaving happens lazily on first load of each class. To pre-generate all proxies during deployment (recommended with debug => false):
php artisan aop:warmupphp artisan about shows the current AOP cache location and debug mode.
- Weaving starts at the beginning of the application's boot phase. Classes that are already loaded before that (very early bootstrap code, other packages'
register()internals) cannot be woven. - Only classes under
appDir+includePathsare considered — vendor code is not woven. - Exception handlers are best excluded via
excludePaths: when a fatal error occurs, a woven handler may not be loadable from a cold cache.
MIT — see LICENSE.