├── .editorconfig
├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── 10up-Default
├── Sniffs
│ └── WP
│ │ └── PostsPerPageNoUnlimitedSniff.php
├── ruleset-test.inc
├── ruleset-test.php
└── ruleset.xml
├── LICENSE
├── README.md
├── bin
├── ruleset-tests
└── xml-lint
├── composer.json
├── composer.lock
└── tests
└── RulesetTest.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | trim_trailing_whitespace = true
7 | indent_style = tab
8 | indent_size = 4
9 |
10 | [*.{json,yml,md}]
11 | indent_style = space
12 | indent_size = 2
13 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | # Run on pushes to `master` and on all pull requests.
5 | # Prevent the "push" build from running when there are only irrelevant changes.
6 | push:
7 | branches:
8 | - master
9 | paths-ignore:
10 | - '**.md'
11 | pull_request:
12 |
13 | # Allow manually triggering the workflow.
14 | workflow_dispatch:
15 |
16 | # Cancels all previous workflow runs for the same branch that have not yet completed.
17 | concurrency:
18 | # The concurrency group contains the workflow name and the branch name.
19 | group: ${{ github.workflow }}-${{ github.ref }}
20 | cancel-in-progress: true
21 |
22 | jobs:
23 |
24 | checkxml:
25 | name: 'Check XML Validates'
26 | runs-on: ubuntu-latest
27 |
28 | steps:
29 | - name: Checkout code
30 | uses: actions/checkout@v3
31 |
32 | - name: Install xmllint
33 | run: |
34 | sudo apt-get update
35 | sudo apt-get install --no-install-recommends -y libxml2-utils
36 |
37 | # Show XML violations inline in the file diff.
38 | # @link https://github.com/marketplace/actions/xmllint-problem-matcher
39 | - uses: korelstar/xmllint-problem-matcher@v1
40 |
41 | - name: 'Validate XML against schema and check code style'
42 | run: ./bin/xml-lint
43 |
44 | test:
45 | runs-on: ubuntu-latest
46 |
47 | strategy:
48 | # Keys:
49 | # - php: The PHP versions to test against.
50 | # - dependencies: The PHPCS dependencies versions to test against.
51 | # IMPORTANT: test runs shouldn't fail because of PHPCS being incompatible with a PHP version.
52 | # - PHPCS will run without errors on PHP 5.4 - 7.4 on any supported version.
53 | # - PHP 8.0 needs PHPCS 3.5.7+ to run without errors, and we require a higher minimum version.
54 | # - PHP 8.1 needs PHPCS 3.6.1+ to run without errors, but works best with 3.7.1+, and we require at least this minimum version.
55 | matrix:
56 | php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
57 | dependencies: ['stable']
58 | name: "Test: PHP ${{ matrix.php }} - PHPCS"
59 |
60 | steps:
61 | - name: Checkout code
62 | uses: actions/checkout@v3
63 |
64 | # With stable PHPCS dependencies, allow for PHP deprecation notices.
65 | # Unit tests don't need to fail on those for stable releases where those issues won't get fixed anymore.
66 | - name: Setup ini config
67 | id: set_ini
68 | run: |
69 | echo 'PHP_INI=error_reporting=E_ALL & ~E_DEPRECATED, display_errors=On' >> $GITHUB_OUTPUT
70 |
71 | - name: Install PHP
72 | uses: shivammathur/setup-php@v2
73 | with:
74 | php-version: ${{ matrix.php }}
75 | ini-values: ${{ steps.set_ini.outputs.PHP_INI }}
76 | coverage: none
77 |
78 | # Install dependencies and handle caching in one go.
79 | # @link https://github.com/marketplace/actions/install-composer-dependencies
80 | - name: Install Composer dependencies - normal
81 | if: ${{ startsWith( matrix.php, '8' ) == false }}
82 | uses: "ramsey/composer-install@v2"
83 | with:
84 | # Bust the cache at least once a month - output format: YYYY-MM.
85 | custom-cache-suffix: $(date -u "+%Y-%m")
86 |
87 | # PHPUnit 7.x does not allow for installation on PHP 8, so ignore platform
88 | # requirements to get PHPUnit 7.x to install on nightly.
89 | - name: Install Composer dependencies - with ignore platform
90 | if: ${{ startsWith( matrix.php, '8' ) }}
91 | uses: "ramsey/composer-install@v2"
92 | with:
93 | composer-options: --ignore-platform-req=php+
94 | custom-cache-suffix: $(date -u "+%Y-%m")
95 |
96 | - name: Run the ruleset tests
97 | run: ./bin/ruleset-tests
98 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 |
3 | # IDE
4 | .history
5 |
--------------------------------------------------------------------------------
/10up-Default/Sniffs/WP/PostsPerPageNoUnlimitedSniff.php:
--------------------------------------------------------------------------------
1 | array(
42 | 'type' => 'warning',
43 | 'keys' => array(
44 | 'posts_per_page',
45 | 'numberposts',
46 | ),
47 | ),
48 | );
49 | }
50 |
51 | /**
52 | * Callback to process each confirmed key, to check value.
53 | *
54 | * @param string $key Array index / key.
55 | * @param mixed $val Assigned value.
56 | * @param int $line Token line.
57 | * @param array $group Group definition.
58 | * @return mixed FALSE if no match, TRUE if matches, STRING if matches
59 | * with custom error message passed to ->process().
60 | */
61 | public function callback( $key, $val, $line, $group ) {
62 | $this->posts_per_page = (int) $this->posts_per_page;
63 |
64 | if ( strval( $val ) === '-1' ) {
65 | return 'Detected unlimited pagination, `%s` is set to `%s`';
66 | }
67 |
68 | return false;
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/10up-Default/ruleset-test.inc:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 999,
48 | );
49 |
50 | // Warnings: WordPress.WP.PostsPerPage.posts_per_page_posts_per_page
51 | _query_posts( 'posts_per_page=999' );
52 |
53 | // Warnings: WordPress.WP.PostsPerPage.posts_per_page_posts_per_page
54 | $query_args['posts_per_page'] = 999;
55 |
56 | // Errors: WordPress.DateTime.RestrictedFunctions.timezone_change_date_default_timezone_set)
57 | date_default_timezone_set( 'FooBar' );
58 |
59 | $b = function () { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
60 | global $wpdb;
61 | $listofthings = wp_cache_get( 'foo' );
62 | if ( ! $listofthings ) {
63 | $foo = "column = 'test'";
64 |
65 | // Errors: WordPress.DB.PreparedSQL.NotPrepared
66 | // Warnings: WordPress.DB.DirectDatabaseQuery.DirectQuery
67 | $listofthings = $wpdb->query( 'SELECT something FROM somewhere WHERE ' . $foo );
68 | wp_cache_set( 'foo', $listofthings );
69 | }
70 | };
71 |
72 | // Warnings: WordPress.DB.DirectDatabaseQuery.DirectQuery & WordPress.DB.PreparedSQLPlaceholders.UnnecessaryPrepare & WordPress.DB.DirectDatabaseQuery.NoCaching
73 | $baz = $wpdb->get_results( $wpdb->prepare( 'SELECT X FROM Y ' ) ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Warning x 2.
74 |
75 | $test = [
76 | // Warnings: WordPress.DB.SlowDBQuery.slow_db_query_tax_query
77 | 'tax_query' => [],
78 | ];
79 |
80 | // Errors: PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket
81 | new WP_Query( array(
82 | // Warnings: WordPress.DB.SlowDBQuery.slow_db_query_meta_query
83 | 'meta_query' => [],
84 | // Warnings: WordPress.DB.SlowDBQuery.slow_db_query_meta_key & WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned
85 | 'meta_key' => 'foo',
86 | // Warnings: Warnings: WordPress.DB.SlowDBQuery.slow_db_query_meta_value
87 | 'meta_value' => 'bar',
88 | // Errors: PEAR.Functions.FunctionCallSignature.CloseBracketLine
89 | ) );
90 |
91 | // WordPress.WP.GlobalVariablesOverride
92 | $GLOBALS['wpdb'] = 'test';
93 |
94 | // Errors: Generic.CodeAnalysis.EmptyStatement.DetectedIf
95 | // Warnings: Universal.Operators.StrictComparisons.LooseEqual
96 | if ( true == $true ) {
97 | }
98 |
99 | // Errors: Generic.CodeAnalysis.EmptyStatement.DetectedIf & Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
100 | // Warnings: Generic.CodeAnalysis.AssignmentInCondition.Found
101 | if ( $test = get_post( $post ) ) {
102 | }
103 |
104 | // Errors: Generic.CodeAnalysis.EmptyStatement.DetectedIf)
105 | // Warnings: WordPress.PHP.StrictInArray.MissingTrueStrict
106 | if ( true === in_array( $foo, $bar ) ) {
107 | }
108 |
109 | // Errors: WordPress.PHP.DontExtract.extract_extract
110 | extract( $foobar );
111 |
112 | // WordPress.WP.CronInterval
113 | function my_add_weekly( $schedules ) {
114 | $schedules['every_6_mins'] = array(
115 | 'interval' => 360,
116 | // Errors: NormalizedArrays.Arrays.CommaAfterLast.MissingMultiLine
117 | // Warnings: WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned
118 | 'display' => __( 'Once every 6 minutes' )
119 | );
120 | return $schedules;
121 | }
122 |
123 | // Errors: PEAR.Functions.FunctionCallSignature.SpaceBeforeCloseBracket
124 | // Warnings:WordPress.WP.CronInterval.CronSchedulesInterval
125 | add_filter( 'cron_schedules', 'my_add_weekly');
126 |
127 | // Errors: Universal.Files.SeparateFunctionsFromOO.Mixed
128 | class TestClass extends MyClass
129 | // Errors: Generic.Classes.OpeningBraceSameLine.BraceOnNewLine
130 | {
131 | // Errors: Squiz.Scope.MethodScope.Missing
132 | function __construct() {
133 | parent::MYCLASS();
134 | parent::__construct();
135 | }
136 | }
137 |
138 | // Errors: Generic.Files.OneObjectStructurePerFile.MultipleFound
139 | class OldClass
140 | // Errors: Generic.Classes.OpeningBraceSameLine.BraceOnNewLine
141 | {
142 |
143 | // Errors: Squiz.Scope.MethodScope.Missing
144 | function OldClass()
145 | // Errors: (Generic.Functions.OpeningFunctionBraceKernighanRitchie.BraceOnNewLine
146 | {
147 | }
148 | }
149 |
150 | // Errors: Generic.Files.OneObjectStructurePerFile.MultipleFound
151 | // Warnings: Generic.Classes.DuplicateClassName.Found
152 | class TestClass extends MyClass {
153 |
154 | // Errors: Squiz.Scope.MethodScope.Missing
155 | function TestClass() {
156 | parent::MyClass();
157 | parent::__construct();
158 | }
159 | }
160 |
161 | // Errors: Squiz.PHP.EmbeddedPhp.ContentAfterEnd & Generic.PHP.DisallowShortOpenTag.EchoFound
162 | ?> = esc_html( $var );
163 |
164 | // Warnings: (Squiz.PHP.CommentedOutCode.Found
165 | // if (empty($this)) {echo 'This is will not work';}
166 |
167 | // Errors: PEAR.Functions.FunctionCallSignature.SpaceAfterOpenBracket & Squiz.PHP.Eval.Discouraged
168 | eval('$var = 4;'); // Error + Message.
169 |
170 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
171 | base64_decode( 'VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw==' );
172 |
173 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
174 | base64_encode( 'This is an encoded string' );
175 |
176 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.obfuscation_convert_uudecode
177 | convert_uudecode( "+22!L;W9E(%!(4\"$`\n`" );
178 |
179 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.obfuscation_convert_uudecode
180 | convert_uuencode( "test\ntext text\r\n" );
181 |
182 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.obfuscation_str_rot13
183 | str_rot13( 'The quick brown fox jumps over the lazy dog.' );
184 |
185 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
186 | serialize();
187 |
188 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
189 | unserialize();
190 |
191 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.urlencode_urlencode
192 | urlencode();
193 |
194 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_passthru
195 | passthru( 'cat myfile.zip', $err );
196 |
197 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_proc_open
198 | $process = proc_open( 'php', $descriptorspec, $pipes, $cwd, $env );
199 |
200 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_system
201 | $last_line = system( 'ls', $retval );
202 |
203 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_popen
204 | $handle = popen( '/bin/ls', 'r' );
205 |
206 | // Warnings: WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting & WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting
207 | error_reporting();
208 |
209 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_ini_restore
210 | ini_restore();
211 |
212 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_apache_setenv
213 | apache_setenv();
214 |
215 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv
216 | putenv();
217 |
218 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_set_include_path
219 | set_include_path();
220 |
221 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_restore_include_path && PHPCompatibility.FunctionUse.RemovedFunctions.restore_include_pathDeprecated
222 | restore_include_path();
223 |
224 | // Errors: PHPCompatibility.FunctionUse.RemovedFunctions.magic_quotes_runtimeDeprecatedRemoved
225 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_magic_quotes_runtime
226 | magic_quotes_runtime();
227 |
228 | // Errors: PHPCompatibility.FunctionUse.RemovedFunctions.set_magic_quotes_runtimeDeprecatedRemoved
229 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_set_magic_quotes_runtime
230 | set_magic_quotes_runtime();
231 |
232 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_dl & PHPCompatibility.FunctionUse.RemovedFunctions.dlDeprecated
233 | dl();
234 |
235 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_exec
236 | exec( 'whoami' );
237 |
238 | // Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_shell_exec
239 | $output = shell_exec( 'ls -lart' ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Error.
240 |
241 | // Warnings: WordPress.PHP.DevelopmentFunctions.error_log_var_dump
242 | var_dump();
243 |
244 | // Warnings: WordPress.PHP.DevelopmentFunctions.error_log_var_export
245 | var_export();
246 |
247 | // Warnings: WordPress.PHP.DevelopmentFunctions.error_log_print_r
248 | print_r();
249 |
250 | // Warnings: WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
251 | trigger_error( 'message' );
252 |
253 | // Warnings: WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
254 | set_error_handler();
255 |
256 | // Warnings: WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
257 | debug_backtrace();
258 |
259 | // Warnings: WordPress.PHP.DevelopmentFunctions.error_log_debug_print_backtrace
260 | debug_print_backtrace();
261 |
262 | // Warnings: WordPress.PHP.DevelopmentFunctions.error_log_wp_debug_backtrace_summary
263 | wp_debug_backtrace_summary();
264 |
265 | // Warnings: WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_phpinfo
266 | phpinfo();
267 |
268 | // Warnings: WordPress.PHP.DevelopmentFunctions.error_log_error_log
269 | error_log();
270 |
271 | // OK to set
272 | ini_set( 'auto_detect_line_endings', true );
273 |
274 | // Errors: PHPCompatibility.IniDirectives.RemovedIniDirectives.highlight_bgDeprecatedRemoved
275 | ini_set( 'highlight.bg', '#000000' );
276 |
277 | // OK to set
278 | ini_set( 'highlight.comment', '#000000' );
279 |
280 | // OK to set
281 | ini_set( 'highlight.default', '#000000' );
282 |
283 | // OK to set
284 | ini_set( 'highlight.html', '#000000' );
285 |
286 | // OK to set
287 | ini_set( 'highlight.keyword', '#000000' );
288 |
289 | // OK to set
290 | ini_set( 'highlight.string', '#000000' );
291 |
292 | // OK to set
293 | ini_set( 'short_open_tag', 1 );
294 |
295 | // Errors: WordPress.PHP.IniSet.bcmath_scale_Disallowed
296 | ini_set( 'bcmath.scale', 1 );
297 |
298 | // Errors: WordPress.PHP.IniSet.display_errors_Disallowed
299 | ini_set( 'display_errors', 1 );
300 |
301 | // Errors: WordPress.PHP.IniSet.error_reporting_Disallowed
302 | ini_set( 'error_reporting', 1 );
303 |
304 | // Errors: WordPress.PHP.IniSet.filter_default_Disallowed
305 | ini_set( 'filter.default', 1 );
306 |
307 | // Errors: WordPress.PHP.IniSet.filter_default_flags_Disallowed
308 | ini_set( 'filter.default_flags', 1 );
309 |
310 | // Errors: WordPress.PHP.IniSet.iconv_input_encoding_Disallowed
311 | // Warnings: PHPCompatibility.IniDirectives.RemovedIniDirectives.iconv_input_encodingDeprecated
312 | ini_set( 'iconv.input_encoding', 1 );
313 |
314 | // Errors: WordPress.PHP.IniSet.iconv_internal_encoding_Disallowed
315 | // Warnings: PHPCompatibility.IniDirectives.RemovedIniDirectives.iconv_internal_encodingDeprecated
316 | ini_set( 'iconv.internal_encoding', 1 );
317 |
318 | // Errors: WordPress.PHP.IniSet.iconv_output_encoding_Disallowed
319 | // Warnings: PHPCompatibility.IniDirectives.RemovedIniDirectives.iconv_output_encodingDeprecated
320 | ini_set( 'iconv.output_encoding', 1 );
321 |
322 | // Errors: WordPress.PHP.IniSet.ignore_user_abort_Disallowed
323 | ini_set( 'ignore_user_abort', 1 );
324 |
325 | // Errors: WordPress.PHP.IniSet.log_errors_Disallowed
326 | ini_set( 'log_errors', 1 );
327 |
328 | // Errors: WordPress.PHP.IniSet.max_execution_time_Disallowed
329 | ini_set( 'max_execution_time', 1 );
330 |
331 | // Errors: WordPress.PHP.IniSet.memory_limit_Disallowed
332 | ini_set( 'memory_limit', 1 );
333 |
334 | // Errors: WordPress.PHP.IniSet.short_open_tag_Disallowed
335 | ini_set( 'short_open_tag', 'off' );
336 |
337 | // Warnings: WordPress.PHP.IniSet.Risky
338 | ini_set( 'foo', true );
339 |
340 | // OK to set
341 | ini_alter( 'auto_detect_line_endings', true );
342 |
343 | // OK to set
344 | ini_alter( 'highlight.bg', '#000000' );
345 |
346 | // OK to set
347 | ini_alter( 'highlight.comment', '#000000' );
348 |
349 | // OK to set
350 | ini_alter( 'highlight.default', '#000000' );
351 |
352 | // OK to set
353 | ini_alter( 'highlight.html', '#000000' );
354 |
355 | // OK to set
356 | ini_alter( 'highlight.keyword', '#000000' );
357 |
358 | // OK to set
359 | ini_alter( 'highlight.string', '#000000' );
360 |
361 | // OK to set
362 | ini_alter( 'short_open_tag', 1 );
363 |
364 | // Errors: WordPress.PHP.IniSet.bcmath_scale_Disallowed
365 | ini_alter( 'bcmath.scale', 1 );
366 |
367 | // Errors: WordPress.PHP.IniSet.display_errors_Disallowed
368 | ini_alter( 'display_errors', 1 );
369 |
370 | // Errors: WordPress.PHP.IniSet.error_reporting_Disallowed
371 | ini_alter( 'error_reporting', 1 );
372 |
373 | // Errors: WordPress.PHP.IniSet.filter_default_Disallowed
374 | ini_alter( 'filter.default', 1 );
375 |
376 | // Errors: WordPress.PHP.IniSet.filter_default_flags_Disallowed
377 | ini_alter( 'filter.default_flags', 1 );
378 |
379 | // Errors: WordPress.PHP.IniSet.iconv_input_encoding_Disallowed
380 | ini_alter( 'iconv.input_encoding', 1 );
381 |
382 | // Errors: WordPress.PHP.IniSet.iconv_internal_encoding_Disallowed
383 | ini_alter( 'iconv.internal_encoding', 1 );
384 |
385 | // Errors: WordPress.PHP.IniSet.iconv_output_encoding_Disallowed
386 | ini_alter( 'iconv.output_encoding', 1 );
387 |
388 | // Errors: WordPress.PHP.IniSet.ignore_user_abort_Disallowed
389 | ini_alter( 'ignore_user_abort', 1 );
390 |
391 | // Errors: WordPress.PHP.IniSet.log_errors_Disallowed
392 | ini_alter( 'log_errors', 1 );
393 |
394 | // Errors: WordPress.PHP.IniSet.max_execution_time_Disallowed
395 | ini_alter( 'max_execution_time', 1 );
396 |
397 | // Errors: WordPress.PHP.IniSet.memory_limit_Disallowed
398 | ini_alter( 'memory_limit', 1 );
399 |
400 | // Errors: WordPress.PHP.IniSet.short_open_tag_Disallowed
401 | ini_alter( 'short_open_tag', 'off' );
402 |
403 | // Warnings: WordPress.PHP.IniSet.Risky
404 | ini_alter( 'foo', true );
405 |
406 | // Warnings: WordPress.WP.AlternativeFunctions.curl_curl_init
407 | curl_init();
408 |
409 | // Warnings: WordPress.WP.AlternativeFunctions.curl_curl_close
410 | curl_close( $ch );
411 |
412 | // Warnings: WordPress.WP.AlternativeFunctions.curl_curl_getinfo
413 | CURL_getinfo();
414 |
415 | // Warnings: WordPress.WP.AlternativeFunctions.parse_url_parse_url
416 | parse_url( 'http://example.com/' );
417 |
418 | // Warnings: WordPress.WP.AlternativeFunctions.json_encode_json_encode
419 | $json = json_encode( $thing ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Warning.
420 |
421 | // Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_readfile
422 | readfile();
423 |
424 | // Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_fclose
425 | fclose();
426 |
427 | // Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_fopen
428 | fopen();
429 |
430 | // Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_fread
431 | fread();
432 |
433 | // Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_fsockopen
434 | fsockopen();
435 |
436 | // Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_pfsockopen
437 | pfsockopen();
438 |
439 | // Warnings: WordPress.WP.AlternativeFunctions.rand_seeding_srand
440 | srand();
441 |
442 | // Warnings: WordPress.WP.AlternativeFunctions.rand_seeding_mt_srand
443 | mt_srand();
444 |
445 | // Warnings: WordPress.WP.AlternativeFunctions.rand_rand
446 | rand();
447 |
448 | // Warnings: WordPress.WP.AlternativeFunctions.rand_mt_rand
449 | mt_rand();
450 |
451 | // Errors: Generic.Files.OneObjectStructurePerFile.MultipleFound
452 | class MyClass {
453 |
454 | // Errors: Squiz.Scope.MethodScope.Missing
455 | function my_function() {
456 | // Errors: Squiz.Functions.MultiLineFunctionDeclaration.SpaceAfterFunction
457 | return function() {
458 | $this->my_callback(); // OK - new VariableAnalysis doesn't flag $this as undefined in closure.
459 | };
460 | }
461 |
462 | // Errors: Squiz.Scope.MethodScope.Missing
463 | function my_callback() {}
464 | }
465 |
466 | // Errors: Generic.VersionControl.GitMergeConflict.OpenerFound
467 | ?>
468 | <<<<<<< HEAD
469 |
470 | >>>>>>>
471 |
479 |
480 |
481 | [
16 | 4 => 1,
17 | 5 => 1,
18 | 6 => 41,
19 | 10 => 5,
20 | 15 => 2,
21 | 20 => 1,
22 | 22 => 1,
23 | 26 => 1,
24 | 31 => 1,
25 | 33 => 3,
26 | 35 => 1,
27 | 40 => 1,
28 | 43 => 2,
29 | 57 => 1,
30 | 67 => 1,
31 | 81 => 1,
32 | 89 => 1,
33 | 96 => 1,
34 | 101 => 2,
35 | 106 => 1,
36 | 110 => 1,
37 | 118 => 1,
38 | 125 => 1,
39 | 128 => 1,
40 | 130 => 1,
41 | 132 => 1,
42 | 139 => 1,
43 | 141 => 1,
44 | 144 => 1,
45 | 146 => 1,
46 | 152 => 1,
47 | 155 => 1,
48 | 162 => 2,
49 | 168 => 3,
50 | 222 => 1,
51 | 226 => 1,
52 | 230 => 1,
53 | 275 => 1,
54 | 296 => 1,
55 | 299 => 1,
56 | 302 => 1,
57 | 305 => 1,
58 | 308 => 1,
59 | 312 => 1,
60 | 316 => 1,
61 | 320 => 1,
62 | 323 => 1,
63 | 326 => 1,
64 | 329 => 1,
65 | 332 => 1,
66 | 335 => 1,
67 | 365 => 1,
68 | 368 => 1,
69 | 371 => 1,
70 | 374 => 1,
71 | 377 => 1,
72 | 380 => 1,
73 | 383 => 1,
74 | 386 => 1,
75 | 389 => 1,
76 | 392 => 1,
77 | 395 => 1,
78 | 398 => 1,
79 | 401 => 1,
80 | 452 => 1,
81 | 455 => 1,
82 | 457 => 1,
83 | 463 => 1,
84 | 468 => 1,
85 | 475 => 1,
86 | 478 => 1,
87 | 483 => 4,
88 | 486 => 1,
89 | ],
90 | 'warnings' => [
91 | 15 => 1,
92 | 40 => 2,
93 | 47 => 1,
94 | 51 => 1,
95 | 54 => 1,
96 | 67 => 1,
97 | 73 => 3,
98 | 77 => 1,
99 | 83 => 1,
100 | 85 => 2,
101 | 87 => 1,
102 | 96 => 1,
103 | 101 => 1,
104 | 106 => 1,
105 | 118 => 1,
106 | 125 => 1,
107 | 152 => 1,
108 | 171 => 1,
109 | 174 => 1,
110 | 177 => 1,
111 | 180 => 1,
112 | 183 => 1,
113 | 186 => 1,
114 | 189 => 1,
115 | 192 => 1,
116 | 195 => 1,
117 | 198 => 1,
118 | 201 => 1,
119 | 204 => 1,
120 | 207 => 2,
121 | 210 => 1,
122 | 213 => 1,
123 | 216 => 1,
124 | 219 => 1,
125 | 222 => 1,
126 | 226 => 1,
127 | 230 => 1,
128 | 233 => 2,
129 | 236 => 1,
130 | 239 => 1,
131 | 242 => 1,
132 | 245 => 1,
133 | 248 => 1,
134 | 251 => 1,
135 | 254 => 1,
136 | 257 => 1,
137 | 260 => 1,
138 | 263 => 1,
139 | 266 => 1,
140 | 269 => 1,
141 | 272 => 1,
142 | 305 => 1,
143 | 312 => 1,
144 | 316 => 1,
145 | 320 => 1,
146 | 338 => 1,
147 | 404 => 1,
148 | 407 => 1,
149 | 410 => 1,
150 | 413 => 1,
151 | 416 => 1,
152 | 419 => 1,
153 | 422 => 1,
154 | 425 => 1,
155 | 428 => 1,
156 | 431 => 1,
157 | 434 => 1,
158 | 437 => 1,
159 | 440 => 1,
160 | 443 => 1,
161 | 446 => 1,
162 | 449 => 1,
163 | 475 => 1,
164 | 486 => 1,
165 | ],
166 | 'messages' => [],
167 | ];
168 |
169 | // If we're running on PHP 7.4, we need to account for the error thrown because `restore_include_path()` is deprecated.
170 | // See https://www.php.net/manual/en/function.restore-include-path.php
171 | if ( version_compare( PHP_VERSION, '7.4.0', '>=' ) && version_compare( PHP_VERSION, '8.0.0', '<' ) ) {
172 | $expected['errors'][ 222 ] = 2;
173 | }
174 |
175 | // We have some specific errors that are only thrown on PHP 8.2+.
176 | if ( version_compare( PHP_VERSION, '8.2.0', '<' ) ) {
177 | $expected['errors'][ 486 ] = 0;
178 | }
179 |
180 | if ( version_compare( PHP_VERSION, '7.2', '<' ) ) {
181 | $expected['errors'][ 483 ] = 4;
182 | $expected['warnings'][ 1 ] = 1;
183 | }
184 |
185 | require __DIR__ . '/../tests/RulesetTest.php';
186 |
187 | // Run the tests!
188 | $test = new \PhpcsComposer\RulesetTest( 'TenUpDefault', $expected );
189 | if ( $test->passes() ) {
190 | printf( 'All TenUpDefault tests passed!' . PHP_EOL );
191 | exit( 0 );
192 | }
193 |
194 | exit( 1 );
195 |
--------------------------------------------------------------------------------
/10up-Default/ruleset.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | A base ruleset that all other 10up rulesets should extend.
4 |
5 | */phpunit.xml*
6 | */dist/*
7 | */languages/*
8 |
9 |
10 | */bower-components/*
11 | */node_modules/*
12 | */vendor/*
13 |
14 |
15 | *\.(css|js)
16 |
17 |
18 |
19 | 0
20 |
21 |
22 |
23 |
24 | 0
25 |
26 |
27 |
28 |
29 | 0
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | warning
87 |
88 |
89 |
90 |
91 | warning
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 10up, LLC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 10up PHPCS Configuration
2 |
3 | > Composer library to provide drop in installation and configuration of [WPCS](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards) and [PHPCompatibilityWP](https://github.com/PHPCompatibility/PHPCompatibilityWP), setting reasonable defaults for WordPress development with nearly zero configuration.
4 |
5 | [](#support-level) [](https://github.com/10up/phpcs-composer/blob/master/LICENSE)
6 |
7 | ## Installation
8 |
9 | Install the library via Composer:
10 |
11 | ```bash
12 | $ composer require --dev 10up/phpcs-composer:"^3.0"
13 | ```
14 |
15 | That's it!
16 |
17 | ## Usage
18 |
19 | Lint your PHP files with the following command:
20 |
21 | ```bash
22 | $ ./vendor/bin/phpcs .
23 | ```
24 |
25 | If relying on Composer, edited the `composer.json` file by adding the following:
26 |
27 | ```json
28 | "scripts": {
29 | "lint": [
30 | "phpcs ."
31 | ],
32 | }
33 | ```
34 |
35 | Then lint via:
36 |
37 | ```bash
38 | $ composer run lint
39 | ```
40 |
41 | ### Continuous Integration
42 |
43 | PHPCS Configuration plays nicely with Continuous Integration solutions. Out of the box, the library loads the `10up-Default` ruleset, and checks for syntax errors for PHP 8.2 or higher.
44 |
45 | To override the default PHP version check, set the `--runtime-set testVersion 7.0-` configuration option. Example for PHP version 7.2 and above:
46 |
47 | ```bash
48 | $ ./vendor/bin/phpcs --runtime-set testVersion 7.2-
49 | ```
50 |
51 | See more [information about specifying PHP version](https://github.com/PHPCompatibility/PHPCompatibility#sniffing-your-code-for-compatibility-with-specific-php-versions).
52 |
53 | Note that you can only overrule PHP version check from the command-line.
54 |
55 | ### IDE Integration
56 |
57 | Some IDE integrations of PHPCS fail to register the `10up-Default` ruleset. In order to rectify this, place `.phpcs.xml.dist` at your project root:
58 |
59 | ```xml
60 |
61 |
62 |
63 |
64 | ```
65 |
66 | ## Support Level
67 |
68 | **Active:** 10up is actively working on this, and we expect to continue work for the foreseeable future including keeping tested up to the most recent version of WordPress. Bug reports, feature requests, questions, and pull requests are welcome.
69 |
70 | ## Like what you see?
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/bin/ruleset-tests:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # Run ruleset tests.
4 | #
5 | # This will check that each ruleset correctly includes the expected sniffs from this and other packages. If a sniff
6 | # reference has changed (moved category, or been renamed, etc.) then the expected errors, warnings, and messages will
7 | # not match what the output is, when the ruleset-test.inc is checked with PHPCS against the relevant ruleset.
8 | #
9 | # To run the tests, make sure you have the PHPCS, including the TenUpDefault standard, installed and executable
10 | # * using the `phpcs --standard=TenUpDefault` command.
11 | #
12 | # From the root of this VIP-Coding-Standards package, you can then run:
13 | #
14 | # ./bin/ruleset-tests
15 |
16 | # Set PHPCS_BIN, which is used in the tests/RulesetTest.php files.
17 | PHPCS_BIN="$(pwd)/vendor/bin/phpcs"
18 | export PHPCS_BIN
19 |
20 | php ./10up-Default/ruleset-test.php
21 |
--------------------------------------------------------------------------------
/bin/xml-lint:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # Check XML files.
4 | #
5 | # @link http://xmlsoft.org/xmllint.html
6 | #
7 | # EXAMPLE TO RUN LOCALLY:
8 | #
9 | # ./bin/xml-lint
10 |
11 | # Validate the ruleset XML files.
12 | xmllint --noout --schema ./vendor/squizlabs/php_codesniffer/phpcs.xsd ./*/ruleset.xml
13 |
14 | # Check the code-style consistency of the XML files.
15 | export XMLLINT_INDENT=" " # This is a tab character.
16 | diff -B --tabsize=4 ./10up-Default/ruleset.xml <(xmllint --format "./10up-Default/ruleset.xml")
17 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "10up/phpcs-composer",
3 | "description": "10up's PHP CodeSniffer Ruleset",
4 | "type": "phpcodesniffer-standard",
5 | "license": "MIT",
6 | "require": {
7 | "automattic/vipwpcs": "^3.0",
8 | "phpcompatibility/phpcompatibility-wp": "^2"
9 | },
10 | "prefer-stable": true,
11 | "authors": [
12 | {
13 | "name": "10up",
14 | "homepage": "https://10up.com/"
15 | }
16 | ],
17 | "scripts": {
18 | "config-cs": [
19 | "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run",
20 | "\"vendor/bin/phpcs\" --config-set default_standard 10up-Default"
21 | ],
22 | "post-install-cmd": "@config-cs",
23 | "post-update-cmd": "@config-cs",
24 | "lint": "\"vendor/bin/phpcs\" . "
25 | },
26 | "config": {
27 | "allow-plugins": {
28 | "dealerdirect/phpcodesniffer-composer-installer": true
29 | }
30 | },
31 | "minimum-stability": "dev",
32 | "require-dev": {
33 | "dealerdirect/phpcodesniffer-composer-installer": "*",
34 | "phpcompatibility/php-compatibility": "dev-develop as 9.99.99"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "5a7e2b9a63bc91d26ccd8c8109b246e8",
8 | "packages": [
9 | {
10 | "name": "automattic/vipwpcs",
11 | "version": "3.0.0",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/Automattic/VIP-Coding-Standards.git",
15 | "reference": "1b8960ebff9ea3eb482258a906ece4d1ee1e25fd"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/1b8960ebff9ea3eb482258a906ece4d1ee1e25fd",
20 | "reference": "1b8960ebff9ea3eb482258a906ece4d1ee1e25fd",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "php": ">=5.4",
25 | "phpcsstandards/phpcsextra": "^1.1.0",
26 | "phpcsstandards/phpcsutils": "^1.0.8",
27 | "sirbrillig/phpcs-variable-analysis": "^2.11.17",
28 | "squizlabs/php_codesniffer": "^3.7.2",
29 | "wp-coding-standards/wpcs": "^3.0"
30 | },
31 | "require-dev": {
32 | "php-parallel-lint/php-console-highlighter": "^1.0.0",
33 | "php-parallel-lint/php-parallel-lint": "^1.3.2",
34 | "phpcompatibility/php-compatibility": "^9",
35 | "phpcsstandards/phpcsdevtools": "^1.0",
36 | "phpunit/phpunit": "^4 || ^5 || ^6 || ^7"
37 | },
38 | "type": "phpcodesniffer-standard",
39 | "notification-url": "https://packagist.org/downloads/",
40 | "license": [
41 | "MIT"
42 | ],
43 | "authors": [
44 | {
45 | "name": "Contributors",
46 | "homepage": "https://github.com/Automattic/VIP-Coding-Standards/graphs/contributors"
47 | }
48 | ],
49 | "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress VIP minimum coding conventions",
50 | "keywords": [
51 | "phpcs",
52 | "standards",
53 | "static analysis",
54 | "wordpress"
55 | ],
56 | "support": {
57 | "issues": "https://github.com/Automattic/VIP-Coding-Standards/issues",
58 | "source": "https://github.com/Automattic/VIP-Coding-Standards",
59 | "wiki": "https://github.com/Automattic/VIP-Coding-Standards/wiki"
60 | },
61 | "time": "2023-09-05T11:01:05+00:00"
62 | },
63 | {
64 | "name": "dealerdirect/phpcodesniffer-composer-installer",
65 | "version": "v1.0.0",
66 | "source": {
67 | "type": "git",
68 | "url": "https://github.com/PHPCSStandards/composer-installer.git",
69 | "reference": "4be43904336affa5c2f70744a348312336afd0da"
70 | },
71 | "dist": {
72 | "type": "zip",
73 | "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da",
74 | "reference": "4be43904336affa5c2f70744a348312336afd0da",
75 | "shasum": ""
76 | },
77 | "require": {
78 | "composer-plugin-api": "^1.0 || ^2.0",
79 | "php": ">=5.4",
80 | "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
81 | },
82 | "require-dev": {
83 | "composer/composer": "*",
84 | "ext-json": "*",
85 | "ext-zip": "*",
86 | "php-parallel-lint/php-parallel-lint": "^1.3.1",
87 | "phpcompatibility/php-compatibility": "^9.0",
88 | "yoast/phpunit-polyfills": "^1.0"
89 | },
90 | "type": "composer-plugin",
91 | "extra": {
92 | "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
93 | },
94 | "autoload": {
95 | "psr-4": {
96 | "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
97 | }
98 | },
99 | "notification-url": "https://packagist.org/downloads/",
100 | "license": [
101 | "MIT"
102 | ],
103 | "authors": [
104 | {
105 | "name": "Franck Nijhof",
106 | "email": "franck.nijhof@dealerdirect.com",
107 | "homepage": "http://www.frenck.nl",
108 | "role": "Developer / IT Manager"
109 | },
110 | {
111 | "name": "Contributors",
112 | "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
113 | }
114 | ],
115 | "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
116 | "homepage": "http://www.dealerdirect.com",
117 | "keywords": [
118 | "PHPCodeSniffer",
119 | "PHP_CodeSniffer",
120 | "code quality",
121 | "codesniffer",
122 | "composer",
123 | "installer",
124 | "phpcbf",
125 | "phpcs",
126 | "plugin",
127 | "qa",
128 | "quality",
129 | "standard",
130 | "standards",
131 | "style guide",
132 | "stylecheck",
133 | "tests"
134 | ],
135 | "support": {
136 | "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
137 | "source": "https://github.com/PHPCSStandards/composer-installer"
138 | },
139 | "time": "2023-01-05T11:28:13+00:00"
140 | },
141 | {
142 | "name": "phpcompatibility/php-compatibility",
143 | "version": "dev-develop",
144 | "source": {
145 | "type": "git",
146 | "url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
147 | "reference": "8770f4f4677cc649a1df5e73dc6195d9887f4641"
148 | },
149 | "dist": {
150 | "type": "zip",
151 | "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/8770f4f4677cc649a1df5e73dc6195d9887f4641",
152 | "reference": "8770f4f4677cc649a1df5e73dc6195d9887f4641",
153 | "shasum": ""
154 | },
155 | "require": {
156 | "php": ">=5.4",
157 | "phpcsstandards/phpcsutils": "^1.0.5",
158 | "squizlabs/php_codesniffer": "^3.7.1"
159 | },
160 | "replace": {
161 | "wimg/php-compatibility": "*"
162 | },
163 | "require-dev": {
164 | "php-parallel-lint/php-console-highlighter": "^1.0.0",
165 | "php-parallel-lint/php-parallel-lint": "^1.3.2",
166 | "phpcsstandards/phpcsdevcs": "^1.1.3",
167 | "phpcsstandards/phpcsdevtools": "^1.2.0",
168 | "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4 || ^10.1.0",
169 | "yoast/phpunit-polyfills": "^1.0.5 || ^2.0.0"
170 | },
171 | "suggest": {
172 | "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
173 | },
174 | "default-branch": true,
175 | "type": "phpcodesniffer-standard",
176 | "extra": {
177 | "branch-alias": {
178 | "dev-master": "9.x-dev",
179 | "dev-develop": "10.x-dev"
180 | }
181 | },
182 | "notification-url": "https://packagist.org/downloads/",
183 | "license": [
184 | "LGPL-3.0-or-later"
185 | ],
186 | "authors": [
187 | {
188 | "name": "Wim Godden",
189 | "homepage": "https://github.com/wimg",
190 | "role": "lead"
191 | },
192 | {
193 | "name": "Juliette Reinders Folmer",
194 | "homepage": "https://github.com/jrfnl",
195 | "role": "lead"
196 | },
197 | {
198 | "name": "Contributors",
199 | "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors"
200 | }
201 | ],
202 | "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.",
203 | "homepage": "http://techblog.wimgodden.be/tag/codesniffer/",
204 | "keywords": [
205 | "compatibility",
206 | "phpcs",
207 | "standards",
208 | "static analysis"
209 | ],
210 | "support": {
211 | "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues",
212 | "source": "https://github.com/PHPCompatibility/PHPCompatibility"
213 | },
214 | "time": "2023-11-13T01:28:23+00:00"
215 | },
216 | {
217 | "name": "phpcompatibility/phpcompatibility-paragonie",
218 | "version": "1.3.2",
219 | "source": {
220 | "type": "git",
221 | "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git",
222 | "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26"
223 | },
224 | "dist": {
225 | "type": "zip",
226 | "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/bba5a9dfec7fcfbd679cfaf611d86b4d3759da26",
227 | "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26",
228 | "shasum": ""
229 | },
230 | "require": {
231 | "phpcompatibility/php-compatibility": "^9.0"
232 | },
233 | "require-dev": {
234 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7",
235 | "paragonie/random_compat": "dev-master",
236 | "paragonie/sodium_compat": "dev-master"
237 | },
238 | "suggest": {
239 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
240 | "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
241 | },
242 | "type": "phpcodesniffer-standard",
243 | "notification-url": "https://packagist.org/downloads/",
244 | "license": [
245 | "LGPL-3.0-or-later"
246 | ],
247 | "authors": [
248 | {
249 | "name": "Wim Godden",
250 | "role": "lead"
251 | },
252 | {
253 | "name": "Juliette Reinders Folmer",
254 | "role": "lead"
255 | }
256 | ],
257 | "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.",
258 | "homepage": "http://phpcompatibility.com/",
259 | "keywords": [
260 | "compatibility",
261 | "paragonie",
262 | "phpcs",
263 | "polyfill",
264 | "standards",
265 | "static analysis"
266 | ],
267 | "support": {
268 | "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues",
269 | "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie"
270 | },
271 | "time": "2022-10-25T01:46:02+00:00"
272 | },
273 | {
274 | "name": "phpcompatibility/phpcompatibility-wp",
275 | "version": "2.1.4",
276 | "source": {
277 | "type": "git",
278 | "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git",
279 | "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5"
280 | },
281 | "dist": {
282 | "type": "zip",
283 | "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5",
284 | "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5",
285 | "shasum": ""
286 | },
287 | "require": {
288 | "phpcompatibility/php-compatibility": "^9.0",
289 | "phpcompatibility/phpcompatibility-paragonie": "^1.0"
290 | },
291 | "require-dev": {
292 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7"
293 | },
294 | "suggest": {
295 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.",
296 | "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
297 | },
298 | "type": "phpcodesniffer-standard",
299 | "notification-url": "https://packagist.org/downloads/",
300 | "license": [
301 | "LGPL-3.0-or-later"
302 | ],
303 | "authors": [
304 | {
305 | "name": "Wim Godden",
306 | "role": "lead"
307 | },
308 | {
309 | "name": "Juliette Reinders Folmer",
310 | "role": "lead"
311 | }
312 | ],
313 | "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.",
314 | "homepage": "http://phpcompatibility.com/",
315 | "keywords": [
316 | "compatibility",
317 | "phpcs",
318 | "standards",
319 | "static analysis",
320 | "wordpress"
321 | ],
322 | "support": {
323 | "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues",
324 | "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP"
325 | },
326 | "time": "2022-10-24T09:00:36+00:00"
327 | },
328 | {
329 | "name": "phpcsstandards/phpcsextra",
330 | "version": "1.1.2",
331 | "source": {
332 | "type": "git",
333 | "url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
334 | "reference": "746c3190ba8eb2f212087c947ba75f4f5b9a58d5"
335 | },
336 | "dist": {
337 | "type": "zip",
338 | "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/746c3190ba8eb2f212087c947ba75f4f5b9a58d5",
339 | "reference": "746c3190ba8eb2f212087c947ba75f4f5b9a58d5",
340 | "shasum": ""
341 | },
342 | "require": {
343 | "php": ">=5.4",
344 | "phpcsstandards/phpcsutils": "^1.0.8",
345 | "squizlabs/php_codesniffer": "^3.7.1"
346 | },
347 | "require-dev": {
348 | "php-parallel-lint/php-console-highlighter": "^1.0",
349 | "php-parallel-lint/php-parallel-lint": "^1.3.2",
350 | "phpcsstandards/phpcsdevcs": "^1.1.6",
351 | "phpcsstandards/phpcsdevtools": "^1.2.1",
352 | "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0"
353 | },
354 | "type": "phpcodesniffer-standard",
355 | "extra": {
356 | "branch-alias": {
357 | "dev-stable": "1.x-dev",
358 | "dev-develop": "1.x-dev"
359 | }
360 | },
361 | "notification-url": "https://packagist.org/downloads/",
362 | "license": [
363 | "LGPL-3.0-or-later"
364 | ],
365 | "authors": [
366 | {
367 | "name": "Juliette Reinders Folmer",
368 | "homepage": "https://github.com/jrfnl",
369 | "role": "lead"
370 | },
371 | {
372 | "name": "Contributors",
373 | "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors"
374 | }
375 | ],
376 | "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.",
377 | "keywords": [
378 | "PHP_CodeSniffer",
379 | "phpcbf",
380 | "phpcodesniffer-standard",
381 | "phpcs",
382 | "standards",
383 | "static analysis"
384 | ],
385 | "support": {
386 | "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues",
387 | "source": "https://github.com/PHPCSStandards/PHPCSExtra"
388 | },
389 | "time": "2023-09-20T22:06:18+00:00"
390 | },
391 | {
392 | "name": "phpcsstandards/phpcsutils",
393 | "version": "1.0.8",
394 | "source": {
395 | "type": "git",
396 | "url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
397 | "reference": "69465cab9d12454e5e7767b9041af0cd8cd13be7"
398 | },
399 | "dist": {
400 | "type": "zip",
401 | "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/69465cab9d12454e5e7767b9041af0cd8cd13be7",
402 | "reference": "69465cab9d12454e5e7767b9041af0cd8cd13be7",
403 | "shasum": ""
404 | },
405 | "require": {
406 | "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0",
407 | "php": ">=5.4",
408 | "squizlabs/php_codesniffer": "^3.7.1 || 4.0.x-dev@dev"
409 | },
410 | "require-dev": {
411 | "ext-filter": "*",
412 | "php-parallel-lint/php-console-highlighter": "^1.0",
413 | "php-parallel-lint/php-parallel-lint": "^1.3.2",
414 | "phpcsstandards/phpcsdevcs": "^1.1.6",
415 | "yoast/phpunit-polyfills": "^1.0.5 || ^2.0.0"
416 | },
417 | "type": "phpcodesniffer-standard",
418 | "extra": {
419 | "branch-alias": {
420 | "dev-stable": "1.x-dev",
421 | "dev-develop": "1.x-dev"
422 | }
423 | },
424 | "autoload": {
425 | "classmap": [
426 | "PHPCSUtils/"
427 | ]
428 | },
429 | "notification-url": "https://packagist.org/downloads/",
430 | "license": [
431 | "LGPL-3.0-or-later"
432 | ],
433 | "authors": [
434 | {
435 | "name": "Juliette Reinders Folmer",
436 | "homepage": "https://github.com/jrfnl",
437 | "role": "lead"
438 | },
439 | {
440 | "name": "Contributors",
441 | "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors"
442 | }
443 | ],
444 | "description": "A suite of utility functions for use with PHP_CodeSniffer",
445 | "homepage": "https://phpcsutils.com/",
446 | "keywords": [
447 | "PHP_CodeSniffer",
448 | "phpcbf",
449 | "phpcodesniffer-standard",
450 | "phpcs",
451 | "phpcs3",
452 | "standards",
453 | "static analysis",
454 | "tokens",
455 | "utility"
456 | ],
457 | "support": {
458 | "docs": "https://phpcsutils.com/",
459 | "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues",
460 | "source": "https://github.com/PHPCSStandards/PHPCSUtils"
461 | },
462 | "time": "2023-07-16T21:39:41+00:00"
463 | },
464 | {
465 | "name": "sirbrillig/phpcs-variable-analysis",
466 | "version": "v2.11.17",
467 | "source": {
468 | "type": "git",
469 | "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git",
470 | "reference": "3b71162a6bf0cde2bff1752e40a1788d8273d049"
471 | },
472 | "dist": {
473 | "type": "zip",
474 | "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/3b71162a6bf0cde2bff1752e40a1788d8273d049",
475 | "reference": "3b71162a6bf0cde2bff1752e40a1788d8273d049",
476 | "shasum": ""
477 | },
478 | "require": {
479 | "php": ">=5.4.0",
480 | "squizlabs/php_codesniffer": "^3.5.6"
481 | },
482 | "require-dev": {
483 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0",
484 | "phpcsstandards/phpcsdevcs": "^1.1",
485 | "phpstan/phpstan": "^1.7",
486 | "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0",
487 | "sirbrillig/phpcs-import-detection": "^1.1",
488 | "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0@beta"
489 | },
490 | "type": "phpcodesniffer-standard",
491 | "autoload": {
492 | "psr-4": {
493 | "VariableAnalysis\\": "VariableAnalysis/"
494 | }
495 | },
496 | "notification-url": "https://packagist.org/downloads/",
497 | "license": [
498 | "BSD-2-Clause"
499 | ],
500 | "authors": [
501 | {
502 | "name": "Sam Graham",
503 | "email": "php-codesniffer-variableanalysis@illusori.co.uk"
504 | },
505 | {
506 | "name": "Payton Swick",
507 | "email": "payton@foolord.com"
508 | }
509 | ],
510 | "description": "A PHPCS sniff to detect problems with variables.",
511 | "keywords": [
512 | "phpcs",
513 | "static analysis"
514 | ],
515 | "support": {
516 | "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues",
517 | "source": "https://github.com/sirbrillig/phpcs-variable-analysis",
518 | "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki"
519 | },
520 | "time": "2023-08-05T23:46:11+00:00"
521 | },
522 | {
523 | "name": "squizlabs/php_codesniffer",
524 | "version": "3.7.2",
525 | "source": {
526 | "type": "git",
527 | "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
528 | "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879"
529 | },
530 | "dist": {
531 | "type": "zip",
532 | "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879",
533 | "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879",
534 | "shasum": ""
535 | },
536 | "require": {
537 | "ext-simplexml": "*",
538 | "ext-tokenizer": "*",
539 | "ext-xmlwriter": "*",
540 | "php": ">=5.4.0"
541 | },
542 | "require-dev": {
543 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
544 | },
545 | "bin": [
546 | "bin/phpcs",
547 | "bin/phpcbf"
548 | ],
549 | "type": "library",
550 | "extra": {
551 | "branch-alias": {
552 | "dev-master": "3.x-dev"
553 | }
554 | },
555 | "notification-url": "https://packagist.org/downloads/",
556 | "license": [
557 | "BSD-3-Clause"
558 | ],
559 | "authors": [
560 | {
561 | "name": "Greg Sherwood",
562 | "role": "lead"
563 | }
564 | ],
565 | "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
566 | "homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
567 | "keywords": [
568 | "phpcs",
569 | "standards",
570 | "static analysis"
571 | ],
572 | "support": {
573 | "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
574 | "source": "https://github.com/squizlabs/PHP_CodeSniffer",
575 | "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
576 | },
577 | "time": "2023-02-22T23:07:41+00:00"
578 | },
579 | {
580 | "name": "wp-coding-standards/wpcs",
581 | "version": "3.0.1",
582 | "source": {
583 | "type": "git",
584 | "url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
585 | "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1"
586 | },
587 | "dist": {
588 | "type": "zip",
589 | "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/b4caf9689f1a0e4a4c632679a44e638c1c67aff1",
590 | "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1",
591 | "shasum": ""
592 | },
593 | "require": {
594 | "ext-filter": "*",
595 | "ext-libxml": "*",
596 | "ext-tokenizer": "*",
597 | "ext-xmlreader": "*",
598 | "php": ">=5.4",
599 | "phpcsstandards/phpcsextra": "^1.1.0",
600 | "phpcsstandards/phpcsutils": "^1.0.8",
601 | "squizlabs/php_codesniffer": "^3.7.2"
602 | },
603 | "require-dev": {
604 | "php-parallel-lint/php-console-highlighter": "^1.0.0",
605 | "php-parallel-lint/php-parallel-lint": "^1.3.2",
606 | "phpcompatibility/php-compatibility": "^9.0",
607 | "phpcsstandards/phpcsdevtools": "^1.2.0",
608 | "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
609 | },
610 | "suggest": {
611 | "ext-iconv": "For improved results",
612 | "ext-mbstring": "For improved results"
613 | },
614 | "type": "phpcodesniffer-standard",
615 | "notification-url": "https://packagist.org/downloads/",
616 | "license": [
617 | "MIT"
618 | ],
619 | "authors": [
620 | {
621 | "name": "Contributors",
622 | "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
623 | }
624 | ],
625 | "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
626 | "keywords": [
627 | "phpcs",
628 | "standards",
629 | "static analysis",
630 | "wordpress"
631 | ],
632 | "support": {
633 | "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues",
634 | "source": "https://github.com/WordPress/WordPress-Coding-Standards",
635 | "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
636 | },
637 | "funding": [
638 | {
639 | "url": "https://opencollective.com/thewpcc/contribute/wp-php-63406",
640 | "type": "custom"
641 | }
642 | ],
643 | "time": "2023-09-14T07:06:09+00:00"
644 | }
645 | ],
646 | "packages-dev": [],
647 | "aliases": [
648 | {
649 | "package": "phpcompatibility/php-compatibility",
650 | "version": "dev-develop",
651 | "alias": "9.99.99",
652 | "alias_normalized": "9.99.99.0"
653 | }
654 | ],
655 | "minimum-stability": "dev",
656 | "stability-flags": {
657 | "phpcompatibility/php-compatibility": 20
658 | },
659 | "prefer-stable": true,
660 | "prefer-lowest": false,
661 | "platform": [],
662 | "platform-dev": [],
663 | "plugin-api-version": "2.3.0"
664 | }
665 |
--------------------------------------------------------------------------------
/tests/RulesetTest.php:
--------------------------------------------------------------------------------
1 | '10up-Default',
90 | ];
91 |
92 | /**
93 | * String returned by PHP_CodeSniffer report for an Error.
94 | */
95 | const ERROR_TYPE = 'ERROR';
96 |
97 | /**
98 | * Init the object by processing the test file.
99 | *
100 | * @param string $ruleset Name of the ruleset e.g. TenUpDefault.
101 | * @param array $expected The array of expected errors, warnings and messages.
102 | */
103 | public function __construct( $ruleset, $expected = [] ) {
104 | $this->ruleset = $ruleset;
105 | $this->expected = $expected;
106 |
107 | if ( isset( $this->directory_mapping[ $ruleset ] ) ) {
108 | $this->ruleset_directory = $this->directory_mapping[ $ruleset ];
109 | } else {
110 | $this->ruleset_directory = $ruleset;
111 | }
112 |
113 | // Travis and Windows support.
114 | $phpcs_bin = getenv( 'PHPCS_BIN' );
115 | if ( $phpcs_bin === false ) {
116 | // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv -- This is test code, not production.
117 | putenv( 'PHPCS_BIN=phpcs' );
118 | } else {
119 | $this->phpcs_bin = realpath( $phpcs_bin );
120 | }
121 |
122 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
123 | printf( 'Testing the ' . $this->ruleset . ' ruleset.' . PHP_EOL );
124 |
125 | $output = $this->collect_phpcs_result();
126 |
127 | if ( empty( $output ) || ! is_object( $output ) ) {
128 | printf( 'The PHPCS command checking the ruleset hasn\'t returned any issues. Bailing ...' . PHP_EOL );
129 | exit( 1 ); // Die early, if we don't have any output.
130 | }
131 |
132 | $this->process_output( $output );
133 | }
134 |
135 | /**
136 | * Run all the tests and return whether test was successful.
137 | *
138 | * @return bool
139 | */
140 | public function passes() {
141 | $this->run();
142 |
143 | return ! $this->found_issue;
144 | }
145 |
146 | /**
147 | * Run all the tests.
148 | *
149 | * @return void
150 | */
151 | private function run() {
152 | // Check for missing expected values.
153 | $this->check_missing_expected_values();
154 | // Check for extra values which were not expected.
155 | $this->check_unexpected_values();
156 | // Check for expected messages.
157 | $this->check_messages();
158 | }
159 |
160 | /**
161 | * Collect the PHP_CodeSniffer result.
162 | *
163 | * @return array Returns an associative array with keys of `totals` and `files`.
164 | */
165 | private function collect_phpcs_result() {
166 | $php = '';
167 | if ( \PHP_BINARY && in_array( \PHP_SAPI, [ 'cgi-fcgi', 'cli', 'cli-server', 'phpdbg' ], true ) ) {
168 | $php = \PHP_BINARY . ' ';
169 | }
170 |
171 | $shell = sprintf(
172 | '%1$s%2$s --severity=1 --standard=%3$s --report=json ./%3$s/ruleset-test.inc',
173 | $php, // Current PHP executable if available.
174 | $this->phpcs_bin,
175 | $this->ruleset_directory
176 | );
177 |
178 | // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_shell_exec -- This is test code, not production.
179 | $output = shell_exec( $shell );
180 |
181 | return json_decode( $output );
182 | }
183 |
184 | /**
185 | * Process the Decoded JSON output from PHP_CodeSniffer.
186 | *
187 | * @param \stdClass $output Decoded JSON output from PHP_CodeSniffer.
188 | *
189 | * @return void
190 | */
191 | private function process_output( $output ) {
192 | foreach ( $output->files as $file ) {
193 | $this->process_file( $file );
194 | }
195 | }
196 |
197 | /**
198 | * Process single file of within PHP_CodeSniffer results.
199 | *
200 | * @param \stdClass $file File output.
201 | *
202 | * @return void
203 | */
204 | private function process_file( $file ) {
205 | foreach ( $file->messages as $violation ) {
206 | $this->process_violation( $violation );
207 | }
208 | }
209 |
210 | /**
211 | * Process single violation within PHP_CodeSniffer results.
212 | *
213 | * @param \stdClass $violation Violation data.
214 | *
215 | * @return void
216 | */
217 | private function process_violation( $violation ) {
218 | if ( $this->violation_type_is_error( $violation ) ) {
219 | $this->add_error_for_line( $violation->line );
220 | } else {
221 | $this->add_warning_for_line( $violation->line );
222 | }
223 |
224 | $this->add_message_for_line( $violation->line, $violation->message );
225 | }
226 |
227 | /**
228 | * Check if violation is an error.
229 | *
230 | * @param \stdClass $violation Violation data.
231 | *
232 | * @return bool True if string matches error type.
233 | */
234 | private function violation_type_is_error( $violation ) {
235 | return $violation->type === self::ERROR_TYPE;
236 | }
237 |
238 | /**
239 | * Add 1 to the number of errors for the given line.
240 | *
241 | * @param int $line Line number.
242 | *
243 | * @return void
244 | */
245 | private function add_error_for_line( $line ) {
246 | $this->errors[ $line ] = isset( $this->errors[ $line ] ) ? ++ $this->errors[ $line ] : 1;
247 | }
248 |
249 | /**
250 | * Add 1 to the number of errors for the given line.
251 | *
252 | * @param int $line Line number.
253 | *
254 | * @return void
255 | */
256 | private function add_warning_for_line( $line ) {
257 | $this->warnings[ $line ] = isset( $this->warnings[ $line ] ) ? ++ $this->warnings[ $line ] : 1;
258 | }
259 |
260 | /**
261 | * Add message for the given line.
262 | *
263 | * @param int $line Line number.
264 | * @param string $message Message.
265 | *
266 | * @return void
267 | */
268 | private function add_message_for_line( $line, $message ) {
269 | $this->messages[ $line ] = ( ! isset( $this->messages[ $line ] ) || ! is_array( $this->messages[ $line ] ) ) ? [ $message ] : array_merge( $this->messages[ $line ],
270 | [ $message ] );
271 | }
272 |
273 | /**
274 | * Check whether all expected numbers of errors and warnings are present in the output.
275 | *
276 | * @return void
277 | */
278 | private function check_missing_expected_values() {
279 | foreach ( $this->expected as $type => $lines ) {
280 | if ( $type === 'messages' ) {
281 | continue;
282 | }
283 |
284 | foreach ( $lines as $line_number => $expected_count_of_type_violations ) {
285 | if ( $expected_count_of_type_violations === 0 ) {
286 | continue;
287 | }
288 |
289 | if ( ! isset( $this->{$type}[ $line_number ] ) ) {
290 | $this->error_warning_message( $expected_count_of_type_violations, $type, 0, $line_number );
291 | } elseif ( $this->{$type}[ $line_number ] !== $expected_count_of_type_violations ) {
292 | $this->error_warning_message( $expected_count_of_type_violations, $type,
293 | $this->{$type}[ $line_number ], $line_number );
294 | }
295 |
296 | unset( $this->{$type}[ $line_number ] );
297 | }
298 | }
299 | }
300 |
301 | /**
302 | * Check whether there are no unexpected numbers of errors and warnings.
303 | *
304 | * @return void
305 | */
306 | private function check_unexpected_values() {
307 | foreach ( [ 'errors', 'warnings' ] as $type ) {
308 | foreach ( $this->$type as $line_number => $actual_count_of_type_violations ) {
309 | if ( $actual_count_of_type_violations === 0 ) {
310 | continue;
311 | }
312 |
313 | if ( ! isset( $this->expected[ $type ][ $line_number ] ) ) {
314 | $this->error_warning_message( 0, $type, $actual_count_of_type_violations, $line_number );
315 | } elseif ( $actual_count_of_type_violations !== $this->expected[ $type ][ $line_number ] ) {
316 | $this->error_warning_message( $this->expected[ $type ][ $line_number ], $type,
317 | $actual_count_of_type_violations, $line_number );
318 | }
319 | }
320 | }
321 | }
322 |
323 | /**
324 | * Check whether all expected messages are present and whether there are no unexpected messages.
325 | *
326 | * @return void
327 | */
328 | private function check_messages() {
329 | foreach ( $this->expected['messages'] as $line_number => $messages ) {
330 | foreach ( $messages as $message ) {
331 | if ( ! isset( $this->messages[ $line_number ] ) ) {
332 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
333 | printf( 'Expected "%s" but found no message for line %d' . PHP_EOL, $message, $line_number );
334 | $this->found_issue = true;
335 | } elseif ( ! in_array( $message, $this->messages[ $line_number ], true ) ) {
336 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
337 | printf( 'Expected message "%s" was not found for line %d.' . PHP_EOL, $message, $line_number );
338 | $this->found_issue = true;
339 | }
340 | }
341 | }
342 | foreach ( $this->messages as $line_number => $messages ) {
343 | foreach ( $messages as $message ) {
344 | if ( isset( $this->expected['messages'][ $line_number ] ) ) {
345 | if ( ! in_array( $message, $this->expected['messages'][ $line_number ], true ) ) {
346 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
347 | printf( 'Unexpected message "%s" was found for line %d.' . PHP_EOL, $message, $line_number );
348 | $this->found_issue = true;
349 | }
350 | }
351 | }
352 | }
353 | }
354 |
355 | /**
356 | * Print out the message reporting found issues.
357 | *
358 | * @param int $expected Expected number of issues.
359 | * @param string $type The type of the issue.
360 | * @param int $number Real number of issues.
361 | * @param int $line Line number.
362 | *
363 | * @return void
364 | */
365 | private function error_warning_message( $expected, $type, $number, $line ) {
366 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
367 | printf( 'Expected %d %s, found %d on line %d.' . PHP_EOL, $expected, $type, $number, $line );
368 | $this->found_issue = true;
369 | }
370 | }
371 |
--------------------------------------------------------------------------------