Skip to content

Fix ConsistentIndent mis-indenting PHP 8.4 property hooks - #76

Merged
dereuromark merged 1 commit into
masterfrom
consistent-indent-property-hooks
Aug 6, 2026
Merged

Fix ConsistentIndent mis-indenting PHP 8.4 property hooks#76
dereuromark merged 1 commit into
masterfrom
consistent-indent-property-hooks

Conversation

@dereuromark

Copy link
Copy Markdown
Contributor

ConsistentIndent dedents the second hook of a PHP 8.4 property hook block, and phpcbf applies it.

Input:

class Hooks
{
    public string $label = 'x' {
        get {
            return $this->label;
        }
        set {
            $this->label = $value;
        }
    }
}

Reported, and auto-fixed:

 13 | ERROR | [x] Line indented incorrectly; expected 4 spaces, found 8 spaces
    |       |     (PhpCollective.WhiteSpace.ConsistentIndent.Incorrect)

Output - set moved to 4 while get, both bodies and every closing brace stayed at 8:

        get {
            return $this->label;
        }
    set {
            $this->label = $value;
        }

Cause

getExpectedIndent() returned count($token['conditions']). PHP_CodeSniffer 4.x does not model property hooks as scopes - the hook braces get no scope_opener/scope_closer and add no entry to conditions. Dumping the parsed tokens shows every token inside both hooks carrying conditions=[T_CLASS], so the whole block reads as one level deep.

Only the set line surfaced it. Lines whose previous content token is an opening brace pass the continuation check, and lines starting with } return early, which leaves exactly the line following the first hook's closing brace. A property with three hooks reports it twice.

Fix

The braces are paired even though they are not scopes - they carry bracket_opener and bracket_closer. Expected indent now adds the number of enclosing curly-brace pairs that phpcs paired but did not map to a scope, so the depth model is repaired rather than special-cased for one syntax.

The ranges are collected in one pass and cached per file with the token count alongside, so the cache self-invalidates when phpcbf re-tokenizes mid fix-loop. That is the same approach already used for arrow function scopes, and it keeps the per-line cost off the file size.

Coverage

The existing fixtures gain a correctly indented hook pair, which must come through byte-identical, and a deliberately misindented one, which proves the sniff still reports inside hook bodies instead of going blind there. Every pre-existing case in those fixtures is untouched - that is the regression guard on the depth-model change.

The sniff derived its expected indent from the size of a token's conditions
map. PHP_CodeSniffer does not model property hooks as scopes: their braces
carry bracket opener and closer metadata but contribute nothing to conditions,
so every token inside a hook block reported the same depth as the property
itself.

Only the second hook actually tripped it. Lines preceded by an opening brace
are let through as continuations, and the hook bodies and closing braces are
skipped, which leaves just the line after the first hook's closing brace. That
was enough for phpcbf to dedent set to the property's own level while get, the
bodies and every brace stayed put, producing exactly the inconsistent
indentation this sniff exists to prevent.

Expected indent now also counts enclosing curly brace pairs that phpcs paired
but did not map to a scope. That repairs the depth model rather than special
casing one syntax. The ranges are collected in a single pass and cached per
file alongside the token count, matching how arrow function scopes are already
handled, so nothing walks the file per line.

The fixtures gain a correctly indented hook pair, which must stay untouched,
and a misindented one, which proves the sniff still sees inside hook bodies
rather than going blind there.
Copilot AI lite review requested due to automatic review settings August 6, 2026 11:36
@dereuromark
dereuromark merged commit 5469d43 into master Aug 6, 2026
6 checks passed
@dereuromark
dereuromark deleted the consistent-indent-property-hooks branch August 6, 2026 11:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes ConsistentIndent mis-indenting PHP 8.4 property hooks by repairing the sniff’s indentation depth model when PHPCS pairs curly braces (bracket_opener/bracket_closer) but does not model them as scopes (so they don’t appear in conditions).

Changes:

  • Extend expected-indent calculation to include enclosing “unscoped” curly-brace pairs (cached per file, self-invalidating on token-count change).
  • Add fixtures covering correctly-indented and misindented property-hook blocks, and update the expected error count accordingly.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
PhpCollective/Sniffs/WhiteSpace/ConsistentIndentSniff.php Adjusts expected-indent calculation by adding a cached count of enclosing unscoped curly-brace ranges.
tests/_data/ConsistentIndent/before.php Adds PHP 8.4 property hook examples (one correct, one intentionally misindented) to exercise the sniff and fixer.
tests/_data/ConsistentIndent/after.php Expected fixed output for the misindented hook-body line; keeps the correctly-indented hook block unchanged.
tests/PhpCollective/Sniffs/WhiteSpace/ConsistentIndentSniffTest.php Updates expected error count to account for the newly-added misindent fixture case.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants