Skip to content

Commit 902de26

Browse files
swissspidyclaude
andauthored
Check the PHP blocks in feature files as the space-indented code they are (#362)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 94ec63b commit 902de26

7 files changed

Lines changed: 253 additions & 91 deletions

File tree

.readme-partials/USING.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,24 +199,34 @@ Unlike the analysis above, a docstring that merely opens with `<?php` does not c
199199
routinely an expectation about the contents of a file rather than a file, and reformatting one would
200200
make it stop matching what it is checked against.
201201

202-
The defaults leave out the sniffs that look at a block as if it were a file of its own, along with
203-
those that ask of a fixture what is only worth asking of production code. They live in
204-
`phpcs/feature-files.sh` and are shared by the check and the fixer, so that the two cannot disagree
205-
over which sniff applies. A package replaces them wholesale by adding a `phpcs-feature-files.xml`
206-
(or `phpcs-feature-files.xml.dist`) ruleset to its root:
202+
Feature files indent with spaces, so the sniffs that would indent a block with tabs are turned around
203+
for this run: a block is checked, and fixed, as the space-indented code it is, and a tab that does
204+
find its way into one is reported and fixed like any other violation. Trailing whitespace is reported
205+
too, as the fixer leaves some behind wherever it breaks a line.
206+
207+
All of this is the `WP_CLI_CS_Feature_Files` ruleset, which is `WP_CLI_CS` with the sniffs that
208+
indent turned around, and with the sniffs that look at a block as if it were a file of its own left
209+
out, along with those that ask of a fixture what is only worth asking of production code. The check
210+
and the fixer both use it, so that the two cannot disagree over which sniff applies to a block. A
211+
package replaces it wholesale by adding a `phpcs-feature-files.xml` (or
212+
`phpcs-feature-files.xml.dist`) ruleset to its root:
207213

208214
```xml
209215
<?xml version="1.0"?>
210216
<ruleset name="WP-CLI-PROJECT-NAME-feature-files">
211217
<arg name="warning-severity" value="0"/>
212218
213-
<rule ref="WP_CLI_CS">
219+
<rule ref="WP_CLI_CS_Feature_Files">
214220
<exclude name="Generic.Files.InlineHTML"/>
215221
<exclude name="Squiz.Commenting.FileComment"/>
216222
</rule>
217223
</ruleset>
218224
```
219225

226+
Starting from `WP_CLI_CS_Feature_Files`, as above, keeps the defaults and adds to them. Starting
227+
from `WP_CLI_CS` instead gives up all of them, including the space indentation, and leaves the
228+
package to say for itself what a padded block cannot satisfy.
229+
220230
The blocks are left alone when a run is narrowed down to a path, as in `composer phpcs -- src/`,
221231
since such an argument is about the files of the package itself.
222232

README.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,24 +210,34 @@ Unlike the analysis above, a docstring that merely opens with `<?php` does not c
210210
routinely an expectation about the contents of a file rather than a file, and reformatting one would
211211
make it stop matching what it is checked against.
212212

213-
The defaults leave out the sniffs that look at a block as if it were a file of its own, along with
214-
those that ask of a fixture what is only worth asking of production code. They live in
215-
`phpcs/feature-files.sh` and are shared by the check and the fixer, so that the two cannot disagree
216-
over which sniff applies. A package replaces them wholesale by adding a `phpcs-feature-files.xml`
217-
(or `phpcs-feature-files.xml.dist`) ruleset to its root:
213+
Feature files indent with spaces, so the sniffs that would indent a block with tabs are turned around
214+
for this run: a block is checked, and fixed, as the space-indented code it is, and a tab that does
215+
find its way into one is reported and fixed like any other violation. Trailing whitespace is reported
216+
too, as the fixer leaves some behind wherever it breaks a line.
217+
218+
All of this is the `WP_CLI_CS_Feature_Files` ruleset, which is `WP_CLI_CS` with the sniffs that
219+
indent turned around, and with the sniffs that look at a block as if it were a file of its own left
220+
out, along with those that ask of a fixture what is only worth asking of production code. The check
221+
and the fixer both use it, so that the two cannot disagree over which sniff applies to a block. A
222+
package replaces it wholesale by adding a `phpcs-feature-files.xml` (or
223+
`phpcs-feature-files.xml.dist`) ruleset to its root:
218224

219225
```xml
220226
<?xml version="1.0"?>
221227
<ruleset name="WP-CLI-PROJECT-NAME-feature-files">
222228
<arg name="warning-severity" value="0"/>
223229
224-
<rule ref="WP_CLI_CS">
230+
<rule ref="WP_CLI_CS_Feature_Files">
225231
<exclude name="Generic.Files.InlineHTML"/>
226232
<exclude name="Squiz.Commenting.FileComment"/>
227233
</rule>
228234
</ruleset>
229235
```
230236

237+
Starting from `WP_CLI_CS_Feature_Files`, as above, keeps the defaults and adds to them. Starting
238+
from `WP_CLI_CS` instead gives up all of them, including the space indentation, and leaves the
239+
package to say for itself what a padded block cannot satisfy.
240+
231241
The blocks are left alone when a run is narrowed down to a path, as in `composer phpcs -- src/`,
232242
since such an argument is about the files of the package itself.
233243

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?xml version="1.0"?>
2+
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="WP_CLI_CS_Feature_Files" xsi:noNamespaceSchemaLocation="../vendor/squizlabs/php_codesniffer/phpcs.xsd">
3+
4+
<description>Coding standard for the PHP blocks embedded in Behat feature files</description>
5+
6+
<!--
7+
The defaults for the code style check of the PHP blocks that Behat feature
8+
files embed in docstrings, used by `run-phpcs-tests` and by
9+
`run-phpcbf-cleanup`.
10+
11+
Keeping them in one place is what makes the check and the fixer agree: a
12+
sniff left out for one but not the other would have the fixer rewrite
13+
feature files over something the check never reports, or have the check
14+
report something the fixer refuses to touch.
15+
16+
A package replaces these defaults wholesale by adding a
17+
`phpcs-feature-files.xml` (or `phpcs-feature-files.xml.dist`) ruleset to its
18+
root, which is then used as the standard instead.
19+
-->
20+
21+
<!-- Warnings are advisory, and the fixer must not rewrite a feature file
22+
over something the check does not report. -->
23+
<arg name="warning-severity" value="0"/>
24+
25+
<rule ref="WP_CLI_CS">
26+
27+
<!--
28+
A block is not a file. It is padded with one empty line per preceding
29+
line of the feature file so that reported line numbers match it, and one
30+
that does not bring its own opening tag is given one. Neither is part of
31+
the snippet, and the shared docstring indentation is taken off before the
32+
check and put back afterwards, so none of the sniffs looking at a file as
33+
a whole apply.
34+
-->
35+
<exclude name="Generic.Files.InlineHTML"/>
36+
<exclude name="Generic.Files.LineEndings"/>
37+
<exclude name="PSR2.Files.EndFileNewline"/>
38+
<exclude name="PSR12.Files.FileHeader"/>
39+
<exclude name="Squiz.Commenting.FileComment"/>
40+
<exclude name="Generic.PHP.RequireStrictTypes"/>
41+
<exclude name="WordPress.Files.FileName"/>
42+
<exclude name="Universal.WhiteSpace.PrecisionAlignment"/>
43+
44+
<!--
45+
A block is a fixture, not production code. Snippets exist to set up a
46+
scenario, run inside a throwaway WordPress installation, are written to
47+
be read at a glance, and are routinely a single class or function on
48+
their own.
49+
-->
50+
<exclude name="WordPress.NamingConventions.PrefixAllGlobals"/>
51+
<exclude name="WordPress.WP.GlobalVariablesOverride"/>
52+
<exclude name="WordPress.PHP.YodaConditions"/>
53+
<exclude name="Universal.Files.SeparateFunctionsFromOO"/>
54+
<exclude name="Generic.Files.OneObjectStructurePerFile"/>
55+
<exclude name="Universal.Namespaces.OneDeclarationPerFile"/>
56+
<exclude name="Universal.Namespaces.DisallowCurlyBraceSyntax"/>
57+
<exclude name="Universal.Namespaces.DisallowDeclarationWithoutName"/>
58+
<exclude name="PSR2.Methods.FunctionClosingBrace"/>
59+
60+
<!-- A snippet testing error handling is deliberately incomplete. -->
61+
<exclude name="Generic.CodeAnalysis.EmptyStatement"/>
62+
63+
<!-- A feature file indents with spaces. -->
64+
<exclude name="Generic.WhiteSpace.DisallowSpaceIndent"/>
65+
</rule>
66+
67+
<!-- The other way round, so that a block arriving with tabs is brought in line. -->
68+
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
69+
70+
<rule ref="Generic.WhiteSpace.ScopeIndent">
71+
<properties>
72+
<property name="tabIndent" value="false"/>
73+
</properties>
74+
</rule>
75+
76+
<rule ref="WordPress.Arrays.ArrayIndentation">
77+
<properties>
78+
<property name="tabIndent" value="false"/>
79+
</properties>
80+
</rule>
81+
82+
<!--
83+
The fixer leaves whitespace behind wherever it breaks a line, so this sniff
84+
has to run. Only the two message codes that look at the file as a whole are
85+
in the way: taking the padding off a block would shift every line number
86+
reported against the feature file it came from.
87+
-->
88+
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace">
89+
<exclude name="Squiz.WhiteSpace.SuperfluousWhitespace.StartFile"/>
90+
<exclude name="Squiz.WhiteSpace.SuperfluousWhitespace.EndFile"/>
91+
</rule>
92+
93+
</ruleset>

bin/run-phpcbf-cleanup

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ done
2525
DIR="$(cd -P "$(dirname "$SOURCE")/.." && pwd)"
2626

2727
# A ruleset of the same purpose in the package root replaces the defaults
28-
# wholesale. Both scripts read the defaults from the same file, so that the
29-
# check and the fixer cannot disagree over which sniff applies to a block.
28+
# wholesale. Both scripts use the same ruleset otherwise, so that the check and
29+
# the fixer cannot disagree over which sniff applies to a block.
3030
FEATURE_STANDARD=""
3131
for CANDIDATE in "phpcs-feature-files.xml" "phpcs-feature-files.xml.dist"
3232
do
@@ -37,13 +37,9 @@ do
3737
fi
3838
done
3939

40-
FEATURE_ARGS=""
41-
if [ -z "$FEATURE_STANDARD" ] && [ -f "$DIR/phpcs/feature-files.sh" ]
40+
if [ -z "$FEATURE_STANDARD" ]
4241
then
43-
. "$DIR/phpcs/feature-files.sh"
44-
FEATURE_STANDARD="$WP_CLI_TESTS_FEATURE_STANDARD"
45-
# Holds no path, so leaving it unquoted below splits it into arguments.
46-
FEATURE_ARGS="$WP_CLI_TESTS_FEATURE_ARGS"
42+
FEATURE_STANDARD="WP_CLI_CS_Feature_Files"
4743
fi
4844

4945
# An argument naming what to fix applies to the files of the package itself, so
@@ -57,7 +53,7 @@ do
5753
esac
5854
done
5955

60-
if [ "$FIX_BLOCKS" -eq 1 ] && [ -d "features" ] && [ -n "$FEATURE_STANDARD" ] \
56+
if [ "$FIX_BLOCKS" -eq 1 ] && [ -d "features" ] \
6157
&& [ -f "$DIR/utils/extract-feature-php.php" ]
6258
then
6359
TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'feature_phpcbf')
@@ -68,8 +64,7 @@ then
6864
then
6965
if [ -n "$(ls -A "$TEMP_DIR" 2>/dev/null)" ]
7066
then
71-
# shellcheck disable=SC2086 # Intentional word splitting.
72-
vendor/bin/phpcbf --standard="$FEATURE_STANDARD" $FEATURE_ARGS \
67+
vendor/bin/phpcbf --standard="$FEATURE_STANDARD" \
7368
"$TEMP_DIR" >/dev/null || EXIT_CODE=$?
7469

7570
php "$DIR/utils/extract-feature-php.php" update features "$TEMP_DIR" >/dev/null || EXIT_CODE=$?

bin/run-phpcs-tests

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ done
4444
DIR="$(cd -P "$(dirname "$SOURCE")/.." && pwd)"
4545

4646
# A ruleset of the same purpose in the package root replaces the defaults
47-
# wholesale. Both scripts read the defaults from the same file, so that the
48-
# check and the fixer cannot disagree over which sniff applies to a block.
47+
# wholesale. Both scripts use the same ruleset otherwise, so that the check and
48+
# the fixer cannot disagree over which sniff applies to a block.
4949
FEATURE_STANDARD=""
5050
for CANDIDATE in "phpcs-feature-files.xml" "phpcs-feature-files.xml.dist"
5151
do
@@ -56,13 +56,9 @@ do
5656
fi
5757
done
5858

59-
FEATURE_ARGS=""
60-
if [ -z "$FEATURE_STANDARD" ] && [ -f "$DIR/phpcs/feature-files.sh" ]
59+
if [ -z "$FEATURE_STANDARD" ]
6160
then
62-
. "$DIR/phpcs/feature-files.sh"
63-
FEATURE_STANDARD="$WP_CLI_TESTS_FEATURE_STANDARD"
64-
# Holds no path, so leaving it unquoted below splits it into arguments.
65-
FEATURE_ARGS="$WP_CLI_TESTS_FEATURE_ARGS"
61+
FEATURE_STANDARD="WP_CLI_CS_Feature_Files"
6662
fi
6763

6864
# An argument naming what to check applies to the files of the package itself,
@@ -76,7 +72,7 @@ do
7672
esac
7773
done
7874

79-
if [ "$CHECK_BLOCKS" -eq 1 ] && [ -d "features" ] && [ -n "$FEATURE_STANDARD" ] \
75+
if [ "$CHECK_BLOCKS" -eq 1 ] && [ -d "features" ] \
8076
&& [ -f "$DIR/utils/extract-feature-php.php" ]
8177
then
8278
TEMP_DIR=$(mktemp -d 2>/dev/null || mktemp -d -t 'feature_phpcs')
@@ -93,8 +89,7 @@ then
9389
# `--basepath` reduces the reported paths to the part that is worth
9490
# showing, which also keeps PHPCS from truncating them from the left
9591
# once they grow past the width of the report.
96-
# shellcheck disable=SC2086 # Intentional word splitting.
97-
vendor/bin/phpcs --standard="$FEATURE_STANDARD" $FEATURE_ARGS \
92+
vendor/bin/phpcs --standard="$FEATURE_STANDARD" \
9893
--basepath="$TEMP_DIR" "$TEMP_DIR" >"$PHPCS_OUTPUT" 2>&1 || EXIT_CODE=$?
9994

10095
# Findings are reported against the feature files they came from.

phpcs/feature-files.sh

Lines changed: 0 additions & 57 deletions
This file was deleted.

0 commit comments

Comments
 (0)