Skip to content

Commit b6abb91

Browse files
authored
Merge pull request #1004 from PHPCSStandards/phpcs-4.0/feature/sq-2197-ruleset-ini-processing-add-tests
Ruleset: document ini overruling behaviour via tests
2 parents 22607ab + 0df5da0 commit b6abb91

File tree

5 files changed

+251
-0
lines changed

5 files changed

+251
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0"?>
2+
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="ProcessRulesetIniDirectivesTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">
3+
4+
<!-- These ini directives should not be applied. Root should win. -->
5+
<ini name="highlight.comment" value="child A"/>
6+
<ini name="highlight.default" value="child A"/>
7+
8+
<!-- For this ini directive, this is the "highest" level, so this one should be applied. -->
9+
<ini name="highlight.html" value="child A"/>
10+
11+
<!-- Include overrides. -->
12+
<rule ref="./ProcessRulesetIniDirectivesSubIncludeATest.xml"/>
13+
14+
<!-- This directive should not be applied. Last included ruleset (child B) should win. -->
15+
<ini name="highlight.keyword" value="child A"/>
16+
17+
<!-- For this ini directive, this is the "highest" (and only) level, so this one should be applied. -->
18+
<ini name="url_rewriter.tags" value="child A"/>
19+
20+
</ruleset>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0"?>
2+
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="ProcessRulesetIniDirectivesTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">
3+
4+
<!-- For this PHP ini directive, this is the "highest" level, but the directive is also set in the "child A"
5+
ruleset, which is at the same "level".
6+
In that case, the value which "wins" is determined by the order in which the rulesets are included.
7+
In this case, that means that "child B" should win as "child A" is included _before_ child B (from the root ruleset),
8+
so the value from "child B" overwrites the value from the earlier read "child A".
9+
-->
10+
<ini name="highlight.keyword" value="child B"/>
11+
12+
<!-- For this ini directive, this is the "highest" level, so this one should be applied. -->
13+
<ini name="highlight.string" value="child B"/>
14+
15+
<!-- These ini directives should not be applied. Root should win. -->
16+
<ini name="highlight.default" value="child B"/>
17+
<ini name="highlight.comment" value="child B"/>
18+
19+
</ruleset>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0"?>
2+
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="ProcessRulesetIniDirectivesTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">
3+
4+
<!-- These ini directives should not be applied. Root should win. -->
5+
<ini name="highlight.comment" value="grandchild A"/>
6+
<ini name="highlight.default" value="grandchild A"/>
7+
<ini name="url_rewriter.hosts" value="grandchild A"/>
8+
9+
<!-- For this ini directive, this is the "highest" (and only) level, so this one should be applied. -->
10+
<ini name="user_agent" value="grandchild A"/>
11+
12+
<!-- This directive should not be applied. Higher level ruleset (child A) should win. -->
13+
<ini name="highlight.html" value="grandchild A"/>
14+
15+
<!-- This directive should not be applied. Last included, higher level, ruleset (child B) should win. -->
16+
<ini name="highlight.string" value="grandchild A"/>
17+
18+
</ruleset>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0"?>
2+
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="ProcessRulesetIniDirectivesTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">
3+
4+
<!-- This ruleset sets three PHP ini directives.
5+
The included rulesets will try to override these and should fail.
6+
Whether the ini is set before, or after, the sub-rulesets are included, should be irrelevant.
7+
-->
8+
<ini name="highlight.comment" value="parent"/>
9+
<ini name="url_rewriter.hosts" value="parent"/>
10+
11+
<!-- Include the overrides. -->
12+
<rule ref="./ProcessRulesetIniDirectivesIncludeATest.xml"/>
13+
<rule ref="./ProcessRulesetIniDirectivesIncludeBTest.xml"/>
14+
15+
<ini name="highlight.default" value="parent"/>
16+
17+
<!-- Prevent a "no sniffs were registered" error. -->
18+
<rule ref="Generic.PHP.BacktickOperator"/>
19+
</ruleset>
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<?php
2+
/**
3+
* Tests for the \PHP_CodeSniffer\Ruleset class.
4+
*
5+
* @copyright 2025 PHPCSStandards and contributors
6+
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
7+
*/
8+
9+
namespace PHP_CodeSniffer\Tests\Core\Ruleset;
10+
11+
use PHP_CodeSniffer\Ruleset;
12+
use PHP_CodeSniffer\Tests\ConfigDouble;
13+
use PHPUnit\Framework\TestCase;
14+
15+
/**
16+
* Tests for the \PHP_CodeSniffer\Ruleset class.
17+
*
18+
* @covers \PHP_CodeSniffer\Ruleset::processRuleset
19+
*/
20+
final class ProcessRulesetIniDirectivesTest extends TestCase
21+
{
22+
23+
/**
24+
* Directory in which the XML fixtures for this test can be found (without trailing slash).
25+
*
26+
* @var string
27+
*/
28+
private const FIXTURE_DIR = __DIR__.'/Fixtures/ProcessRulesetIniDirectivesTest';
29+
30+
/**
31+
* Cache to store the original ini values for ini settings being changed in these tests.
32+
*
33+
* @var array<string, string|null>
34+
*/
35+
private static $originalIniValues = [
36+
'highlight.comment' => null,
37+
'highlight.default' => null,
38+
'highlight.html' => null,
39+
'highlight.keyword' => null,
40+
'highlight.string' => null,
41+
'url_rewriter.hosts' => null,
42+
'url_rewriter.tags' => null,
43+
'user_agent' => null,
44+
];
45+
46+
/**
47+
* Whether or not the Config and Ruleset have been initialized.
48+
*
49+
* @var boolean
50+
*/
51+
private static $initialized = false;
52+
53+
54+
/**
55+
* Store the original ini values to allow for restoring them after the tests.
56+
*
57+
* @return void
58+
*/
59+
public static function setUpBeforeClass(): void
60+
{
61+
foreach (self::$originalIniValues as $name => $null) {
62+
$value = ini_get($name);
63+
if ($value !== false) {
64+
self::$originalIniValues[$name] = $value;
65+
}
66+
}
67+
68+
}//end setUpBeforeClass()
69+
70+
71+
/**
72+
* Initialize the config and ruleset objects for this test only once (but do allow recording code coverage).
73+
*
74+
* @return void
75+
*/
76+
protected function setUp(): void
77+
{
78+
if (self::$initialized === false) {
79+
// Set up the ruleset.
80+
$standard = self::FIXTURE_DIR.'/ProcessRulesetIniDirectivesTest.xml';
81+
$config = new ConfigDouble(["--standard=$standard"]);
82+
new Ruleset($config);
83+
84+
self::$initialized = true;
85+
}
86+
87+
}//end setUp()
88+
89+
90+
/**
91+
* Restore the ini values after the tests.
92+
*
93+
* @return void
94+
*/
95+
public static function tearDownAfterClass(): void
96+
{
97+
foreach (self::$originalIniValues as $name => $value) {
98+
if ($value === null) {
99+
continue;
100+
}
101+
102+
ini_set($name, $value);
103+
}
104+
105+
}//end tearDownAfterClass()
106+
107+
108+
/**
109+
* Verify the PHP ini directives are set based on the nesting level of the ruleset.
110+
*
111+
* - Highest level ruleset (root) should win over lower level (included) ruleset.
112+
* - When two rulesets at the same "level" both set the same ini, last included ruleset should win.
113+
* - But if no higher level ruleset sets the value, the values from lowel levels should be applied.
114+
* - The order of includes versus ini directives in a ruleset file is deliberately irrelevant.
115+
*
116+
* @param string $name The name of the PHP ini setting we're checking.
117+
* @param string $expected The expected value for that ini setting.
118+
*
119+
* @dataProvider dataIniDirectives
120+
*
121+
* @return void
122+
*/
123+
public function testIniDirectives($name, $expected)
124+
{
125+
$this->assertSame($expected, ini_get($name));
126+
127+
}//end testIniDirectives()
128+
129+
130+
/**
131+
* Data provider.
132+
*
133+
* @return array<string, array<string, string>>
134+
*/
135+
public static function dataIniDirectives()
136+
{
137+
return [
138+
'Ini set in parent before includes; all includes try to overrule; parent should win' => [
139+
'name' => 'highlight.comment',
140+
'expected' => 'parent',
141+
],
142+
'Ini set in parent after includes; all includes try to overrule; parent should win' => [
143+
'name' => 'highlight.default',
144+
'expected' => 'parent',
145+
],
146+
'Ini set in child A before includes; also set in grandchild A; child A should win' => [
147+
'name' => 'highlight.html',
148+
'expected' => 'child A',
149+
],
150+
'Ini set in both child A and child B; B is included last via ruleset includes; child B should win' => [
151+
'name' => 'highlight.keyword',
152+
'expected' => 'child B',
153+
],
154+
'Ini set in child B; also set in non-direct grandchild A, child B should win' => [
155+
'name' => 'highlight.string',
156+
'expected' => 'child B',
157+
],
158+
'Ini set in parent; also set in grandchild A, parent should win' => [
159+
'name' => 'url_rewriter.hosts',
160+
'expected' => 'parent',
161+
],
162+
'Ini set in child A - no overrules' => [
163+
'name' => 'url_rewriter.tags',
164+
'expected' => 'child A',
165+
],
166+
'Ini set only in grandchild A - no overrules' => [
167+
'name' => 'user_agent',
168+
'expected' => 'grandchild A',
169+
],
170+
];
171+
172+
}//end dataIniDirectives()
173+
174+
175+
}//end class

0 commit comments

Comments
 (0)