diff --git a/.github/workflows/php-coding-standards.yml b/.github/workflows/php-coding-standards.yml new file mode 100644 index 00000000..d0cafb4b --- /dev/null +++ b/.github/workflows/php-coding-standards.yml @@ -0,0 +1,44 @@ +name: PHP Coding Standards + +on: + push: + branches: + - "trunk" + paths: + - "**.php" + - .github/workflows/php-coding-standards.yml + pull_request: + paths: + - "**.php" + - .github/workflows/php-coding-standards.yml + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + phpcs: + name: PHP coding standards + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Prepare PHP + uses: woocommerce/grow/prepare-php@actions-v1 + with: + php-version: 7.4 + tools: cs2pr + install-deps: "no" + + - name: Install Composer dependencies + run: | + # Skip installing packages from private repos. + composer remove --dev woocommerce/woorelease + composer install --prefer-dist --no-interaction + + - name: Log PHPCS information + run: vendor/bin/phpcs -i + + - name: Run PHPCS on all files + run: vendor/bin/phpcs -q --report=checkstyle | cs2pr diff --git a/README.md b/README.md index ffa7dc9a..5259835a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Grow Packages [![JavaScript Linting](https://github.com/woocommerce/grow/actions/workflows/js-linting.yml/badge.svg)](https://github.com/woocommerce/grow/actions/workflows/js-linting.yml) +[![PHP Coding Standards](https://github.com/woocommerce/grow/actions/workflows/php-coding-standards.yml/badge.svg)](https://github.com/woocommerce/grow/actions/workflows/php-coding-standards.yml) This repository is a container for packages, mostly dev tools to serve the Grow Team. The packages here are too experimental or too Grow-specific to be shared Woo-wide. diff --git a/composer.json b/composer.json index ff343c89..69988e97 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,9 @@ "phpcompatibility/php-compatibility": "^9.0", "php-parallel-lint/php-parallel-lint": "^1.3.1", "woocommerce/woorelease": "^2.4", - "yoast/phpunit-polyfills": "^1.0" + "yoast/phpunit-polyfills": "^1.0", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "wp-coding-standards/wpcs": "^3.0" }, "repositories": [ { @@ -38,5 +40,10 @@ "scripts-descriptions": { "assumeReleaseUnchanged": "Tell Git to assume that the 'release.txt' file is unchanged. This allows changes in this file to be ignored by Git.", "noAssumeReleaseUnchanged": "Tell Git to stop assuming that the 'release.txt' file is unchanged, and track it normally." + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } } } diff --git a/composer.lock b/composer.lock index 62d405fe..8597d933 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "69f948974aac014b79e8134be2e3645e", + "content-hash": "fad753b0dacd3ea0a19af9ec6b179c13", "packages": [], "packages-dev": [ { @@ -97,6 +97,84 @@ }, "time": "2021-12-05T09:10:49+00:00" }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "4be43904336affa5c2f70744a348312336afd0da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", + "reference": "4be43904336affa5c2f70744a348312336afd0da", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "source": "https://github.com/PHPCSStandards/composer-installer" + }, + "time": "2023-01-05T11:28:13+00:00" + }, { "name": "doctrine/instantiator", "version": "1.5.0", @@ -614,6 +692,157 @@ }, "time": "2019-12-27T09:44:58+00:00" }, + { + "name": "phpcsstandards/phpcsextra", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "78b2cae1e9de1c05f0416de6f9a658cbb83ac324" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/78b2cae1e9de1c05f0416de6f9a658cbb83ac324", + "reference": "78b2cae1e9de1c05f0416de6f9a658cbb83ac324", + "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", + "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSExtra" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2023-12-02T14:30:12+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": "phpunit/php-code-coverage", "version": "9.2.26", @@ -4118,6 +4347,72 @@ }, "time": "2022-07-21T01:05:57+00:00" }, + { + "name": "wp-coding-standards/wpcs", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", + "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1" + }, + "dist": { + "type": "zip", + "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", + "phpcsstandards/phpcsextra": "^1.1.0", + "phpcsstandards/phpcsutils": "^1.0.8", + "squizlabs/php_codesniffer": "^3.7.2" + }, + "require-dev": { + "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.2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-iconv": "For improved results", + "ext-mbstring": "For improved results" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", + "keywords": [ + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", + "source": "https://github.com/WordPress/WordPress-Coding-Standards", + "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/thewpcc/contribute/wp-php-63406", + "type": "custom" + } + ], + "time": "2023-09-14T07:06:09+00:00" + }, { "name": "yoast/phpunit-polyfills", "version": "1.0.5", diff --git a/package.json b/package.json index f66cf54a..c066f7f4 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version": "0.1.0", "description": "This repository is a container for packages, mostly dev tools to serve the Grow Team. The packages here are too experimental or too Grow-specific to be shared Woo-wide.", "scripts": { - "lint:js": "wp-scripts lint-js --ext .js,.mjs" + "lint:js": "wp-scripts lint-js --ext .js,.mjs", + "lint:php": "vendor/bin/phpcs" }, "repository": { "type": "git", diff --git a/packages/github-actions/actions/hook-documentation/bin/github-actions-hook-documentor.php b/packages/github-actions/actions/hook-documentation/bin/github-actions-hook-documentor.php index e0e009d6..4ad99557 100755 --- a/packages/github-actions/actions/hook-documentation/bin/github-actions-hook-documentor.php +++ b/packages/github-actions/actions/hook-documentation/bin/github-actions-hook-documentor.php @@ -3,13 +3,13 @@ use Automattic\WooCommerce\Grow\GitHubActions\HookDocumentation\Documentor; -$autoloadPath = dirname( __DIR__ ) . '/vendor/autoload.php'; -if ( ! file_exists( $autoloadPath ) ) { +$autoload_path = dirname( __DIR__ ) . '/vendor/autoload.php'; +if ( ! file_exists( $autoload_path ) ) { echo "Please run 'composer install'!\n"; exit( 1 ); } -require_once $autoloadPath; +require_once $autoload_path; // Set up variables from the environment. $env = getenv(); @@ -21,7 +21,7 @@ // Source directories need the full path prepended. $source_dirs = array_map( - function( $path ) { + function ( $path ) { return ltrim( trim( $path ), '/' ); }, explode( ',', $source_dirs ) @@ -29,7 +29,7 @@ function( $path ) { $args = [ 'github_blob' => $ref, - 'base_url' => $base_url, + 'base_url' => $base_url, 'source_dirs' => $source_dirs, 'workspace' => $base_path, ]; diff --git a/packages/github-actions/actions/hook-documentation/src/Documentor.php b/packages/github-actions/actions/hook-documentation/src/Documentor.php index 7e2f971b..45e76616 100644 --- a/packages/github-actions/actions/hook-documentation/src/Documentor.php +++ b/packages/github-actions/actions/hook-documentation/src/Documentor.php @@ -102,9 +102,11 @@ protected function get_hooks( array $files_to_scan ): array { continue; } - $current_file = $f; + // It's a local file. + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents $tokens = token_get_all( file_get_contents( $f ) ); $token_type = false; + $current_file = $f; $current_class = ''; $current_function = ''; @@ -139,7 +141,7 @@ protected function get_hooks( array $files_to_scan ): array { // Keep adding to hook until we find a comma or colon. $loop = 0; do { - $loop++; + ++$loop; $prev_hook = is_string( $tokens[ $index + $loop - 1 ] ) ? $tokens[ $index + $loop - 1 ] : $tokens[ $index + $loop - 1 ][1]; $next_hook = is_string( $tokens[ $index + $loop ] ) ? $tokens[ $index + $loop ] @@ -259,7 +261,7 @@ protected function get_file_link( array $file ): string { * @param array $hook_list List of hooks. */ protected function get_delimited_list_output( array $hook_list ): string { - $output = "# Hooks Reference\n\n"; + $output = "# Hooks Reference\n\n"; $output .= "A list of hooks, e.g. \`actions\` and \`filters\`, that are defined or used in this project.\n\n"; foreach ( $hook_list as $hooks ) { @@ -269,7 +271,7 @@ protected function get_delimited_list_output( array $hook_list ): string { $link_list[] = "- {$this->get_file_link( $file )}"; } - $links = implode( "\n", $link_list ); + $links = implode( "\n", $link_list ); $output .= sprintf( "## %s\n\n**Type**: %s\n\n**Used in**:\n\n%s\n\n", $hook, @@ -284,6 +286,8 @@ protected function get_delimited_list_output( array $hook_list ): string { /** * Generate hooks documentation. + * + * @throws RuntimeException When no hooks are found. */ public function generate_hooks_docs(): string { $hook_list = $this->get_hooks( $this->get_files_to_scan() ); @@ -313,6 +317,8 @@ protected function get_finder(): Finder { * @param array $args * * @return void + * + * @throws RuntimeException When any arguments are missing. */ protected function validate_args( array $defaults, array $args ): void { $arg_count = count( $args ); diff --git a/packages/github-actions/actions/hook-documentation/tests/Data/sample-file-1.php b/packages/github-actions/actions/hook-documentation/tests/Data/sample-file-1.php index 4423ef6d..e52baba0 100644 --- a/packages/github-actions/actions/hook-documentation/tests/Data/sample-file-1.php +++ b/packages/github-actions/actions/hook-documentation/tests/Data/sample-file-1.php @@ -17,7 +17,7 @@ function sample_function_1() { } function sample_function_2() { - $some_var = 'foo'; + $some_var = 'foo'; $some_other_var = new stdClass(); /** * Do another sample action. diff --git a/packages/github-actions/actions/hook-documentation/tests/Data/sample-file-2.php b/packages/github-actions/actions/hook-documentation/tests/Data/sample-file-2.php index 49ea6264..aee3ce08 100644 --- a/packages/github-actions/actions/hook-documentation/tests/Data/sample-file-2.php +++ b/packages/github-actions/actions/hook-documentation/tests/Data/sample-file-2.php @@ -3,6 +3,7 @@ namespace Automattic\WooCommerce\Grow\GitHubActions\HookDocumentation\Tests\Data; +// phpcs:ignore Squiz.Classes.ClassFileName.NoMatch class SampleClassWithHooks { protected string $filtered_value = 'foo'; diff --git a/packages/github-actions/actions/hook-documentation/tests/Pest.php b/packages/github-actions/actions/hook-documentation/tests/Pest.php index 2a336725..febad3cd 100644 --- a/packages/github-actions/actions/hook-documentation/tests/Pest.php +++ b/packages/github-actions/actions/hook-documentation/tests/Pest.php @@ -16,10 +16,9 @@ | case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may | need to change it using the "uses()" function to bind a different classes or traits. | +| For example: uses( Tests\TestCase::class )->in( 'Feature' ); */ -// uses(Tests\TestCase::class)->in('Feature'); - /* |-------------------------------------------------------------------------- | Expectations @@ -31,13 +30,9 @@ | */ -//expect()->extend('toBeOne', function () { -// return $this->toBe(1); -//}); - expect()->extend( 'toContainCount', - function( string $value, int $count ) { + function ( string $value, int $count ) { $result = substr_count( $this->value, $value ); Assert::assertEquals( $count, $result ); @@ -56,16 +51,12 @@ function( string $value, int $count ) { | */ -//function something() -//{ -// // .. -//} /** - * @param array $source_dirs + * @param array ...$source_dirs * * @return Documentor */ -function getTestDataDocumentor( ...$source_dirs ): Documentor { +function get_test_data_documentor( ...$source_dirs ): Documentor { $args = [ 'base_url' => 'https://github.com/example/test', 'github_blob' => 'abc123', diff --git a/packages/github-actions/actions/hook-documentation/tests/Unit/DocumentorContentTest.php b/packages/github-actions/actions/hook-documentation/tests/Unit/DocumentorContentTest.php index 50fa0709..d66fa9cf 100644 --- a/packages/github-actions/actions/hook-documentation/tests/Unit/DocumentorContentTest.php +++ b/packages/github-actions/actions/hook-documentation/tests/Unit/DocumentorContentTest.php @@ -8,7 +8,7 @@ it( 'should find actions in sample files', - function() { + function () { $workspace = dirname( __DIR__ ); $github_repo = 'https://github.com/example/test'; $sha = 'abc123'; @@ -29,35 +29,35 @@ function() { it( 'should find all actions in sample file 1', - function( string $results ) { + function ( string $results ) { expect( $results )->toContain( 'sample_action_1', 'sample_action_2' ); } )->depends( 'it should find actions in sample files' ); it( 'should find sample action 1 only once', - function( string $results ) { + function ( string $results ) { expect( $results )->toContainCount( 'sample_action_1', 1 ); } )->depends( 'it should find actions in sample files' ); it( 'should find the sample-file-1 filename 6 times – twice for each action', - function( string $results ) { + function ( string $results ) { expect( $results )->toContainCount( 'sample-file-1.php', 6 ); } )->depends( 'it should find actions in sample files' ); it( 'should not contain the full workspace directory', - function( string $results ) { + function ( string $results ) { expect( $results )->not()->toContain( dirname( __DIR__ ) . '/Data' ); } )->depends( 'it should find actions in sample files' ); it( 'should find all actions in sample file 2', - function( string $results ) { + function ( string $results ) { expect( $results )->toContain( 'class_action_1', 'class_action_2', @@ -71,7 +71,7 @@ function( string $results ) { it( 'should find the sample-file-2 filename 12 times – twice for each action', - function( string $results ) { + function ( string $results ) { expect( $results )->toContainCount( 'sample-file-2.php', 12 ); } )->depends( 'it should find actions in sample files' ); diff --git a/packages/github-actions/actions/hook-documentation/tests/Unit/DocumentorTest.php b/packages/github-actions/actions/hook-documentation/tests/Unit/DocumentorTest.php index 1ece8893..c3a76915 100644 --- a/packages/github-actions/actions/hook-documentation/tests/Unit/DocumentorTest.php +++ b/packages/github-actions/actions/hook-documentation/tests/Unit/DocumentorTest.php @@ -8,13 +8,13 @@ use Closure; use RuntimeException; -use function Automattic\WooCommerce\Grow\GitHubActions\HookDocumentation\Tests\getTestDataDocumentor; +use function Automattic\WooCommerce\Grow\GitHubActions\HookDocumentation\Tests\get_test_data_documentor; it( 'should throw an exception for missing constructor args', - function( array $args, string $message ) { + function ( array $args, string $message ) { expect( - function() use ( $args ) { + function () use ( $args ) { new Documentor( $args ); } )->toThrow( RuntimeException::class, $message ); @@ -24,7 +24,7 @@ function() use ( $args ) { it( 'should correctly replace local path with github url', - function() { + function () { $workspace = '/path/to/the/workspace/example-test'; $github_repo = 'https://github.com/example/test'; $sha = 'abc123'; @@ -35,13 +35,14 @@ function() { ]; // Mock parts of the class that we need with an anonymous class and function. - $class = new class( $args ) extends Documentor { + // phpcs:ignore Universal.WhiteSpace.AnonClassKeywordSpacing.Incorrect + $class = new class ($args) extends Documentor { /** No-op */ protected function validate_args( array $defaults, array $args ): void {} }; $get_file_url = Closure::bind( - function( array $file ) { + function ( array $file ) { return $this->get_file_url( $file ); }, $class, @@ -69,19 +70,19 @@ function( array $file ) { it( 'should gracefully handle a non-existent directory', - function() { + function () { expect( - getTestDataDocumentor( 'Data/', 'FakeDir/' )->generate_hooks_docs() + get_test_data_documentor( 'Data/', 'FakeDir/' )->generate_hooks_docs() )->not()->toBeEmpty()->toBeString(); } ); it( 'should throw an exception when no hooks were found', - function() { + function () { expect( - function() { - getTestDataDocumentor( 'FakeDir/' )->generate_hooks_docs(); + function () { + get_test_data_documentor( 'FakeDir/' )->generate_hooks_docs(); } )->toThrow( RuntimeException::class, 'No hooks found' ); } diff --git a/packages/php/compat-checker/src/Checker.php b/packages/php/compat-checker/src/Checker.php index 60fdc68c..749e8141 100644 --- a/packages/php/compat-checker/src/Checker.php +++ b/packages/php/compat-checker/src/Checker.php @@ -44,7 +44,7 @@ public static function instance() { * @param string $file_version The plugin file version. Can be the same as the plugin version. */ public function get_plugin_data( $plugin_file, $file_version ) { - $default_headers = array( + $default_headers = [ 'Name' => 'Plugin Name', 'Version' => 'Version', 'RequiresWP' => 'Requires at least', @@ -52,7 +52,7 @@ public function get_plugin_data( $plugin_file, $file_version ) { 'RequiresWC' => 'WC requires at least', 'TestedWP' => 'Tested up to', 'TestedWC' => 'WC tested up to', - ); + ]; $transient_key = 'wc_grow_compat_checker_' . plugin_basename( $plugin_file ) . $file_version; $plugin_data = get_transient( $transient_key ); @@ -75,10 +75,10 @@ public function get_plugin_data( $plugin_file, $file_version ) { * @return bool */ public function is_compatible( $plugin_file_path, $file_version ) { - $checks = array( + $checks = [ WPCompatibility::class, WCCompatibility::class, - ); + ]; $plugin_data = $this->get_plugin_data( $plugin_file_path, $file_version ); $plugin_basename = plugin_basename( $plugin_file_path ); diff --git a/packages/php/compat-checker/src/Checks/CompatCheck.php b/packages/php/compat-checker/src/Checks/CompatCheck.php index b0525353..738681d3 100644 --- a/packages/php/compat-checker/src/Checks/CompatCheck.php +++ b/packages/php/compat-checker/src/Checks/CompatCheck.php @@ -21,21 +21,21 @@ abstract class CompatCheck { * * @var array */ - protected $notices = array(); + protected $notices = []; /** * Array of CompatCheck instances. * * @var array */ - private static $instances = array(); + private static $instances = []; /** * The plugin data. * * @var array */ - protected $plugin_data = array(); + protected $plugin_data = []; /** * Run checks @@ -69,7 +69,7 @@ public static function instance( $plugin_basename ) { */ protected function add_admin_notice( $slug, $css_class, $message ) { $screen = get_current_screen(); - $hidden = array( 'update', 'update-network', 'update-core', 'update-core-network', 'upgrade', 'upgrade-network', 'network' ); + $hidden = [ 'update', 'update-network', 'update-core', 'update-core-network', 'upgrade', 'upgrade-network', 'network' ]; $show = isset( $screen->id ) && ! in_array( $screen->id, $hidden, true ); $slug = isset( $this->plugin_data['File'] ) ? plugin_basename( $this->plugin_data['File'] ) . '-' . $slug : $slug; @@ -92,10 +92,10 @@ protected function add_admin_notice( $slug, $css_class, $message ) { WC_Admin_Notices::add_custom_notice( $slug, $message ); } } else { - $this->notices[ $slug ] = array( + $this->notices[ $slug ] = [ 'class' => $css_class, 'message' => $message, - ); + ]; } } @@ -122,14 +122,14 @@ protected function compare_major_version( $left, $right, $operator = null ) { * Display admin notices generated by the checker. */ public function display_admin_notices() { - $allowed_tags = array( - 'a' => array( - 'class' => array(), - 'href' => array(), - 'target' => array(), - ), - 'strong' => array(), - ); + $allowed_tags = [ + 'a' => [ + 'class' => [], + 'href' => [], + 'target' => [], + ], + 'strong' => [], + ]; foreach ( $this->notices as $key => $notice ) { $class = $notice['class']; @@ -148,7 +148,7 @@ public function display_admin_notices() { * @param array $plugin_data The plugin data. */ protected function set_plugin_data( $plugin_data ) { - $defaults = array( + $defaults = [ 'Name' => '', 'Version' => '', 'RequiresWP' => '', @@ -156,7 +156,7 @@ protected function set_plugin_data( $plugin_data ) { 'RequiresWC' => '', 'TestedWP' => '', 'TestedWC' => '', - ); + ]; $this->plugin_data = wp_parse_args( $plugin_data, $defaults ); } @@ -169,7 +169,7 @@ protected function set_plugin_data( $plugin_data ) { */ public function is_compatible( $plugin_data ) { $this->set_plugin_data( $plugin_data ); - add_action( 'admin_notices', array( $this, 'display_admin_notices' ), 20 ); + add_action( 'admin_notices', [ $this, 'display_admin_notices' ], 20 ); return $this->run_checks(); } } diff --git a/packages/php/compat-checker/src/Checks/WCCompatibility.php b/packages/php/compat-checker/src/Checks/WCCompatibility.php index 37314aa2..685a8274 100644 --- a/packages/php/compat-checker/src/Checks/WCCompatibility.php +++ b/packages/php/compat-checker/src/Checks/WCCompatibility.php @@ -21,7 +21,7 @@ class WCCompatibility extends CompatCheck { * * @var string */ - const WC_PLUGIN_FILE = 'woocommerce/woocommerce.php'; + private const WC_PLUGIN_FILE = 'woocommerce/woocommerce.php'; /** * Define the L-n support policy here. @@ -101,7 +101,7 @@ private function is_wc_untested() { */ private function check_wc_installation_and_activation() { if ( ! $this->is_wc_activated() ) { - add_action( 'admin_notices', array( $this, 'wc_fail_load' ) ); + add_action( 'admin_notices', [ $this, 'wc_fail_load' ] ); throw new IncompatibleException( esc_html__( 'WooCommerce is not installed or activated.', 'woogrow-compat-checker' ) ); } return true; @@ -115,12 +115,12 @@ private function check_wc_installation_and_activation() { */ private function check_wc_version() { if ( ! $this->is_wc_compatible() ) { - add_action( 'admin_notices', array( $this, 'wc_out_of_date' ) ); + add_action( 'admin_notices', [ $this, 'wc_out_of_date' ] ); throw new IncompatibleException( esc_html__( 'WooCommerce version not compatible.', 'woogrow-compat-checker' ) ); } if ( ! $this->is_wc_untested() ) { - add_action( 'admin_notices', array( $this, 'wc_untested' ) ); + add_action( 'admin_notices', [ $this, 'wc_untested' ] ); } return true; @@ -144,7 +144,7 @@ private function get_latest_wc_versions() { * * @link https://codex.wordpress.org/WordPress.org_API */ - $wp_org_request = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.0/woocommerce.json', array( 'timeout' => 1 ) ); + $wp_org_request = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.0/woocommerce.json', [ 'timeout' => 1 ] ); if ( is_array( $wp_org_request ) && isset( $wp_org_request['body'] ) ) { @@ -152,7 +152,7 @@ private function get_latest_wc_versions() { if ( is_array( $plugin_info ) && ! empty( $plugin_info['versions'] ) && is_array( $plugin_info['versions'] ) ) { - $latest_wc_versions = array(); + $latest_wc_versions = []; // Reverse the array as WordPress supplies oldest version first, newest last. foreach ( array_keys( array_reverse( $plugin_info['versions'] ) ) as $wc_version ) { @@ -173,7 +173,7 @@ private function get_latest_wc_versions() { } } - return is_array( $latest_wc_versions ) ? $latest_wc_versions : array(); + return is_array( $latest_wc_versions ) ? $latest_wc_versions : []; } /** @@ -182,7 +182,6 @@ private function get_latest_wc_versions() { * @return string */ private function get_supported_wc_version() { - $latest_wc_versions = $this->get_latest_wc_versions(); if ( empty( $latest_wc_versions ) ) { @@ -205,7 +204,7 @@ private function get_supported_wc_version() { $older_semver = explode( '.', $older_wc_version ); $older_major = (int) $older_semver[0]; - $older_minor = isset( $older_semver[1] ) ? (int) $older_semver[1]: 0; + $older_minor = isset( $older_semver[1] ) ? (int) $older_semver[1] : 0; // if major is ignored, skip; if the minor hasn't changed (patch must be), skip. if ( $older_major > $supported_major || $older_minor === $previous_minor ) { @@ -241,7 +240,7 @@ private function check_wc_upgrade_recommendation() { $supported_wc_version = $this->get_supported_wc_version(); if ( ! $this->compare_major_version( $current_wc_version, $supported_wc_version, '>=' ) ) { - add_action( 'admin_notices', array( $this, 'make_upgrade_recommendation' ) ); + add_action( 'admin_notices', [ $this, 'make_upgrade_recommendation' ] ); } return true; } @@ -301,7 +300,7 @@ public function wc_out_of_date() { } $plugin_name = $this->plugin_data['Name']; - $wc_version_required = ( 1 === substr_count( $this->plugin_data['RequiresWC'], '.' ) ) ? $this->plugin_data['RequiresWC'] . '.0' : $this->plugin_data['RequiresWC'] ; // Pad .0 if the min required WC version is not in semvar format. + $wc_version_required = ( 1 === substr_count( $this->plugin_data['RequiresWC'], '.' ) ) ? $this->plugin_data['RequiresWC'] . '.0' : $this->plugin_data['RequiresWC']; // Pad .0 if the min required WC version is not in semvar format. $message = sprintf( /* translators: %1$s - Plugin Name, %2$s - minimum WooCommerce version, %3$s - update WooCommerce link open, %4$s - update WooCommerce link close, %5$s - download minimum WooCommerce link open, %6$s - download minimum WooCommerce link close. */ diff --git a/packages/php/compat-checker/src/Checks/WPCompatibility.php b/packages/php/compat-checker/src/Checks/WPCompatibility.php index ead034c2..b22daf08 100644 --- a/packages/php/compat-checker/src/Checks/WPCompatibility.php +++ b/packages/php/compat-checker/src/Checks/WPCompatibility.php @@ -20,7 +20,7 @@ class WPCompatibility extends CompatCheck { private function check_wp_version() { global $wp_version; if ( $this->compare_major_version( $this->plugin_data['TestedWP'], $wp_version, '<' ) ) { - add_action( 'admin_notices', array( $this, 'wp_not_tested' ) ); + add_action( 'admin_notices', [ $this, 'wp_not_tested' ] ); } return true; } diff --git a/packages/php/woorelease-extension/src/Commands/Bulk.php b/packages/php/woorelease-extension/src/Commands/Bulk.php index 370f3ba7..631d37ee 100644 --- a/packages/php/woorelease-extension/src/Commands/Bulk.php +++ b/packages/php/woorelease-extension/src/Commands/Bulk.php @@ -45,6 +45,11 @@ protected function configure() { * This is mainly useful when a lot of commands extends one main command * where some things need to be initialized based on the input arguments and options. * + * @param InputInterface $input The input interface, to get options, arguments, etc. + * @param OutputInterface $output The output interface. + * + * @throws InvalidArgumentException When the command is not found. + * * @see InputInterface::bind() * @see InputInterface::validate() */ @@ -62,50 +67,53 @@ protected function initialize( InputInterface $input, OutputInterface $output ) } // Get the command definition and merge it to this command definition. - $newDefinition = new InputDefinition(); - $newOptions = []; + $new_definition = new InputDefinition(); + $new_options = []; - $commandOptions = $command->getDefinition()->getOptions(); - foreach ( $commandOptions as $commandOption ) { + $command_options = $command->getDefinition()->getOptions(); + foreach ( $command_options as $command_option ) { // Skip the product_version option. - if ( 'product_version' === $commandOption->getName() ) { + if ( 'product_version' === $command_option->getName() ) { continue; } - $newOptions[] = $commandOption; + $new_options[] = $command_option; } - $newDefinition->setArguments( $this->getDefinition()->getArguments() ); - $newDefinition->setOptions( $this->getDefinition()->getOptions() ); - $newDefinition->addOptions( $newOptions ); + $new_definition->setArguments( $this->getDefinition()->getArguments() ); + $new_definition->setOptions( $this->getDefinition()->getOptions() ); + $new_definition->addOptions( $new_options ); - $this->setDefinition( $newDefinition ); + $this->setDefinition( $new_definition ); $input->bind( $this->getDefinition() ); } /** * Executes the current command. * + * @param InputInterface $input The input interface, to get options, arguments, etc. + * @param OutputInterface $output The output interface. + * * @return int 0 if everything went fine, or an exit code * - * @throws LogicException When this abstract method is not implemented + * @throws LogicException When this abstract method is not implemented. * * @see setCode() */ protected function execute( InputInterface $input, OutputInterface $output ) { - /** @var Release $releaseCommand */ - $releaseCommand = $this->getApplication()->get( $input->getArgument( 'release-command' ) ); + /** @var Release $release_command */ + $release_command = $this->getApplication()->get( $input->getArgument( 'release-command' ) ); // Set up the provided options as flag values. $options = []; foreach ( array_filter( $input->getOptions() ) as $option => $value ) { - $options["--{$option}"] = $value; + $options[ "--{$option}" ] = $value; } $errors = []; foreach ( $this->getReleaseData() as $item ) { $output->writeln( sprintf( 'Starting release for %s...', $item['name'] ) ); - $gitHubUrl = sprintf( + $git_hub_url = sprintf( 'https://github.com/%1$s/%2$s/tree/%3$s', $item['organization'], $item['repo'], @@ -115,12 +123,12 @@ protected function execute( InputInterface $input, OutputInterface $output ) { $args = array_merge( $options, [ - 'github_url' => $gitHubUrl, + 'github_url' => $git_hub_url, '--product_version' => $item['version'], ] ); - $result = $releaseCommand->run( new ArrayInput( $args ), $output ); + $result = $release_command->run( new ArrayInput( $args ), $output ); if ( static::SUCCESS !== $result ) { $output->writeln( sprintf( "\nRelease FAILED for %s\n", $item['name'] ) ); @@ -150,6 +158,8 @@ protected function execute( InputInterface $input, OutputInterface $output ) { * Get data from the release.txt file about what extensions to release. * * @return array + * + * @throws RuntimeException When the application is not an instance of `Application` or the release.txt file does not exist. */ private function getReleaseData(): array { $app = $this->getApplication(); @@ -157,20 +167,25 @@ private function getReleaseData(): array { throw new RuntimeException( sprintf( 'Expected application to be an instance of %s', Application::class ) ); } - $fileSystem = new Filesystem(); - $file = "{$app->get_meta('root_dir')}/release.txt"; - if ( ! $fileSystem->exists( $file ) ) { + $file_system = new Filesystem(); + $file = "{$app->get_meta('root_dir')}/release.txt"; + if ( ! $file_system->exists( $file ) ) { throw new RuntimeException( sprintf( 'Release file does not exist. Expected path: %s', $file ) ); } - $extensionData = json_decode( file_get_contents( "{$app->get_meta('root_dir')}/extensions.json" ), true ); - $defaultBranch = $extensionData['defaultBranch'] ?? 'trunk'; - $githubOrganization = $extensionData['githubOrganization'] ?? 'woocommerce'; - $extensions = array_column( $extensionData['extensions'] ?? [], null, 'repoSlug' ); - - $toRelease = []; - $resource = fopen( $file, 'r' ); - while ( false !== ( $line = fgets( $resource ) ) ) { + // It's a local file. + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents + $extension_data = json_decode( file_get_contents( "{$app->get_meta('root_dir')}/extensions.json" ), true ); + $default_branch = $extension_data['defaultBranch'] ?? 'trunk'; + $github_organization = $extension_data['githubOrganization'] ?? 'woocommerce'; + $extensions = array_column( $extension_data['extensions'] ?? [], null, 'repoSlug' ); + + // This woorelease extension is not performed in WordPress. + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen + $resource = fopen( $file, 'r' ); + $to_release = []; + while ( ! feof( $resource ) ) { + $line = fgets( $resource ); $line = trim( $line ); // Lines starting with # are a comment and should be ignored. @@ -184,15 +199,15 @@ private function getReleaseData(): array { } [ $slug, $version, $branch ] = array_pad( explode( "\t", $line ), 3, null ); - $toRelease[ $slug ] = [ + $to_release[ $slug ] = [ 'name' => $extensions[ $slug ]['name'], 'repo' => $extensions[ $slug ]['repoSlug'], 'version' => $version, - 'organization' => $extensions[ $slug ]['githubOrganization'] ?? $githubOrganization, - 'branch' => $branch ?? $extensions[ $slug ]['defaultBranch'] ?? $defaultBranch, + 'organization' => $extensions[ $slug ]['githubOrganization'] ?? $github_organization, + 'branch' => $branch ?? $extensions[ $slug ]['defaultBranch'] ?? $default_branch, ]; } - return $toRelease; + return $to_release; } } diff --git a/packages/php/woorelease-extension/src/Utils/VersionReplace.php b/packages/php/woorelease-extension/src/Utils/VersionReplace.php index 2a6c5e31..ba47ee6b 100644 --- a/packages/php/woorelease-extension/src/Utils/VersionReplace.php +++ b/packages/php/woorelease-extension/src/Utils/VersionReplace.php @@ -45,7 +45,7 @@ public static function maybe_replace( $product, $folder, $version ) { $sed_command = sprintf( $sed_command, 'since', 'version', $version ); $find_replace = 'find ./%1$s -iname \'*.php\' -exec ' . $sed_command . '{} \;'; foreach ( $paths as $path ) { - $logger->notice( "Replacing version placeholders in ./{path}", array( 'path' => $path ) ); + $logger->notice( 'Replacing version placeholders in ./{path}', [ 'path' => $path ] ); $local_path = $folder . '/' . $path; if ( is_dir( $local_path ) ) { @@ -53,7 +53,7 @@ public static function maybe_replace( $product, $folder, $version ) { $cleanup = sprintf( 'find ./%1$s -name "*.php.bak" -type f -delete', $path ); } else { $command = $sed_command . $local_path; - $cleanup = sprintf('unlink %s.bak', $local_path); + $cleanup = sprintf( 'unlink %s.bak', $local_path ); } Utils::exec_sprintf( $command ); Utils::exec_sprintf( $cleanup ); diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 00000000..258642a5 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,112 @@ + + + Generally-applicable sniffs for WordPress plugins + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ./packages/github-actions/actions/hook-documentation/bin/* + ./packages/github-actions/actions/hook-documentation/src/Documentor.php + ./packages/php/woorelease-extension/* + + + + + */tests/* + + + */tests/* + + + */tests/* + + + */tests/* + + + + + ./plugins + ./packages/php + ./packages/github-actions/actions + + + + + + */node_modules/* + */vendor/* + diff --git a/plugins/grow-smooth-generator/includes/CLI/AutomateWoo.php b/plugins/grow-smooth-generator/includes/CLI/AutomateWoo.php index d390f83e..30313ded 100644 --- a/plugins/grow-smooth-generator/includes/CLI/AutomateWoo.php +++ b/plugins/grow-smooth-generator/includes/CLI/AutomateWoo.php @@ -146,5 +146,4 @@ public static function add_commands() { ] ); } - } diff --git a/plugins/grow-smooth-generator/includes/Generator/AutomateWoo/Conversion.php b/plugins/grow-smooth-generator/includes/Generator/AutomateWoo/Conversion.php index d54b3419..45ce6837 100644 --- a/plugins/grow-smooth-generator/includes/Generator/AutomateWoo/Conversion.php +++ b/plugins/grow-smooth-generator/includes/Generator/AutomateWoo/Conversion.php @@ -20,7 +20,6 @@ class Conversion extends Generator { * @return WC_Order|false Order object with conversion data populated or false when failed. */ public static function generate( $save = true, $assoc_args = [] ) { - $workflow = Workflow_Factory::get( $assoc_args['workflow'] ); if ( ! $workflow || ! $workflow->is_conversion_tracking_enabled() ) { WP_CLI::error( 'Workflow does not have conversion tracking enabled.' ); @@ -49,5 +48,4 @@ public static function generate( $save = true, $assoc_args = [] ) { return $order; } - } diff --git a/plugins/grow-smooth-generator/includes/Generator/AutomateWoo/EmailTracking.php b/plugins/grow-smooth-generator/includes/Generator/AutomateWoo/EmailTracking.php index 124ca039..d3dad496 100644 --- a/plugins/grow-smooth-generator/includes/Generator/AutomateWoo/EmailTracking.php +++ b/plugins/grow-smooth-generator/includes/Generator/AutomateWoo/EmailTracking.php @@ -20,7 +20,6 @@ class EmailTracking extends Generator { * @return Log|false Workflow Run log with tracking data populated or false when failed. */ public static function generate( $save = true, $assoc_args = [] ) { - $workflow = Workflow_Factory::get( $assoc_args['workflow'] ); if ( ! $workflow || ! $workflow->is_conversion_tracking_enabled() ) { WP_CLI::error( 'Workflow does not have conversion tracking enabled.' ); @@ -106,7 +105,7 @@ public static function get_user() { * @return string Date string (Y-m-d) */ protected static function get_date( $assoc_args, $include_time = false ) { - $current = date( 'Y-m-d', time() ); + $current = gmdate( 'Y-m-d', time() ); if ( ! empty( $assoc_args['date-start'] ) && empty( $assoc_args['date-end'] ) ) { $start = $assoc_args['date-start']; $end = $current; @@ -119,7 +118,7 @@ protected static function get_date( $assoc_args, $include_time = false ) { $date_start = strtotime( $start ); $date_end = strtotime( $end ); - $date = date( 'Y-m-d', wp_rand( $date_start, $date_end ) ); + $date = gmdate( 'Y-m-d', wp_rand( $date_start, $date_end ) ); if ( $include_time ) { return $date . ' ' . wp_rand( 0, 23 ) . ':00:00'; @@ -127,5 +126,4 @@ protected static function get_date( $assoc_args, $include_time = false ) { return $date; } - } diff --git a/plugins/grow-smooth-generator/includes/Plugin.php b/plugins/grow-smooth-generator/includes/Plugin.php index 341acf93..213886bd 100644 --- a/plugins/grow-smooth-generator/includes/Plugin.php +++ b/plugins/grow-smooth-generator/includes/Plugin.php @@ -8,10 +8,8 @@ class Plugin { /** * Constructor. - * - * @param string $file Main plugin __FILE__ reference. */ - public function __construct( $file ) { + public function __construct() { if ( class_exists( 'WP_CLI' ) ) { $cli = new CLI(); }