Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify wiki documentation about naming of XML configuration files - standards vs custom rulesets #601

Closed
4 tasks done
joachim-n opened this issue Aug 25, 2024 · 10 comments
Closed
4 tasks done

Comments

@joachim-n
Copy link
Contributor

Describe the bug

The docs page https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki/Annotated-Ruleset says:

This XML file can be named anything you like, as long as it has an xml extension and complies to the ruleset.xml format. The file can be stored anywhere, making it perfect for placing under version control with a project's source code and unit tests.

I'm not sure this is true.

When I register a path to a standard with the 'installed_paths' config, I find the following:

  • the ruleset XML file must be called ruleset.xml
  • the ruleset.xml must be inside a folder that has the name of the ruleset, and is a child folder of the path declared to installed_paths.

In other words, this appears to be the required structure:

  • path/declared/to/installed_paths
    -- MyRuleset
    --- ruleset.xml

Versions (please complete the following information)

Operating System MacOS 13.6.8]
PHP version 8.3
PHP_CodeSniffer version 3.10.2]
Standard custom
Install type Composer local

Additional context

Add any other context about the problem here.

Please confirm

  • I have searched the issue list and am not opening a duplicate issue.
  • I have read the Contribution Guidelines and this is not a support question.
  • I confirm that this bug is a bug in PHP_CodeSniffer and not in one of the external standards.
  • I have verified the issue still exists in the master branch of PHP_CodeSniffer.
@jrfnl
Copy link
Member

jrfnl commented Aug 25, 2024

@joachim-n Thanks for opening this issue.

The docs are actually correct, but I do agree this can be clarified some more.

Basically it comes down to this:

  • For a standard (which can have its own sniffs), there are strict naming conventions, both for the ruleset as well as the sniffs and the directory layout.
    Those naming conventions are like you mentioned above and those are outlined in the Coding Standards Tutorial.
  • For a project ruleset, there are no naming conventions other than that the file is an XML file.
    The recommended way is to save the file to the project root and name it [.]phpcs.xml.dist, which will allow for it to be automatically picked up by PHPCS, but as stated in the paragraph you quoted above, the file can be called anything and placed anywhere.

Both of these are rulesets though and everything that is outlined on the Annotated Ruleset wiki page can be used in either of them.
There are, of course, best practices about what directives to add in a standard and what to add in a project ruleset, but all directives should work just fine in either type of ruleset.

Does that clarify things ?

@joachim-n
Copy link
Contributor Author

Thanks for the quick response!

I hadn't realised that a Standard and a ruleset are two different things.

In the docs TOC on the RHS, there's a subheading 'For coding standard creators' and the first page under that is 'Annotated Ruleset'.

I've now found the docs that explain the structure for standards in 'Coding Standard Tutorial', but that's under 'For sniff developers' heading, so I didn't look at that originally!

@jrfnl
Copy link
Member

jrfnl commented Aug 25, 2024

I hadn't realised that a Standard and a ruleset are two different things.

Well, they are and they aren't 😅. That's what makes it complicated.

For the ruleset file, there is not that much difference, other than in best practices.

The biggest difference, from a technical point of view, is really that a Standard can contain sniffs and can be "installed" and once installed, it can be referenced by name in (other/project) rulesets, including referencing individual sniffs/error codes by name (and setting properties on those etc).

