genealabs/php-coding-standards
A ready-made set of PHP coding standards from GeneaLabs for Laravel and general PHP projects. Drop-in configuration helps enforce consistent style, simplify linting, and keep your codebase clean, uniform, and easy to maintain across teams.
This is a PHP_CodeSniffer ruleset, not application code. Treat it as a dev-only artifact and validate it before making it a gate.
Install it with the engine explicitly pinned:
composer require --dev genealabs/php-coding-standards
composer require --dev squizlabs/php_codesniffer
Then inspect what the package actually ships before writing config:
ls -R vendor/genealabs/php-coding-standards | head -40
cat vendor/genealabs/php-coding-standards/composer.json
head -40 vendor/genealabs/php-coding-standards/phpcs.xml
Look first for:
phpcs.xml or ruleset.xml,name attribute inside that file,src/ or similar,require block, especially the Slevomat version.Check whether the standard is registered by name:
vendor/bin/phpcs -i
Then list the sniffs in the shipped ruleset. Adjust the filename to whatever the package actually contains:
vendor/bin/phpcs \
--standard=vendor/genealabs/php-coding-standards/phpcs.xml \
-e | less
First useful use case: run a report-only audit against one Laravel application directory, not the whole repository:
vendor/bin/phpcs \
--standard=vendor/genealabs/php-coding-standards/phpcs.xml \
--report=summary \
app
Once the noise level is understood, add a project ruleset at the repository root:
<?xml version="1.0"?>
<ruleset name="App">
<description>App standards, currently borrowing the GeneaLabs ruleset.</description>
<file>app</file>
<file>config</file>
<file>routes</file>
<file>tests</file>
<exclude-pattern>vendor/*</exclude-pattern>
<exclude-pattern>storage/*</exclude-pattern>
<exclude-pattern>bootstrap/cache/*</exclude-pattern>
<exclude-pattern>node_modules/*</exclude-pattern>
<arg name="extensions" value="php"/>
<arg name="parallel" value="4"/>
<!-- Adjust to the actual ruleset filename shipped by the package. -->
<rule ref="vendor/genealabs/php-coding-standards/phpcs.xml"/>
</ruleset>
Run it through the project config:
vendor/bin/phpcs --standard=phpcs.xml
Prefer referencing the ruleset by relative path. Do not rely on a machine-local installed_paths registration unless you have verified it works identically in CI and every editor.
Layer the ruleset, do not replace project config. The package should be one <rule ref="..."/> inside an internally owned phpcs.xml. That gives you one place to exclude paths, suppress specific sniff codes, raise or lower severity, and eventually swap the reference for a fork or internal standard.
Use Composer scripts as the stable developer interface.
{
"scripts": {
"lint": "phpcs --standard=phpcs.xml",
"lint:fix": "phpcbf --standard=phpcs.xml",
"lint:ci": "phpcs --standard=phpcs.xml --report=checkstyle --report-file=build/phpcs.xml"
}
}
Then day-to-day commands are composer lint, composer lint:fix, and composer lint:ci.
Start with changed files, not the whole repository. For pre-commit or local checks:
git diff --cached --name-only --diff-filter=ACM -z -- '*.php' \
| xargs -0 -r vendor/bin/phpcs --standard=phpcs.xml -s
The -s flag shows sniff codes. Those codes are what you use for precise phpcs:ignore annotations and project-level exclusions.
Use a report-only CI stage before blocking. First publish a checkstyle report without failing the build:
- run: composer install --no-interaction --prefer-dist
- run: vendor/bin/phpcs --standard=phpcs.xml --report=checkstyle --report-file=build/phpcs.xml
Then publish build/phpcs.xml as a CI artifact. Once false positives are triaged, make the same command blocking. If your PHP_CodeSniffer version supports baselines, generate one so CI fails only on new violations. If it does not, use changed-files checks or a maintained ignore list.
Fix incrementally with phpcbf. Preview first:
vendor/bin/phpcs --standard=phpcs.xml --report=diff app
Then run auto-fix on one directory or module, review the diff, and run tests:
vendor/bin/phpcbf --standard=phpcs.xml app
git diff
Do not run phpcbf across the whole legacy codebase in one commit. It creates a large, hard-to-review diff and makes it harder to separate style changes from behavior changes.
Keep editors and CI aligned. In PhpStorm, point the Code Sniffer executable at vendor/bin/phpcs and the standard at the project phpcs.xml. In VS Code, set the PHP Sniffer extension to the same phpcs.xml and vendor/bin/phpcs. The common failure mode is “passes in my editor, fails in CI” because the editor is using a different PHPCS version, a different ruleset path, or a globally registered standard.
Exclude generated and non-source paths deliberately. At minimum exclude vendor, storage, bootstrap/cache, public, and node_modules. Consider excluding database/migrations temporarily if the ruleset creates a large legacy backlog there; add it back after a focused cleanup.
Suppress by sniff code, not by disabling the whole standard. When a rule conflicts with Laravel conventions, exclude the specific sniff code:
<rule ref="vendor/genealabs/php-coding-standards/phpcs.xml">
<exclude name="SlevomatCodingStandard.Files.TypeNameMatchesFileName"/>
</rule>
Use the exact code from phpcs -s. For one-off exceptions, prefer // phpcs:ignore Sniff.Code -- reason or // phpcs:disable Sniff.Code / // phpcs:enable Sniff.Code over broad // phpcs:ignore comments.
Choose one formatting owner. If the project already uses Laravel Pint or PHP-CS-Fixer, do not let two tools rewrite the same lines. Either make Pint the formatter and use PHPCS only for non-conflicting sniffs, or keep PHPCS as the single style gate. Running both as blocking formatters causes oscillating diffs and developer frustration.
Assume the package is unmaintained. The last release was 2021-11-18, it has 16 stars, and 0 dependents. The 0.2.0 release only bumped Slevomat to v7. That is not a signal to make it the platform standard. Pin it exactly, fork it if you keep it, and assign an internal owner. The durable artifact is the ruleset configuration, not the package lifecycle.
Slevomat 7-era tooling is the compatibility risk. A 2021-vintage ruleset may pull older PHP_CodeSniffer and Slevomat constraints that conflict with modern PHP/Laravel projects. Test Composer resolution in a disposable repository before adopting. Pin squizlabs/php_codesniffer and inspect composer why slevomat/coding-standard if resolution becomes messy. If Composer fights you, stop and vendor the rules instead of forcing an old dependency graph.
The installed_paths trap is real. PHPCS can register standards by name, but that registration can be machine-local or written into vendor config. CI and editors may resolve differently. Prefer --standard=vendor/genealabs/php-coding-standards/phpcs.xml or a relative <rule ref="..."/> in the project ruleset. If you must use a registered name, verify phpcs -i in CI and document the setup.
Relative ruleset references can still fail if the path is wrong. Confirm the actual filename the package ships. If PHPCS cannot resolve the relative <rule ref="..."/>, fix the path; do not silently switch to a standard name that only works on one developer’s machine.
The first full-repo run will be noisy. Start with --report=summary on app/ or one module. Use --report=source to see which sniffs dominate. Fix or suppress the high-volume rules first; do not create a blocking gate while the baseline is unknown.
Auto-fix has blast radius. phpcbf is safe for most style changes, but it can rewrite many files and occasionally conflict with custom formatting or generated code. Run it on a clean branch, review the diff, and keep it out of pre-commit until the team trusts it.
Be precise with ignore annotations. A bare // phpcs:ignore hides everything on that line, including future meaningful violations. Use phpcs -s to get the sniff code and annotate only that code.
Warnings and errors can behave differently in CI. Check the exit codes your PHPCS version produces for warnings versus errors. If warnings should not fail the build, configure that explicitly rather than discovering it after the first CI failure.
Do not put this in require. It is a development tool. Keep it in require-dev so production deployments with --no-dev do not carry it.
A green CI run can create false confidence. If the ruleset is stale, a passing build may no longer reflect current conventions. Treat it as a starting reference, diff it against your current Pint/PHPCS/PHP-CS-Fixer baseline, and internalize the rules you actually want. Verify the license before forking or redistributing the ruleset.
How can I help you explore Laravel packages today?