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

Change ruleset processing to be top-to-bottom instead of processing tags in groups #2197

Closed
grappler opened this issue Oct 19, 2018 · 11 comments
Milestone

Comments

@grappler
Copy link

@jrfnl second point (Custom ruleset cannot overrule value in included ruleset) was not fixed when the issue #1821 was closed.

I have done a bit of more debugging. We have the following two rulesets.

<?xml version="1.0"?>
<ruleset name="Company-Ruleset">
	<rule ref="PHPCompatibility"/>
	<config name="testVersion" value="5.6-"/>
</ruleset>

The problem is that the config here does not override the testVersion defined in Company-Ruleset

<?xml version="1.0"?>
<ruleset name="Project-Ruleset">
	<rule ref="Company-Ruleset" />
	<config name="testVersion" value="7.1-"/>
</ruleset>

When running phpcs . -vvv you can see why the config is not being overwritten.

Processing ruleset project/phpcs.xml.dist
	=> set config value testVersion: 7.1-
	Processing rule "Company-Ruleset"
		=> Company-Ruleset/ruleset.xml
		* rule is referencing a standard using ruleset path; processing *
		Processing ruleseCompany-Ruleset/ruleset.xml
			=> set config value testVersion: 5.6-

What I realized I could override the config with another ruleset.

<?xml version="1.0"?>
<ruleset name="Project-Ruleset">
	<rule ref="Company-Ruleset" />
	<config name="testVersion" value="7.1-"/>
	<rule ref="config-override.xml"/>
</ruleset>

The config-override.xml looks like the following

<?xml version="1.0"?>
<ruleset>
	<config name="testVersion" value="7.1-"/>
</ruleset>

When running phpcs . -vvv now the output looks like the following.

Processing ruleset project/phpcs.xml.dist
	=> set config value testVersion: 7.1-
	Processing rule "Company-Ruleset"
		=> Company-Ruleset/ruleset.xml
		* rule is referencing a standard using ruleset path; processing *
		Processing ruleseCompany-Ruleset/ruleset.xml
			=> set config value testVersion: 5.6-
[...]
	Processing rule "config-override.xml"
		* rule is referencing a standard using ruleset path; processing *
		Processing ruleset config-override.xml
			=> set config value testVersion: 7.1-
@dingo-d
Copy link

dingo-d commented Oct 25, 2018

Have the exact same issue 😄 I didn't add another ruleset, but I've just silenced the error thrown by specifying severity to 0 of the specific rule that was causing me issues (which I know won't be an issue in production).

@gsherwood
Copy link
Member

This is due to the way ruleset parsing currently happens. All config vars are processed before the rules are processed, as you've seen from the debug output.

Changing the ruleset to be parsed top-to-bottom, line-by-line, is actually pretty easy, but it does mean that some existing rulesets may stop working as they are relying on the existing behaviour.

I would like to see this changed, so I'll put this on the 4.0 roadmap.

@gsherwood gsherwood added this to the 4.0.0 milestone Nov 7, 2018
@gsherwood gsherwood changed the title Overruling arbitrary config directives in the ruleset Change ruleset processing to be top-to-bottom instead of processing tags in groups Nov 7, 2018
@gsherwood
Copy link
Member

gsherwood commented Nov 7, 2018

To do this, change Ruleset::processRuleset() to look over $ruleset->children() and switch on the name. E.g.,

foreach ($ruleset->children() as $child) {
    switch ($child->getName()) {
        case 'autoload':
        break;        
    }
}

@jrfnl
Copy link
Contributor

jrfnl commented Feb 6, 2019

To think over as a solution direction as suggested in #2395 (comment)

  1. Read in the custom ruleset used.
  2. Process the tags in a set order recursively.

Something along the lines of (pseudo-code):

function processRuleset($name) {
    $ruleset = // read custom ruleset XML file or standard XML file.

    foreach ($ruleset->rule as $rule) {
        processRuleset($rule);
    }

    foreach ($ruleset->config as $config) {
    }

    foreach ($ruleset->arg as $arg) {
    } 
}

@gsherwood
Copy link
Member

Something along the lines of (pseudo-code):

That's basically what it does now, just in a different order.

I like the idea of telling developers that their rulesets are processed line by line as it gives greater control and understanding than is the case right now. But this suggested change would have less impact overall and may be a better option.

I'm still not sure if it could come in before 4.0 - I'm not entirely sure what the implications of the change are as I haven't gone digging - but it is possible that a smaller change like this might be able to make it into 3.5.0.

jrfnl added a commit to Yoast/yoastcs that referenced this issue May 17, 2019
Properties are supposed to be set per sniff, but (bug or feature?) it turns out that setting them for a standard will set them for all sniffs included in that standard.

Sniffs which don't use the property will just ignore it.
Sniffs which do use it, now have it available as if it were set for the individual sniff.

As this was unknown (and possibly/probably not supported) in the past with older PHPCS versions, WPCS added the ability to set the `minimum_supported_wp_version` for all sniffs in one go via a `config`directive.

The problem with a `config` directive is that it is currently impossible to overrule it from a repo ruleset. It can only be overruled on the command-line, and even then only since PHPCS 3.3.0.

For that reason, the `config` directive was previously set in each individual repo ruleset.

Properties, however, can easily be overruled from within repo rulesets, so this change will allow us to manage the "_minimum supported WP version_" centrally, while still allowing for individual repos to overrule the default value.

Ref:
* https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#minimum-wp-version-to-check-for-usage-of-deprecated-functions-classes-and-function-parameters - WPCS documentation about the property/config directive.
* squizlabs/PHP_CodeSniffer#2501 - PR through which this "feature" was discovered, included unit tests demonstrating it.
* squizlabs/PHP_CodeSniffer#1821 - Issue which made the config directive possible to be overruled from the command-line.
* squizlabs/PHP_CodeSniffer#2197 Feature request to allow for config directives to be overruled by other rulesets.
@dingo-d
Copy link

dingo-d commented Jun 24, 2019

Any possibility of this landing in 3.5.0? 🤞

@gsherwood
Copy link
Member

Any possibility of this landing in 3.5.0?

This potentially breaks existing rulesets, so it wont be introduced before 4.0.

jrfnl added a commit to PHPCSStandards/PHPCSDevTools that referenced this issue Aug 14, 2019
This adds a new external PHPCS standard called `PHPCSDev` for use by sniff developers to check the code style of their sniff repo code.

Often, sniff repos will use the code style of the standard they are adding. However, not all sniff repos are actually about code style.

So for those repos which need a basic standard which will still keep their code-base consistent, this standard should be useful.

The standard checks the following:
* Compliance with PSR-2.
* Use of camelCaps variable and function names.
* Use of normalized arrays.
* All files, classes, functions and properties are documented with a docblock and contain the minimally needed information.
* A small number of arbitrary additional code style checks.
* PHP cross-version compatibility, while allowing for the tokens back-filled by PHPCS itself.
    **Note**: for optimal results, the project custom ruleset should set the `testVersion` config variable.
    This is not done by default as config variables are currently difficult to overrule.
    Refs:
    - squizlabs/PHP_CodeSniffer#2197
    - squizlabs/PHP_CodeSniffer#1821

The ruleset can be used like any other ruleset and specific sniffs and settings can be added to or overruled from a custom project based ruleset.

Includes adding QA checks for this ruleset to the Travis script.
jrfnl added a commit to PHPCSStandards/PHPCSDevTools that referenced this issue Aug 14, 2019
This adds a new external PHPCS standard called `PHPCSDev` for use by sniff developers to check the code style of their sniff repo code.

Often, sniff repos will use the code style of the standard they are adding. However, not all sniff repos are actually about code style.

So for those repos which need a basic standard which will still keep their code-base consistent, this standard should be useful.

The standard checks the following:
* Compliance with PSR-2.
* Use of camelCaps variable and function names.
* Use of normalized arrays.
* All files, classes, functions and properties are documented with a docblock and contain the minimally needed information.
* A small number of arbitrary additional code style checks.
* PHP cross-version compatibility, while allowing for the tokens back-filled by PHPCS itself.
    **Note**: for optimal results, the project custom ruleset should set the `testVersion` config variable.
    This is not done by default as config variables are currently difficult to overrule.
    Refs:
    - squizlabs/PHP_CodeSniffer#2197
    - squizlabs/PHP_CodeSniffer#1821

The ruleset can be used like any other ruleset and specific sniffs and settings can be added to or overruled from a custom project based ruleset.

Includes adding QA checks for this ruleset to the Travis script.
jrfnl added a commit to PHPCSStandards/PHPCSDevTools that referenced this issue Aug 14, 2019
This adds a new external PHPCS standard called `PHPCSDev` for use by sniff developers to check the code style of their sniff repo code.

Often, sniff repos will use the code style of the standard they are adding. However, not all sniff repos are actually about code style.

So for those repos which need a basic standard which will still keep their code-base consistent, this standard should be useful.

The standard checks the following:
* Compliance with PSR-2.
* Use of camelCaps variable and function names.
* Use of normalized arrays.
* All files, classes, functions and properties are documented with a docblock and contain the minimally needed information.
* A small number of arbitrary additional code style checks.
* PHP cross-version compatibility, while allowing for the tokens back-filled by PHPCS itself.
    **Note**: for optimal results, the project custom ruleset should set the `testVersion` config variable.
    This is not done by default as config variables are currently difficult to overrule.
    Refs:
    - squizlabs/PHP_CodeSniffer#2197
    - squizlabs/PHP_CodeSniffer#1821

The ruleset can be used like any other ruleset and specific sniffs and settings can be added to or overruled from a custom project based ruleset.

Includes adding QA checks for this ruleset to the Travis script.
@alexanderpas
Copy link

alexanderpas commented Sep 8, 2019

To summarize the current status:

  • An included ruleset can set a config value.
  • An included ruleset can override a config value that has been set by a previously included ruleset.
  • A config value can be set in a ruleset, if it has not been set in an included ruleset.
  • Command line arguments override any value that has been set in a ruleset.

Overriding a config value in a ruleset where the config value has previously been set in an included config value is not supported, and is currently handled as if the directive doesn't exist at all (even if it does process them, there is no difference between the directive being present or not in the end result).


While line-by line processing does constitute a BC break, permitting overriding config variables in a ruleset does not constitute a BC break, since this is essentially the equivalent to the implementation of a new feature. (overriding an included config value in a ruleset)

This gives us the following:

  • An included ruleset can set a config value.
  • An included ruleset can override a config value that has been set by a previously included ruleset.
  • A config value can be set in a ruleset, if it has not been set in an included ruleset.
  • (NEW) A config value can be set in a ruleset, which overrides the value set in the included ruleset.
  • Command line arguments override any value that has been set in a ruleset.

This also matches the expected and intended behavior, where the primary rulesets have the final say, not the rulesets they are including.

jrfnl added a commit to PHPCSStandards/PHPCSDevCS that referenced this issue Feb 12, 2020
This adds a new external PHPCS standard called `PHPCSDev` for use by sniff developers to check the code style of their sniff repo code.

Often, sniff repos will use the code style of the standard they are adding. However, not all sniff repos are actually about code style.

So for those repos which need a basic standard which will still keep their code-base consistent, this standard should be useful.

The standard checks the following:
* Compliance with PSR-12 with a few, selective exceptions:
    - Constant visibility will not be demanded as the minimum PHP requirement of PHPCS is PHP 5.4 and constant visibility did not become available until PHP 7.1.
    - The first condition of a multi-line control structure is allowed to be on the same line as the control structure keyword (PSR2 style).
    **Note:** PSR12 enforces a blank line between each of the file header blocks.
    This rule can currently not (yet) be turned off in a modular way, i.e. for just one block. It is either on or off.
    This rule conflicts with the common practice of having no blank line between the PHP open tag and the file docblock.
    To turn this rule off, add `<exclude name="PSR12.Files.FileHeader.SpacingAfterBlock"/>` to the project specific ruleset.
* Use of camelCaps variable and function names.
* Use of normalized arrays.
* All files, classes, functions and properties are documented with a docblock and contain the minimally needed information.
    Includes allowing for more than one `@since` tag to document a class's changelog.
* Don't use Yoda conditions.
* A small number of arbitrary additional code style checks.
* PHP cross-version compatibility, while allowing for the tokens back-filled by PHPCS itself.
    **Note**: for optimal results, the project custom ruleset should set the `testVersion` config variable.
    This is not done by default as config variables are currently difficult to overrule.
    Refs:
    - squizlabs/PHP_CodeSniffer#2197
    - squizlabs/PHP_CodeSniffer#1821

The ruleset can be used like any other ruleset and specific sniffs and settings can be added to or overruled from a custom project based ruleset.

Includes:
* Setting up the `composer.json`.
* Adding QA checks for this ruleset via the Travis.
@gsherwood
Copy link
Member

I've modified the ruleset processing code to process the ruleset top to bottom instead of in the old defined group order. The original test case now works - you can override the value of the testVersion config value after including the PHPCompatibility ruleset - you don't need to use a second included ruleset to achieve this.

@jrfnl
Copy link
Contributor

jrfnl commented Feb 16, 2020

@gsherwood Looking good! I'm going to have to have a play with this to see if I can find some more test cases to add.

The only thing I'm currently a little concerned about is the <autoload> directive. With the ruleset now processing top to bottom, this would always have to be at the top of a ruleset as otherwise sniffs/reports etc may not be found.

Should that directive be an exception and be processed before anything else to prevent such issues ?
It's not as if one <autoload> directive would overrule another. Every <autoload> should be read and preferably before any sniffs are loaded.

@gsherwood
Copy link
Member

Should that directive be an exception and be processed before anything else to prevent such issues ?

I intentionally did not make it an exception. I think it's fine to require developers to include it at the top of their ruleset, as they would with code.

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

No branches or pull requests

5 participants