phpcq/plugin-api
phpcq/plugin-api provides the plugin interfaces used by the phpcq tool runner, enabling PHP code quality checks to be integrated and automated in CI pipelines. It defines the contracts plugins implement to extend phpcq’s analysis and reporting.
Install Dependencies
Require both the API and the core runner in composer.json:
composer require phpcq/plugin-api phpcq/phpcq
First Use Case: Custom Rule Create a Laravel Artisan command to execute a basic phpcq check:
php artisan make:command RunPhpcqCheck
Implement the command to run phpcq with a custom plugin:
// app/Console/Commands/RunPhpcqCheck.php
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
public function handle()
{
$process = new Process(['phpcq', 'run', '--plugin=MyCustomPlugin']);
$process->run();
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
$this->info($process->getOutput());
}
Where to Look First
src/PluginInterface.php, src/RuleInterface.php, src/ConfigInterface.phpProcess facade for CLI execution or embed phpcq logic directly..phpcq.php in your project root for default settings.Basic Plugin Structure
Create a plugin class implementing PluginInterface:
// app/Plugins/CustomLaravelRule.php
namespace App\Plugins;
use Phpcq\PluginApi\PluginInterface;
use Phpcq\PluginApi\ResultInterface;
use Phpcq\PluginApi\RuleInterface;
class CustomLaravelRule implements PluginInterface
{
public function getRules(): array
{
return [new LaravelMagicMethodRule()];
}
public function run(): ResultInterface
{
// Initialize and execute rules
return new Result();
}
}
Rule Implementation
Implement RuleInterface for specific checks:
// app/Rules/LaravelMagicMethodRule.php
use Phpcq\PluginApi\RuleInterface;
use Phpcq\PluginApi\Violation;
class LaravelMagicMethodRule implements RuleInterface
{
public function check(string $filePath): array
{
$violations = [];
$content = file_get_contents($filePath);
if (preg_match('/__get|__set|__call/', $content)) {
$violations[] = new Violation(
'Magic methods detected',
$filePath,
1
);
}
return $violations;
}
}
Configuration Integration Merge Laravel config with phpcq settings:
// config/phpcq.php
return [
'plugins' => [
'custom_laravel_rule' => [
'enabled' => env('PHPcq_CUSTOM_RULE_ENABLED', true),
'severity' => 'high',
],
],
];
CI/CD Integration Add a GitHub Actions workflow:
# .github/workflows/phpcq.yml
name: PHP Code Quality
on: [push, pull_request]
jobs:
phpcq:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: composer install
- run: php artisan phpcq:check
Pre-Commit Hooks Use Laravel Pint or a custom script to run phpcq before commits:
# .git/hooks/pre-commit
#!/bin/bash
php artisan phpcq:check || exit 1
Dynamic Plugin Loading Register plugins dynamically in a service provider:
// app/Providers/PhpcqServiceProvider.php
use Phpcq\PluginApi\PluginManager;
public function register()
{
$this->app->singleton(PluginManager::class, function ($app) {
$manager = new PluginManager();
$manager->addPlugin(new CustomLaravelRule());
return $manager;
});
}
Artisan Command Wrapper Extend phpcq functionality with Laravel commands:
// app/Console/Commands/PhpcqFix.php
public function handle()
{
$process = new Process(['phpcq', 'fix', '--plugin=MyCustomPlugin']);
$process->run();
$this->info('Fixed issues: ' . $process->getOutput());
}
Event-Driven Execution Trigger phpcq checks on file changes:
// app/Providers/EventServiceProvider.php
protected $listen = [
'Illuminate\Filesystem\Events\FileUpdated' => [
'App\Listeners\RunPhpcqOnUpdate',
],
];
Notification Integration Convert phpcq violations to Laravel notifications:
// app/Listeners/NotifyPhpcqViolations.php
public function handle($event)
{
if ($event->violations->count()) {
Notification::send(
auth()->user(),
new PhpcqViolationNotification($event->violations)
);
}
}
Configuration Conflicts
.phpcq.php may override each other unpredictably.mergeConfigFrom to prioritize settings:
$this->mergeConfigFrom(__DIR__.'/phpcq.php', 'phpcq');
Plugin Isolation
class CustomLaravelRule implements PluginInterface
{
public function getId(): string
{
return 'app::laravel.magic_methods';
}
}
Performance Overhead
// config/phpcq.php
'run_in_ci_only' => env('APP_ENV') !== 'local',
Dependency Hell
phpcq/plugin-api may lag behind phpcq/phpcq.composer.json:
"require-dev": {
"phpcq/plugin-api": "1.0.0",
"phpcq/phpcq": "2.0.0"
}
Log Output Redirect phpcq logs to Laravel’s storage:
$process = new Process(['phpcq', 'run'], null, [
'phpcq.log' => ['file', '/tmp/phpcq.log', 'a'],
]);
Mocking Plugins Use Laravel’s mocking for testing:
$this->mock(PluginManager::class, function ($mock) {
$mock->shouldReceive('getPlugin')
->with('custom_rule')
->andReturn(new MockPlugin());
});
Violation Tracing Track violations back to source files:
$violations = $result->getViolations();
foreach ($violations as $violation) {
$this->error("File {$violation->getFile()}, Line {$violation->getLine()}: {$violation->getMessage()}");
}
Leverage Laravel’s DI Bind phpcq interfaces to Laravel’s container for easier testing:
$this->app->bind(RuleInterface::class, function ($app) {
return new LaravelMagicMethodRule();
});
Custom Result Formatters Convert phpcq results to Laravel-friendly formats (e.g., JSON for APIs):
$result = $plugin->run();
return response()->json($result->toArray());
Plugin Scaffolding Create a Laravel command to scaffold new plugins:
php artisan make:phpcq-plugin CustomRule --rule="CheckForDeprecatedMethods"
Environment-Specific Rules Dynamically enable/disable rules based on environment:
// In your PluginInterface implementation
public function isEnabled(): bool
{
return app()->environment('local') || config('phpcq.debug_mode');
}
Cache Results Cache phpcq results to avoid redundant scans:
$cacheKey = 'phpcq.results.' . md5_file($filePath);
$result = Cache::remember($cacheKey, now()->addHours(1), function () use ($filePath) {
return $this->check($filePath);
});
Extend with Events Dispatch Laravel events for phpcq violations:
event(new
How can I help you explore Laravel packages today?