Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -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\.#'

11 changes: 10 additions & 1 deletion src/Check.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ abstract class Check {

/**
* Initialize the check.
*
* @param array<string, mixed> $options
*/
public function __construct( $options = array() ) {

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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<string, string>
*/
public function get_results() {
return array(
Expand Down
10 changes: 9 additions & 1 deletion src/Check/Autoload_Options_Size.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 ) ];
}
}
3 changes: 3 additions & 0 deletions src/Check/Cache_Flush.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
*/
class Cache_Flush extends File_Contents {

/**
* @return void
*/
public function run() {

// Path to wp-content directory.
Expand Down
23 changes: 17 additions & 6 deletions src/Check/Constant_Definition.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -39,6 +39,8 @@ class Constant_Definition extends Check {

/**
* Initialize the constant check
*
* @param array<string, mixed> $options
*/
public function __construct( $options = array() ) {
parent::__construct( $options );
Expand All @@ -47,6 +49,9 @@ public function __construct( $options = array() ) {
}
}

/**
* @return void
*/
public function run() {

if ( isset( $this->falsy ) ) {
Expand Down Expand Up @@ -99,7 +104,7 @@ public function run() {
return;
}

if ( $this->defined && ! isset( $this->value ) ) {
if ( ! isset( $this->value ) ) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Removing $this->defined from this condition changes the behavior of the check. Previously, this block only executed if the configuration explicitly requested a definition check (e.g., defined: true in YAML). Now, it will execute for any constant check where a value is not specified, potentially leading to incorrect success reports if the constant is not actually defined. If PHPStan is flagging $this->defined as an undefined property, it should be explicitly declared in the class rather than removed from the logic.

if ( $this->defined && ! isset( $this->value ) ) {

$this->set_status( 'success' );
$this->set_message( "Constant '{$this->constant}' is defined." );
return;
Expand All @@ -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;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Casting $value directly to a string can cause a PHP notice or warning if the constant's value is an array (e.g., Array to string conversion). Since WordPress constants can occasionally be arrays or other non-scalar types, it's safer to handle these cases explicitly.

return is_scalar( $value ) ? (string) $value : json_encode( $value );

}
}
6 changes: 6 additions & 0 deletions src/Check/Core_Verify_Checksums.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@
*/
class Core_Verify_Checksums extends Check {

/**
* @param array<string, mixed> $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',
Expand Down
6 changes: 6 additions & 0 deletions src/Check/Cron.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@

abstract class Cron extends Check {

/**
* @var array<mixed>|null
*/
protected static $crons;

/**
* @return array<mixed>
*/
protected static function get_crons() {

if ( isset( self::$crons ) ) {
Expand Down
3 changes: 3 additions & 0 deletions src/Check/Cron_Count.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) {
Expand Down
3 changes: 3 additions & 0 deletions src/Check/Cron_Duplicates.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
14 changes: 11 additions & 3 deletions src/Check/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Changing $_when from false to 'manual' breaks the discovery logic for file-based checks. In src/Command.php, the code identifies File checks by checking if get_when() returns a falsy value (see lines 142 and 156-161). By setting this to a string, these checks will be incorrectly treated as regular hooked checks and will not be added to the $file_checks array, causing the filesystem scan to skip them entirely. If this change was made to satisfy PHPStan's type requirements, consider updating the base class type hint or using a @phpstan-ignore instead.

protected $_when = false;


/**
* File extension to check.
Expand Down Expand Up @@ -42,14 +42,14 @@ abstract class File extends Check {
/**
* Any files matching the check.
*
* @var array
* @var array<string|\SplFileInfo>
*/
protected $_matches = array();

/**
* Get the options for this check
*
* @return string
* @return array<string, bool|string>
*/
public function get_options() {
return array(
Expand All @@ -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 );
}
8 changes: 6 additions & 2 deletions src/Check/File_Contents.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
12 changes: 7 additions & 5 deletions src/Check/File_Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace WP_CLI\Doctor\Check;

use SplFileInfo;

/**
* Checks files on the filesystem to be of a certain type.
*/
Expand All @@ -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() {

Expand All @@ -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;
Expand Down
14 changes: 9 additions & 5 deletions src/Check/Network_Required_Plugins.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ class Network_Required_Plugins extends Check {
/**
* List of required plugin slugs.
*
* @var array
* @var array<string>
*/
protected $plugins = array();

/**
* @param array<string, mixed> $options
*/
public function __construct( $options = array() ) {
parent::__construct( $options );

Expand All @@ -32,6 +35,9 @@ public function __construct( $options = array() ) {
}
}

/**
* @return void
*/
public function run() {
if ( ! is_multisite() ) {
$this->set_status( 'success' );
Expand Down Expand Up @@ -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<string, mixed> $installed_plugins Installed plugins keyed by plugin file.
* @return string|null
*/
private function get_plugin_file( $plugin_slug, $installed_plugins ) {
Expand Down
6 changes: 6 additions & 0 deletions src/Check/Network_Site_Count.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class Network_Site_Count extends Check {
*/
protected $maximum = 500;

/**
* @param array<string, mixed> $options
*/
public function __construct( $options = array() ) {
parent::__construct( $options );

Expand All @@ -34,6 +37,9 @@ public function __construct( $options = array() ) {
}
}

/**
* @return void
*/
public function run() {
if ( ! is_multisite() ) {
$this->set_status( 'success' );
Expand Down
3 changes: 3 additions & 0 deletions src/Check/Network_Site_Option_Value.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' );
Expand Down
Loading
Loading