diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..2900f95
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,97 @@
+name: Test
+
+on:
+ # Run on pushes to `master` and on all pull requests.
+ # Prevent the "push" build from running when there are only irrelevant changes.
+ push:
+ branches:
+ - master
+ paths-ignore:
+ - '**.md'
+ pull_request:
+
+ # Allow manually triggering the workflow.
+ workflow_dispatch:
+
+# Cancels all previous workflow runs for the same branch that have not yet completed.
+concurrency:
+ # The concurrency group contains the workflow name and the branch name.
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+
+ checkxml:
+ name: 'Check XML Validates'
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ - name: Install xmllint
+ run: |
+ sudo apt-get update
+ sudo apt-get install --no-install-recommends -y libxml2-utils
+
+ # Show XML violations inline in the file diff.
+ # @link https://github.com/marketplace/actions/xmllint-problem-matcher
+ - uses: korelstar/xmllint-problem-matcher@v1
+
+ - name: 'Validate XML against schema and check code style'
+ run: ./bin/xml-lint
+
+ test:
+ runs-on: ubuntu-latest
+
+ strategy:
+ # Keys:
+ # - php: The PHP versions to test against.
+ # - dependencies: The PHPCS dependencies versions to test against.
+ # IMPORTANT: test runs shouldn't fail because of PHPCS being incompatible with a PHP version.
+ # - PHPCS will run without errors on PHP 5.4 - 7.4 on any supported version.
+ # - PHP 8.0 needs PHPCS 3.5.7+ to run without errors, and we require a higher minimum version.
+ # - 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.
+ matrix:
+ php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
+ dependencies: ['stable']
+ name: "Test: PHP ${{ matrix.php }} - PHPCS"
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v3
+
+ # With stable PHPCS dependencies, allow for PHP deprecation notices.
+ # Unit tests don't need to fail on those for stable releases where those issues won't get fixed anymore.
+ - name: Setup ini config
+ id: set_ini
+ run: |
+ echo 'PHP_INI=error_reporting=E_ALL & ~E_DEPRECATED, display_errors=On' >> $GITHUB_OUTPUT
+
+ - name: Install PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ ini-values: ${{ steps.set_ini.outputs.PHP_INI }}
+ coverage: none
+
+ # Install dependencies and handle caching in one go.
+ # @link https://github.com/marketplace/actions/install-composer-dependencies
+ - name: Install Composer dependencies - normal
+ if: ${{ startsWith( matrix.php, '8' ) == false }}
+ uses: "ramsey/composer-install@v2"
+ with:
+ # Bust the cache at least once a month - output format: YYYY-MM.
+ custom-cache-suffix: $(date -u "+%Y-%m")
+
+ # PHPUnit 7.x does not allow for installation on PHP 8, so ignore platform
+ # requirements to get PHPUnit 7.x to install on nightly.
+ - name: Install Composer dependencies - with ignore platform
+ if: ${{ startsWith( matrix.php, '8' ) }}
+ uses: "ramsey/composer-install@v2"
+ with:
+ composer-options: --ignore-platform-req=php+
+ custom-cache-suffix: $(date -u "+%Y-%m")
+
+ - name: Run the ruleset tests
+ run: ./bin/ruleset-tests
diff --git a/10up-Default/ruleset-test.inc b/10up-Default/ruleset-test.inc
new file mode 100644
index 0000000..278759e
--- /dev/null
+++ b/10up-Default/ruleset-test.inc
@@ -0,0 +1,486 @@
+
+
+
+ 999,
+);
+
+// Warnings: WordPress.WP.PostsPerPage.posts_per_page_posts_per_page
+_query_posts( 'posts_per_page=999' );
+
+// Warnings: WordPress.WP.PostsPerPage.posts_per_page_posts_per_page
+$query_args['posts_per_page'] = 999;
+
+// Errors: WordPress.DateTime.RestrictedFunctions.timezone_change_date_default_timezone_set)
+date_default_timezone_set( 'FooBar' );
+
+$b = function () { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ global $wpdb;
+ $listofthings = wp_cache_get( 'foo' );
+ if ( ! $listofthings ) {
+ $foo = "column = 'test'";
+
+ // Errors: WordPress.DB.PreparedSQL.NotPrepared
+ // Warnings: WordPress.DB.DirectDatabaseQuery.DirectQuery
+ $listofthings = $wpdb->query( 'SELECT something FROM somewhere WHERE ' . $foo );
+ wp_cache_set( 'foo', $listofthings );
+ }
+};
+
+// Warnings: WordPress.DB.DirectDatabaseQuery.DirectQuery & WordPress.DB.PreparedSQLPlaceholders.UnnecessaryPrepare & WordPress.DB.DirectDatabaseQuery.NoCaching
+$baz = $wpdb->get_results( $wpdb->prepare( 'SELECT X FROM Y ' ) ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Warning x 2.
+
+$test = [
+ // Warnings: WordPress.DB.SlowDBQuery.slow_db_query_tax_query
+ 'tax_query' => [],
+];
+
+// Errors: PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket
+new WP_Query( array(
+ // Warnings: WordPress.DB.SlowDBQuery.slow_db_query_meta_query
+ 'meta_query' => [],
+ // Warnings: WordPress.DB.SlowDBQuery.slow_db_query_meta_key & WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned
+ 'meta_key' => 'foo',
+ // Warnings: Warnings: WordPress.DB.SlowDBQuery.slow_db_query_meta_value
+ 'meta_value' => 'bar',
+ // Errors: PEAR.Functions.FunctionCallSignature.CloseBracketLine
+) );
+
+// WordPress.WP.GlobalVariablesOverride
+$GLOBALS['wpdb'] = 'test';
+
+// Errors: Generic.CodeAnalysis.EmptyStatement.DetectedIf
+// Warnings: Universal.Operators.StrictComparisons.LooseEqual
+if ( true == $true ) {
+}
+
+// Errors: Generic.CodeAnalysis.EmptyStatement.DetectedIf & Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
+// Warnings: Generic.CodeAnalysis.AssignmentInCondition.Found
+if ( $test = get_post( $post ) ) {
+}
+
+// Errors: Generic.CodeAnalysis.EmptyStatement.DetectedIf)
+// Warnings: WordPress.PHP.StrictInArray.MissingTrueStrict
+if ( true === in_array( $foo, $bar ) ) {
+}
+
+// Errors: WordPress.PHP.DontExtract.extract_extract
+extract( $foobar );
+
+// WordPress.WP.CronInterval
+function my_add_weekly( $schedules ) {
+ $schedules['every_6_mins'] = array(
+ 'interval' => 360,
+ // Errors: NormalizedArrays.Arrays.CommaAfterLast.MissingMultiLine
+ // Warnings: WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned
+ 'display' => __( 'Once every 6 minutes' )
+ );
+ return $schedules;
+}
+
+// Errors: PEAR.Functions.FunctionCallSignature.SpaceBeforeCloseBracket
+// Warnings:WordPress.WP.CronInterval.CronSchedulesInterval
+add_filter( 'cron_schedules', 'my_add_weekly');
+
+// Errors: Universal.Files.SeparateFunctionsFromOO.Mixed
+class TestClass extends MyClass
+// Errors: Generic.Classes.OpeningBraceSameLine.BraceOnNewLine
+{
+ // Errors: Squiz.Scope.MethodScope.Missing
+ function __construct() {
+ parent::MYCLASS();
+ parent::__construct();
+ }
+}
+
+// Errors: Generic.Files.OneObjectStructurePerFile.MultipleFound
+class OldClass
+// Errors: Generic.Classes.OpeningBraceSameLine.BraceOnNewLine
+{
+
+ // Errors: Squiz.Scope.MethodScope.Missing
+ function OldClass()
+ // Errors: (Generic.Functions.OpeningFunctionBraceKernighanRitchie.BraceOnNewLine
+ {
+ }
+}
+
+// Errors: Generic.Files.OneObjectStructurePerFile.MultipleFound
+// Warnings: Generic.Classes.DuplicateClassName.Found
+class TestClass extends MyClass {
+
+ // Errors: Squiz.Scope.MethodScope.Missing
+ function TestClass() {
+ parent::MyClass();
+ parent::__construct();
+ }
+}
+
+// Errors: Squiz.PHP.EmbeddedPhp.ContentAfterEnd & Generic.PHP.DisallowShortOpenTag.EchoFound
+?> = esc_html( $var );
+
+// Warnings: (Squiz.PHP.CommentedOutCode.Found
+// if (empty($this)) {echo 'This is will not work';}
+
+// Errors: PEAR.Functions.FunctionCallSignature.SpaceAfterOpenBracket & Squiz.PHP.Eval.Discouraged
+eval('$var = 4;'); // Error + Message.
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
+base64_decode( 'VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw==' );
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
+base64_encode( 'This is an encoded string' );
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.obfuscation_convert_uudecode
+convert_uudecode( "+22!L;W9E(%!(4\"$`\n`" );
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.obfuscation_convert_uudecode
+convert_uuencode( "test\ntext text\r\n" );
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.obfuscation_str_rot13
+str_rot13( 'The quick brown fox jumps over the lazy dog.' );
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
+serialize();
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
+unserialize();
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.urlencode_urlencode
+urlencode();
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_passthru
+passthru( 'cat myfile.zip', $err );
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_proc_open
+$process = proc_open( 'php', $descriptorspec, $pipes, $cwd, $env );
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_system
+$last_line = system( 'ls', $retval );
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_popen
+$handle = popen( '/bin/ls', 'r' );
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting & WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting
+error_reporting();
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_ini_restore
+ini_restore();
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_apache_setenv
+apache_setenv();
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv
+putenv();
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_set_include_path
+set_include_path();
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_restore_include_path && PHPCompatibility.FunctionUse.RemovedFunctions.restore_include_pathDeprecated
+restore_include_path();
+
+// Errors: PHPCompatibility.FunctionUse.RemovedFunctions.magic_quotes_runtimeDeprecatedRemoved
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_magic_quotes_runtime
+magic_quotes_runtime();
+
+// Errors: PHPCompatibility.FunctionUse.RemovedFunctions.set_magic_quotes_runtimeDeprecatedRemoved
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_set_magic_quotes_runtime
+set_magic_quotes_runtime();
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_dl & PHPCompatibility.FunctionUse.RemovedFunctions.dlDeprecated
+dl();
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_exec
+exec( 'whoami' );
+
+// Warnings: WordPress.PHP.DiscouragedPHPFunctions.system_calls_shell_exec
+$output = shell_exec( 'ls -lart' ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Error.
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.error_log_var_dump
+var_dump();
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.error_log_var_export
+var_export();
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.error_log_print_r
+print_r();
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
+trigger_error( 'message' );
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler
+set_error_handler();
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+debug_backtrace();
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.error_log_debug_print_backtrace
+debug_print_backtrace();
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.error_log_wp_debug_backtrace_summary
+wp_debug_backtrace_summary();
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_phpinfo
+phpinfo();
+
+// Warnings: WordPress.PHP.DevelopmentFunctions.error_log_error_log
+error_log();
+
+// OK to set
+ini_set( 'auto_detect_line_endings', true );
+
+// Errors: PHPCompatibility.IniDirectives.RemovedIniDirectives.highlight_bgDeprecatedRemoved
+ini_set( 'highlight.bg', '#000000' );
+
+// OK to set
+ini_set( 'highlight.comment', '#000000' );
+
+// OK to set
+ini_set( 'highlight.default', '#000000' );
+
+// OK to set
+ini_set( 'highlight.html', '#000000' );
+
+// OK to set
+ini_set( 'highlight.keyword', '#000000' );
+
+// OK to set
+ini_set( 'highlight.string', '#000000' );
+
+// OK to set
+ini_set( 'short_open_tag', 1 );
+
+// Errors: WordPress.PHP.IniSet.bcmath_scale_Disallowed
+ini_set( 'bcmath.scale', 1 );
+
+// Errors: WordPress.PHP.IniSet.display_errors_Disallowed
+ini_set( 'display_errors', 1 );
+
+// Errors: WordPress.PHP.IniSet.error_reporting_Disallowed
+ini_set( 'error_reporting', 1 );
+
+// Errors: WordPress.PHP.IniSet.filter_default_Disallowed
+ini_set( 'filter.default', 1 );
+
+// Errors: WordPress.PHP.IniSet.filter_default_flags_Disallowed
+ini_set( 'filter.default_flags', 1 );
+
+// Errors: WordPress.PHP.IniSet.iconv_input_encoding_Disallowed
+// Warnings: PHPCompatibility.IniDirectives.RemovedIniDirectives.iconv_input_encodingDeprecated
+ini_set( 'iconv.input_encoding', 1 );
+
+// Errors: WordPress.PHP.IniSet.iconv_internal_encoding_Disallowed
+// Warnings: PHPCompatibility.IniDirectives.RemovedIniDirectives.iconv_internal_encodingDeprecated
+ini_set( 'iconv.internal_encoding', 1 );
+
+// Errors: WordPress.PHP.IniSet.iconv_output_encoding_Disallowed
+// Warnings: PHPCompatibility.IniDirectives.RemovedIniDirectives.iconv_output_encodingDeprecated
+ini_set( 'iconv.output_encoding', 1 );
+
+// Errors: WordPress.PHP.IniSet.ignore_user_abort_Disallowed
+ini_set( 'ignore_user_abort', 1 );
+
+// Errors: WordPress.PHP.IniSet.log_errors_Disallowed
+ini_set( 'log_errors', 1 );
+
+// Errors: WordPress.PHP.IniSet.max_execution_time_Disallowed
+ini_set( 'max_execution_time', 1 );
+
+// Errors: WordPress.PHP.IniSet.memory_limit_Disallowed
+ini_set( 'memory_limit', 1 );
+
+// Errors: WordPress.PHP.IniSet.short_open_tag_Disallowed
+ini_set( 'short_open_tag', 'off' );
+
+// Warnings: WordPress.PHP.IniSet.Risky
+ini_set( 'foo', true );
+
+// OK to set
+ini_alter( 'auto_detect_line_endings', true );
+
+// OK to set
+ini_alter( 'highlight.bg', '#000000' );
+
+// OK to set
+ini_alter( 'highlight.comment', '#000000' );
+
+// OK to set
+ini_alter( 'highlight.default', '#000000' );
+
+// OK to set
+ini_alter( 'highlight.html', '#000000' );
+
+// OK to set
+ini_alter( 'highlight.keyword', '#000000' );
+
+// OK to set
+ini_alter( 'highlight.string', '#000000' );
+
+// OK to set
+ini_alter( 'short_open_tag', 1 );
+
+// Errors: WordPress.PHP.IniSet.bcmath_scale_Disallowed
+ini_alter( 'bcmath.scale', 1 );
+
+// Errors: WordPress.PHP.IniSet.display_errors_Disallowed
+ini_alter( 'display_errors', 1 );
+
+// Errors: WordPress.PHP.IniSet.error_reporting_Disallowed
+ini_alter( 'error_reporting', 1 );
+
+// Errors: WordPress.PHP.IniSet.filter_default_Disallowed
+ini_alter( 'filter.default', 1 );
+
+// Errors: WordPress.PHP.IniSet.filter_default_flags_Disallowed
+ini_alter( 'filter.default_flags', 1 );
+
+// Errors: WordPress.PHP.IniSet.iconv_input_encoding_Disallowed
+ini_alter( 'iconv.input_encoding', 1 );
+
+// Errors: WordPress.PHP.IniSet.iconv_internal_encoding_Disallowed
+ini_alter( 'iconv.internal_encoding', 1 );
+
+// Errors: WordPress.PHP.IniSet.iconv_output_encoding_Disallowed
+ini_alter( 'iconv.output_encoding', 1 );
+
+// Errors: WordPress.PHP.IniSet.ignore_user_abort_Disallowed
+ini_alter( 'ignore_user_abort', 1 );
+
+// Errors: WordPress.PHP.IniSet.log_errors_Disallowed
+ini_alter( 'log_errors', 1 );
+
+// Errors: WordPress.PHP.IniSet.max_execution_time_Disallowed
+ini_alter( 'max_execution_time', 1 );
+
+// Errors: WordPress.PHP.IniSet.memory_limit_Disallowed
+ini_alter( 'memory_limit', 1 );
+
+// Errors: WordPress.PHP.IniSet.short_open_tag_Disallowed
+ini_alter( 'short_open_tag', 'off' );
+
+// Warnings: WordPress.PHP.IniSet.Risky
+ini_alter( 'foo', true );
+
+// Warnings: WordPress.WP.AlternativeFunctions.curl_curl_init
+curl_init();
+
+// Warnings: WordPress.WP.AlternativeFunctions.curl_curl_close
+curl_close( $ch );
+
+// Warnings: WordPress.WP.AlternativeFunctions.curl_curl_getinfo
+CURL_getinfo();
+
+// Warnings: WordPress.WP.AlternativeFunctions.parse_url_parse_url
+parse_url( 'http://example.com/' );
+
+// Warnings: WordPress.WP.AlternativeFunctions.json_encode_json_encode
+$json = json_encode( $thing ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Warning.
+
+// Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_readfile
+readfile();
+
+// Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_fclose
+fclose();
+
+// Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_fopen
+fopen();
+
+// Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_fread
+fread();
+
+// Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_fsockopen
+fsockopen();
+
+// Warnings: WordPress.WP.AlternativeFunctions.file_system_operations_pfsockopen
+pfsockopen();
+
+// Warnings: WordPress.WP.AlternativeFunctions.rand_seeding_srand
+srand();
+
+// Warnings: WordPress.WP.AlternativeFunctions.rand_seeding_mt_srand
+mt_srand();
+
+// Warnings: WordPress.WP.AlternativeFunctions.rand_rand
+rand();
+
+// Warnings: WordPress.WP.AlternativeFunctions.rand_mt_rand
+mt_rand();
+
+// Errors: Generic.Files.OneObjectStructurePerFile.MultipleFound
+class MyClass {
+
+ // Errors: Squiz.Scope.MethodScope.Missing
+ function my_function() {
+ // Errors: Squiz.Functions.MultiLineFunctionDeclaration.SpaceAfterFunction
+ return function() {
+ $this->my_callback(); // OK - new VariableAnalysis doesn't flag $this as undefined in closure.
+ };
+ }
+
+ // Errors: Squiz.Scope.MethodScope.Missing
+ function my_callback() {}
+}
+
+// Errors: Generic.VersionControl.GitMergeConflict.OpenerFound
+?>
+<<<<<<< HEAD
+
+>>>>>>>
+
+
+
+ [
+ 4 => 1,
+ 5 => 1,
+ 6 => 41,
+ 10 => 5,
+ 15 => 2,
+ 20 => 1,
+ 22 => 1,
+ 26 => 1,
+ 31 => 1,
+ 33 => 3,
+ 35 => 1,
+ 40 => 1,
+ 43 => 2,
+ 57 => 1,
+ 67 => 1,
+ 81 => 1,
+ 89 => 1,
+ 96 => 1,
+ 101 => 2,
+ 106 => 1,
+ 110 => 1,
+ 118 => 1,
+ 125 => 1,
+ 128 => 1,
+ 130 => 1,
+ 132 => 1,
+ 139 => 1,
+ 141 => 1,
+ 144 => 1,
+ 146 => 1,
+ 152 => 1,
+ 155 => 1,
+ 162 => 2,
+ 168 => 3,
+ 222 => 1,
+ 226 => 1,
+ 230 => 1,
+ 275 => 1,
+ 296 => 1,
+ 299 => 1,
+ 302 => 1,
+ 305 => 1,
+ 308 => 1,
+ 312 => 1,
+ 316 => 1,
+ 320 => 1,
+ 323 => 1,
+ 326 => 1,
+ 329 => 1,
+ 332 => 1,
+ 335 => 1,
+ 365 => 1,
+ 368 => 1,
+ 371 => 1,
+ 374 => 1,
+ 377 => 1,
+ 380 => 1,
+ 383 => 1,
+ 386 => 1,
+ 389 => 1,
+ 392 => 1,
+ 395 => 1,
+ 398 => 1,
+ 401 => 1,
+ 452 => 1,
+ 455 => 1,
+ 457 => 1,
+ 463 => 1,
+ 468 => 1,
+ 475 => 1,
+ 478 => 1,
+ 483 => 4,
+ 486 => 1,
+ ],
+ 'warnings' => [
+ 15 => 1,
+ 40 => 2,
+ 47 => 1,
+ 51 => 1,
+ 54 => 1,
+ 67 => 1,
+ 73 => 3,
+ 77 => 1,
+ 83 => 1,
+ 85 => 2,
+ 87 => 1,
+ 96 => 1,
+ 101 => 1,
+ 106 => 1,
+ 118 => 1,
+ 125 => 1,
+ 152 => 1,
+ 171 => 1,
+ 174 => 1,
+ 177 => 1,
+ 180 => 1,
+ 183 => 1,
+ 186 => 1,
+ 189 => 1,
+ 192 => 1,
+ 195 => 1,
+ 198 => 1,
+ 201 => 1,
+ 204 => 1,
+ 207 => 2,
+ 210 => 1,
+ 213 => 1,
+ 216 => 1,
+ 219 => 1,
+ 222 => 1,
+ 226 => 1,
+ 230 => 1,
+ 233 => 2,
+ 236 => 1,
+ 239 => 1,
+ 242 => 1,
+ 245 => 1,
+ 248 => 1,
+ 251 => 1,
+ 254 => 1,
+ 257 => 1,
+ 260 => 1,
+ 263 => 1,
+ 266 => 1,
+ 269 => 1,
+ 272 => 1,
+ 305 => 1,
+ 312 => 1,
+ 316 => 1,
+ 320 => 1,
+ 338 => 1,
+ 404 => 1,
+ 407 => 1,
+ 410 => 1,
+ 413 => 1,
+ 416 => 1,
+ 419 => 1,
+ 422 => 1,
+ 425 => 1,
+ 428 => 1,
+ 431 => 1,
+ 434 => 1,
+ 437 => 1,
+ 440 => 1,
+ 443 => 1,
+ 446 => 1,
+ 449 => 1,
+ 475 => 1,
+ 486 => 1,
+ ],
+ 'messages' => [],
+];
+
+// If we're running on PHP 7.4, we need to account for the error thrown because `restore_include_path()` is deprecated.
+// See https://www.php.net/manual/en/function.restore-include-path.php
+if ( version_compare( PHP_VERSION, '7.4.0', '>=' ) && version_compare( PHP_VERSION, '8.0.0', '<' ) ) {
+ $expected['errors'][ 222 ] = 2;
+}
+
+// We have some specific errors that are only thrown on PHP 8.2+.
+if ( version_compare( PHP_VERSION, '8.2.0', '<' ) ) {
+ $expected['errors'][ 486 ] = 0;
+}
+
+if ( version_compare( PHP_VERSION, '7.2', '<' ) ) {
+ $expected['errors'][ 483 ] = 4;
+ $expected['warnings'][ 1 ] = 1;
+}
+
+require __DIR__ . '/../tests/RulesetTest.php';
+
+// Run the tests!
+$test = new \PhpcsComposer\RulesetTest( 'TenUpDefault', $expected );
+if ( $test->passes() ) {
+ printf( 'All TenUpDefault tests passed!' . PHP_EOL );
+ exit( 0 );
+}
+
+exit( 1 );
diff --git a/10up-Default/ruleset.xml b/10up-Default/ruleset.xml
index d2bfdaa..f78578a 100644
--- a/10up-Default/ruleset.xml
+++ b/10up-Default/ruleset.xml
@@ -51,7 +51,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
@@ -79,9 +79,8 @@
-
-
-
+
+
warning
@@ -103,14 +102,14 @@
-
+
-
+
-
+
diff --git a/README.md b/README.md
index ef2d992..f4ac47f 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
Install the library via Composer:
```bash
-$ composer require --dev 10up/phpcs-composer:dev-master
+$ composer require --dev 10up/phpcs-composer:"~3"
```
That's it!
@@ -40,7 +40,7 @@ $ composer run lint
### Continuous Integration
-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 7 or higher.
+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.
To override the default PHP version check, set the `--runtime-set testVersion 7.0-` configuration option. Example for PHP version 7.2 and above:
diff --git a/bin/ruleset-tests b/bin/ruleset-tests
new file mode 100755
index 0000000..59125a3
--- /dev/null
+++ b/bin/ruleset-tests
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+#
+# Run ruleset tests.
+#
+# This will check that each ruleset correctly includes the expected sniffs from this and other packages. If a sniff
+# reference has changed (moved category, or been renamed, etc.) then the expected errors, warnings, and messages will
+# not match what the output is, when the ruleset-test.inc is checked with PHPCS against the relevant ruleset.
+#
+# To run the tests, make sure you have the PHPCS, including the TenUpDefault standard, installed and executable
+# * using the `phpcs --standard=TenUpDefault` command.
+#
+# From the root of this VIP-Coding-Standards package, you can then run:
+#
+# ./bin/ruleset-tests
+
+# Set PHPCS_BIN, which is used in the tests/RulesetTest.php files.
+PHPCS_BIN="$(pwd)/vendor/bin/phpcs"
+export PHPCS_BIN
+
+php ./10up-Default/ruleset-test.php
diff --git a/bin/xml-lint b/bin/xml-lint
new file mode 100755
index 0000000..8ea2980
--- /dev/null
+++ b/bin/xml-lint
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+#
+# Check XML files.
+#
+# @link http://xmlsoft.org/xmllint.html
+#
+# EXAMPLE TO RUN LOCALLY:
+#
+# ./bin/xml-lint
+
+# Validate the ruleset XML files.
+xmllint --noout --schema ./vendor/squizlabs/php_codesniffer/phpcs.xsd ./*/ruleset.xml
+
+# Check the code-style consistency of the XML files.
+export XMLLINT_INDENT=" " # This is a tab character.
+diff -B --tabsize=4 ./10up-Default/ruleset.xml <(xmllint --format "./10up-Default/ruleset.xml")
diff --git a/composer.json b/composer.json
index 4c40963..4136644 100644
--- a/composer.json
+++ b/composer.json
@@ -1,34 +1,36 @@
{
- "name": "10up/phpcs-composer",
- "description": "10up's PHP CodeSniffer Ruleset",
- "type": "phpcodesniffer-standard",
- "license": "MIT",
- "require": {
- "dealerdirect/phpcodesniffer-composer-installer": "*",
- "phpcompatibility/phpcompatibility-wp": "^2",
- "squizlabs/php_codesniffer" : "3.7.1",
- "wp-coding-standards/wpcs": "*",
- "automattic/vipwpcs": "^2.3"
- },
- "prefer-stable" : true,
- "authors": [
- {
- "name": "10up",
- "homepage": "https://10up.com/"
- }
+ "name": "10up/phpcs-composer",
+ "description": "10up's PHP CodeSniffer Ruleset",
+ "type": "phpcodesniffer-standard",
+ "license": "MIT",
+ "require": {
+ "automattic/vipwpcs": "^3.0",
+ "phpcompatibility/phpcompatibility-wp": "^2"
+ },
+ "prefer-stable": true,
+ "authors": [
+ {
+ "name": "10up",
+ "homepage": "https://10up.com/"
+ }
+ ],
+ "scripts": {
+ "config-cs": [
+ "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run",
+ "\"vendor/bin/phpcs\" --config-set default_standard 10up-Default"
],
- "scripts": {
- "config-cs": [
- "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run",
- "\"vendor/bin/phpcs\" --config-set default_standard 10up-Default"
- ],
- "post-install-cmd": "@config-cs",
- "post-update-cmd": "@config-cs",
- "lint": "\"vendor/bin/phpcs\" . "
- },
- "config": {
- "allow-plugins": {
- "dealerdirect/phpcodesniffer-composer-installer": true
- }
+ "post-install-cmd": "@config-cs",
+ "post-update-cmd": "@config-cs",
+ "lint": "\"vendor/bin/phpcs\" . "
+ },
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
}
+ },
+ "minimum-stability": "dev",
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "*",
+ "phpcompatibility/php-compatibility": "dev-develop as 9.99.99"
+ }
}
diff --git a/composer.lock b/composer.lock
index 5a6c6c0..85dc10b 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,28 +4,29 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "5a64f044808a357a1ef6f92e99ffaca1",
+ "content-hash": "5a7e2b9a63bc91d26ccd8c8109b246e8",
"packages": [
{
"name": "automattic/vipwpcs",
- "version": "2.3.4",
+ "version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/Automattic/VIP-Coding-Standards.git",
- "reference": "b8610e3837f49c5f2fcc4b663b6c0a7c9b3509b6"
+ "reference": "1b8960ebff9ea3eb482258a906ece4d1ee1e25fd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/b8610e3837f49c5f2fcc4b663b6c0a7c9b3509b6",
- "reference": "b8610e3837f49c5f2fcc4b663b6c0a7c9b3509b6",
+ "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/1b8960ebff9ea3eb482258a906ece4d1ee1e25fd",
+ "reference": "1b8960ebff9ea3eb482258a906ece4d1ee1e25fd",
"shasum": ""
},
"require": {
- "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0",
"php": ">=5.4",
+ "phpcsstandards/phpcsextra": "^1.1.0",
+ "phpcsstandards/phpcsutils": "^1.0.8",
"sirbrillig/phpcs-variable-analysis": "^2.11.17",
- "squizlabs/php_codesniffer": "^3.7.1",
- "wp-coding-standards/wpcs": "^2.3"
+ "squizlabs/php_codesniffer": "^3.7.2",
+ "wp-coding-standards/wpcs": "^3.0"
},
"require-dev": {
"php-parallel-lint/php-console-highlighter": "^1.0.0",
@@ -57,7 +58,7 @@
"source": "https://github.com/Automattic/VIP-Coding-Standards",
"wiki": "https://github.com/Automattic/VIP-Coding-Standards/wiki"
},
- "time": "2023-08-24T15:11:13+00:00"
+ "time": "2023-09-05T11:01:05+00:00"
},
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
@@ -139,33 +140,45 @@
},
{
"name": "phpcompatibility/php-compatibility",
- "version": "9.3.5",
+ "version": "dev-develop",
"source": {
"type": "git",
"url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
- "reference": "9fb324479acf6f39452e0655d2429cc0d3914243"
+ "reference": "8770f4f4677cc649a1df5e73dc6195d9887f4641"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243",
- "reference": "9fb324479acf6f39452e0655d2429cc0d3914243",
+ "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/8770f4f4677cc649a1df5e73dc6195d9887f4641",
+ "reference": "8770f4f4677cc649a1df5e73dc6195d9887f4641",
"shasum": ""
},
"require": {
- "php": ">=5.3",
- "squizlabs/php_codesniffer": "^2.3 || ^3.0.2"
+ "php": ">=5.4",
+ "phpcsstandards/phpcsutils": "^1.0.5",
+ "squizlabs/php_codesniffer": "^3.7.1"
},
- "conflict": {
- "squizlabs/php_codesniffer": "2.6.2"
+ "replace": {
+ "wimg/php-compatibility": "*"
},
"require-dev": {
- "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0"
+ "php-parallel-lint/php-console-highlighter": "^1.0.0",
+ "php-parallel-lint/php-parallel-lint": "^1.3.2",
+ "phpcsstandards/phpcsdevcs": "^1.1.3",
+ "phpcsstandards/phpcsdevtools": "^1.2.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4 || ^10.1.0",
+ "yoast/phpunit-polyfills": "^1.0.5 || ^2.0.0"
},
"suggest": {
- "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.",
"roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues."
},
+ "default-branch": true,
"type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev",
+ "dev-develop": "10.x-dev"
+ }
+ },
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
@@ -191,13 +204,14 @@
"keywords": [
"compatibility",
"phpcs",
- "standards"
+ "standards",
+ "static analysis"
],
"support": {
"issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues",
"source": "https://github.com/PHPCompatibility/PHPCompatibility"
},
- "time": "2019-12-27T09:44:58+00:00"
+ "time": "2023-11-13T01:28:23+00:00"
},
{
"name": "phpcompatibility/phpcompatibility-paragonie",
@@ -311,6 +325,142 @@
},
"time": "2022-10-24T09:00:36+00:00"
},
+ {
+ "name": "phpcsstandards/phpcsextra",
+ "version": "1.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHPCSExtra.git",
+ "reference": "746c3190ba8eb2f212087c947ba75f4f5b9a58d5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/746c3190ba8eb2f212087c947ba75f4f5b9a58d5",
+ "reference": "746c3190ba8eb2f212087c947ba75f4f5b9a58d5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4",
+ "phpcsstandards/phpcsutils": "^1.0.8",
+ "squizlabs/php_codesniffer": "^3.7.1"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.3.2",
+ "phpcsstandards/phpcsdevcs": "^1.1.6",
+ "phpcsstandards/phpcsdevtools": "^1.2.1",
+ "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-stable": "1.x-dev",
+ "dev-develop": "1.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Juliette Reinders Folmer",
+ "homepage": "https://github.com/jrfnl",
+ "role": "lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors"
+ }
+ ],
+ "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.",
+ "keywords": [
+ "PHP_CodeSniffer",
+ "phpcbf",
+ "phpcodesniffer-standard",
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues",
+ "source": "https://github.com/PHPCSStandards/PHPCSExtra"
+ },
+ "time": "2023-09-20T22:06:18+00:00"
+ },
+ {
+ "name": "phpcsstandards/phpcsutils",
+ "version": "1.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHPCSUtils.git",
+ "reference": "69465cab9d12454e5e7767b9041af0cd8cd13be7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/69465cab9d12454e5e7767b9041af0cd8cd13be7",
+ "reference": "69465cab9d12454e5e7767b9041af0cd8cd13be7",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^3.7.1 || 4.0.x-dev@dev"
+ },
+ "require-dev": {
+ "ext-filter": "*",
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.3.2",
+ "phpcsstandards/phpcsdevcs": "^1.1.6",
+ "yoast/phpunit-polyfills": "^1.0.5 || ^2.0.0"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-stable": "1.x-dev",
+ "dev-develop": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "PHPCSUtils/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Juliette Reinders Folmer",
+ "homepage": "https://github.com/jrfnl",
+ "role": "lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors"
+ }
+ ],
+ "description": "A suite of utility functions for use with PHP_CodeSniffer",
+ "homepage": "https://phpcsutils.com/",
+ "keywords": [
+ "PHP_CodeSniffer",
+ "phpcbf",
+ "phpcodesniffer-standard",
+ "phpcs",
+ "phpcs3",
+ "standards",
+ "static analysis",
+ "tokens",
+ "utility"
+ ],
+ "support": {
+ "docs": "https://phpcsutils.com/",
+ "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues",
+ "source": "https://github.com/PHPCSStandards/PHPCSUtils"
+ },
+ "time": "2023-07-16T21:39:41+00:00"
+ },
{
"name": "sirbrillig/phpcs-variable-analysis",
"version": "v2.11.17",
@@ -371,16 +521,16 @@
},
{
"name": "squizlabs/php_codesniffer",
- "version": "3.7.1",
+ "version": "3.7.2",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
- "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619"
+ "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619",
- "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879",
+ "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879",
"shasum": ""
},
"require": {
@@ -416,41 +566,50 @@
"homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
"keywords": [
"phpcs",
- "standards"
+ "standards",
+ "static analysis"
],
"support": {
"issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
"source": "https://github.com/squizlabs/PHP_CodeSniffer",
"wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
},
- "time": "2022-06-18T07:21:10+00:00"
+ "time": "2023-02-22T23:07:41+00:00"
},
{
"name": "wp-coding-standards/wpcs",
- "version": "2.3.0",
+ "version": "3.0.1",
"source": {
"type": "git",
"url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
- "reference": "7da1894633f168fe244afc6de00d141f27517b62"
+ "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7da1894633f168fe244afc6de00d141f27517b62",
- "reference": "7da1894633f168fe244afc6de00d141f27517b62",
+ "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/b4caf9689f1a0e4a4c632679a44e638c1c67aff1",
+ "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1",
"shasum": ""
},
"require": {
+ "ext-filter": "*",
+ "ext-libxml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlreader": "*",
"php": ">=5.4",
- "squizlabs/php_codesniffer": "^3.3.1"
+ "phpcsstandards/phpcsextra": "^1.1.0",
+ "phpcsstandards/phpcsutils": "^1.0.8",
+ "squizlabs/php_codesniffer": "^3.7.2"
},
"require-dev": {
- "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6",
+ "php-parallel-lint/php-console-highlighter": "^1.0.0",
+ "php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcompatibility/php-compatibility": "^9.0",
- "phpcsstandards/phpcsdevtools": "^1.0",
+ "phpcsstandards/phpcsdevtools": "^1.2.0",
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"suggest": {
- "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically."
+ "ext-iconv": "For improved results",
+ "ext-mbstring": "For improved results"
},
"type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
@@ -467,6 +626,7 @@
"keywords": [
"phpcs",
"standards",
+ "static analysis",
"wordpress"
],
"support": {
@@ -474,13 +634,28 @@
"source": "https://github.com/WordPress/WordPress-Coding-Standards",
"wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
},
- "time": "2020-05-13T23:57:56+00:00"
+ "funding": [
+ {
+ "url": "https://opencollective.com/thewpcc/contribute/wp-php-63406",
+ "type": "custom"
+ }
+ ],
+ "time": "2023-09-14T07:06:09+00:00"
}
],
"packages-dev": [],
- "aliases": [],
- "minimum-stability": "stable",
- "stability-flags": [],
+ "aliases": [
+ {
+ "package": "phpcompatibility/php-compatibility",
+ "version": "dev-develop",
+ "alias": "9.99.99",
+ "alias_normalized": "9.99.99.0"
+ }
+ ],
+ "minimum-stability": "dev",
+ "stability-flags": {
+ "phpcompatibility/php-compatibility": 20
+ },
"prefer-stable": true,
"prefer-lowest": false,
"platform": [],
diff --git a/tests/RulesetTest.php b/tests/RulesetTest.php
new file mode 100644
index 0000000..9f33eed
--- /dev/null
+++ b/tests/RulesetTest.php
@@ -0,0 +1,370 @@
+ '10up-Default',
+ ];
+
+ /**
+ * String returned by PHP_CodeSniffer report for an Error.
+ */
+ const ERROR_TYPE = 'ERROR';
+
+ /**
+ * Init the object by processing the test file.
+ *
+ * @param string $ruleset Name of the ruleset e.g. TenUpDefault.
+ * @param array $expected The array of expected errors, warnings and messages.
+ */
+ public function __construct( $ruleset, $expected = [] ) {
+ $this->ruleset = $ruleset;
+ $this->expected = $expected;
+
+ if ( isset( $this->directory_mapping[ $ruleset ] ) ) {
+ $this->ruleset_directory = $this->directory_mapping[ $ruleset ];
+ } else {
+ $this->ruleset_directory = $ruleset;
+ }
+
+ // Travis and Windows support.
+ $phpcs_bin = getenv( 'PHPCS_BIN' );
+ if ( $phpcs_bin === false ) {
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv -- This is test code, not production.
+ putenv( 'PHPCS_BIN=phpcs' );
+ } else {
+ $this->phpcs_bin = realpath( $phpcs_bin );
+ }
+
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ printf( 'Testing the ' . $this->ruleset . ' ruleset.' . PHP_EOL );
+
+ $output = $this->collect_phpcs_result();
+
+ if ( empty( $output ) || ! is_object( $output ) ) {
+ printf( 'The PHPCS command checking the ruleset hasn\'t returned any issues. Bailing ...' . PHP_EOL );
+ exit( 1 ); // Die early, if we don't have any output.
+ }
+
+ $this->process_output( $output );
+ }
+
+ /**
+ * Run all the tests and return whether test was successful.
+ *
+ * @return bool
+ */
+ public function passes() {
+ $this->run();
+
+ return ! $this->found_issue;
+ }
+
+ /**
+ * Run all the tests.
+ *
+ * @return void
+ */
+ private function run() {
+ // Check for missing expected values.
+ $this->check_missing_expected_values();
+ // Check for extra values which were not expected.
+ $this->check_unexpected_values();
+ // Check for expected messages.
+ $this->check_messages();
+ }
+
+ /**
+ * Collect the PHP_CodeSniffer result.
+ *
+ * @return array Returns an associative array with keys of `totals` and `files`.
+ */
+ private function collect_phpcs_result() {
+ $php = '';
+ if ( \PHP_BINARY && in_array( \PHP_SAPI, [ 'cgi-fcgi', 'cli', 'cli-server', 'phpdbg' ], true ) ) {
+ $php = \PHP_BINARY . ' ';
+ }
+
+ $shell = sprintf(
+ '%1$s%2$s --severity=1 --standard=%3$s --report=json ./%3$s/ruleset-test.inc',
+ $php, // Current PHP executable if available.
+ $this->phpcs_bin,
+ $this->ruleset_directory
+ );
+
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_shell_exec -- This is test code, not production.
+ $output = shell_exec( $shell );
+
+ return json_decode( $output );
+ }
+
+ /**
+ * Process the Decoded JSON output from PHP_CodeSniffer.
+ *
+ * @param \stdClass $output Decoded JSON output from PHP_CodeSniffer.
+ *
+ * @return void
+ */
+ private function process_output( $output ) {
+ foreach ( $output->files as $file ) {
+ $this->process_file( $file );
+ }
+ }
+
+ /**
+ * Process single file of within PHP_CodeSniffer results.
+ *
+ * @param \stdClass $file File output.
+ *
+ * @return void
+ */
+ private function process_file( $file ) {
+ foreach ( $file->messages as $violation ) {
+ $this->process_violation( $violation );
+ }
+ }
+
+ /**
+ * Process single violation within PHP_CodeSniffer results.
+ *
+ * @param \stdClass $violation Violation data.
+ *
+ * @return void
+ */
+ private function process_violation( $violation ) {
+ if ( $this->violation_type_is_error( $violation ) ) {
+ $this->add_error_for_line( $violation->line );
+ } else {
+ $this->add_warning_for_line( $violation->line );
+ }
+
+ $this->add_message_for_line( $violation->line, $violation->message );
+ }
+
+ /**
+ * Check if violation is an error.
+ *
+ * @param \stdClass $violation Violation data.
+ *
+ * @return bool True if string matches error type.
+ */
+ private function violation_type_is_error( $violation ) {
+ return $violation->type === self::ERROR_TYPE;
+ }
+
+ /**
+ * Add 1 to the number of errors for the given line.
+ *
+ * @param int $line Line number.
+ *
+ * @return void
+ */
+ private function add_error_for_line( $line ) {
+ $this->errors[ $line ] = isset( $this->errors[ $line ] ) ? ++ $this->errors[ $line ] : 1;
+ }
+
+ /**
+ * Add 1 to the number of errors for the given line.
+ *
+ * @param int $line Line number.
+ *
+ * @return void
+ */
+ private function add_warning_for_line( $line ) {
+ $this->warnings[ $line ] = isset( $this->warnings[ $line ] ) ? ++ $this->warnings[ $line ] : 1;
+ }
+
+ /**
+ * Add message for the given line.
+ *
+ * @param int $line Line number.
+ * @param string $message Message.
+ *
+ * @return void
+ */
+ private function add_message_for_line( $line, $message ) {
+ $this->messages[ $line ] = ( ! isset( $this->messages[ $line ] ) || ! is_array( $this->messages[ $line ] ) ) ? [ $message ] : array_merge( $this->messages[ $line ],
+ [ $message ] );
+ }
+
+ /**
+ * Check whether all expected numbers of errors and warnings are present in the output.
+ *
+ * @return void
+ */
+ private function check_missing_expected_values() {
+ foreach ( $this->expected as $type => $lines ) {
+ if ( $type === 'messages' ) {
+ continue;
+ }
+
+ foreach ( $lines as $line_number => $expected_count_of_type_violations ) {
+ if ( $expected_count_of_type_violations === 0 ) {
+ continue;
+ }
+
+ if ( ! isset( $this->{$type}[ $line_number ] ) ) {
+ $this->error_warning_message( $expected_count_of_type_violations, $type, 0, $line_number );
+ } elseif ( $this->{$type}[ $line_number ] !== $expected_count_of_type_violations ) {
+ $this->error_warning_message( $expected_count_of_type_violations, $type,
+ $this->{$type}[ $line_number ], $line_number );
+ }
+
+ unset( $this->{$type}[ $line_number ] );
+ }
+ }
+ }
+
+ /**
+ * Check whether there are no unexpected numbers of errors and warnings.
+ *
+ * @return void
+ */
+ private function check_unexpected_values() {
+ foreach ( [ 'errors', 'warnings' ] as $type ) {
+ foreach ( $this->$type as $line_number => $actual_count_of_type_violations ) {
+ if ( $actual_count_of_type_violations === 0 ) {
+ continue;
+ }
+
+ if ( ! isset( $this->expected[ $type ][ $line_number ] ) ) {
+ $this->error_warning_message( 0, $type, $actual_count_of_type_violations, $line_number );
+ } elseif ( $actual_count_of_type_violations !== $this->expected[ $type ][ $line_number ] ) {
+ $this->error_warning_message( $this->expected[ $type ][ $line_number ], $type,
+ $actual_count_of_type_violations, $line_number );
+ }
+ }
+ }
+ }
+
+ /**
+ * Check whether all expected messages are present and whether there are no unexpected messages.
+ *
+ * @return void
+ */
+ private function check_messages() {
+ foreach ( $this->expected['messages'] as $line_number => $messages ) {
+ foreach ( $messages as $message ) {
+ if ( ! isset( $this->messages[ $line_number ] ) ) {
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ printf( 'Expected "%s" but found no message for line %d' . PHP_EOL, $message, $line_number );
+ $this->found_issue = true;
+ } elseif ( ! in_array( $message, $this->messages[ $line_number ], true ) ) {
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ printf( 'Expected message "%s" was not found for line %d.' . PHP_EOL, $message, $line_number );
+ $this->found_issue = true;
+ }
+ }
+ }
+ foreach ( $this->messages as $line_number => $messages ) {
+ foreach ( $messages as $message ) {
+ if ( isset( $this->expected['messages'][ $line_number ] ) ) {
+ if ( ! in_array( $message, $this->expected['messages'][ $line_number ], true ) ) {
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ printf( 'Unexpected message "%s" was found for line %d.' . PHP_EOL, $message, $line_number );
+ $this->found_issue = true;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Print out the message reporting found issues.
+ *
+ * @param int $expected Expected number of issues.
+ * @param string $type The type of the issue.
+ * @param int $number Real number of issues.
+ * @param int $line Line number.
+ *
+ * @return void
+ */
+ private function error_warning_message( $expected, $type, $number, $line ) {
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ printf( 'Expected %d %s, found %d on line %d.' . PHP_EOL, $expected, $type, $number, $line );
+ $this->found_issue = true;
+ }
+}