phpro/grumphp
GrumPHP is a Composer plugin that installs Git hooks to run quality checks (tests, linters, code style) on staged changes before you commit. If tasks fail, the commit is blocked—helping teams enforce standards and improve code quality automatically.
Installation:
composer require --dev phpro/grumphp
This auto-configures Git hooks. Verify with:
composer show grumphp
First Run:
composer grumphp
Or trigger via Git pre-commit hook by staging changes and running:
git commit -m "Test commit"
Default Tasks:
GrumPHP includes built-in tasks (e.g., phpunit, psalm, phpcs). Check grumphp.yml for active tasks.
Add PSR-12 validation to grumphp.yml:
grumphp:
tasks:
phpcs:
standard: "PSR12"
triggered_by: ["php"]
ignore_patterns: ["vendor/"]
Now, commits violating PSR-12 will fail.
CI/CD Pipeline:
Use grumphp run in CI (e.g., GitHub Actions) to block merges:
# .github/workflows/grumphp.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install --no-dev
- run: composer grumphp
Parallel Execution:
Enable parallel tasks in grumphp.yml:
grumphp:
parallel:
enabled: true
max_workers: 8
Faster feedback for large codebases.
Task Grouping:
Group related tasks (e.g., testing + linting) using testsuites:
grumphp:
testsuites:
test:
tasks: [phpunit, phpmd]
lint:
tasks: [phpcs, phpcbf]
Run with:
composer grumphp --testsuite=test
Artisan Task Integration:
Run GrumPHP via Artisan (add to composer.json scripts):
{
"scripts": {
"grumphp": "grumphp run"
}
}
Trigger with:
php artisan grumphp
Custom Tasks:
Create a Laravel-specific task (e.g., laravel-migrations):
// app/Console/Commands/GrumLaravelMigrations.php
use GrumPHP\Task\TaskInterface;
use GrumPHP\Runner\TaskResult;
class GrumLaravelMigrations implements TaskInterface {
public function getConfig(): array { return []; }
public function run(): TaskResult {
$result = new TaskResult();
if (!Artisan::call('migrate:status')) {
$result->setOk(false);
$result->addViolation('Migrations not up-to-date!');
}
return $result;
}
}
Register in grumphp.yml:
grumphp:
tasks:
app.tasks.laravel-migrations:
class: App\Console\Commands\GrumLaravelMigrations
Environment-Specific Hooks:
Use git_hook_variables for Laravel Forge/Envoyer:
grumphp:
git_hook_variables:
EXEC_GRUMPHP_COMMAND: "php /home/forge/app.php"
Hook Overwrites:
cp .git/hooks/pre-commit .git/hooks/pre-commit.bak
composer install --no-scripts
Parallel Execution Quirks:
parallel.enabled: false for such tasks.Environment Variables:
environment.variables must be quoted (even for numbers):
environment:
variables:
APP_ENV: "testing"
DEBUG: "1"
Task Dependencies:
phpunit) require specific setup. Ensure dependencies are installed:
composer require --dev phpro/grumphp phpunit/phpunit
Dry Run: Test without committing:
composer grumphp --no-git-hook
Verbose Output: Enable debug mode:
composer grumphp --verbose
Task Isolation: Run a single task:
composer grumphp --task=phpcs
Git Hook Debugging:
GIT_TRACE=1 git commit -m "Test"
git config core.hooksPath .git/hooks/
Custom Config File:
Use a .grumphp.yml in project root for team-specific rules:
grumphp:
tasks:
phpcs:
ignore_patterns: ["tests/"]
Fixer Mode:
Auto-fix issues with fixer.enabled: true and fix_by_default: true:
grumphp:
fixer:
enabled: true
fix_by_default: true
Run with:
composer grumphp --fix
Event Listeners: Extend GrumPHP with events (e.g., Slack notifications on failure):
// app/Listeners/GrumSlackListener.php
use GrumPHP\Event\RunnerFailedEvent;
class GrumSlackListener {
public function onRunnerFailed(RunnerFailedEvent $event) {
$client = new SlackClient();
$client->sendMessage("GrumPHP failed: " . $event->getMessage());
}
}
Register in grumphp.yml:
services:
App\Listeners\GrumSlackListener:
tags:
- { name: grumphp.event_listener, event: grumphp.runner.failed }
CI-Specific Config: Override settings for CI (e.g., disable parallelism):
# .grumphp.ci.yml
grumphp:
parallel:
enabled: false
Load in CI:
composer grumphp --config=.grumphp.ci.yml
Task Prioritization:
Control task order with priority (lower = earlier):
grumphp:
tasks:
phpcs:
priority: 100
phpunit:
priority: 200
Local vs. Remote:
Use hooks_preset: vagrant for remote environments:
grumphp:
hooks_preset: vagrant
git_hook_variables:
VAGRANT_PROJECT_DIR: "/var/www/project"
How can I help you explore Laravel packages today?