realodix/haiku
realodix/haiku is a minimalist Laravel/PHP package focused on simplicity and a clean, lightweight API. It is easy to drop into small projects and utilities, keeping setup quick and code readable without heavy dependencies.
Haiku is an opinionated PHP-CS-Fixer preset aimed at Laravel-style codebases — you install it, point a config file at it, and let it own your formatting rules instead of hand-maintaining dozens of fixer entries.
composer require --dev realodix/haiku
.php-cs-fixer.dist.php at the project root and return Haiku's config object. The README's current class is Realodix\Haiku\Config\FixerConfig (older examples use HaikuConfig) — copy the snippet from the README verbatim rather than from blog posts, since the config surface has changed across releases.
<?php
use Realodix\Haiku\Config\FixerConfig;
return (new FixerConfig())
->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__.'/app')
->in(__DIR__.'/tests')
);
vendor/bin/php-cs-fixer fix app/ --dry-run --diff
Read the diff, then run without --dry-run on that directory. Only expand to the full repo once you have committed the mass-reformat as its own commit (add the resulting SHA to .git-blame-ignore-revs).Where to look first: the README's preset list, then the config class itself (vendor/realodix/haiku/src/Config/) — that file is the authoritative list of which rules ship enabled, which is far more useful than any changelog.
Treat the preset as a base, override on top. The pattern that survives upgrades is: instantiate the config, then apply a small, deliberate set of project overrides. Anything you set explicitly wins; anything you don't set follows the package.
$config = (new FixerConfig())->setFinder($finder);
$config->setRules(array_merge($config->getRules(), [
'declare_strict_types' => false,
'yoda_style' => false,
]));
return $config;
Separate the "adopt" PR from the "enforce" PR. Run the fixer over the whole repo once, merge the reformat alone, then add the CI gate in the next PR. Reviewers cannot meaningfully review a 400-file whitespace diff, and mixing it with logic changes is how formatting tools get reverted.
CI gate that fails, not fixes. In CI run fix --dry-run --diff (exit code 8 means changes needed); locally run fix. Wire it through Composer so contributors and CI call the same entry point:
"scripts": {
"lint:style": "php-cs-fixer fix --dry-run --diff --ansi",
"fix:style": "php-cs-fixer fix --ansi"
}
Pin the version in composer.json. Style tools that auto-bump produce surprise reformat PRs when a Renovate bot runs. Use a caret constraint you control ("realodix/haiku": "~1.13.0") and bump deliberately — this release is a good example of why (see Gotchas).
Gate on the config, not only on the code. If your setup references Haiku's rule-name linting (the [lint] half of the changelog), run it as a separate CI step against .php-cs-fixer.dist.php. A typo'd or deprecated rule name is silently ignored by PHP-CS-Fixer, so a config check is the only thing that catches a rule you think is enforcing but isn't. v1.13.32 expanded exactly this surface — separator checks for rule names and a stricter exception-rule check.
Editor integration. Use the php-cs-fixer PhpStorm/VSCode extension pointed at the same .php-cs-fixer.dist.php, so save-on-format matches CI. Do not run Haiku and Laravel Pint over the same paths with different presets — they will fight and produce alternating diffs on the same lines.
Path-scoped rules for mixed codebases. Where legacy code can't be reformatted yet, exclude it in the Finder rather than disabling rules globally, so new code is held to the standard.
v1.13.32 removes the removeUnnecessaryWildcard fixer feature. This is the one change in this release with upgrade risk: if your config references that feature or option it will no longer be applied. PHP-CS-Fixer generally ignores unknown rules rather than erroring, so the failure mode is silent — your code stops being normalised and nobody notices for weeks. Grep your config for removeUnnecessaryWildcard, and if you depend on the behaviour either delete the reference (accepting the style change) or pin 1.13.31 while you decide. There is no replacement option documented in the release.
New lint rules can fail CI without a single code change. "Bad cosmetic rule separator" and "wrong domain separator" are new checks in v1.13.32. If you run Haiku's linting in CI, this minor bump can turn the build red on a config that was green yesterday. Land the dependency bump as its own commit so the failure is unambiguous, and fix rule names to their canonical form (@PhpCsFixer vs @PhpCsFixer:risky style separators, domain-prefixed rule names) rather than suppressing the check.
The improved exception-rule check may flag existing allowlists. Exception entries you wrote months ago that previously passed can now be reported. Treat the output as informational on the first run — some exceptions are legitimate and need rewording to match the new expectations, not deletion.
Silent no-ops are the main debugging trap. PHP-CS-Fixer will not warn about a rule name that doesn't exist. When a rule "isn't working", verify the exact name against vendor/realodix/haiku/src/Config/, and confirm your overrides are applied after the preset merge — overriding before the merge means the preset silently wins.
Keep .php-cs-fixer.cache out of git and don't share it between CI and local runs. Stale caches cause "passes locally, fails in CI" diffs, especially after a version bump; rm .php-cs-fixer.cache is the first debugging step.
Match PHP versions. Fixer output legitimately differs across PHP versions (new syntax targets, parsing). Run CI and local on the same minor version or you will chase phantom diffs.
Extension point: the config object is a normal PHP-CS-Fixer config underneath, so custom fixers, HeaderCommentFixer headers, and per-directory rule sets are all still available — add them via registerCustomFixers() / additional setRules() calls rather than forking the preset.
How can I help you explore Laravel packages today?