diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..87732c3 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,14 @@ +parameters: + level: 6 + paths: + - src + - doctor-command.php + scanDirectories: + - vendor/wp-cli/wp-cli/php + scanFiles: + - vendor/php-stubs/wordpress-stubs/wordpress-stubs.php + - tests/phpstan/scan-files.php + treatPhpDocTypesAsCertain: false + ignoreErrors: + - '#Call to deprecated method setAccessible\(\) of class ReflectionProperty\.#' + diff --git a/src/Check.php b/src/Check.php index c0fc4e2..c86b31b 100644 --- a/src/Check.php +++ b/src/Check.php @@ -31,6 +31,8 @@ abstract class Check { /** * Initialize the check. + * + * @param array $options */ public function __construct( $options = array() ) { @@ -48,6 +50,8 @@ public function __construct( $options = array() ) { /** * Get when the check is expected to run. + * + * @return string */ public function get_when() { return $this->_when; @@ -57,6 +61,7 @@ public function get_when() { * Set when the check is expected to run. * * @param string $when + * @return void */ public function set_when( $when ) { $this->_when = $when; @@ -66,6 +71,7 @@ public function set_when( $when ) { * Set the status of the check. * * @param string $status + * @return void */ protected function set_status( $status ) { $this->_status = $status; @@ -75,6 +81,7 @@ protected function set_status( $status ) { * Set the message of the check. * * @param string $message + * @return void */ protected function set_message( $message ) { $this->_message = $message; @@ -85,13 +92,15 @@ protected function set_message( $message ) { * * Because each check checks for something different, this method must be * subclassed. Method is expected to set $status_code and $status_message. + * + * @return void */ abstract public function run(); /** * Get results of the check. * - * @return array + * @return array */ public function get_results() { return array( diff --git a/src/Check/Autoload_Options_Size.php b/src/Check/Autoload_Options_Size.php index db2437d..8752410 100644 --- a/src/Check/Autoload_Options_Size.php +++ b/src/Check/Autoload_Options_Size.php @@ -17,6 +17,9 @@ class Autoload_Options_Size extends Check { */ protected $threshold_kb = 900; + /** + * @return void + */ public function run() { ob_start(); WP_CLI::run_command( @@ -40,9 +43,14 @@ public function run() { } } + /** + * @param int|float $size Size in bytes. + * @param int $precision Precision. + * @return string + */ private static function format_bytes( $size, $precision = 2 ) { $base = log( $size, 1024 ); $suffixes = array( '', 'kb', 'mb', 'g', 't' ); - return round( pow( 1024, $base - floor( $base ) ), $precision ) . $suffixes[ floor( $base ) ]; + return round( pow( 1024, $base - floor( $base ) ), $precision ) . $suffixes[ (int) floor( $base ) ]; } } diff --git a/src/Check/Cache_Flush.php b/src/Check/Cache_Flush.php index 3f82d9f..27d868d 100644 --- a/src/Check/Cache_Flush.php +++ b/src/Check/Cache_Flush.php @@ -11,6 +11,9 @@ */ class Cache_Flush extends File_Contents { + /** + * @return void + */ public function run() { // Path to wp-content directory. diff --git a/src/Check/Constant_Definition.php b/src/Check/Constant_Definition.php index 765a6b8..89618dc 100644 --- a/src/Check/Constant_Definition.php +++ b/src/Check/Constant_Definition.php @@ -26,9 +26,9 @@ class Constant_Definition extends Check { /** * Whether or not the constant is expected to be a falsy value. * - * @var bool + * @var bool|null */ - protected $falsy; + protected $falsy = null; /** * Expected value of the constant. @@ -39,6 +39,8 @@ class Constant_Definition extends Check { /** * Initialize the constant check + * + * @param array $options */ public function __construct( $options = array() ) { parent::__construct( $options ); @@ -47,6 +49,9 @@ public function __construct( $options = array() ) { } } + /** + * @return void + */ public function run() { if ( isset( $this->falsy ) ) { @@ -99,7 +104,7 @@ public function run() { return; } - if ( $this->defined && ! isset( $this->value ) ) { + if ( ! isset( $this->value ) ) { $this->set_status( 'success' ); $this->set_message( "Constant '{$this->constant}' is defined." ); return; @@ -118,12 +123,18 @@ public function run() { } } + /** + * @param mixed $value + * @return string + */ private static function human_value( $value ) { if ( true === $value ) { - $value = 'true'; + return 'true'; } elseif ( false === $value ) { - $value = 'false'; + return 'false'; + } elseif ( is_null( $value ) ) { + return 'null'; } - return $value; + return (string) $value; } } diff --git a/src/Check/Core_Verify_Checksums.php b/src/Check/Core_Verify_Checksums.php index 3b7abdf..300dc50 100644 --- a/src/Check/Core_Verify_Checksums.php +++ b/src/Check/Core_Verify_Checksums.php @@ -10,11 +10,17 @@ */ class Core_Verify_Checksums extends Check { + /** + * @param array $options + */ public function __construct( $options = array() ) { parent::__construct( $options ); $this->set_when( 'before_wp_load' ); } + /** + * @return void + */ public function run() { $return_code = WP_CLI::runcommand( 'core verify-checksums', diff --git a/src/Check/Cron.php b/src/Check/Cron.php index 3b4e1de..8e09109 100644 --- a/src/Check/Cron.php +++ b/src/Check/Cron.php @@ -7,8 +7,14 @@ abstract class Cron extends Check { + /** + * @var array|null + */ protected static $crons; + /** + * @return array + */ protected static function get_crons() { if ( isset( self::$crons ) ) { diff --git a/src/Check/Cron_Count.php b/src/Check/Cron_Count.php index 31cdde9..f9494c6 100644 --- a/src/Check/Cron_Count.php +++ b/src/Check/Cron_Count.php @@ -14,6 +14,9 @@ class Cron_Count extends Cron { */ protected $threshold_count = 50; + /** + * @return void + */ public function run() { $crons = self::get_crons(); if ( count( $crons ) >= $this->threshold_count ) { diff --git a/src/Check/Cron_Duplicates.php b/src/Check/Cron_Duplicates.php index b21eef5..0fec8e1 100644 --- a/src/Check/Cron_Duplicates.php +++ b/src/Check/Cron_Duplicates.php @@ -14,6 +14,9 @@ class Cron_Duplicates extends Cron { */ protected $threshold_count = 10; + /** + * @return void + */ public function run() { $crons = self::get_crons(); $job_counts = array(); diff --git a/src/Check/File.php b/src/Check/File.php index fc1b496..2d9b95d 100644 --- a/src/Check/File.php +++ b/src/Check/File.php @@ -12,7 +12,7 @@ abstract class File extends Check { /** * File checks are run as their own group. */ - protected $_when = false; + protected $_when = 'manual'; // Run manually via group /** * File extension to check. @@ -42,14 +42,14 @@ abstract class File extends Check { /** * Any files matching the check. * - * @var array + * @var array */ protected $_matches = array(); /** * Get the options for this check * - * @return string + * @return array */ public function get_options() { return array( @@ -58,4 +58,12 @@ public function get_options() { 'path' => $this->path, ); } + + /** + * Check a specific file. + * + * @param \SplFileInfo $file File to check. + * @return void + */ + abstract public function check_file( \SplFileInfo $file ); } diff --git a/src/Check/File_Contents.php b/src/Check/File_Contents.php index 4927c08..20475c7 100644 --- a/src/Check/File_Contents.php +++ b/src/Check/File_Contents.php @@ -12,9 +12,9 @@ class File_Contents extends File { /** * Regex pattern to check against each file’s contents. * - * @var string + * @var string|null */ - protected $regex; + protected $regex = null; /** * Assert existence or absence of the regex pattern. @@ -56,6 +56,10 @@ public function run() { } } + /** + * @param SplFileInfo $file + * @return void + */ public function check_file( SplFileInfo $file ) { if ( $file->isDir() || ! isset( $this->regex ) ) { return; diff --git a/src/Check/File_Type.php b/src/Check/File_Type.php index 1a161f1..445c3d5 100644 --- a/src/Check/File_Type.php +++ b/src/Check/File_Type.php @@ -2,8 +2,6 @@ namespace WP_CLI\Doctor\Check; -use SplFileInfo; - /** * Checks files on the filesystem to be of a certain type. */ @@ -12,9 +10,9 @@ class File_Type extends File { /** * Assert the file type is or isn't a symlink. * - * @var bool + * @var bool|null */ - protected $symlink; + protected $symlink = null; public function run() { @@ -32,7 +30,11 @@ public function run() { } } - public function check_file( SplFileInfo $file ) { + /** + * @param \SplFileInfo $file + * @return void + */ + public function check_file( \SplFileInfo $file ) { if ( isset( $this->symlink ) ) { if ( 'link' === $file->getType() && false === $this->symlink ) { $this->_matches[] = $file; diff --git a/src/Check/Network_Required_Plugins.php b/src/Check/Network_Required_Plugins.php index 970bb90..a510bda 100644 --- a/src/Check/Network_Required_Plugins.php +++ b/src/Check/Network_Required_Plugins.php @@ -13,10 +13,13 @@ class Network_Required_Plugins extends Check { /** * List of required plugin slugs. * - * @var array + * @var array */ protected $plugins = array(); + /** + * @param array $options + */ public function __construct( $options = array() ) { parent::__construct( $options ); @@ -32,6 +35,9 @@ public function __construct( $options = array() ) { } } + /** + * @return void + */ public function run() { if ( ! is_multisite() ) { $this->set_status( 'success' ); @@ -80,10 +86,8 @@ public function run() { } /** - * Resolve a plugin slug or file to an installed plugin file path. - * - * @param string $plugin_slug Requested plugin slug/file. - * @param array $installed_plugins Installed plugins keyed by plugin file. + * @param string $plugin_slug Requested plugin slug/file. + * @param array $installed_plugins Installed plugins keyed by plugin file. * @return string|null */ private function get_plugin_file( $plugin_slug, $installed_plugins ) { diff --git a/src/Check/Network_Site_Count.php b/src/Check/Network_Site_Count.php index f2359ef..721c498 100644 --- a/src/Check/Network_Site_Count.php +++ b/src/Check/Network_Site_Count.php @@ -24,6 +24,9 @@ class Network_Site_Count extends Check { */ protected $maximum = 500; + /** + * @param array $options + */ public function __construct( $options = array() ) { parent::__construct( $options ); @@ -34,6 +37,9 @@ public function __construct( $options = array() ) { } } + /** + * @return void + */ public function run() { if ( ! is_multisite() ) { $this->set_status( 'success' ); diff --git a/src/Check/Network_Site_Option_Value.php b/src/Check/Network_Site_Option_Value.php index 3b750b7..9a3780f 100644 --- a/src/Check/Network_Site_Option_Value.php +++ b/src/Check/Network_Site_Option_Value.php @@ -30,6 +30,9 @@ class Network_Site_Option_Value extends Check { */ protected $value_is_not; + /** + * @return void + */ public function run() { if ( ! is_multisite() ) { $this->set_status( 'success' ); diff --git a/src/Check/Option_Value.php b/src/Check/Option_Value.php index 5122fe5..ef61396 100644 --- a/src/Check/Option_Value.php +++ b/src/Check/Option_Value.php @@ -30,6 +30,9 @@ class Option_Value extends Check { */ protected $value_is_not; + /** + * @return void + */ public function run() { if ( isset( $this->value ) && isset( $this->value_is_not ) ) { @@ -38,6 +41,12 @@ public function run() { return; } + if ( ! isset( $this->value ) && ! isset( $this->value_is_not ) ) { + $this->set_status( 'error' ); + $this->set_message( 'You must use either "value" or "value_is_not".' ); + return; + } + $actual_value = get_option( $this->option ); if ( isset( $this->value ) ) { @@ -48,14 +57,13 @@ public function run() { $status = 'error'; $message = "Option '{$this->option}' is '{$actual_value}' but expected to be '{$this->value}'."; } - } elseif ( isset( $this->value_is_not ) ) { - if ( $actual_value == $this->value_is_not ) { // phpcs:ignore Universal.Operators.StrictComparisons -- Keep existing behavior. - $status = 'error'; - $message = "Option '{$this->option}' is '{$actual_value}' and expected not to be."; - } else { - $status = 'success'; - $message = "Option '{$this->option}' is not '{$this->value_is_not}' as expected."; - } + } elseif ( $actual_value == $this->value_is_not ) { // phpcs:ignore Universal.Operators.StrictComparisons -- Keep existing behavior. + $status = 'error'; + $message = "Option '{$this->option}' is '{$actual_value}' and expected not to be."; + + } else { + $status = 'success'; + $message = "Option '{$this->option}' is not '{$this->value_is_not}' as expected."; } $this->set_status( $status ); diff --git a/src/Check/PHP_In_Upload.php b/src/Check/PHP_In_Upload.php index ae257d5..a011dd1 100644 --- a/src/Check/PHP_In_Upload.php +++ b/src/Check/PHP_In_Upload.php @@ -14,10 +14,13 @@ class PHP_In_Upload extends Check { /** * Array containing list of files found in the uploads folder * - * @var array + * @var array<\SplFileInfo> */ protected $php_files_array = array(); + /** + * @return void + */ public function run() { // Path to the uploads folder. diff --git a/src/Check/Plugin.php b/src/Check/Plugin.php index b759be9..a191d62 100644 --- a/src/Check/Plugin.php +++ b/src/Check/Plugin.php @@ -7,8 +7,14 @@ abstract class Plugin extends Check { + /** + * @var array|null + */ protected static $plugins; + /** + * @return array + */ protected static function get_plugins() { if ( isset( self::$plugins ) ) { diff --git a/src/Check/Plugin_Active_Count.php b/src/Check/Plugin_Active_Count.php index ea0e7c5..a26b7e5 100644 --- a/src/Check/Plugin_Active_Count.php +++ b/src/Check/Plugin_Active_Count.php @@ -14,6 +14,9 @@ class Plugin_Active_Count extends Plugin { */ protected $threshold_count = 80; + /** + * @return void + */ public function run() { $active = 0; foreach ( self::get_plugins() as $plugin ) { diff --git a/src/Check/Plugin_Deactivated.php b/src/Check/Plugin_Deactivated.php index b63c95d..2b82649 100644 --- a/src/Check/Plugin_Deactivated.php +++ b/src/Check/Plugin_Deactivated.php @@ -14,6 +14,9 @@ class Plugin_Deactivated extends Plugin { */ protected $threshold_percentage = 40; + /** + * @return void + */ public function run() { $plugins = self::get_plugins(); diff --git a/src/Check/Plugin_Status.php b/src/Check/Plugin_Status.php index 20e5fe0..1ac9756 100644 --- a/src/Check/Plugin_Status.php +++ b/src/Check/Plugin_Status.php @@ -27,6 +27,9 @@ class Plugin_Status extends Plugin { */ protected $status; + /** + * @param array $options + */ public function __construct( $options = array() ) { $valid_statuses = array( 'uninstalled', 'installed', 'active' ); if ( ! in_array( $options['status'], $valid_statuses, true ) ) { @@ -35,6 +38,9 @@ public function __construct( $options = array() ) { parent::__construct( $options ); } + /** + * @return void + */ public function run() { $plugins = self::get_plugins(); diff --git a/src/Check/Plugin_Update.php b/src/Check/Plugin_Update.php index c37a277..b354f55 100644 --- a/src/Check/Plugin_Update.php +++ b/src/Check/Plugin_Update.php @@ -7,6 +7,9 @@ */ class Plugin_Update extends Plugin { + /** + * @return void + */ public function run() { $plugins = self::get_plugins(); $update_count = 0; diff --git a/src/Check/Theme_Update.php b/src/Check/Theme_Update.php index dd3a679..6bc8c7f 100644 --- a/src/Check/Theme_Update.php +++ b/src/Check/Theme_Update.php @@ -10,6 +10,9 @@ */ class Theme_Update extends Check { + /** + * @return void + */ public function run() { ob_start(); WP_CLI::run_command( array( 'theme', 'list' ), array( 'format' => 'json' ) ); diff --git a/src/Checks.php b/src/Checks.php index d13f9b7..e61b628 100644 --- a/src/Checks.php +++ b/src/Checks.php @@ -4,15 +4,29 @@ use Mustangostang\Spyc; use WP_CLI; -use WP_CLI\Utils; +use WP_CLI\Path; + class Checks { + /** + * @var Checks|null + */ private static $instance; - private $checks = array(); + /** + * @var array + */ + private $checks = array(); + + /** + * @var array> + */ private $skipped_checks = array(); + /** + * @return Checks + */ public static function get_instance() { if ( ! isset( self::$instance ) ) { self::$instance = new Checks(); @@ -24,6 +38,7 @@ public static function get_instance() { * Register checks from a config file * * @param string $file + * @return void */ public static function register_config( $file ) { if ( ! is_file( $file ) ) { @@ -77,8 +92,9 @@ public static function register_config( $file ) { /** * Register a check with the Doctor * - * @param string $name Name for the check. - * @param string $class Check class name. + * @param string $name Name for the check. + * @param object|string $check Check class name or instance. + * @return void */ public static function add_check( $name, $check ) { @@ -100,9 +116,10 @@ public static function add_check( $name, $check ) { } /** - * Get checks registred with the Doctor. + * Get checks registered with the Doctor. * - * @param array $args Filter checks based on some attribute. + * @param array $args Filter checks based on some attribute. + * @return array */ public static function get_checks( $args = array() ) { if ( ! empty( $args['name'] ) ) { @@ -123,9 +140,10 @@ public static function get_checks( $args = array() ) { * * @param string $path Path to file. * @param string $base Base path to prepend. + * @return string */ private static function absolutize( $path, $base ) { - if ( ! empty( $path ) && ! Utils\is_path_absolute( $path ) ) { + if ( ! empty( $path ) && ! Path::is_absolute( $path ) ) { $path = $base . DIRECTORY_SEPARATOR . $path; } return $path; diff --git a/src/Command.php b/src/Command.php index 2f6d6db..077a10a 100644 --- a/src/Command.php +++ b/src/Command.php @@ -108,6 +108,10 @@ class Command { * +--------------------+--------+--------------------------------------------+ * * @when before_wp_load + * + * @param array $args Names of one or more checks to run. + * @param array $assoc_args Command options. + * @return void */ public function check( $args, $assoc_args ) { @@ -185,6 +189,7 @@ static function () use ( $file_checks, &$completed, &$progress ) { if ( ! in_array( $file->getExtension(), $extension, true ) ) { continue; } + /** @var Check\File $check */ $check->check_file( $file ); } } @@ -332,6 +337,10 @@ function ( $check ) { * * @when before_wp_load * @subcommand list + * + * @param array $args Command arguments. + * @param array $assoc_args Command options. + * @return void */ public function list_( $args, $assoc_args ) { @@ -378,6 +387,8 @@ public function list_( $args, $assoc_args ) { /** * Runs through the entirety of the WP bootstrap process + * + * @return void */ private function load_wordpress_with_template() { global $wp_query; @@ -445,6 +456,8 @@ private static function remove_decorations( $comment ) { /** * Get the path to the default config file + * + * @return string */ private static function get_default_config() { return dirname( __DIR__ ) . '/doctor.yml'; diff --git a/tests/phpstan/scan-files.php b/tests/phpstan/scan-files.php new file mode 100644 index 0000000..81f3d91 --- /dev/null +++ b/tests/phpstan/scan-files.php @@ -0,0 +1,6 @@ +