diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml new file mode 100644 index 00000000..07e4fd1f --- /dev/null +++ b/.github/workflows/code-quality.yml @@ -0,0 +1,12 @@ +name: Code Quality Checks + +on: + pull_request: + push: + branches: + - main + - master + +jobs: + code-quality: + uses: wp-cli/.github/.github/workflows/reusable-code-quality.yml@main diff --git a/bin/command.php b/bin/command.php index c7b545e6..c76fd99d 100644 --- a/bin/command.php +++ b/bin/command.php @@ -1,4 +1,7 @@ false, 'return' => 'stdout', 'parse' => 'json' ] ); + $apis = WP_CLI::runcommand( + 'handbook api-dump', + [ + 'launch' => false, + 'return' => 'stdout', + 'parse' => 'json', + ] + ); $categories = [ 'Registration' => [], 'Output' => [], @@ -58,7 +65,7 @@ public function gen_api_docs() { 'Misc' => [], ]; - $prepare_api_slug = function( $full_name ) { + $prepare_api_slug = function ( $full_name ) { $replacements = [ '()' => '', '::' => '-', @@ -68,7 +75,7 @@ public function gen_api_docs() { return strtolower( str_replace( array_keys( $replacements ), array_values( $replacements ), $full_name ) ); }; - foreach( $apis as $api ) { + foreach ( $apis as $api ) { $api['api_slug'] = $prepare_api_slug( $api['full_name'] ); @@ -94,26 +101,29 @@ public function gen_api_docs() { self::empty_dir( WP_CLI_HANDBOOK_PATH . '/internal-api/' ); - foreach( $categories as $name => $apis ) { + foreach ( $categories as $name => $apis ) { $out .= '## ' . $name . PHP_EOL . PHP_EOL; $out .= self::render( 'internal-api-list.mustache', [ 'apis' => $apis ] ); - foreach( $apis as $i => $api ) { - $api['category'] = $name; - $api['related'] = $apis; - $api['phpdoc']['parameters'] = array_map( function( $parameter ){ - foreach( $parameter as $key => $values ) { - if ( isset( $values[2] ) ) { - $values[2] = str_replace( array( PHP_EOL ), array( '
' ), $values[2] ); - $parameter[ $key ] = $values; + foreach ( $apis as $i => $api ) { + $api['category'] = $name; + $api['related'] = $apis; + $api['phpdoc']['parameters'] = array_map( + function ( $parameter ) { + foreach ( $parameter as $key => $values ) { + if ( isset( $values[2] ) ) { + $values[2] = str_replace( array( PHP_EOL ), array( '
' ), $values[2] ); + $parameter[ $key ] = $values; + } } - } - return $parameter; - }, $api['phpdoc']['parameters'] ); + return $parameter; + }, + $api['phpdoc']['parameters'] + ); unset( $api['related'][ $i ] ); - $api['related'] = array_values( $api['related'] ); + $api['related'] = array_values( $api['related'] ); $api['has_related'] = ! empty( $api['related'] ); - $api_doc = self::render( 'internal-api.mustache', $api ); - $path = WP_CLI_HANDBOOK_PATH . "/internal-api/{$api['api_slug']}.md"; + $api_doc = self::render( 'internal-api.mustache', $api ); + $path = WP_CLI_HANDBOOK_PATH . "/internal-api/{$api['api_slug']}.md"; if ( ! is_dir( dirname( $path ) ) ) { mkdir( dirname( $path ) ); } @@ -143,7 +153,7 @@ public function gen_commands( $args, $assoc_args ) { } // Check non-bundled commands installed. - $runner = WP_CLI::get_runner(); + $runner = WP_CLI::get_runner(); $have_nonbundled_installed = true; foreach ( [ 'admin', 'find', 'profile', 'dist-archive' ] as $cmd ) { $have_nonbundled_installed = $have_nonbundled_installed && is_array( $runner->find_command_to_run( [ $cmd ] ) ); @@ -154,7 +164,14 @@ public function gen_commands( $args, $assoc_args ) { self::empty_dir( WP_CLI_HANDBOOK_PATH . '/commands/' ); - $wp = WP_CLI::runcommand( 'cli cmd-dump', [ 'launch' => false, 'return' => 'stdout', 'parse' => 'json' ] ); + $wp = WP_CLI::runcommand( + 'cli cmd-dump', + [ + 'launch' => false, + 'return' => 'stdout', + 'parse' => 'json', + ] + ); $verbose = Utils\get_flag_value( $assoc_args, 'verbose', false ); @@ -173,16 +190,16 @@ public function gen_commands( $args, $assoc_args ) { */ private static function update_commands_data( $command, &$commands_data, $full ) { $reflection = new \ReflectionClass( $command ); - $repo_url = ''; + $repo_url = ''; if ( 'help' === substr( $full, 0, 4 ) || 'cli' === substr( $full, 0, 3 ) ) { $repo_url = 'https://github.com/wp-cli/wp-cli'; } if ( $reflection->hasProperty( 'when_invoked' ) ) { - $filename = ''; + $filename = ''; $when_invoked = $reflection->getProperty( 'when_invoked' ); $when_invoked->setAccessible( true ); - $closure = $when_invoked->getValue( $command ); + $closure = $when_invoked->getValue( $command ); $closure_reflection = new \ReflectionFunction( $closure ); // PHP stores use clause arguments of closures as static variables internally - see https://bugs.php.net/bug.php?id=71250 $static = $closure_reflection->getStaticVariables(); @@ -190,10 +207,10 @@ private static function update_commands_data( $command, &$commands_data, $full ) // See `CommandFactory::create_subcommand()`. if ( is_array( $static['callable'] ) && isset( $static['callable'][0] ) ) { $reflection_class = new \ReflectionClass( $static['callable'][0] ); - $filename = $reflection_class->getFileName(); + $filename = $reflection_class->getFileName(); } elseif ( is_callable( $static['callable'] ) ) { $reflection_func = new \ReflectionFunction( $static['callable'] ); - $filename = $reflection_func->getFileName(); + $filename = $reflection_func->getFileName(); } } if ( $filename ) { @@ -225,21 +242,21 @@ private static function update_commands_data( $command, &$commands_data, $full ) * @subcommand gen-commands-manifest */ public function gen_commands_manifest() { - $manifest = []; - $paths = [ + $manifest = []; + $paths = [ WP_CLI_HANDBOOK_PATH . '/commands/*.md', WP_CLI_HANDBOOK_PATH . '/commands/*/*.md', - WP_CLI_HANDBOOK_PATH . '/commands/*/*/*.md' + WP_CLI_HANDBOOK_PATH . '/commands/*/*/*.md', ]; $commands_data = []; - foreach( WP_CLI::get_root_command()->get_subcommands() as $command ) { + foreach ( WP_CLI::get_root_command()->get_subcommands() as $command ) { self::update_commands_data( $command, $commands_data, $command->get_name() ); } - foreach( $paths as $path ) { - foreach( glob( $path ) as $file ) { - $slug = basename( $file, '.md' ); + foreach ( $paths as $path ) { + foreach ( glob( $path ) as $file ) { + $slug = basename( $file, '.md' ); $cmd_path = str_replace( [ WP_CLI_HANDBOOK_PATH . '/commands/', '.md' ], '', $file ); - $title = ''; + $title = ''; $contents = file_get_contents( $file ); if ( preg_match( '/^#\swp\s(.+)/', $contents, $matches ) ) { $title = $matches[1]; @@ -289,12 +306,12 @@ public function gen_commands_manifest() { public function gen_hb_manifest() { $manifest = []; // Top-level pages - foreach( glob( WP_CLI_HANDBOOK_PATH . '/*.md' ) as $file ) { + foreach ( glob( WP_CLI_HANDBOOK_PATH . '/*.md' ) as $file ) { $slug = basename( $file, '.md' ); if ( 'README' === $slug ) { continue; } - $title = ''; + $title = ''; $contents = file_get_contents( $file ); if ( preg_match( '/^#\s(.+)/', $contents, $matches ) ) { $title = $matches[1]; @@ -310,9 +327,9 @@ public function gen_hb_manifest() { ]; } // Internal API pages. - foreach( glob( WP_CLI_HANDBOOK_PATH . '/internal-api/*.md' ) as $file ) { - $slug = basename( $file, '.md' ); - $title = ''; + foreach ( glob( WP_CLI_HANDBOOK_PATH . '/internal-api/*.md' ) as $file ) { + $slug = basename( $file, '.md' ); + $title = ''; $contents = file_get_contents( $file ); if ( preg_match( '/^#\s(.+)/', $contents, $matches ) ) { $title = $matches[1]; @@ -337,25 +354,25 @@ public function gen_hb_manifest() { * @subcommand api-dump */ public function api_dump() { - $apis = []; + $apis = []; $functions = get_defined_functions(); - foreach( $functions['user'] as $function ) { + foreach ( $functions['user'] as $function ) { $reflection = new \ReflectionFunction( $function ); - $phpdoc = $reflection->getDocComment(); + $phpdoc = $reflection->getDocComment(); if ( false === stripos( $phpdoc, '@access public' ) ) { continue; } $apis[] = self::get_simple_representation( $reflection ); } $classes = get_declared_classes(); - foreach( $classes as $class ) { + foreach ( $classes as $class ) { if ( false === stripos( $class, 'WP_CLI' ) ) { continue; } $reflection = new \ReflectionClass( $class ); - foreach( $reflection->getMethods() as $method ) { + foreach ( $reflection->getMethods() as $method ) { $method_reflection = new \ReflectionMethod( $method->class, $method->name ); - $phpdoc = $method_reflection->getDocComment(); + $phpdoc = $method_reflection->getDocComment(); if ( false === stripos( $phpdoc, '@access public' ) ) { continue; } @@ -365,17 +382,24 @@ public function api_dump() { echo json_encode( $apis ); } - private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) { + private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.parentFound $parent[] = $cmd['name']; static $params; if ( ! isset( $params ) ) { - $params = WP_CLI::runcommand( 'cli param-dump', [ 'launch' => false, 'return' => 'stdout', 'parse' => 'json' ] ); + $params = WP_CLI::runcommand( + 'cli param-dump', + [ + 'launch' => false, + 'return' => 'stdout', + 'parse' => 'json', + ] + ); // Preserve positioning of 'url' param. $url_param = $params['url']; unset( $params['url'] ); $new_params = []; - foreach( $params as $param => $meta ) { + foreach ( $params as $param => $meta ) { $new_params[ $param ] = $meta; if ( 'path' === $param ) { $new_params['url'] = $url_param; @@ -384,12 +408,12 @@ private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) { $params = $new_params; } - $binding = $cmd; - $binding['synopsis'] = implode( ' ', $parent ); - $binding['path'] = implode( '/', $parent ); - $path = '/commands/'; + $binding = $cmd; + $binding['synopsis'] = implode( ' ', $parent ); + $binding['path'] = implode( '/', $parent ); + $path = '/commands/'; $binding['breadcrumbs'] = '[Commands](' . $path . ')'; - foreach( $parent as $i => $p ) { + foreach ( $parent as $i => $p ) { $path .= $p . '/'; if ( $i < ( count( $parent ) - 1 ) ) { $binding['breadcrumbs'] .= " » [{$p}]({$path})"; @@ -399,7 +423,7 @@ private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) { } $binding['has-subcommands'] = isset( $cmd['subcommands'] ) ? [ true ] : false; - $hook_name = $cmd['hook']; + $hook_name = $cmd['hook']; $hook_description = $hook_name ? Utils\get_hook_description( $hook_name ) : null; if ( $hook_description && 'after_wp_load' !== $hook_name ) { if ( $binding['has-subcommands'] ) { @@ -422,9 +446,11 @@ private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) { // Remove word wrapping from docs // Match words, '().,;', and --arg before/after the newline. - $bits = explode( "\n", $docs ); - $in_yaml_doc = $in_code_bloc = false; - for ( $i=0; $i < count( $bits ); $i++ ) { + $bits = explode( "\n", $docs ); + $in_yaml_doc = false; + $in_code_bloc = false; + $total_bits = count( $bits ); + for ( $i = 0; $i < $total_bits; $i++ ) { if ( ! isset( $bits[ $i ] ) || ! isset( $bits[ $i + 1 ] ) ) { continue; } @@ -463,7 +489,7 @@ private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) { | **Argument** | **Description** | |:----------------|:-----------------------------| EOT; - foreach( $params as $param => $meta ) { + foreach ( $params as $param => $meta ) { if ( false === $meta['runtime'] || empty( $meta['desc'] ) || ! empty( $meta['deprecated'] ) ) { @@ -490,8 +516,8 @@ private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) { $binding['docs'] = $docs; } - $path = dirname( __DIR__ ) . "/commands/" . $binding['path']; - if ( !is_dir( dirname( $path ) ) ) { + $path = dirname( __DIR__ ) . '/commands/' . $binding['path']; + if ( ! is_dir( dirname( $path ) ) ) { mkdir( dirname( $path ) ); } file_put_contents( "$path.md", self::render( 'subcmd-list.mustache', $binding ) ); @@ -499,8 +525,9 @@ private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) { WP_CLI::log( 'Generated commands/' . $binding['path'] . '/' ); } - if ( !isset( $cmd['subcommands'] ) ) + if ( ! isset( $cmd['subcommands'] ) ) { return; + } foreach ( $cmd['subcommands'] as $subcmd ) { self::gen_cmd_pages( $subcmd, $parent, $verbose ); @@ -514,9 +541,9 @@ private static function gen_cmd_pages( $cmd, $parent = [], $verbose = false ) { * @return array */ private static function get_simple_representation( $reflection ) { - $signature = $reflection->getName(); + $signature = $reflection->getName(); $parameters = []; - foreach( $reflection->getParameters() as $parameter ) { + foreach ( $reflection->getParameters() as $parameter ) { $parameter_signature = '$' . $parameter->getName(); if ( $parameter->isOptional() && $parameter->isDefaultValueAvailable() ) { $default_value = $parameter->getDefaultValue(); @@ -542,12 +569,12 @@ private static function get_simple_representation( $reflection ) { $signature = $signature . '()'; } $phpdoc = $reflection->getDocComment(); - $type = strtolower( str_replace( 'Reflection', '', get_class( $reflection ) ) ); - $class = ''; + $type = strtolower( str_replace( 'Reflection', '', get_class( $reflection ) ) ); + $class = ''; switch ( $type ) { case 'method': $separator = $reflection->isStatic() ? '::' : '->'; - $class = $reflection->class; + $class = $reflection->class; $full_name = $class . $separator . $reflection->getName(); $signature = $class . $separator . $signature; break; @@ -557,7 +584,7 @@ private static function get_simple_representation( $reflection ) { break; } return [ - 'phpdoc' => self::parse_docblock($phpdoc), + 'phpdoc' => self::parse_docblock( $phpdoc ), 'type' => $type, 'signature' => $signature, 'short_name' => $reflection->getShortName(), @@ -573,28 +600,28 @@ private static function get_simple_representation( $reflection ) { * @return array */ private static function parse_docblock( $docblock ) { - $ret = [ + $ret = [ 'description' => '', 'parameters' => [], ]; $extra_line = ''; - $in_param = false; - foreach( preg_split("/(\r?\n)/", $docblock ) as $line ){ - if ( preg_match('/^(?=\s+?\*[^\/])(.+)/', $line, $matches ) ) { + $in_param = false; + foreach ( preg_split( "/(\r?\n)/", $docblock ) as $line ) { + if ( preg_match( '/^(?=\s+?\*[^\/])(.+)/', $line, $matches ) ) { $info = trim( $matches[1] ); $info = preg_replace( '/^(\*\s+?)/', '', $info ); if ( $in_param ) { - list( $param_name, $key ) = $in_param; + list( $param_name, $key ) = $in_param; $ret['parameters'][ $param_name ][ $key ][2] .= PHP_EOL . $info; if ( '}' === substr( $info, -1 ) ) { $in_param = false; } - } elseif ( $info[0] !== "@" ) { + } elseif ( '@' === $info[0] ) { $ret['description'] .= PHP_EOL . "{$extra_line}{$info}"; } else { preg_match( '/@(\w+)/', $info, $matches ); $param_name = $matches[1]; - $value = str_replace( "@$param_name ", '', $info ); + $value = str_replace( "@$param_name ", '', $info ); if ( ! isset( $ret['parameters'][ $param_name ] ) ) { $ret['parameters'][ $param_name ] = []; } @@ -602,8 +629,8 @@ private static function parse_docblock( $docblock ) { end( $ret['parameters'][ $param_name ] ); $key = key( $ret['parameters'][ $param_name ] ); reset( $ret['parameters'][ $param_name ] ); - if ( ! empty( $ret['parameters'][ $param_name ][ $key ][ 2 ] ) - && '{' === substr( $ret['parameters'][ $param_name ][ $key ][ 2 ] , -1 ) ) { + if ( ! empty( $ret['parameters'][ $param_name ][ $key ][2] ) + && '{' === substr( $ret['parameters'][ $param_name ][ $key ][2], -1 ) ) { $in_param = [ $param_name, $key ]; } } @@ -613,19 +640,19 @@ private static function parse_docblock( $docblock ) { } } $ret['description'] = str_replace( '\/', '/', trim( $ret['description'], PHP_EOL ) ); - $bits = explode( PHP_EOL, $ret['description'] ); - $short_desc = [ array_shift( $bits ) ]; + $bits = explode( PHP_EOL, $ret['description'] ); + $short_desc = [ array_shift( $bits ) ]; while ( isset( $bits[0] ) && ! empty( $bits[0] ) ) { $short_desc[] = array_shift( $bits ); } $ret['short_description'] = trim( implode( ' ', $short_desc ) ); - $long_description = trim( implode( PHP_EOL, $bits ), PHP_EOL ); - $ret['long_description'] = $long_description; + $long_description = trim( implode( PHP_EOL, $bits ), PHP_EOL ); + $ret['long_description'] = $long_description; return $ret; } private static function render( $path, $binding ) { - $m = new Mustache_Engine; + $m = new Mustache_Engine(); $template = file_get_contents( WP_CLI_HANDBOOK_PATH . "/bin/templates/$path" ); return $m->render( $template, $binding ); } @@ -637,9 +664,9 @@ private static function render( $path, $binding ) { */ private static function empty_dir( $dir ) { $cmd = Utils\esc_cmd( 'rm -rf %s', $dir ); - $pr = WP_CLI::launch( $cmd, false /*exit_on_error*/, true /*return_detailed*/ ); // Won't fail if directory doesn't exist. + $pr = WP_CLI::launch( $cmd, false /*exit_on_error*/, true /*return_detailed*/ ); // Won't fail if directory doesn't exist. if ( $pr->return_code ) { - WP_CLI::error( sprintf( "Failed to `%s`: (%d) %s", $cmd, $pr->return_code, $pr->stderr ) ); + WP_CLI::error( sprintf( 'Failed to `%s`: (%d) %s', $cmd, $pr->return_code, $pr->stderr ) ); } if ( ! mkdir( $dir ) ) { $error = error_get_last(); @@ -650,4 +677,3 @@ private static function empty_dir( $dir ) { } WP_CLI::add_command( 'handbook', '\WP_CLI\Handbook\Command' ); - diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..93b46f69 --- /dev/null +++ b/composer.json @@ -0,0 +1,28 @@ +{ + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + }, + "sort-packages": true + }, + "require-dev": { + "wp-cli/wp-cli-tests": "^4.2" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "scripts": { + "behat": "run-behat-tests", + "behat-rerun": "rerun-behat-tests", + "lint": "run-linter-tests", + "phpcs": "run-phpcs-tests", + "phpcbf": "run-phpcbf-cleanup", + "phpunit": "run-php-unit-tests", + "prepare-tests": "install-package-tests", + "test": [ + "@lint", + "@phpcs", + "@phpunit", + "@behat" + ] + } +} diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 00000000..053f857b --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,43 @@ + + + Custom ruleset for WP-CLI command + + + + + . + + + + + + + + + + + + + + + + + + + +