akeneo/php-coupling-detector
Detect PHP coupling issues based on configurable rules by analyzing class use statements. Supports forbidden, discouraged, and only rules, with error/warning violations. Includes commands to detect violations and list unused requirements.
Installation
composer require --dev akeneo/php-coupling-detector
Add the package to your project’s composer.json under require-dev.
Basic Configuration
Create a coupling_detector.php config file in your project root (or extend Laravel’s config directory):
return [
'rules' => [
'Laravel\\Framework\\Routing\\Router' => [
'allowed' => [
'Laravel\\Http\\Request',
'Illuminate\\Contracts\\Routing\\UrlGenerator',
],
'forbidden' => [
'App\\Services\\*', // Example: Avoid direct coupling to services
],
],
],
'paths' => [
app_path(),
app('path.base') . '/Providers',
],
];
First Run Execute the detector via Artisan (if integrated) or directly:
vendor/bin/php-coupling-detector
For Laravel, create a custom Artisan command (see Implementation Patterns).
Review Output The tool outputs violations in a structured format (e.g., JSON or CLI table). Example:
[ERROR] app/Http/Controllers/UserController.php:10
- Coupling violation: Uses 'App\Services\UserService' (forbidden)
- Rule: 'Laravel\Framework\Routing\Router' forbids 'App\Services\*'
Rule Definition
App\Services\* in controllers).$rules = config('coupling_detector.rules');
$detector->setRules($rules);
* for namespace matching (e.g., 'App\Repositories\*').Integration with Laravel
// app/Console/Commands/DetectCoupling.php
namespace App\Console\Commands;
use Akeneo\CouplingDetector\Detector;
class DetectCoupling extends Command {
protected $signature = 'coupling:detect';
public function handle() {
$detector = new Detector(config('coupling_detector'));
$violations = $detector->detect();
$this->line(json_encode($violations, JSON_PRETTY_PRINT));
}
}
Register the command in app/Console/Kernel.php:
protected $commands = [
Commands\DetectCoupling::class,
];
Run with:
php artisan coupling:detect
CI/CD Pipeline
# .github/workflows/coupling-check.yml
jobs:
coupling-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install
- run: vendor/bin/php-coupling-detector --fail-on-violations
Visualization
file_put_contents(
storage_path('app/coupling-violations.json'),
json_encode($violations)
);
App\Services\* in controllers) before diving into granular checks.paths but exclude them from rule enforcement:
'paths' => [
app_path(),
app_path('/Providers'),
],
'exclude' => [
app_path('/Tests'),
],
// In a service provider
Event::listen(Deployed::class, function () {
$detector = new Detector(config('coupling_detector'));
$violations = $detector->detect();
if (!empty($violations)) {
throw new \RuntimeException('Coupling violations detected!');
}
});
Performance
paths to critical directories or run incrementally:
vendor/bin/php-coupling-detector --paths=app/Http
$cacheFile = storage_path('coupling-cache.json');
if (file_exists($cacheFile)) {
$violations = json_decode(file_get_contents($cacheFile), true);
} else {
$violations = $detector->detect();
file_put_contents($cacheFile, json_encode($violations));
}
False Positives
class_alias).$service = new \App\Services\$className).allowed rules to whitelist edge cases:
'allowed' => [
'ReflectionClass',
],
Rule Conflicts
priority in rules to resolve conflicts (if supported):
'rules' => [
'Laravel\Framework\Routing\Router' => [
'forbidden' => ['App\Services\*'],
'priority' => 10, // Higher priority = stricter
],
],
Laravel-Specific Quirks
app()->make() or resolve() calls as violations. Explicitly allow the container:
'allowed' => [
'Illuminate\Container\Container',
],
Cache::get()) are technically coupled to Illuminate\Support\Facades\Cache. Decide whether to allow or forbid:
'forbidden' => [
'Illuminate\Support\Facades\*',
],
vendor/bin/php-coupling-detector --verbose
$ast = include app_path('Http/Controllers/UserController.php');
var_dump($ast);
// Test file: app/Http/Controllers/TestController.php
class TestController {
public function __construct(\App\Services\ForbiddenService $service) {}
}
Run the detector and confirm the violation is caught.Custom Detectors Extend the base detector to add logic (e.g., check for circular dependencies):
namespace App\Coupling;
use Akeneo\CouplingDetector\Detector;
class CircularDependencyDetector extends Detector {
public function detectCircularDependencies() {
// Custom logic
}
}
Plugin System Design a plugin architecture to support:
// app/Coupling/Plugins/JsonRuleLoader.php
namespace App\Coupling\Plugins;
use Akeneo\CouplingDetector\RuleLoaderInterface;
class JsonRuleLoader implements RuleLoaderInterface {
public function load(string $file): array {
return json_decode(file_get_contents($file), true);
}
}
Git Hooks
Automate checks on pre-commit or pre-push:
# .git/hooks/pre-commit
#!/bin/bash
vendor/bin/php-coupling-detector --fail-on-violations
IDE Integration
How can I help you explore Laravel packages today?