typo3/coding-standards
TYPO3's official coding standards, packaged plug-and-play for any Composer project. Bring TYPO3 Core's PHP-CS-Fixer rules and .editorconfig to your extension or project with one setup command, so every editor and teammate follows the same conventions.
This is a TYPO3 package, but it is really just a thin distribution layer for two things any Composer project can consume: a curated PHP-CS-Fixer rule set (PSR-12 + TYPO3 Core additions) and a .editorconfig. Nothing in it is TYPO3-runtime-specific, so it drops into a Laravel app cleanly — you are buying the rule set, not an integration.
Minimal path for a Laravel project:
composer require --dev typo3/coding-standards
composer exec typo3-coding-standards setup extension # force "extension" for a Laravel app
composer exec php-cs-fixer -- --dry-run --diff -v
Why extension and not project: the project preset hardcodes the Finder path to src/, which does not exist in a standard Laravel skeleton. It will happily run, scan zero files, and exit 0 — the classic silent failure. The extension preset scans from the base directory, which is closer to what you want before you prune paths.
Where to look first:
.php-cs-fixer.dist.php generated in your root — this is the package. Read the getRules() / rule array; that is the entire value proposition.vendor/typo3/coding-standards/ — the Resources/ templates and the setup command source. Everything is boilerplate copying; there is no magic, so a 10-minute read tells you the whole story..editorconfig — if you have one from frontend tooling (Prettier, ESLint, Vite), diff it before letting the setup command write.First real use case: generate a report on an existing codebase without changing a byte.
composer exec php-cs-fixer -- fix --dry-run --diff --format=txt > cs-report.txt
That gives you a prioritised map of what the TYPO3 rules disagree with your Laravel code on — realistically concat_space, no_leading_namespace_slash, unused imports, and trailing whitespace.
Prerequisite check: the package requires PHP 8.1+ and PHP-CS-Fixer 3.x. Apps still on PHP 7.4 (Laravel 8 era) are ruled out.
Never run fix across a Laravel app in one commit — the diff will be enormous and unreviewable. The workable sequence:
# 1. Freeze the noise out
# edit .php-cs-fixer.dist.php Finder to exclude vendor, storage, bootstrap/cache
# 2. Fix one directory at a time, in dedicated commits
composer exec php-cs-fixer -- fix app/Models
composer exec php-cs-fixer -- fix app/Http/Controllers
Keep --diff in the first pass and read the output; it teaches you what the rules actually do faster than any docs page.
The generated config uses PhpCsFixer\Finder. Adjust it once, commit it:
$finder = (new PhpCsFixer\Finder())
->in(__DIR__)
->exclude(['bootstrap/cache', 'storage', 'vendor', 'node_modules', 'public'])
->notPath('_ide_helper.php');
The generated file returns a PhpCsFixer\Config. Don't edit it destructively — overlay:
$config = require __DIR__ . '/.php-cs-fixer.dist.php';
$config->setRules(array_merge($config->getRules(), [
'declare_strict_types' => false,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
]));
$config->setCacheFile(__DIR__ . '/.php-cs-fixer.cache');
return $config;
If you keep the TYPO3 config untouched and want your own, place your .php-cs-fixer.php next to it — PHP-CS-Fixer prefers the non-.dist file, so you get TYPO3 defaults in VCS and personal overrides ignored.
Laravel teams live in Composer scripts. Mirror the conventions Laravel devs already know from Pint:
"scripts": {
"cs": "php-cs-fixer fix",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:report": "php-cs-fixer fix --dry-run --format=txt"
}
Then composer cs:check in CI, composer cs before pushing. This is the single highest-leverage change: rules that aren't one command away don't get followed.
- run: composer install --no-interaction --prefer-dist
- run: composer cs:check
For pre-commit, pair with captainhook (Laravel-idiomatic) or lefthook and call the same script — never duplicate the flags. Note the fixer is fast on changed files only if you use the cache; add .php-cs-fixer.cache to .gitignore but keep it in CI caches between runs.
Pint is a PHP-CS-Fixer wrapper with its own pint.json preset (laravel). Running both creates a rules tug-of-war. Pick one:
typo3/coding-standards + PHP-CS-Fixer directly. You get one lock file, one rule source, no ambiguity.pint.json by hand. You cannot require a Config object from pint.json (JSON only), so this is manual and will drift.Do not commit both a pint.json and a .php-cs-fixer.dist.php that disagree — contributors will run the one in their muscle memory and produce conflicting diffs.
For a packages/ monorepo, generate config per package rather than one root config with a wide Finder:
composer exec -- t3-cs s -d=packages/domain -r=php-cs-fixer
composer exec -- t3-cs s -r=editorconfig # editorconfig only
When the config lives outside the root, invoke with --config=packages/domain/.php-cs-fixer.dist.php.
composer update typo3/coding-standards changes the PHP rules immediately — there is no copy step, the rule set is consumed from vendor/. Plan for this: a composer update in an unrelated PR can turn CI red. Pin the version or run the update as its own PR with a cs commit alongside.
The .editorconfig is not consumed from vendor; it's a copied template. Propagate with:
composer exec t3-cs u # update .editorconfig to TYPO3 defaults (overwrites!)
The src/ trap. setup project writes a Finder pointing at src/. Laravel has no src/. The command succeeds, the fixer runs, zero files are checked, and your CI is green forever. Always sanity-check with composer exec php-cs-fixer -- fix --dry-run -v and confirm it lists PHP files.
--force and composer exec option passing. composer exec -- typo3-coding-standards setup --force — the -- is mandatory when passing options through Composer, and it is easy to omit. Without it, Composer swallows the flag and you get a no-op that looks like it worked.
.editorconfig is overwritten without mercy. t3-cs u and setup --force reset it to TYPO3 defaults. If your team has [*.blade.php] or [*.vue] blocks, they're gone. Commit before running, then git diff and re-merge your blocks. Consider generating to a temp target (--target-dir=Build) and merging manually instead.
concat_space is the big Laravel clash. TYPO3 requires at least one space around .; Laravel/Pint's default emits 'a'.$b.'c'. This alone can touch hundreds of lines in a typical app. Decide once, document it in CONTRIBUTING.md, and don't let reviewers bikeshed it per-PR.
Yoda conditions are deliberately disabled (since 0.5.3), diverging from TYPO3 Core. If you also contribute to Core, keep two configs; don't assume this package's output will be Core-clean.
License headers are not enabled. The README explains why (namespace ordering conflict). If you want SPDX headers, add header_comment yourself in your overlay config — don't wait for the package.
vendor/bin/typo3-coding-standards exists if you prefer not to go through Composer exec. Prefer composer exec — it resolves the bin dir correctly and is what the docs assume.
Debugging a single surprise change. Isolate the fixer before blaming the package:
composer exec php-cs-fixer -- fix path/to/File.php --rules=concat_space --diff -vvv
-vvv tells you which fixer touched which line. Also useful: --using-cache=no when a stale .php-cs-fixer.cache masks a newly-excluded path.
PHP version guard. PHP-CS-Fixer refuses to run under a PHP version outside its supported range. If you're testing on an odd runtime, PHP_CS_FIXER_IGNORE_ENV=1 bypasses the check — handy in a pinch, never in CI.
Blade, JSON, and YAML are untouched. This is a PHP-only tool. resources/views Blade templates get no formatting. Frontend formatting stays with Prettier/ESLint, which is fine — but don't expect the TYPO3 .editorconfig to carry Vue/SCSS sections; it's PHP/JSON/YAML/Markdown-oriented.
Maturity signal. 67 stars, zero dependents, releases synced manually from TYPO3 Core on an irregular cadence ("not meant to be updated regularly"). Treat it as a curated rules snapshot, not a maintained framework. If you're not touching TYPO3, the honest ROI calculation is: you're adopting it for the .editorconfig boilerplate and a reasonable PSR-12-plus rule set that you could equally assemble in 30 lines of your own .php-cs-fixer.php. Adopt it if your team already knows TYPO3 conventions or you want a defensible off-the-shelf baseline; otherwise a hand-rolled config gives you the same outcome with fewer surprise updates.
How can I help you explore Laravel packages today?