Skip to content

Ruleset: (CLI) <arg> settings in higher level ruleset(s) overrule args set in included ruleset(s) #1005

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,23 @@ class Config
*/
const DEFAULT_REPORT_WIDTH = 80;

/**
* Translation table for config settings which can be changed via multiple CLI flags.
*
* If the flag name matches the setting name, there is no need to add it to this translation table.
* Similarly, if there is only one flag which can change a setting, there is no need to include
* it in this table, even if the flag name and the setting name don't match.
*
* @var array<string, string> Key is the CLI flag name, value the corresponding config setting name.
*/
public const CLI_FLAGS_TO_SETTING_NAME = [
'n' => 'warningSeverity',
'w' => 'warningSeverity',
'warning-severity' => 'warningSeverity',
'no-colors' => 'colors',
'no-cache' => 'cache',
];

/**
* An array of settings that PHPCS and PHPCBF accept.
*
Expand Down
118 changes: 94 additions & 24 deletions src/Ruleset.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ class Ruleset
*/
private $configDirectivesApplied = [];

/**
* The `<arg>` directives which have been applied.
*
* @var array<string, int> Key is the name of the setting. Value is the ruleset depth
* at which this setting was applied (if not overruled from the CLI).
*/
private $cliSettingsApplied = [];

/**
* An array of the names of sniffs which have been marked as deprecated.
*
Expand Down Expand Up @@ -613,6 +621,92 @@ public function processRuleset($rulesetPath, $depth=0)
}
}//end foreach

// Process custom command line arguments.
// Processing rules:
// - Highest level ruleset take precedence.
// - If the same CLI flag is being set from two rulesets at the same level, *first* one "wins".
$cliArgs = [];
foreach ($ruleset->{'arg'} as $arg) {
if ($this->shouldProcessElement($arg) === false) {
continue;
}

// "Long" CLI argument. Arg is in the format `<arg name="name" [value="value"]/>`.
if (isset($arg['name']) === true) {
$name = (string) $arg['name'];
$cliSettingName = $name;
if (isset($this->config::CLI_FLAGS_TO_SETTING_NAME[$name]) === true) {
$cliSettingName = $this->config::CLI_FLAGS_TO_SETTING_NAME[$name];
}

if (isset($this->cliSettingsApplied[$cliSettingName]) === true
&& $this->cliSettingsApplied[$cliSettingName] < $depth
) {
// Ignore this CLI flag. A higher level ruleset has already set a value for this setting.
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$statusMessage = str_repeat("\t", $depth);
$statusMessage .= "\t=> ignoring command line arg --".$name;
if (isset($arg['value']) === true) {
$statusMessage .= '='.(string) $arg['value'];
}

echo $statusMessage.' => already changed by a higher level ruleset '.PHP_EOL;
}

continue;
}

// Remember which settings we've seen.
$this->cliSettingsApplied[$cliSettingName] = $depth;

$argString = '--'.$name;
if (isset($arg['value']) === true) {
$argString .= '='.(string) $arg['value'];
}
} else {
// "Short" CLI flag. Arg is in the format `<arg value="value"/>` and
// value can contain multiple flags, like `<arg value="ps"/>`.
$cleanedValue = '';
$cliFlagsAsArray = str_split((string) $arg['value']);
foreach ($cliFlagsAsArray as $flag) {
$cliSettingName = $flag;
if (isset($this->config::CLI_FLAGS_TO_SETTING_NAME[$flag]) === true) {
$cliSettingName = $this->config::CLI_FLAGS_TO_SETTING_NAME[$flag];
}

if (isset($this->cliSettingsApplied[$cliSettingName]) === true
&& $this->cliSettingsApplied[$cliSettingName] < $depth
) {
// Ignore this CLI flag. A higher level ruleset has already set a value for this setting.
if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\t=> ignoring command line flag -".$flag.' => already changed by a higher level ruleset '.PHP_EOL;
}

continue;
}

// Remember which settings we've seen.
$cleanedValue .= $flag;
$this->cliSettingsApplied[$cliSettingName] = $depth;
}//end foreach

if ($cleanedValue === '') {
// No flags found which should be applied.
continue;
}

$argString = '-'.$cleanedValue;
}//end if

$cliArgs[] = $argString;

if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\t=> set command line value $argString".PHP_EOL;
}
}//end foreach

foreach ($ruleset->rule as $rule) {
if (isset($rule['ref']) === false
|| $this->shouldProcessElement($rule) === false
Expand Down Expand Up @@ -706,30 +800,6 @@ public function processRuleset($rulesetPath, $depth=0)
$this->processRule($rule, $newSniffs, $depth);
}//end foreach

// Process custom command line arguments.
$cliArgs = [];
foreach ($ruleset->{'arg'} as $arg) {
if ($this->shouldProcessElement($arg) === false) {
continue;
}

if (isset($arg['name']) === true) {
$argString = '--'.(string) $arg['name'];
if (isset($arg['value']) === true) {
$argString .= '='.(string) $arg['value'];
}
} else {
$argString = '-'.(string) $arg['value'];
}

$cliArgs[] = $argString;

if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo str_repeat("\t", $depth);
echo "\t=> set command line value $argString".PHP_EOL;
}
}//end foreach

// Set custom php ini values as CLI args.
foreach ($ruleset->{'ini'} as $arg) {
if ($this->shouldProcessElement($arg) === false) {
Expand Down
27 changes: 27 additions & 0 deletions tests/Core/Ruleset/Fixtures/ProcessRulesetCliArgsTest/.phpcs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="ProcessRulesetCliArgsTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">

<!-- Issues squizlabs/PHP_CodeSniffer#2597 + squizlabs/PHP_CodeSniffer#2602: allow overruling CLI args when they have a single CLI flag names.
One value is set before the includes, one after to safeguard that the order in the file does not influence the results.
-->
<arg name="extensions" value="inc,install,module,php,profile,test,theme"/>

<!-- Issue squizlabs/PHP_CodeSniffer#2395: allow overruling CLI args when they have different CLI flag names. -->
<arg name="no-colors"/>

<!-- Miscellaneous other settings. -->
<arg name="report" value="full,summary,source"/>

<!-- Include the overrides. -->
<rule ref="./phpcs.xml.dist"/>
<rule ref="./config/phpcs/include-overrules.xml"/>

<!-- Issues squizlabs/PHP_CodeSniffer#2597 + squizlabs/PHP_CodeSniffer#2602: second test. -->
<arg name="tab-width" value="3"/>

<!-- Issue squizlabs/PHP_CodeSniffer#2395: second test. -->
<arg value="w"/>

<!-- Prevent a "no sniffs were registered" error. -->
<rule ref="Generic.PHP.BacktickOperator"/>
</ruleset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="ProcessRulesetCliArgsTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">

<!-- Issues squizlabs/PHP_CodeSniffer#2597 + squizlabs/PHP_CodeSniffer#2602: allow overruling CLI args when they have the CLI flag names.
The value from the parent ruleset should be applied, not the one from the child.
-->
<arg name="tab-width" value="5"/>

<!-- Paths must be resolved based on the ruleset location which included the directive. -->
<arg name="report-file" value="./report.txt"/>

<!-- Miscellaneous other settings. -->
<arg name="report-width" value="60"/>

</ruleset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="ProcessRulesetCliArgsTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">

<!-- Issue squizlabs/PHP_CodeSniffer#2395: allow overruling CLI args when they have different CLI flag names.
The values from the parent ruleset should be applied, not those from the child.
-->
<arg name="colors"/>
<arg value="qn"/>

<!-- Paths must be resolved based on the ruleset location which included the directive. -->
<arg name="basepath" value="./"/>

<!-- Miscellaneous other settings. -->
<arg name="parallel" value="10"/>

<!-- Include the overrides. -->
<rule ref="./vendor/OrgName/ExtStandard/ruleset.xml"/>

<!-- Issues squizlabs/PHP_CodeSniffer#2597 + squizlabs/PHP_CodeSniffer#2602: allow overruling CLI args when they have the CLI flag names.
The values from the parent ruleset should be applied, not those from the child.
-->
<arg name="extensions" value="inc,php"/>
<arg name="tab-width" value="4"/>

<!-- Miscellaneous other settings. -->
<arg name="report-width" value="120"/>

</ruleset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php

// Do nothing. This is just a placeholder file for the "bootstrap" CLI flag test.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="ProcessRulesetCliArgsTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">

<!-- Issue squizlabs/PHP_CodeSniffer#2395. -->
<arg name="warning-severity" value="3"/>
<arg value="p"/>

<!-- Paths must be resolved based on the ruleset location which included the directive. -->
<arg name="bootstrap" value="./bootstrap.php"/>

<!-- Miscellaneous other settings. -->
<arg name="parallel" value="2"/>
<arg name="report" value="code,summary"/>

</ruleset>
Loading