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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions