artisanpack-ui/code-style-pint
Laravel Pint preset that enforces ArtisanPack UI coding standards, matching the artisanpack-ui/code-style PHPCS rules. Publish a ready-made pint.json via artisan command, generate configs for packages with PintConfigBuilder, or opt into WordPress-style spacing with PHP-CS-Fixer.
Install the preset as a dev dependency and publish the config in one breath:
composer require artisanpack-ui/code-style-pint --dev
php artisan artisanpack:publish-pint-config # or: vendor:publish --tag=artisanpack-pint-config
Then verify the setup by running Pint in test mode — it should report zero changes on a fresh Laravel skeleton:
./vendor/bin/pint --test
Where to look first:
pint.json at your project root — this is the single source of truth for the entire preset. Read it before customizing anything.docs/customization.md — covers the PintConfigBuilder fluent API, rule-group toggles, and manual JSON overrides.docs/rules-mapping.md — maps PHPCS rules to their Pint equivalents; essential when you need parity with artisanpack-ui/code-style.First use case: Laravel packages (not applications) don't have php artisan, so the fastest path is copying the stub directly:
cp vendor/artisanpack-ui/code-style-pint/stubs/pint.json.stub pint.json
./vendor/bin/pint
If you need programmatic control, generate it once with a throwaway script:
<?php
require __DIR__ . '/vendor/autoload.php';
use ArtisanPackUI\CodeStylePint\Config\PintConfigBuilder;
PintConfigBuilder::create()
->withArtisanPackUIPreset()
->exclude(['vendor', 'tests/fixtures', 'workbench'])
->save(__DIR__ . '/pint.json');
Composer scripts for a repeatable workflow. Wire the commands once so every developer and CI job runs them identically:
{
"scripts": {
"pint": "./vendor/bin/pint",
"pint:test": "./vendor/bin/pint --test"
}
}
CI enforcement via --test. Pint's --test flag exits non-zero when files would change, which is exactly what you want in a GitHub Actions or GitLab CI job. Never run plain pint in CI — it silently rewrites files and masks failures.
Pint + PHPCS as a two-stage pipeline. Pint cannot enforce security checks, naming conventions, or line length. Run Pint first for auto-fixable formatting, then PHPCS for the rest:
./vendor/bin/pint && ./vendor/bin/phpcs --standard=ArtisanPackUIStandard .
Selective rule groups when the full preset is too strict. The preset ships three groups — formatting, code structure, and best practices. On legacy or third-party-heavy codebases, disabling best practices (Yoda style, declare_strict_types, void_return) while keeping formatting is a common compromise:
PintConfigBuilder::create()
->withFormattingRules(true)
->withCodeStructureRules(true)
->withBestPracticeRules(false)
->withArtisanPackUIPreset()
->save(base_path('pint.json'));
Targeted rule surgery on top of the preset. The builder composes cleanly, so apply the preset first and then layer edits:
PintConfigBuilder::create()
->withArtisanPackUIPreset()
->addRule('concat_space', ['spacing' => 'one'])
->removeRule('yoda_style')
->exclude('app/Legacy')
->toJson();
Accessing rule groups directly. When you need rules in a context that isn't Pint (custom tooling, a lint reporter, a static analyser bridge), instantiate the preset without the builder:
$preset = new ArtisanPackUIPreset();
$formatting = $preset->getFormattingRules();
$all = $preset->getAllRules();
$excludes = $preset->getDefaultExcludes();
WordPress-style spacing requires a detour. Spaces-inside-parens is not expressible in Pint. Publish the PHP-CS-Fixer stub instead (php artisan artisanpack:publish-pint-config --wordpress) and swap tools entirely:
composer require --dev friendsofphp/php-cs-fixer
./vendor/bin/php-cs-fixer fix --dry-run --diff # test
./vendor/bin/php-cs-fixer fix # apply
Do not run both Pint and PHP-CS-Fixer against the same files — they will fight over brace position and spacing.
AI-assisted work. If you run Laravel Boost, publish the guideline override so assistants stop suggesting Laravel's default Pint rules:
php artisan vendor:publish --tag=artisanpack-boost-override
License mismatch. The repository metadata lists MIT, but the README's license section states GPL-3.0-or-later. For a dev-only tool this rarely matters, but confirm before vendoring into closed-source distributions or a commercial dependency audit.
--force is required to republish. artisanpack:publish-pint-config will not overwrite an existing pint.json without --force. This is a sensible default but a frequent source of "why didn't my config update" confusion — check for a stale pint.json before assuming the package is broken.
Package vs application paths. PintConfigBuilder::save() needs an explicit path. Use base_path() inside a Laravel application, __DIR__ inside a package. Passing a relative path produces confusing silent failures depending on your working directory.
Pint can't enforce everything. This is repeated in the README for a reason — reach for artisanpack-ui/code-style (PHPCS) the moment you need naming conventions, line-length limits, or security sniffing. Treating the Pint preset as a complete replacement for the standard will leave gaps.
declare_strict_types and yoda_style are opinionated. Both are on by default and both surprise developers encountering them for the first time. On a brownfield codebase these will generate enormous diffs. Either disable them via removeRule() for the initial adoption, or scope them to newly touched files.
Ordered imports and class elements rewrite a lot. ordered_imports, ordered_class_elements, and single_trait_insert_per_statement reorder code, which can produce merge conflicts with long-lived feature branches. Land a formatting-only PR first, then rebase — otherwise reviewers can't see the real changes through the noise.
Exclusions need absolute-ish relative paths. The exclude() method takes paths relative to where Pint runs. 'tests/fixtures' is correct; './tests/fixtures' or an absolute path is not. Use getDefaultExcludes() as a reference for the format Pint expects.
--test in pre-commit hooks, pint in editors. The two execution contexts want different behavior: editors should auto-fix on save, hooks should fail. PhpStorm external tool and the VS Code Laravel Pint extension both accept a configPath — point it at your pint.json so the preset is picked up rather than Pint's defaults.
The stub is the escape hatch. When the builder feels like overkill (one-off packages, CI images, Dockerfiles), cp vendor/artisanpack-ui/code-style-pint/stubs/pint.json.stub pint.json gives you the identical rule set with no PHP execution. This is the most underrated path in the docs — use it whenever you want reproducibility without a build step.
How can I help you explore Laravel packages today?