Skip to content

[FR] Attributes: sniffs to standardize the placement of attribute blocks in relation to docblocks #388

@jrfnl

Description

@jrfnl

Setting the scene

PHP 8.0 introduced support for attributes via the #[...] syntax. At this moment, neither PHPCS itself, nor PHPCSExtra contain any sniffs to handle the formatting of attributes.

The PER Coding Standard from FIG, since PER 2.0, outlines a set of rules for attribute formatting to comply with, so using those rules as a starting point would allow for creating an initial set of sniffs to address attribute formatting.

Proposed new sniffs: Universal.Attributes.EnforceBelowDocblock and Universal.Attributes.EnforceAboveDocblock

To address these rules from PER:

Attributes on classes, methods, functions, constants and properties MUST be placed ...., immediately prior to the structure being described.

If a comment docblock is present on a structure that also includes an attribute, the comment block MUST come first, followed by any attributes, followed by the structure itself.

I deliberately propose two separate sniffs to give users a choice of what they want to enforce as not everyone will want to use the PER rules, though as far as I have seen, most project currently adhere to the rule in PER, so the Universal.Attributes.EnforceBelowDocblock sniff should get priority when developing these sniffs.

Notes for the implementation

This needs a good think on the sniff names as these rules do not typically apply to parameter attributes.

Suggested error codes:

  • IncorrectOrder ("block order" - docblock, block with all attributes, "block" for structure)

These sniffs will probably be more straight forward to write with the upcoming PHPCSUtils *::getAttributeOpeners() methods, then again, they also need to find the docblocks, so maybe custom logic is better.

Also needs a good think about whether the sniff should take the construct tokens as a starting point or the attribute opener token.

  • I think the attribute token will likely be more efficient in combination with the upcoming PHPCSUtils AttributeBlock::appliesTo() method, though would still need custom logic to find a potential docblock.
  • Then again, the sniff should probably want to fix the whole "preamble" (all attributes + docblocks) in one go, not one attribute at a time, so in that case, using the construct token may be better.

Describe the solution you'd like

A new sniff as outlined above.

The sniff should be able to flag and auto-fix the following:

// Universal.Attributes.EnforceBelowDocblock
/**
 * Docblock.
 */
#[MyAttribute] // OK.
function foo() {}

#[MyAttribute] // Error.
/**
 * Docblock.
 */
function foo() {}

// Universal.Attributes.EnforceAboveDocblock
#[MyAttribute] // OK.
/**
 * Docblock.
 */
function foo() {}

/**
 * Docblock.
 */
#[MyAttribute] // Error.
function foo() {}

Also see the examples outlined in the PER documents (rules + migration guide).

Additional context (optional)

This ticket is part of a series of tickets related to PHP attributes and is the result of a detailed analysis of the rules as outlined in PER 2.0, as well as a critical look at what's still missing rule-wise.

  • I intend to create a pull request to implement this feature.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions