Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Git Hooks Laravel Package

bruli/php-git-hooks

bruli/php-git-hooks lets PHP projects configure and manage Git hooks in PHP. Define pre-commit, pre-push, and other hooks to automatically run linters, tests, and custom checks before commits or pushes, helping catch issues early.

Deep Wiki
Context7

Getting Started

bruli/php-git-hooks is a dev-only Composer package that installs Git hook scripts into .git/hooks and runs PHP quality tasks (lint, phpcs, phpunit, etc.) defined in a YAML file. Day-to-day, it exists to stop broken code from being committed — treat it as a pre-commit lint gate, not a full CI replacement.

Minimum setup:

composer require --dev bruli/php-git-hooks
vendor/bin/githooks --help        # see available commands/tasks for your installed version

Then create githooks.yml in the repository root and install the hooks:

vendor/bin/githooks install       # writes .git/hooks/pre-commit, commit-msg, ...
ls -l .git/hooks/                 # verify script exists and is executable

Where to look first, in order:

  1. githooks.yml — the only file that matters for configuration; keep it in version control.
  2. .git/hooks/pre-commit — the generated shell script; read it to understand exactly what runs and from which working directory.
  3. vendor/bin/githooks — the CLI entry point; --help/list reveals the commands and task names available in the 2018 release you pulled.
  4. composer.json → scripts — your integration point for install and CI.

First use case — block syntax errors before they reach the remote:

# githooks.yml (shape is illustrative; confirm task keys with `vendor/bin/githooks --help`)
pre-commit:
  - task: phplint

Commit a deliberately broken file on a scratch branch; if the commit is rejected, the wiring works. That is your smoke test before touching anything clever.

Implementation Patterns

Split checks by hook stage. The biggest quality-of-life decision is not putting everything in pre-commit. A workable split:

  • pre-commit — php -l on changed files, phpcs on changed files (target: under ~3 seconds).
  • commit-msg — regex check for ticket ID / Conventional Commits format.
  • pre-push — unit tests or a static-analysis pass where a 30-second delay is tolerable.

Make Composer scripts the single source of truth. Rather than encoding tool flags in YAML, define commands once and call them from both the hook and CI:

"scripts": {
  "lint": "phpcs --standard=PSR12 src",
  "test": "phpunit",
  "hooks:install": "githooks install",
  "post-install-cmd": ["@hooks:install"],
  "post-update-cmd": ["@hooks:install"]
}

Then the hook is a thin wrapper around composer lint. This keeps local and CI behavior identical and means replacing the hook tool later only touches one file.

Bridge to containers explicitly. Git hooks execute on the host. If PHP lives in Docker, php -l in the hook will run against host PHP (or fail entirely). Configure the task command to shell into the container:

pre-commit:
  - task: custom
    options:
      command: "docker compose exec -T php composer lint"

Filter to staged files. Most hook tools, this one included, tend to run against a whole directory. That gives false confidence with partially staged files (you lint the working tree, but commit the index). Either accept whole-tree linting as "cheap enough", or use a custom command based on git diff --cached --name-only --diff-filter=ACM.

Rollout to the team. Hooks are not cloned, so every developer needs an install step. Wire it into post-install-cmd (as above) and mention it in the README next to git commit --no-verify. The hook is a courtesy, not a security control — CI must run the same checks.

Keep the config small and boring. A dozen task types exist, but most real projects only need lint + one style checker + tests on push. Large hook configs drift out of sync with CI and get bypassed.

Gotchas and Tips

  • The package is from 2018 and unmaintained. It pulls era-appropriate Symfony/YAML versions. Expect friction on PHP 8.x. Check before committing to it: composer why-not php "^8.0". On modern projects, CaptainHook or GrumPHP are actively maintained equivalents; use this one only for legacy repos or if it is already proven in your stack.
  • vendor/bin/githooks may be missing in deploys where --no-dev was used, or if the binary is not on PATH. Guard any install script so a hook failure never breaks composer install.
  • .git/hooks is untracked. Fresh clones have no hooks until someone runs the install command. The post-install-cmd wiring above is not optional.
  • core.hooksPath conflicts. A global core.hooksPath (Husky, corporate hook setups) will shadow anything written into .git/hooks. Verify with git config --get core.hooksPath.
  • Executable bit and line endings. On Windows, CRLF in the generated hook script or a lost +x produces confusing "hook ignored" behavior. Run chmod +x .git/hooks/* and ensure core.autocrlf isn't rewriting hook scripts.
  • PHP binary mismatch. The hook uses whatever php resolves to in the developer's shell, which is often not the PHP version running your app or CI. Pin it where it matters.
  • Exit codes decide everything. A task that prints errors but exits 0 lets the commit through. For custom commands, use command && exit 0 || exit 1 semantics (or rely on the shell's last exit status) and test deliberately.
  • Debug by running the hook by hand, not by guessing: bash -x .git/hooks/pre-commit. You will see the exact command, working directory, and exit code. Hooks run from the top level of the working tree, so relative paths in config resolve predictably.
  • YAML quoting. Commands containing : or starting with a */& need quoting; use > folded scalars for multi-line shell snippets. A YAML parse error typically surfaces as an opaque "could not load config" message.
  • Bypass is one flag away. git commit --no-verify skips all hooks. Assume it happens and keep CI authoritative.
  • Performance is adoption. If pre-commit takes more than a few seconds, people will bypass it within a week. Move tests to pre-push.
  • Extension points: prefer a custom-style task that shells out to a Composer script over writing a PHP task class. If you must write one, implement the package's task interface and register it in githooks.yml — but pin the version, since the interface is frozen at 2018 API shape and a future fork could break it.
  • CI parity tip: since the hooks are just shell invocations of your Composer scripts, do not attempt to run the hook tool in CI. Run composer lint && composer test directly and let the hook be purely local ergonomics.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource