arnaud-23/attribute-execution-bundle
Installation: Add the bundle via Composer:
composer require arnaud-23/attribute-execution-bundle
Ensure symfony/security-bundle is installed if using the Security attribute.
First Use Case: Apply attributes to a service method to enable middleware behavior:
use Arnaud23\AttributeExecutionBundle\Attribute\Cache;
class UserService {
#[Cache] // Applies caching with default strategy (array) and TTL (300s)
public function getUserData(int $id): array {
return ['id' => $id, 'name' => 'John Doe'];
}
}
Verify Setup: Call the method and observe the behavior (e.g., cached responses).
Security, Cache, Transactional) in src/Attribute/.config/packages/attribute_execution.yaml for cache/strategy overrides.src/Middleware/ for built-in logic (e.g., SecurityMiddleware, CacheMiddleware).Attribute Placement:
@Transactional on a UserRepository class).@Cache(ttl: 60) on a specific method).Middleware Chaining: Combine attributes for layered behavior:
#[Security('ROLE_ADMIN')]
#[Cache(strategy: 'redis', ttl: 3600)]
public function sensitiveData(): array { ... }
Execution order: Security → Cache → Method logic.
Custom Strategies:
Extend the CacheStrategyInterface to add new cache backends (e.g., MemcachedStrategy).
Dependency Injection: Services using attributes are auto-configured. No manual proxy setup is needed.
Caching Workflow:
Security Workflow:
AccessDeniedException if unauthorized.Transactional Workflow:
Symfony Controllers: Use attributes on controller actions to enforce security/caching:
#[Security('ROLE_USER')]
#[Cache(ttl: 1800)]
public function dashboard(): Response { ... }
Doctrine Repositories:
Apply @Transactional to repository methods for atomic operations:
#[Transactional('default')]
public function transferFunds(User $from, User $to, float $amount): void { ... }
Event Listeners:
Decorate listeners with @Cache to avoid redundant processing:
#[Cache(strategy: 'redis', ttl: 300)]
public function onUserCreated(UserCreatedEvent $event): void { ... }
Testing:
Mock middleware in tests using the bundle’s AttributeExecutionContext:
$context = new AttributeExecutionContext($service);
$context->execute([new CacheAttribute()]);
Attribute Conflicts:
Cache Invalidation:
Cache::forget()) for critical data.Security Shortcuts:
@Security on public APIs. Validate input early to fail fast.Transaction Boundaries:
@Transactional selectively.Configuration Overrides:
attribute_execution.yaml must match attribute names (case-sensitive).Middleware Logs: Enable debug mode to log middleware execution:
attribute_execution:
debug: true
Attribute Introspection: Use reflection to inspect applied attributes:
$reflection = new ReflectionMethod(UserService::class, 'getUserData');
$attributes = $reflection->getAttributes(Cache::class);
Cache Debugging: Check cache hits/misses with:
$cache = $this->container->get('attribute_execution.cache.array');
$cache->getStats(); // Returns hit/miss counts.
Performance Tuning:
redis strategy for high-throughput caching (lower latency than array).Custom Middleware:
Extend AbstractMiddleware to create reusable logic:
class LogExecutionMiddleware extends AbstractMiddleware {
public function process(ExecutionContext $context): mixed {
Log::info('Executing ' . $context->getMethod());
return $context->next($context);
}
}
Register it in config/packages/attribute_execution.yaml:
attribute_execution:
middleware:
log: App\Middleware\LogExecutionMiddleware
Attribute Composition: Combine attributes dynamically via dependency injection:
public function __construct(
private readonly CacheAttributeFactory $cacheFactory,
private readonly SecurityAttributeFactory $securityFactory
) {}
public function buildAttributes(): array {
return [
$this->securityFactory->create('ROLE_ADMIN'),
$this->cacheFactory->create('redis', 3600),
];
}
Symfony Messenger:
Use @Cache on message handlers to avoid duplicate processing:
#[Cache(ttl: 900)]
#[Asynchronous]
public function __invoke(ProcessPaymentMessage $message): void { ... }
Attribute Validation: Validate attributes at runtime using Symfony’s validator:
use Symfony\Component\Validator\Constraints as Assert;
#[Security(roles: ['ROLE_ADMIN'])]
#[Assert\Expression(
"value === 'admin'",
message: "Invalid role specified."
)]
public function secureMethod(): void { ... }
Testing Attributes:
Mock the AttributeExecutionContext in unit tests:
$context = $this->createMock(ExecutionContext::class);
$context->method('next')->willReturn('mocked_result');
$this->service->executeWithContext($context);
Attribute Inheritance: Use traits to share attribute configurations across services:
trait CachedTrait {
#[Cache(strategy: 'redis', ttl: 3600)]
public function cachedMethod(): void { ... }
}
Fallback Logic: Handle middleware failures gracefully:
#[Security('ROLE_ADMIN', fallback: true)] // Returns null instead of throwing
public function adminOnly(): ?array { ... }
How can I help you explore Laravel packages today?