diff --git a/composer.json b/composer.json index 6500e7ccbf8af..a309ae762ac1a 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "wp-coding-standards/wpcs": "~3.3.0", "phpcompatibility/phpcompatibility-wp": "~2.1.3", "phpstan/phpstan": "2.2.2", + "phpstan/phpstan-phpunit": "2.0.16", "yoast/phpunit-polyfills": "^1.1.0" }, "config": { diff --git a/phpstan.neon.dist b/phpstan.neon.dist index e74e6ec1a441b..7ea25ab18e205 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -8,6 +8,12 @@ includes: # The base configuration file for using PHPStan with the WordPress core codebase. - tests/phpstan/base.neon + # Type-specifying extension so PHPUnit assertions (e.g. assertArrayHasKey(), + # assertInstanceOf(), assertNotNull()) narrow types in the analysis. Only the + # extension is included, not phpstan-phpunit's rules.neon, to avoid introducing + # new strict rules. + - vendor/phpstan/phpstan-phpunit/extension.neon + # The baseline file includes preexisting errors in the codebase that should be ignored. # https://phpstan.org/user-guide/baseline - tests/phpstan/baseline.php diff --git a/src/wp-admin/includes/file.php b/src/wp-admin/includes/file.php index 0c6d968ea02d3..d7c771444ac98 100644 --- a/src/wp-admin/includes/file.php +++ b/src/wp-admin/includes/file.php @@ -801,6 +801,9 @@ function validate_file_to_edit( $file, $allowed_files = array() ) { * @type string $url URL of the newly-uploaded file. * @type string $type Mime type of the newly-uploaded file. * } + * + * @phpstan-return array{ file: non-empty-string, url: non-empty-string, type: non-empty-string } + * |array{ error: non-empty-string } */ function _wp_handle_upload( &$file, $overrides, $time, $action ) { // The default error handler. @@ -1094,6 +1097,9 @@ function wp_handle_upload_error( &$file, $message ) { * See _wp_handle_upload() for accepted values. * @param string|null $time Optional. Time formatted in 'yyyy/mm'. Default null. * @return array See _wp_handle_upload() for return value. + * + * @phpstan-return array{ file: non-empty-string, url: non-empty-string, type: non-empty-string } + * |array{ error: non-empty-string } */ function wp_handle_upload( &$file, $overrides = false, $time = null ) { /* @@ -1121,6 +1127,9 @@ function wp_handle_upload( &$file, $overrides = false, $time = null ) { * See _wp_handle_upload() for accepted values. * @param string|null $time Optional. Time formatted in 'yyyy/mm'. Default null. * @return array See _wp_handle_upload() for return value. + * + * @phpstan-return array{ file: non-empty-string, url: non-empty-string, type: non-empty-string } + * |array{ error: non-empty-string } */ function wp_handle_sideload( &$file, $overrides = false, $time = null ) { /* diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 64dce4a159755..b576896d9904b 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -2341,6 +2341,14 @@ function win_is_writable( $path ) { * @see wp_upload_dir() * * @return array See wp_upload_dir() for description. + * @phpstan-return array{ + * path: non-empty-string, + * url: non-empty-string, + * subdir: non-empty-string, + * basedir: non-empty-string, + * baseurl: non-empty-string, + * } + * |array{ error: non-empty-string } */ function wp_get_upload_dir() { return wp_upload_dir( null, false ); @@ -2382,6 +2390,14 @@ function wp_get_upload_dir() { * @type string $baseurl URL path without subdir. * @type string|false $error False or error message. * } + * @phpstan-return array{ + * path: non-empty-string, + * url: non-empty-string, + * subdir: non-empty-string, + * basedir: non-empty-string, + * baseurl: non-empty-string, + * } + * |array{ error: non-empty-string } */ function wp_upload_dir( $time = null, $create_dir = true, $refresh_cache = false ) { static $cache = array(), $tested_paths = array(); diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 005ccadd62e34..ed60d42b022a5 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -6888,6 +6888,39 @@ function wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ) { * @type array $image_meta Image metadata. * @type int $filesize File size of the attachment. * } + * + * @phpstan-return array{ + * width?: int<1, max>, + * height?: int<1, max>, + * file?: non-empty-string, + * filesize?: int<0, max>, + * original_image?: non-empty-string, + * source_image?: non-empty-string, + * sizes?: array, + * height: int<1, max>, + * 'mime-type': non-empty-string, + * filesize?: int<0, max>, + * ... + * }>, + * image_meta?: array{ + * aperture: numeric-string|int, + * credit: string, + * camera: string, + * caption: string, + * created_timestamp: numeric-string|int, + * copyright: string, + * focal_length: numeric-string|int, + * iso: numeric-string|int, + * shutter_speed: numeric-string|int, + * title: string, + * orientation: numeric-string|int, + * keywords: list, + * alt: string, + * }, + * ... + * }|false */ function wp_get_attachment_metadata( $attachment_id = 0, $unfiltered = false ) { $attachment_id = (int) $attachment_id; diff --git a/src/wp-includes/rest-api/class-wp-rest-request.php b/src/wp-includes/rest-api/class-wp-rest-request.php index 7148d931f7149..a6169ada2c05b 100644 --- a/src/wp-includes/rest-api/class-wp-rest-request.php +++ b/src/wp-includes/rest-api/class-wp-rest-request.php @@ -588,6 +588,15 @@ public function set_body_params( $params ) { * @since 4.4.0 * * @return array Parameter map of key to value. + * + * @phpstan-return array, + * full_path?: non-empty-string, + * }> */ public function get_file_params() { return $this->params['FILES']; @@ -601,6 +610,15 @@ public function get_file_params() { * @since 4.4.0 * * @param array $params Parameter map of key to value. + * + * @phpstan-param array, + * full_path?: non-empty-string, + * }> $params */ public function set_file_params( $params ) { $this->params['FILES'] = $params;