Allow multiple developers to inject behaviors into specific lifecycle points without modifying core business logic.
When multiple developers need to add behaviors to a service:
- Traditional approach: Everyone modifies the same class, causing conflicts and complexity
- LifeCycle Hooks: Core logic stays untouched, behaviors are injected via hooks
use PhpDiffused\Lifecycle\Attributes\LifeCyclePoint;
use PhpDiffused\Lifecycle\Traits\HasLifecycle;
#[LifeCyclePoint('before_payment', ['user_id', 'amount'])]
#[LifeCyclePoint('after_payment', ['user_id', 'amount', 'payment_id'])]
class PaymentService
{
use HasLifecycle;
public function process(int $userId, float $amount): string
{
runHook($this, 'before_payment', $userId, $amount);
$paymentId = $this->doPayment($userId, $amount);
runHook($this, 'after_payment', $userId, $amount, $paymentId);
return $paymentId;
}
}use PhpDiffused\Lifecycle\Attributes\Hook;
use PhpDiffused\Lifecycle\Attributes\Severity;
#[Hook(scope: 'PaymentService', point: 'before_payment', severity: Severity::Critical)]
class FraudDetectionHook
{
use Hookable;
public function handle(array &$args): void
{
if ($this->isFraudulent($args)) {
throw new FraudException('Suspicious activity detected');
}
}
}composer require php-diffused/lifecyclephp artisan vendor:publish --provider="PhpDiffused\Lifecycle\LifeCycleServiceProvider"The configuration and kernel files are published automatically during installation.
- Critical: Must succeed or the entire operation fails
- Optional: Failures are logged but don't stop execution
All hooks are registered in app/Hooks/Kernel.php:
public array $hooks = [
\App\Services\PaymentService::class => [
'before_payment' => [
\App\Hooks\ValidateAmountHook::class, // Runs first
\App\Hooks\FraudDetectionHook::class, // Runs second
\App\Hooks\ApplyDiscountHook::class, // Runs third
],
],
];# Generate a service with lifecycle points
php artisan lifecycle:main App/Services/PaymentService
# Generate a hook
php artisan lifecycle:hook FraudDetectionHook --scope=payment --point=before_payment --severity=Critical
# Analyze hooks and detect conflicts
php artisan lifecycle:analyze App/Services/PaymentServiceAnalyzing lifecycle for: App\Services\PaymentService
Lifecycle Points (3):
• before_payment: [user_id, amount]
• after_payment: [user_id, amount, payment_id]
Hook Analysis:
• before_payment: 2 hooks (1 critical, 1 optional)
- FraudDetectionHook [critical]
- ApplyDiscountHook [optional]
Potential Issues:
⚠️ Multiple hooks modify 'amount' in before_payment
Order matters! Check execution sequence in Kernel.
Hooks can modify values passed by reference:
#[Hook(scope: 'PaymentService', point: 'before_payment')]
class ApplyDiscountHook
{
public function handle(array &$args): void
{
$args['amount'] *= 0.9; // 10% discount
}
}// Add hooks at runtime
addHook(PaymentService::class, new CustomHook());
// Remove hooks for specific lifecycle
removeHooksFor(PaymentService::class, 'payment_failed');Execute hooks from anywhere:
// In a controller
public function processPayment(Request $request)
{
$amount = $request->input('amount');
// Execute hooks without instantiating the service
runHook(PaymentService::class, 'before_payment', auth()->id(), $amount);
$service = app(PaymentService::class);
return $service->process(auth()->id(), $amount);
}Use LifeCycle Hooks for:
- Complex business processes (checkout, onboarding, provisioning)
- Multi-team systems requiring isolated customizations
- Features that need to be toggled without code changes
Don't use for:
- Simple CRUD operations
- Microservices with single responsibilities
- Performance-critical paths
// config/lifecycle.php
return [
'error_handling' => [
'log_failures' => true,
'throw_on_critical' => true,
],
'debug' => env('LIFECYCLE_DEBUG', false),
];composer test
composer test:coverageMIT License - see LICENSE for details.