diff --git a/composer.json b/composer.json index 3da063d2..ae65dcff 100644 --- a/composer.json +++ b/composer.json @@ -16,14 +16,15 @@ }, "require-dev": { "wp-cli/entity-command": "^1.3 || ^2", - "wp-cli/wp-cli-tests": "^4" + "wp-cli/wp-cli-tests": "dev-add/phpstan-enhancements" }, "config": { "process-timeout": 7200, "sort-packages": true, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, - "johnpbloch/wordpress-core-installer": true + "johnpbloch/wordpress-core-installer": true, + "phpstan/extension-installer": true }, "lock": false }, @@ -72,12 +73,14 @@ "behat-rerun": "rerun-behat-tests", "lint": "run-linter-tests", "phpcs": "run-phpcs-tests", + "phpstan": "run-phpstan-tests", "phpcbf": "run-phpcbf-cleanup", "phpunit": "run-php-unit-tests", "prepare-tests": "install-package-tests", "test": [ "@lint", "@phpcs", + "@phpstan", "@phpunit", "@behat" ] diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000..5f68c8b7 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,24 @@ +parameters: + level: 9 + paths: + - src + - cache-command.php + scanDirectories: + - vendor/wp-cli/wp-cli/php + - vendor/wp-cli/wp-cli-tests + scanFiles: + - vendor/php-stubs/wordpress-stubs/wordpress-stubs.php + treatPhpDocTypesAsCertain: false + dynamicConstantNames: + - WP_DEBUG + - WP_DEBUG_LOG + - WP_DEBUG_DISPLAY + strictRules: + uselessCast: true + closureUsesThis: true + overwriteVariablesWithLoop: true + matchingInheritedMethodNames: true + numericOperandsInArithmeticOperators: true + switchConditionsMatchingType: true + ignoreErrors: + - identifier: missingType.return diff --git a/src/Cache_Command.php b/src/Cache_Command.php index b56eda0f..62435d04 100644 --- a/src/Cache_Command.php +++ b/src/Cache_Command.php @@ -58,11 +58,14 @@ class Cache_Command extends WP_CLI_Command { * # Add cache. * $ wp cache add my_key my_group my_value 300 * Success: Added object 'my_key' in group 'my_value'. + * + * @param array{string, string, string, string} $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function add( $args, $assoc_args ) { list( $key, $value, $group, $expiration ) = $args; - if ( ! wp_cache_add( $key, $value, $group, $expiration ) ) { + if ( ! wp_cache_add( $key, $value, $group, (int) $expiration ) ) { WP_CLI::error( "Could not add object '$key' in group '$group'. Does it already exist?" ); } @@ -96,10 +99,13 @@ public function add( $args, $assoc_args ) { * # Decrease cache value. * $ wp cache decr my_key 2 my_group * 48 + * + * @param array{string, string, string} $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function decr( $args, $assoc_args ) { list( $key, $offset, $group ) = $args; - $value = wp_cache_decr( $key, $offset, $group ); + $value = wp_cache_decr( $key, (int) $offset, $group ); if ( false === $value ) { WP_CLI::error( 'The value was not decremented.' ); @@ -129,6 +135,9 @@ public function decr( $args, $assoc_args ) { * # Delete cache. * $ wp cache delete my_key my_group * Success: Object deleted. + * + * @param array{string, string} $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function delete( $args, $assoc_args ) { list( $key, $group ) = $args; @@ -157,8 +166,9 @@ public function delete( $args, $assoc_args ) { * $ wp cache flush * Success: The cache was flushed. */ - public function flush( $args, $assoc_args ) { - + public function flush() { + // TODO: Needs fixing in wp-cli/wp-cli + // @phpstan-ignore offsetAccess.nonOffsetAccessible if ( WP_CLI::has_config( 'url' ) && ! empty( WP_CLI::get_config()['url'] ) && is_multisite() ) { WP_CLI::warning( 'Flushing the cache may affect all sites in a multisite installation, depending on the implementation of the object cache.' ); } @@ -192,6 +202,9 @@ public function flush( $args, $assoc_args ) { * # Get cache. * $ wp cache get my_key my_group * my_value + * + * @param array{string, string} $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function get( $args, $assoc_args ) { list( $key, $group ) = $args; @@ -231,10 +244,13 @@ public function get( $args, $assoc_args ) { * # Increase cache value. * $ wp cache incr my_key 2 my_group * 50 + * + * @param array{string, string, string} $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function incr( $args, $assoc_args ) { list( $key, $offset, $group ) = $args; - $value = wp_cache_incr( $key, $offset, $group ); + $value = wp_cache_incr( $key, (int) $offset, $group ); if ( false === $value ) { WP_CLI::error( 'The value was not incremented.' ); @@ -273,10 +289,13 @@ public function incr( $args, $assoc_args ) { * # Replace cache. * $ wp cache replace my_key new_value my_group * Success: Replaced object 'my_key' in group 'my_group'. + * + * @param array{string, string, string, string} $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function replace( $args, $assoc_args ) { list( $key, $value, $group, $expiration ) = $args; - $result = wp_cache_replace( $key, $value, $group, $expiration ); + $result = wp_cache_replace( $key, $value, $group, (int) $expiration ); if ( false === $result ) { WP_CLI::error( "Could not replace object '$key' in group '$group'. Does it not exist?" ); @@ -315,10 +334,13 @@ public function replace( $args, $assoc_args ) { * # Set cache. * $ wp cache set my_key my_value my_group 300 * Success: Set object 'my_key' in group 'my_group'. + * + * @param array{string, string, string, string} $args Positional arguments. + * @param array $assoc_args Associative arguments. */ public function set( $args, $assoc_args ) { list( $key, $value, $group, $expiration ) = $args; - $result = wp_cache_set( $key, $value, $group, $expiration ); + $result = wp_cache_set( $key, $value, $group, (int) $expiration ); if ( false === $result ) { WP_CLI::error( "Could not add object '$key' in group '$group'." ); @@ -341,7 +363,7 @@ public function set( $args, $assoc_args ) { * $ wp cache type * Default */ - public function type( $args, $assoc_args ) { + public function type() { $message = WP_CLI\Utils\wp_get_cache_type(); WP_CLI::line( $message ); } @@ -365,8 +387,10 @@ public function type( $args, $assoc_args ) { * if ! wp cache supports non_existing; then * echo 'non_existing is not supported' * fi + * + * @param array{string} $args Positional arguments. */ - public function supports( $args, $assoc_args ) { + public function supports( $args ) { list ( $feature ) = $args; if ( ! function_exists( 'wp_cache_supports' ) ) { @@ -396,8 +420,10 @@ public function supports( $args, $assoc_args ) { * Success: Cache group 'my_group' was flushed. * * @subcommand flush-group + * + * @param array{string} $args Positional arguments. */ - public function flush_group( $args, $assoc_args ) { + public function flush_group( $args ) { list( $group ) = $args; if ( ! function_exists( 'wp_cache_supports' ) || ! wp_cache_supports( 'flush_group' ) ) { @@ -436,10 +462,17 @@ public function flush_group( $args, $assoc_args ) { * - json * - yaml * --- + * + * @param array{string, string} $args Positional arguments. + * @param array{group: string, format: string} $assoc_args Associative arguments. */ public function pluck( $args, $assoc_args ) { list( $key ) = $args; - $group = Utils\get_flag_value( $assoc_args, 'group' ); + + /** + * @var string $group + */ + $group = Utils\get_flag_value( $assoc_args, 'group' ); $value = wp_cache_get( $key, $group ); @@ -512,11 +545,22 @@ function ( $key ) { * - plaintext * - json * --- + * + * @param string[] $args Positional arguments. + * @param array{group: string, expiration: string, format: string} $assoc_args Associative arguments. */ public function patch( $args, $assoc_args ) { list( $action, $key ) = $args; - $group = Utils\get_flag_value( $assoc_args, 'group' ); - $expiration = Utils\get_flag_value( $assoc_args, 'expiration' ); + + /** + * @var string $group + */ + $group = Utils\get_flag_value( $assoc_args, 'group' ); + + /** + * @var string|null $expiration + */ + $expiration = Utils\get_flag_value( $assoc_args, 'expiration' ); $key_path = array_map( function ( $key ) { @@ -569,7 +613,7 @@ function ( $key ) { if ( $patched_value === $old_value ) { WP_CLI::success( "Value passed for cache key '$key' is unchanged." ); } else { - $success = wp_cache_set( $key, $patched_value, $group, $expiration ); + $success = wp_cache_set( $key, $patched_value, $group, (int) $expiration ); if ( $success ) { WP_CLI::success( "Updated cache key '$key'." ); } else { diff --git a/src/Transient_Command.php b/src/Transient_Command.php index 04c09074..2d0756de 100644 --- a/src/Transient_Command.php +++ b/src/Transient_Command.php @@ -77,6 +77,9 @@ class Transient_Command extends WP_CLI_Command { * * $ wp transient get random_key * Warning: Transient with key "random_key" is not set. + * + * @param array{string} $args Positional arguments. + * @param array{format: string} $assoc_args Associative arguments. */ public function get( $args, $assoc_args ) { list( $key ) = $args; @@ -120,14 +123,17 @@ public function get( $args, $assoc_args ) { * * $ wp transient set sample_key "test data" 3600 * Success: Transient added. + * + * @param array{0: string, 1: string, 2?: string} $args Positional arguments. + * @param array{network?: bool} $assoc_args Associative arguments. */ public function set( $args, $assoc_args ) { list( $key, $value ) = $args; - $expiration = Utils\get_flag_value( $args, 2, 0 ); + $expiration = $args[2] ?? 0; $func = Utils\get_flag_value( $assoc_args, 'network' ) ? 'set_site_transient' : 'set_transient'; - if ( $func( $key, $value, $expiration ) ) { + if ( $func( $key, $value, (int) $expiration ) ) { WP_CLI::success( 'Transient added.' ); } else { WP_CLI::error( 'Transient could not be set.' ); @@ -180,6 +186,9 @@ public function set( $args, $assoc_args ) { * * # Delete all transients in a multisite. * $ wp transient delete --all --network && wp site list --field=url | xargs -n1 -I % wp --url=% transient delete --all + * + * @param array{string} $args Positional arguments. + * @param array{network?: bool, all?: bool, expired?: bool} $assoc_args Associative arguments. */ public function delete( $args, $assoc_args ) { $key = ( ! empty( $args ) ) ? $args[0] : null; @@ -297,6 +306,9 @@ public function type() { * +------+-------+---------------+ * * @subcommand list + * + * @param string[] $args Positional arguments. Unused. + * @param array{search?: string, exclude?: string, network?: bool, unserialize?: bool, 'human-readable'?: bool, fields?: string, format?: string} $assoc_args Associative arguments. */ public function list_( $args, $assoc_args ) { global $wpdb; @@ -434,6 +446,9 @@ public function list_( $args, $assoc_args ) { * : Get the value of a network|site transient. On single site, this is * a specially-named cache key. On multisite, this is a global cache * (instead of local to the site). + * + * @param string[] $args Positional arguments. + * @param array{format: string} $assoc_args Associative arguments. */ public function pluck( $args, $assoc_args ) { list( $key ) = $args; @@ -506,10 +521,14 @@ function ( $key ) { * : Get the value of a network|site transient. On single site, this is * a specially-named cache key. On multisite, this is a global cache * (instead of local to the site). + * + * @param string[] $args Positional arguments. + * @param array{format: string} $assoc_args Associative arguments. */ public function patch( $args, $assoc_args ) { list( $action, $key ) = $args; - $expiration = (int) Utils\get_flag_value( $assoc_args, 'expiration', 0 ); + + $expiration = (int) Utils\get_flag_value( $assoc_args, 'expiration', 0 ); $read_func = Utils\get_flag_value( $assoc_args, 'network' ) ? 'get_site_transient' : 'get_transient'; $write_func = Utils\get_flag_value( $assoc_args, 'network' ) ? 'set_site_transient' : 'set_transient'; @@ -586,20 +605,31 @@ function ( $key ) { private function get_transient_expiration( $name, $is_site_transient = false, $human_readable = false ) { if ( $is_site_transient ) { if ( is_multisite() ) { - $expiration = (int) get_site_option( '_site_transient_timeout_' . $name ); + /** + * @var string $expiration + */ + $expiration = get_site_option( '_site_transient_timeout_' . $name ); } else { - $expiration = (int) get_option( '_site_transient_timeout_' . $name ); + /** + * @var string $expiration + */ + $expiration = get_option( '_site_transient_timeout_' . $name ); } } else { - $expiration = (int) get_option( '_transient_timeout_' . $name ); + /** + * @var string $expiration + */ + $expiration = get_option( '_transient_timeout_' . $name ); } + $expiration = (int) $expiration; + if ( 0 === $expiration ) { return $human_readable ? 'never expires' : 'false'; } if ( ! $human_readable ) { - return $expiration; + return (string) $expiration; } $now = time();