What makes it complicated (terminology-wise) is that a "standard" from a functional point of view can be multiple things.

  • A standard can be a descriptive document, outlining what to do and what not to do, like PSR12.
  • This descriptive standard can then be codified into a PHPCS ruleset using only existing sniffs and published as a project/package ruleset (typically using a [.]phpcs.xml.dist file).
  • If a project/package standard, however, is to be shared by a group of projects/packages, it can be beneficial for it to be published as an (external) standard for PHPCS which can be installed, like, for example, the Doctrine Standard. This is also how the PSR-PER standard(s) are intended to work in the future (see PSR-PER 2.0.0 has been released #29).
    In that case, the standard has to have a name usable on the command-line and a ruleset.xml file.
  • Depending on the need for this, this external standard for PHPCS (ruleset.xml) can then be enhanced with custom sniffs, either by contributing those to existing sniff libraries, which are already in use by the (external) standard, or by adding them to the standards package itself. An example of this is the PSR12 standard which is included in PHPCS itself, or for example, the WordPress standard.

In the docs TOC on the RHS, there's a subheading 'For coding standard creators' and the first page under that is 'Annotated Ruleset'.

I've now found the docs that explain the structure for standards in 'Coding Standard Tutorial', but that's under 'For sniff developers' heading, so I didn't look at that originally!

For the "Annotated Ruleset" page, typically, the audience would be the people working on standards as described in the second and third bullet above. ("coding standard creators").

For the "Coding Standard Tutorial" page, typically, the audience would be the people working on a standard as described in the fourth bullet ("sniff developers"), though I agree that there is information on that page which also needs to be read by people working on a standard as described in the third bullet.

Does that help explain why things are under certain headers a little better ?

I'll have a think about how to make all of this clearer, but this will probably have to wait until there is a proper website.

I do find these type of questions useful though as input for that. Happy to have your help in writing things up for a website/reviewing content and structure (by the time I get to it).

@jrfnl jrfnl changed the title error in Annotated Ruleset docs Clarify wiki documentation about naming of XML configuration files - standards vs custom rulesets Nov 27, 2024
@jrfnl
Copy link
Member

jrfnl commented Feb 27, 2025

Just realized I forgot to update you, but I have added a new page "About Standards for PHP_CodeSniffer" to the wiki, which contains a short explanation about the difference between a project ruleset and a standard, as well as includes detailed information and examples about the naming conventions (and more).

@joachim-n Would you mind having a read through that page to see if that sufficiently addressed the concerns from this issue ?
I'd welcome feedback and am happy to update the page if you have suggestions.

@joachim-n
Copy link
Contributor Author

That looks great!
It could maybe do with an explanation of the difference between a Rule and a Sniff -- is that the difference between four name pieces and three name pieces?

@jrfnl
Copy link
Member

jrfnl commented Feb 28, 2025

@joachim-n Thanks for reading though this. I'll try to answer your follow-up question, but AFAICS that wasn't part of the original question posed in this ticket.

It could maybe do with an explanation of the difference between a Rule and a Sniff -- is that the difference between four name pieces and three name pieces?

Not necessarily.

A rule is something you want to enforce for your codebase.
A sniff is a piece of code which allows for enforcing one or more rules.

But they don't necessarily map one-on-one to each other as different code bases define different rules and describe them in their own way, so you may need multiple sniffs to enforce a certain rule and you may only need a select part of a sniff (one or more error codes) to enforce another rule.

The three-part names are sniff names and map directly to a sniff class.
The four-part names are error codes and map to a specific part within a sniff class.

Let's take the following example rule: "PHP keywords should be in lowercase".
This might call into question "what is a PHP keyword ?"
I suppose class, foreach etc, but what about true/false/null ? Should those also be regarded as keywords ? And what about when class is used in the Name::class syntax ?

So for a descriptive standard, like PSR-12, it is really important to be as specific as possible when defining the rules.

At the same time, sniffs are often written in a modular manner to allow for multiple different preferences and it is up to the ruleset maintainer to then include/exclude the right sniffs and error codes to enforce the rules from a descriptive standard.

Just to give an example:

PSR-1 contains the following rule:

PHP code MUST use the long tags or the short-echo tags; it MUST NOT use the other tag variations

To enforce this rule, PHPCS uses two different sniffs and excludes one error code from one of those sniffs:

    <rule ref="Generic.PHP.DisallowAlternativePHPTags"/>
    <rule ref="Generic.PHP.DisallowShortOpenTag"/>
    <rule ref="Generic.PHP.DisallowShortOpenTag.EchoFound">
        <severity>0</severity>
    </rule>

The Generic.PHP.DisallowAlternativePHPTags sniff forbids ASP tags and <script type="php"... tags and has multiple error codes, like ASPOpenTagFound, ScriptOpenTagFound, ASPShortOpenTagFound etc.
In this case that complete sniff is used to cover most of the "it MUST NOT use the other tag variations" part of the rule.

The Generic.PHP.DisallowShortOpenTag sniff forbids the use of short open tags, including short open echo tags, so both <? as well as <?=.
Again, this sniff has multiple error codes, like Found and EchoFound. Now as PSR-1 allows for the short-echo tag, but forbids the short open tag <? (which is enabled via the php.ini short_open_tag setting), the Generic.PHP.DisallowShortOpenTag sniff is included, but the Generic.PHP.DisallowShortOpenTag.EchoFound error code is excluded to allow for the short echo tags.

Does that help ?

@joachim-n
Copy link
Contributor Author

Ah, I meant sniffs vs error codes... which shows how much I've misunderstood! I don't think it's especially confusing once it's explained but I think a piece of software like PHPCS is only used intermittently by most people, so things are quickly forgotten!

It might be out of scope for this issue, but your explanation in your latest comment would be extremely useful in the docs!

@jrfnl
Copy link
Member

jrfnl commented Feb 28, 2025

@joachim-n I agree, there's definitely a lot more documentation which would be helpful to be written. I'll keep the above in mind as one of those things ;-)

I have a number of slidedecks/presentations about PHPCS, you may also find some of those useful. For some you should be able to find video's online, but the slidedecks are all available either way. For example: https://speakerdeck.com/jrf/dont-work-for-phpcs-make-phpcs-work-for-you-4

@jrfnl
Copy link
Member

jrfnl commented Feb 28, 2025

Oh and before I forget: considering the new wiki page addresses the original issue, can we close this one ?

@joachim-n
Copy link
Contributor Author

Yup, closing this one.
Thank you for your help!

@jrfnl jrfnl added this to the 3.12.0 milestone Feb 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants