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 Styler Laravel Package

pmjones/php-styler

PHP-Styler is a PHP code formatter that tokenizes source files and rebuilds them with consistent spacing, indentation, and automatic line splitting. Configurable Styles, Rules, and Parses customize the diff-friendly output, while logic and comments are preserved. No dependencies beyond PHP 8.1.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Confirm your PHP version before anything else. The README says "no dependencies beyond PHP 8.1," but the 0.17.0 changelog states the minimum is now PHP 8.4 because the rewrite dropped nikic/php-parser in favour of the native PhpToken lexer. Since the current release line is 0.17+, assume 8.4 and verify against the tag you actually install:

php -v
composer require --dev pmjones/php-styler 0.x@dev

2. Initialize the config, then look at it before you look at the docs.

./vendor/bin/php-styler init

This writes a php-styler.php at your project root that returns a PhpStyler\Config. That file is the single source of truth for what gets formatted and how — read it first, because every command except explicit-path apply/preview is driven entirely by it.

<?php
use PhpStyler\Config;
use PhpStyler\Files;
use PhpStyler\Format\DeclarationFormat;

return new Config(
    files: new Files(__DIR__ . '/src'),
    format: new DeclarationFormat(lineLen: 84, indentLen: 4, eol: "\n"),
);

3. Your first use case is always preview, never apply. The tool does not adjust your formatting; it replaces it with its own matrix. Run it on one file you know well before trusting it on a directory:

./vendor/bin/php-styler preview ./src/My/Source/File.php

4. Then look at the examples repo. tests/Examples/ in the package is described as "nearly-exhaustive" and is the fastest way to understand what each Format actually produces. Reading PlainFormat and DeclarationFormat source after that tells you which knobs exist. That is a much shorter path than experimenting blind on your own codebase.

5. Pick a format before you pick settings. Three tiers exist:

Format Use when
PlainFormat You want minimal, non-structural reformatting and a blank slate
DeclarationFormat You want opinionated class-file defaults (next-line braces, sorted imports, ordered modifiers/types, trailing commas, brace insertion)
Percs30Format / SymfonyFormat / DoctrineFormat You already follow one of these standards and want least-surprise output

For an existing codebase, a vendor format is almost always the least disruptive first step — it will fight your team's muscle memory the least.

6. Land it as one isolated commit, then hide it from blame.

./vendor/bin/php-styler diff          # review the whole change set first
./vendor/bin/php-styler apply
git add -A && git commit -m "Apply php-styler"
# put that commit hash in .git-blame-ignore-revs, then:
git config blame.ignoreRevsFile .git-blame-ignore-revs

GitHub's blame UI respects that file too, which matters if your team reviews history through the web.

7. Wire up check as a CI gate immediately after. Exit code 0 means clean, 1 means drift:

./vendor/bin/php-styler check --workers=auto

Implementation Patterns

The adoption workflow that actually works

Do not flip apply on across src/ on day one of a busy sprint. The sequence that minimises pain:

  1. preview on 3–5 representative files (one big controller, one value object, one test).
  2. diff across the whole configured path to size the blast radius.
  3. Tune the Format until the diff is boring — brace position, lineLen, keywordCase, concatenationSpacing, returnTypeColonSpacing, blankLineAfterBlock are the six knobs that account for most of the disagreement.
  4. apply in a commit that touches nothing else.
  5. check in CI.

apply also takes explicit paths, which is how you stage this incrementally without editing the config each time:

./vendor/bin/php-styler apply ./src/Domain ./src/Support

Scope the file list deliberately

The Files class recurses directories and picks up every .php file — it has no exclude logic. That means vendor/, generated proxies, Blade-compiled stubs, and test fixtures all get reformatted if they live under a configured path. Two mitigations:

  • Pass Files a narrow list of directories rather than __DIR__ . '/src' when src/ contains generated subtrees.
  • Swap in any iterable of paths — the README explicitly points at Symfony Finder for exclusion rules:
$finder = (new Finder())
    ->files()->in(__DIR__.'/src')->name('*.php')
    ->notPath('Generated')->notPath('Resources');

return new Config(files: $finder, format: new DeclarationFormat());

The corollary is the sneaky one: a file that isn't in the file list is never checked, so check can be green while half your code is unformatted. Any config change needs its own review.

Use one format instance, subclassed, as your house standard

styles, rules, and parseAs are plain arrays you pass into the format constructor, so the idiomatic pattern is a small project-local class rather than a hundred-line config closure:

namespace App\Tooling;

use PhpStyler\Format\Vendor\Percs30Format;

final class HouseFormat extends Percs30Format
{
    public function __construct()
    {
        parent::__construct(
            lineLen: 100,
            styles: [ /* token-level overrides */ ],
            rules:  [ /* add or drop structural rules */ ],
        );
    }
}

This keeps php-styler.php to three lines and gives new team members a single named thing to grep for when the formatter does something surprising.

Two configs for monorepos

-c / --config exists specifically so you can hold a different config per package — packages/api/php-styler.php with Percs30Format, packages/legacy/php-styler.php with PlainFormat(lineLen: 120). Then:

./vendor/bin/php-styler check -c packages/api/php-styler.php
./vendor/bin/php-styler check -c packages/legacy/php-styler.php

Do not try to express this as one config with conditional logic inside the file; the config is a returned object, not a script with arguments.

Parallelism is a repo-size switch, not a default

-w / --workers applies to apply, check, and diff. It shells out via proc_open (works on Linux and Windows, no extension needed) and splits the file list into N chunks. Two things worth knowing operationally:

  • --workers=auto detects core count; use it in CI for anything over a few hundred files.
  • Below 8 files the tool takes the sequential path regardless of the flag, so don't bother in pre-commit hooks that pass a handful of staged files.

Pre-commit hooks are the natural fit for the explicit-paths form:

./vendor/bin/php-styler apply $(git diff --cached --name-only --diff-filter=ACM -- '*.php')

Combine with review flow, not with another formatter

php-styler diff produces a unified diff of current vs. styled output, which is what you want in a "formatting-only PR" so reviewers can skim structurally. The one integration anti-pattern: running PHP-CS-Fixer or PHPCBF in the same pipeline. Both rewrite whitespace, and whichever runs second wins non-deterministically. Pick php-styler as the sole whitespace authority and let other tools handle only what it can't (see Gotchas).

The escalation hatch: @php-styler-expansive

Automatic splitting only kicks in when a line exceeds lineLen. When you want a specific array or argument list expanded regardless, annotate it — this is the supported way to express intent that the splitter can't infer:

/** @php-styler-expansive */
$config = ['a' => 1, 'b' => 2];

/** ... */, // ..., and multi-line docblock forms are all recognised. Note the annotation survives reformatting because comment tokens are preserved in place, so this is stable across repeated apply runs.

Self-hosting as a smoke test

The package formats itself: composer cs-check runs its own formatter, composer cs-preview shows output without writing. If you're building a custom Format or rule, running it against the package's own src/ (or your largest internal package) is a cheap way to surface pathological cases — deeply nested match arms, attributes, enums with interfaces — before they hit your team's code.


Gotchas and Tips

The tool is destructive by design, and that's not a bug

The README leads with the WARNING for a reason. There is no "preserve existing formatting where it's already fine" mode. Consequences you will hit:

  • Horizontal alignment is removed. Columns of = and . get collapsed to single spaces. If your team maintains aligned assignment blocks, this alone can be a dealbreaker — decide up front.
  • Multiple blank lines collapse to one. If you use double blank lines to separate sections within a method, that signal is gone. Comments are your only remaining separator.
  • Comment text is never reformatted or wrapped. The tool preserves block comments in place and keeps end-of-line comments on their original lines, but it will not split a 200-character comment line, and it will not reflow a docblock.

lineLen is a target, not a guarantee

After all seven split priorities are exhausted (attributes → arrow functions → commas → loose operators → tight operators → fluent calls → for semicolons), a line can still exceed the limit — a long string literal, a heredoc, or a single unbreakable call. Don't "fix" this by lowering lineLen; you'll just get over-split everything else. Accept the outliers or refactor the string into a constant.

Verify the PHP version claim against your installed tag

The README's introduction (PHP 8.1) and the 0.17.0 changelog (PHP 8.4, nikic/php-parser removed) contradict each other. The changelog is newer and authoritative for 0.17+. Practically: if you're on PHP 8.1–8.3, you must pin a pre-0.17 release, and nothing in the README will tell you that.

0.x@dev is not a stability guarantee

Install uses 0.x@dev, there are 0 dependents and ~42 stars. That combination means:

  • Commit composer.lock and treat composer update pmjones/php-styler as a reviewed change, not routine maintenance.
  • 0.17.0 was a ground-up rewrite with breaking config API changes — Config no longer accepts a Styler; it takes a Format. Any tutorial, blog post, or Stack Overflow answer referencing new Config(styler: new Styler(...)) or overriding s*() methods is for a dead API. Read UPGRADE.md in the package if you're migrating.
  • The changelog shows a cache: argument in the new Config signature but the README doesn't document it. Check the Config constructor in your installed version rather than assuming.

Debugging a surprising reformat

The pipeline is fixed and knowing it short-circuits most confusion:

Parser (tokenize + contextual AToken mapping, modifier ordering, missing-visibility insertion, control-structure brace insertion) → TokenRules → Assembler (grouping into Line objects, indent tracking) → Splitter (line-length enforcement, normalizeIndents run before and after expansion) → LineRules → Render

The practical consequence: rule ordering is not arbitrary. NormalizeTrailingCommas is a line rule — it can only add trailing commas to lists it can see are split, which is why it runs after the Splitter. NormalizeImports is a token rule and runs before assembly. If you write a custom TokenRule that reorders tokens, it will not see the split structure; if you write a LineRule, it runs after splitting and can't change token-level spacing. Put your logic on the correct side of the pipeline.

Two specific debugging techniques:

  • preview a minimal reproduction (three lines) rather than your real file. The token classes are context-sensitive, so the same construct can behave differently at class-body scope vs. function scope.
  • src/Token/ contains ~550 files in a flat directory. Grep, never ls. The relevant token class is named for the construct (TFunction, TMatch, …); searching for the token type from PhpToken gets you there fast.

Rules can contradict each other; blankLineAfterBlock is the usual culprit

DeclarationFormat enables blankLineAfterBlock and a batch of normalization rules simultaneously. Combined with the "collapse multiple blank lines to one" behaviour, you can get churn on repeated runs where a rule removes a blank line and the style re-adds one. Test any custom rules array by running check twice — if the second run is not a no-op, your format isn't idempotent and you'll get dirty CI on every build.

Custom extension points, ranked by cost

  1. Constructor parameters on PlainFormat — brace positions, keywordCase, concatenationSpacing, returnTypeColonSpacing, blankLineAfterBlock. Covers ~80% of taste disagreements.
  2. styles / rules / parseAs arrays — per-token spaceBefore, spaceAfter, lineBreakBefore, lineBreakAfter, blankLineBefore, blankLineAfter, case; structural rules like NormalizeImports, NormalizeTypeOrder, CollapseEmptyBody, MergeParenBrace; parses like else if → elseif, array() → [], double → single quotes.
  3. Subclassing a format — see the HouseFormat pattern above.
  4. Implementing TokenRule / LineRule / an AToken subclass — only if 1–3 genuinely can't express it. This is the "you now maintain a fork of a 0.x package" tier.

Don't format in-flight work

Straight from the project's own AGENTS.md: do not run cs-fix on in-flight code; fix style issues manually. The generalisation for your team: never let the formatter run as a save-hook mid-feature. Formatting churn mixed into a feature branch destroys reviewability, and if a preview reveals a rule doing something wrong, you've now baked it into thirty files. Format on clean trees, in dedicated commits, with .git-blame-ignore-revs updated each time.

Two small ones

  • check returning 1 needs handling in CI — set -e will abort before reporting which files failed. Capture the output, then fail.
  • Empty class/function bodies and "missing visibility" keywords get rewritten by the Parser stage itself, not by a toggleable rule. public function insertion happens during tokenization and cannot be disabled via rules.
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.
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo