Changeset 63420
- Timestamp:
- 09/01/2026 09:29:49 AM (6 weeks ago)
- Location:
- trunk
- Files:
-
- 1 added
- 18 edited
-
.github/workflows/reusable-phpstan-static-analysis-v1.yml (modified) (2 diffs)
-
src/wp-admin/includes/class-wp-list-table.php (modified) (1 diff)
-
src/wp-admin/includes/misc.php (modified) (1 diff)
-
src/wp-admin/includes/post.php (modified) (1 diff)
-
src/wp-includes/class-wp-date-query.php (modified) (1 diff)
-
src/wp-includes/class-wp-http.php (modified) (3 diffs)
-
src/wp-includes/class-wp-xmlrpc-server.php (modified) (1 diff)
-
src/wp-includes/class-wpdb.php (modified) (1 diff)
-
src/wp-includes/cron.php (modified) (1 diff)
-
src/wp-includes/functions.php (modified) (5 diffs)
-
src/wp-includes/link-template.php (modified) (1 diff)
-
src/wp-includes/post.php (modified) (3 diffs)
-
src/wp-includes/taxonomy.php (modified) (1 diff)
-
tests/phpstan/HashNotationVisitor.php (added)
-
tests/phpstan/README.md (modified) (2 diffs)
-
tests/phpstan/base.neon (modified) (2 diffs)
-
tests/phpstan/baselines/argument.type.neon (modified) (9 diffs)
-
tests/phpstan/baselines/assign.propertyType.neon (modified) (1 diff)
-
tests/phpstan/baselines/offsetAccess.notFound.neon (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/.github/workflows/reusable-phpstan-static-analysis-v1.yml
r63019 r63420 85 85 run: npm run build:dev 86 86 87 # The directory holds more than the analysis results. PHPStan also stores what it read out of 88 # each source file there, the docblocks and signatures it found, keyed by that file's contents 89 # and nothing else. The extensions in `tests/phpstan` change what reading a file yields without 90 # changing the file: a parser node visitor rewrites a docblock in the syntax tree, and the bytes 91 # on disk stay as they were. A cache written before one of them changed therefore answers with 92 # what the old code saw, and the analysis silently runs against types no longer derived. 93 # 94 # Keying on the configuration and the extensions keeps a run from restoring a cache that 95 # predates either. The baselines are left out of the key: they only decide which reported errors 96 # are ignored, PHPStan invalidates the results cache on a configuration change by itself, and 97 # including them would discard the whole cache every time one is regenerated. 98 # 99 # The cache is renamed rather than only prefixed, because the key this replaces restored on 100 # `phpstan-result-cache-`, which every key below still begins with. A branch that has not 101 # merged this commit yet runs the workflow it has, and that workflow would prefix-match the 102 # caches written here: it would analyse without the extensions against reflection derived with 103 # them. A name of its own leaves those runs restoring only their own caches. 87 104 - name: Cache PHP Static Analysis scan cache 88 105 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 89 106 with: 90 107 path: .cache # This is defined in the base.neon file. 91 key: "phpstan- result-cache-${{ github.run_id }}"108 key: "phpstan-analysis-cache-${{ hashFiles('phpstan.neon.dist', 'tests/phpstan/*.neon', 'tests/phpstan/*.php') }}-${{ github.run_id }}" 92 109 restore-keys: | 93 phpstan- result-cache-110 phpstan-analysis-cache-${{ hashFiles('phpstan.neon.dist', 'tests/phpstan/*.neon', 'tests/phpstan/*.php') }}- 94 111 95 112 - name: Run PHP static analysis tests … … 194 211 with: 195 212 path: .cache 196 key: "phpstan- result-cache-${{ github.run_id }}"213 key: "phpstan-analysis-cache-${{ hashFiles('phpstan.neon.dist', 'tests/phpstan/*.neon', 'tests/phpstan/*.php') }}-${{ github.run_id }}" 197 214 198 215 - name: Ensure version-controlled files are not modified or deleted -
trunk/src/wp-admin/includes/class-wp-list-table.php
r62868 r63420 428 428 * 429 429 * @param array $link_data { 430 * An array of link data. 431 * 432 * @type string $url The link URL. 433 * @type string $label The link label. 434 * @type bool $current Optional. Whether this is the currently selected view. 430 * An array of link data, keyed by view. 431 * 432 * @type array ...$0 { 433 * Data for a single view link. 434 * 435 * @type string $url The link URL. 436 * @type string $label The link label. 437 * @type bool $current Optional. Whether this is the currently selected view. 438 * } 435 439 * } 436 440 * @return string[] An array of link markup. Keys match the `$link_data` input array. -
trunk/src/wp-admin/includes/misc.php
r62334 r63420 1571 1571 * Array of PHP version data. False on failure. 1572 1572 * 1573 * @type string $recommended_version The PHP version recommended by WordPress. 1574 * @type string $minimum_version The minimum required PHP version. 1575 * @type bool $is_supported Whether the PHP version is actively supported. 1576 * @type bool $is_secure Whether the PHP version receives security updates. 1577 * @type bool $is_acceptable Whether the PHP version is still acceptable or warnings 1578 * should be shown and an update recommended. 1573 * @type string $recommended_version The PHP version recommended by WordPress. 1574 * @type string $minimum_version The minimum required PHP version. 1575 * @type bool $is_supported Whether the PHP version is actively supported. 1576 * @type bool $is_secure Whether the PHP version receives security updates. 1577 * @type bool $is_acceptable Whether the PHP version is still acceptable or warnings 1578 * should be shown and an update recommended. 1579 * @type bool $is_lower_than_future_minimum Whether the PHP version is lower than the minimum PHP 1580 * version WordPress will require in a future release. 1579 1581 * } 1580 1582 */ -
trunk/src/wp-admin/includes/post.php
r62704 r63420 1394 1394 * Defaults to the `$_GET` superglobal. 1395 1395 * @return array { 1396 * Array containing the post mime types and available post mime types.1397 * 1398 * @type array [] $post_mime_types Post mime types.1399 * @type string[] $avail_post_mime_typesAvailable post mime types.1396 * Array containing the post mime types and the available post mime types, in that order. 1397 * 1398 * @type array<string, array{0: string, 1: string, 2: array}> $0 Post mime types. See get_post_mime_types(). 1399 * @type string[] $1 Available post mime types. 1400 1400 * } 1401 1401 */ -
trunk/src/wp-includes/class-wp-date-query.php
r61724 r63420 74 74 * Array of date query clauses. 75 75 * 76 * @type array ...$0 { 77 * @type string $column Optional. The column to query against. If undefined, inherits the value of 78 * the `$default_column` parameter. See WP_Date_Query::validate_column() and 79 * the {@see 'date_query_valid_columns'} filter for the list of accepted values. 80 * Default 'post_date'. 81 * @type string $compare Optional. The comparison operator. Accepts '=', '!=', '>', '>=', '<', '<=', 82 * 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'. Default '='. 83 * @type string $relation Optional. The boolean relationship between the date queries. Accepts 'OR' or 'AND'. 84 * Default 'OR'. 85 * @type array ...$0 { 86 * Optional. An array of first-order clause parameters, or another fully-formed date query. 87 * 88 * @type string|array $before { 89 * Optional. Date to retrieve posts before. Accepts `strtotime()`-compatible string, 90 * or array of 'year', 'month', 'day' values. 91 * 92 * @type string $year The four-digit year. Default empty. Accepts any four-digit year. 93 * @type string $month Optional when passing array. The month of the year. 94 * Default (string:empty)|(array:1). Accepts numbers 1-12. 95 * @type string $day Optional when passing array. The day of the month. 96 * Default (string:empty)|(array:1). Accepts numbers 1-31. 97 * } 98 * @type string|array $after { 99 * Optional. Date to retrieve posts after. Accepts `strtotime()`-compatible string, 100 * or array of 'year', 'month', 'day' values. 101 * 102 * @type string $year The four-digit year. Accepts any four-digit year. Default empty. 103 * @type string $month Optional when passing array. The month of the year. Accepts numbers 1-12. 104 * Default (string:empty)|(array:12). 105 * @type string $day Optional when passing array. The day of the month. Accepts numbers 1-31. 106 * Default (string:empty)|(array:last day of month). 107 * } 108 * @type string $column Optional. Used to add a clause comparing a column other than 109 * the column specified in the top-level `$column` parameter. 110 * See WP_Date_Query::validate_column() and 111 * the {@see 'date_query_valid_columns'} filter for the list 112 * of accepted values. Default is the value of top-level `$column`. 113 * @type string $compare Optional. The comparison operator. Accepts '=', '!=', '>', '>=', 114 * '<', '<=', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'. Comparisons 115 * support arrays in some time-related parameters. Default '='. 116 * @type bool $inclusive Optional. Include results from dates specified in 'before' or 117 * 'after'. Default false. 118 * @type int|int[] $year Optional. The four-digit year number. Accepts any four-digit year 119 * or an array of years if `$compare` supports it. Default empty. 120 * @type int|int[] $month Optional. The two-digit month number. Accepts numbers 1-12 or an 121 * array of valid numbers if `$compare` supports it. Default empty. 122 * @type int|int[] $week Optional. The week number of the year. Accepts numbers 1-53 or an 123 * array of valid numbers if `$compare` supports it. Default empty. 124 * @type int|int[] $dayofyear Optional. The day number of the year. Accepts numbers 1-366 or an 125 * array of valid numbers if `$compare` supports it. 126 * @type int|int[] $day Optional. The day of the month. Accepts numbers 1-31 or an array 127 * of valid numbers if `$compare` supports it. Default empty. 128 * @type int|int[] $dayofweek Optional. The day number of the week. Accepts numbers 1-7 (1 is 129 * Sunday) or an array of valid numbers if `$compare` supports it. 130 * Default empty. 131 * @type int|int[] $dayofweek_iso Optional. The day number of the week (ISO). Accepts numbers 1-7 132 * (1 is Monday) or an array of valid numbers if `$compare` supports it. 133 * Default empty. 134 * @type int|int[] $hour Optional. The hour of the day. Accepts numbers 0-23 or an array 135 * of valid numbers if `$compare` supports it. Default empty. 136 * @type int|int[] $minute Optional. The minute of the hour. Accepts numbers 0-59 or an array 137 * of valid numbers if `$compare` supports it. Default empty. 138 * @type int|int[] $second Optional. The second of the minute. Accepts numbers 0-59 or an 139 * array of valid numbers if `$compare` supports it. Default empty. 76 * @type string $column Optional. The column to query against. If undefined, inherits the value of 77 * the `$default_column` parameter. See WP_Date_Query::validate_column() and 78 * the {@see 'date_query_valid_columns'} filter for the list of accepted values. 79 * Default 'post_date'. 80 * @type string $compare Optional. The comparison operator. Accepts '=', '!=', '>', '>=', '<', '<=', 81 * 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'. Default '='. 82 * @type string $relation Optional. The boolean relationship between the date queries. Accepts 'OR' or 'AND'. 83 * Default 'OR'. 84 * @type array ...$0 { 85 * Optional. An array of first-order clause parameters, or another fully-formed date query. 86 * 87 * @type string|array $before { 88 * Optional. Date to retrieve posts before. Accepts `strtotime()`-compatible string, 89 * or array of 'year', 'month', 'day' values. 90 * 91 * @type string $year The four-digit year. Default empty. Accepts any four-digit year. 92 * @type string $month Optional when passing array. The month of the year. 93 * Default (string:empty)|(array:1). Accepts numbers 1-12. 94 * @type string $day Optional when passing array. The day of the month. 95 * Default (string:empty)|(array:1). Accepts numbers 1-31. 140 96 * } 97 * @type string|array $after { 98 * Optional. Date to retrieve posts after. Accepts `strtotime()`-compatible string, 99 * or array of 'year', 'month', 'day' values. 100 * 101 * @type string $year The four-digit year. Accepts any four-digit year. Default empty. 102 * @type string $month Optional when passing array. The month of the year. Accepts numbers 1-12. 103 * Default (string:empty)|(array:12). 104 * @type string $day Optional when passing array. The day of the month. Accepts numbers 1-31. 105 * Default (string:empty)|(array:last day of month). 106 * } 107 * @type string $column Optional. Used to add a clause comparing a column other than 108 * the column specified in the top-level `$column` parameter. 109 * See WP_Date_Query::validate_column() and 110 * the {@see 'date_query_valid_columns'} filter for the list 111 * of accepted values. Default is the value of top-level `$column`. 112 * @type string $compare Optional. The comparison operator. Accepts '=', '!=', '>', '>=', 113 * '<', '<=', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'. Comparisons 114 * support arrays in some time-related parameters. Default '='. 115 * @type bool $inclusive Optional. Include results from dates specified in 'before' or 116 * 'after'. Default false. 117 * @type int|int[] $year Optional. The four-digit year number. Accepts any four-digit year 118 * or an array of years if `$compare` supports it. Default empty. 119 * @type int|int[] $month Optional. The two-digit month number. Accepts numbers 1-12 or an 120 * array of valid numbers if `$compare` supports it. Default empty. 121 * @type int|int[] $week Optional. The week number of the year. Accepts numbers 1-53 or an 122 * array of valid numbers if `$compare` supports it. Default empty. 123 * @type int|int[] $dayofyear Optional. The day number of the year. Accepts numbers 1-366 or an 124 * array of valid numbers if `$compare` supports it. 125 * @type int|int[] $day Optional. The day of the month. Accepts numbers 1-31 or an array 126 * of valid numbers if `$compare` supports it. Default empty. 127 * @type int|int[] $dayofweek Optional. The day number of the week. Accepts numbers 1-7 (1 is 128 * Sunday) or an array of valid numbers if `$compare` supports it. 129 * Default empty. 130 * @type int|int[] $dayofweek_iso Optional. The day number of the week (ISO). Accepts numbers 1-7 131 * (1 is Monday) or an array of valid numbers if `$compare` supports it. 132 * Default empty. 133 * @type int|int[] $hour Optional. The hour of the day. Accepts numbers 0-23 or an array 134 * of valid numbers if `$compare` supports it. Default empty. 135 * @type int|int[] $minute Optional. The minute of the hour. Accepts numbers 0-59 or an array 136 * of valid numbers if `$compare` supports it. Default empty. 137 * @type int|int[] $second Optional. The second of the minute. Accepts numbers 0-59 or an 138 * array of valid numbers if `$compare` supports it. Default empty. 141 139 * } 142 140 * } -
trunk/src/wp-includes/class-wp-http.php
r62649 r63420 154 154 * Array of response data, or a WP_Error instance upon error. 155 155 * 156 * @type \WpOrg\Requests\Utility\CaseInsensitiveDictionary $headers Response headers keyed by name. 157 * @type string $body Response body. 158 * @type array $response { 156 * @type \WpOrg\Requests\Utility\CaseInsensitiveDictionary|array $headers Response headers keyed by name. 157 * An empty array for a non-blocking request. 158 * @type string $body Response body. 159 * @type array $response { 159 160 * Array of HTTP response data. 160 161 * … … 162 163 * @type string|false $message HTTP response message. 163 164 * } 164 * @type WP_Http_Cookie[] $cookies Array of cookies set by the server.165 * @type string|null $filename Optional. Filename of the response.166 * @type WP_HTTP_Requests_Response|null $http_response Response object.165 * @type WP_Http_Cookie[] $cookies Array of cookies set by the server. 166 * @type string|null $filename Optional. Filename of the response. 167 * @type WP_HTTP_Requests_Response|null $http_response Response object. 167 168 * } 168 169 */ … … 712 713 * then a numbered array is returned as the value of that header-key. 713 714 * 714 * @type array $response {715 * @type array $response { 715 716 * @type int $code The response status code. Default 0. 716 717 * @type string $message The response message. Default empty. 717 718 * } 718 * @type array $newheaders The processed header data as a multidimensional array.719 * @type WP_Http_Cookie[] $cookiesIf the original headers contain the 'Set-Cookie' key,720 * an array containing `WP_Http_Cookie` objects is returned.719 * @type array<string, string|string[]> $headers The processed header data, keyed by lowercased header name. 720 * @type WP_Http_Cookie[] $cookies If the original headers contain the 'Set-Cookie' key, 721 * an array containing `WP_Http_Cookie` objects is returned. 721 722 * } 722 723 */ -
trunk/src/wp-includes/class-wp-xmlrpc-server.php
r63393 r63420 3251 3251 * @type string $2 Username. 3252 3252 * @type string $3 Password. 3253 * @type string $4 Content.3253 * @type array $4 Content struct, with keys documented on {@see self::mw_newPost()}. 3254 3254 * @type int $5 Publish flag. 0 for draft, 1 for publish. 3255 3255 * } -
trunk/src/wp-includes/class-wpdb.php
r62529 r63420 2066 2066 * 2067 2067 * @type string $0 Host name. 2068 * @type string|null$1 Port.2068 * @type int|null $1 Port. 2069 2069 * @type string|null $2 Socket. 2070 2070 * @type bool $3 Whether it is an IPv6 address. -
trunk/src/wp-includes/cron.php
r62894 r63420 766 766 * @param int|null $timestamp Optional. Unix timestamp (UTC) of the event. If not specified, the next scheduled event 767 767 * is returned. Default null. 768 * @return object|false {768 * @return stdClass|false { 769 769 * The event object. False if the event does not exist. 770 770 * -
trunk/src/wp-includes/functions.php
r63380 r63420 2347 2347 * path: non-empty-string, 2348 2348 * url: non-empty-string, 2349 * subdir: non-empty-string,2349 * subdir: string, 2350 2350 * basedir: non-empty-string, 2351 2351 * baseurl: non-empty-string, 2352 * error: non-empty-string|false, 2352 2353 * } 2353 * |array{ error: non-empty-string }2354 2354 */ 2355 2355 function wp_get_upload_dir() { … … 2396 2396 * path: non-empty-string, 2397 2397 * url: non-empty-string, 2398 * subdir: non-empty-string,2398 * subdir: string, 2399 2399 * basedir: non-empty-string, 2400 2400 * baseurl: non-empty-string, 2401 * error: non-empty-string|false, 2401 2402 * } 2402 * |array{ error: non-empty-string }2403 2403 */ 2404 2404 function wp_upload_dir( $time = null, $create_dir = true, $refresh_cache = false ) { … … 2464 2464 * @param string|null $time Optional. Time formatted in 'yyyy/mm'. Default null. 2465 2465 * @return array See wp_upload_dir() 2466 * @phpstan-return array{ 2467 * path: non-empty-string, 2468 * url: non-empty-string, 2469 * subdir: string, 2470 * basedir: non-empty-string, 2471 * baseurl: non-empty-string, 2472 * error: false, 2473 * } 2466 2474 */ 2467 2475 function _wp_upload_dir( $time = null ) { … … 2916 2924 * Information about the newly-uploaded file. 2917 2925 * 2918 * @type string $file Filename of the newly-uploaded file. 2919 * @type string $url URL of the uploaded file. 2920 * @type string $type File type. 2926 * @type string $file Optional. Filename of the newly-uploaded file. Not set if there has been an error. 2927 * @type string $url Optional. URL of the uploaded file. Not set if there has been an error. 2928 * @type string|false $type Optional. File type, or false if the file doesn't match a mime type. 2929 * Not set if there has been an error. 2921 2930 * @type string|false $error Error message, if there has been an error. 2922 2931 * } 2932 * @phpstan-return array{ file: non-empty-string, url: non-empty-string, type: string|false, error: false } 2933 * |array{ error: string, ... } 2923 2934 */ 2924 2935 function wp_upload_bits( $name, $deprecated, $bits, $time = null ) { … … 3799 3810 * as the response code. Default empty array. 3800 3811 * 3801 * @type int $response The HTTP response code. Default 200 for Ajax requests, 500 otherwise. 3802 * @type string $link_url A URL to include a link to. Only works in combination with $link_text. 3803 * Default empty string. 3804 * @type string $link_text A label for the link to include. Only works in combination with $link_url. 3805 * Default empty string. 3806 * @type bool $back_link Whether to include a link to go back. Default false. 3807 * @type string $text_direction The text direction. This is only useful internally, when WordPress is still 3808 * loading and the site's locale is not set up yet. Accepts 'rtl' and 'ltr'. 3809 * Default is the value of is_rtl(). 3810 * @type string $charset Character set of the HTML output. Default 'utf-8'. 3811 * @type string $code Error code to use. Default is 'wp_die', or the main error code if $message 3812 * is a WP_Error. 3813 * @type bool $exit Whether to exit the process after completion. Default true. 3812 * @type int|null $response The HTTP response code, or null to send no status header. The Ajax, JSON, 3813 * JSONP and XML handlers all accept null, for backward compatibility. 3814 * Default 200 for Ajax requests, 500 otherwise. 3815 * @type string $link_url A URL to include a link to. Only works in combination with $link_text. 3816 * Default empty string. 3817 * @type string $link_text A label for the link to include. Only works in combination with $link_url. 3818 * Default empty string. 3819 * @type bool $back_link Whether to include a link to go back. Default false. 3820 * @type string $text_direction The text direction. This is only useful internally, when WordPress is still 3821 * loading and the site's locale is not set up yet. Accepts 'rtl' and 'ltr'. 3822 * Default is the value of is_rtl(). 3823 * @type string $charset Character set of the HTML output. Default 'utf-8'. 3824 * @type string $code Error code to use. Default is 'wp_die', or the main error code if $message 3825 * is a WP_Error. 3826 * @type bool $exit Whether to exit the process after completion. Default true. 3814 3827 * } 3815 3828 * @return void Never returns if `$args['exit']` is true (the default), otherwise returns void. -
trunk/src/wp-includes/link-template.php
r63379 r63420 4426 4426 * @type string|false $url The URL of the avatar that was found, or false. 4427 4427 * } 4428 * @phpstan-return array{ found_avatar: bool, url: string|false, ... } 4428 4429 */ 4429 4430 function get_avatar_data( $id_or_email, $args = null ) { -
trunk/src/wp-includes/post.php
r63419 r63420 2013 2013 * 2014 2014 * @param object $args Post type registration arguments. 2015 * @return object{2015 * @return stdClass { 2016 2016 * Object with all the capabilities as member variables. 2017 2017 * … … 3630 3630 * @since 5.3.0 Added the 'Documents', 'Spreadsheets', and 'Archives' mime type groups. 3631 3631 * 3632 * @return array List of post mime types. 3632 * @return array<string, array{0: string, 1: string, 2: array}> List of post mime types, keyed by mime type group 3633 * or by a comma-separated list of mime types. Each 3634 * value is a three-item array: the plural name of the 3635 * group, the label for its "Manage" screen, and the 3636 * translatable count strings returned by _n_noop(). 3633 3637 */ 3634 3638 function get_post_mime_types() { … … 3723 3727 * @since 2.5.0 3724 3728 * 3725 * @param array $post_mime_types Default list of post mime types. 3729 * @param array<string, array{0: string, 1: string, 2: array}> $post_mime_types Default list of post mime types. 3730 * See {@see get_post_mime_types()}. 3726 3731 */ 3727 3732 return apply_filters( 'post_mime_types', $post_mime_types ); -
trunk/src/wp-includes/taxonomy.php
r63419 r63420 651 651 * 652 652 * @param WP_Taxonomy $tax Taxonomy object. 653 * @return object{653 * @return stdClass { 654 654 * Taxonomy labels object. The first default value is for non-hierarchical taxonomies 655 655 * (like tags) and the second one is for hierarchical taxonomies (like categories). -
trunk/tests/phpstan/README.md
r63019 r63420 59 59 60 60 Core documents the globals a function uses with `@global Type $varname`. `GlobalDocBlockVisitor` bridges that convention to PHPStan's variable type resolution, so those globals are typed rather than `mixed` inside the function. 61 62 ### Hash notation 63 64 Core documents the contents of an array or object with a nested list of `@type` tags, [hash notation](https://developer.wordpress.org/coding-standards/inline-documentation-standards/php/#1-1-parameters-that-are-arrays): 65 66 ```php 67 /** 68 * @param array $args { 69 * Optional. An array of arguments. 70 * 71 * @type string $post_type Post type. Default 'post'. 72 * @type int $post_author Post author ID. 73 * } 74 */ 75 ``` 76 77 PHPStan reads that hash as free text, so the value stays a plain `array` and nothing inside it is typed. `HashNotationVisitor` translates it into the array or object shape PHPStan understands, which for the example above is `array{post_type?: string, post_author?: int, ...}`, so the same documentation serves the reader and the analysis rather than each shape having to be written a second time as a `@phpstan-param`. 78 79 A hash whose translation would be a guess is left alone, and the value keeps whatever type it has today. The visitor therefore only ever narrows a type, and never contradicts one: 80 81 - A `@phpstan-param` or `@phpstan-return` written by hand always wins. Hash notation cannot express everything a type can — a function returning either of two shapes, or one whose return carries keys beyond those it documents, as `get_avatar_data()` returns the processed `$args` too — so a shape that has been tuned in the source is never overwritten by the derived one. Only the tag it was written for: a `@phpstan-param` for one parameter says nothing about the next, and a conditional `@phpstan-return` naming a parameter is not a shape for it. 82 - The declared type has to name something a shape can be put on: a bare `array` or `object`, or a class, on its own or as one member of a union such as `string|array` or `array|WP_Error` — in a union the bare member is the one the hash is about, since a `WP_Error` carries no keys. A type that is already more specific than the hash, such as `array<string, string|bool>`, is left as written. 83 - The hash has to be well formed: every `{` closed by a `}` on a line of its own, and every `@type` carrying a `$name` and a type that PHPStan's own type parser reads as one. Asking the parser rather than describing what a type may contain is what keeps `?string`, `(string|null)[]` and `callable(): void` from being turned away as malformed. 84 - A hash on a bare `object` that would have to stay open is skipped, because PHPStan's object shapes have a fixed member list and no `...`, and sealing one would report every member the hash does not name. A hash on a named class is kept: `stdClass&object{...}` is not sealed, since the intersection leaves the class to say what else may be read and a `stdClass` accepts anything. That is what lets the theme data `WP_Theme_Install_List_Table::single_row()` documents keep its shape while the wordpress.org API is still free to add to it. 85 - A parameter taken by reference is skipped, because PHPStan checks a by-reference argument in both directions, and a shape there would be a contract every caller's variable has to satisfy before the call rather than a description of what the function reads. 86 87 Keys of a `@param` hash are optional, at every level, and the shape is left open with a trailing `...`, because the hash lists the keys core reads rather than the only keys a caller may pass. Keys of a `@return` hash are required and the shape is sealed, since they describe a value core itself builds. Numbered keys such as `$0` and `$1` are required in either case, because positional arguments are passed in order. A description opening with `Optional.` overrides all of that, and is the only thing that does: the word further into a description is prose, as in the "Optional self closing slash" of a match array whose every group is always set. 88 89 Reading a key that a `@return` hash does not document is therefore reported rather than silently typed as `mixed` — unless the hash says otherwise itself, by listing a `...$N` entry beside its named keys, which is how core writes "and the rest" for the positional hashes of `wp_maybe_grant_site_health_caps()` and `WP_Meta_Query`. 90 91 A hash on a class rather than on `array` or `object` produces an intersection, `stdClass&object{...}`, rather than a bare object shape. PHPStan's object shapes are structural, so a bare `object{...}` derived for a value core builds as a `stdClass` would no longer be assignable to a property declared `stdClass`. Intersecting keeps both: the value stays the class it is documented as, and its members are typed. This is why the returns that build one, such as `get_taxonomy_labels()`, document `stdClass` rather than `object`. 92 93 One kind of hash is outside what the visitor covers today: **a `@var` hash on a property**. A property declaration is inherited by every subclass and has to accept its own default, so a shape there would say more than the hash does — that no subclass may widen the property, and that the declared default already has the shape. 94 95 Hashes are also written on hook docblocks, where core documents `apply_filters()` and `do_action()`. Those are not attached to a function, so they are outside what this visitor sees, and the value a filter passes stays typed by [the hook extensions below](#hook-documentation). 61 96 62 97 ### Hook documentation … … 165 200 PHPStan caches analysis results to speed up subsequent runs. You can see information about the results cache by running `analyse` with the `-vv` or `-vvv` flag. 166 201 202 ### Clear the cache after changing anything in this directory 203 204 The `.cache` directory holds more than the results. PHPStan also stores what it read out of each source file there, the docblocks and signatures it found, keyed by that file's contents and nothing else. The sources in this directory change what reading a file yields without changing the file: `HashNotationVisitor` rewrites a docblock in the syntax tree, and the bytes on disk stay as they were. 205 206 A cache written before one of them changed therefore answers with what the old code saw. The run does not fail or warn; it reports against types that are no longer derived, so a visitor can look as though it does nothing, or as though it does less than it does. So clear the cache by hand after editing anything here: 207 208 ```bash 209 rm -rf .cache 210 ``` 211 212 The results cache alone is not the problem. PHPStan invalidates that itself when the configuration changes, and [`HookDocsResultCacheMetaExtension`](HookDocsResultCacheMetaExtension.php) already folds every file in this directory into its key, so it is discarded when one of them is edited. What survives either is the per-file reflection, which is keyed by the source file's own contents and has no way to know that reading it now yields something else. CI keys its cache on these files for the same reason; see [`.github/workflows/reusable-phpstan-static-analysis-v1.yml`](../../.github/workflows/reusable-phpstan-static-analysis-v1.yml). 213 214 The same cache is worth clearing after analysing a subset of the tree. A file named on the command line is analysed, but a file merely *read* on its behalf is parsed without these extensions, and the reflection stored for it carries no derived shape. A later full run reads that back and reports against a type that is no longer what the docblock says, which is the same silence as above arriving from the other direction. 215 167 216 Sometimes, due to the lack of type information in legacy code, PHPStan may still struggle to analyze certain parts of the codebase. In such cases, you can use the `--debug` flag to disable caching and see which files are causing issues. -
trunk/tests/phpstan/base.neon
r63356 r63420 10 10 - 11 11 class: WordPress\PHPStan\GlobalDocBlockVisitor 12 tags: 13 - phpstan.parser.richParserNodeVisitor 14 15 # Bridges WordPress core's hash notation, the nested `@type` list documenting the 16 # contents of an array or object, to PHPStan's array and object shapes. 17 # See tests/phpstan/HashNotationVisitor.php. 18 - 19 class: WordPress\PHPStan\HashNotationVisitor 12 20 tags: 13 21 - phpstan.parser.richParserNodeVisitor … … 158 166 - ../../src/xmlrpc.php 159 167 - GlobalDocBlockVisitor.php 168 - HashNotationVisitor.php 160 169 - HookDocsVisitor.php 161 170 - HookDocBlock.php -
trunk/tests/phpstan/baselines/argument.type.neon
r63352 r63420 365 365 path: ../../../src/wp-admin/includes/template.php 366 366 - 367 message: '#^Parameter \#1 \$update of method Language_Pack_Upgrader\:\:upgrade\(\) expects string\|false, stdClass given\.$#'367 message: '#^Parameter \#1 \$update of method Language_Pack_Upgrader\:\:upgrade\(\) expects string\|false, object\{language\: string, version\: string, updated\: string, english_name\: string, native_name\: string, package\: string, iso\: array\<string\>, strings\: array\}&stdClass given\.$#' 368 368 identifier: argument.type 369 369 count: 1 … … 500 500 path: ../../../src/wp-content/themes/twentyeleven/inc/theme-options.php 501 501 - 502 message: '#^Parameter \#3 \$args of function register_setting expects array , stringgiven\.$#'502 message: '#^Parameter \#3 \$args of function register_setting expects array\{type\?\: string, label\?\: string, description\?\: string, sanitize_callback\?\: \(callable\(\)\: mixed\)\|null, show_in_rest\?\: array\|bool, default\?\: mixed, \.\.\.\}, ''twentyeleven_theme…'' given\.$#' 503 503 identifier: argument.type 504 504 count: 1 … … 805 805 path: ../../../src/wp-includes/class-wp-customize-manager.php 806 806 - 807 message: '#^Parameter \#1 \$args of method WP_Customize_Manager\:\:get_changeset_posts\(\) expects array\{posts_per_page\?\: int, author\?\: int, post_status\?\: string, exclude_restore_dismissed\?\: bool, \.\.\.\}, array\{post_status\: array\<non\-falsy\-string, non\-falsy\-string\>, exclude_restore_dismissed\: false, author\: ''any'', posts_per_page\: 1, order\: ''DESC'', orderby\: ''date''\} given\.$#' 808 identifier: argument.type 809 count: 1 810 path: ../../../src/wp-includes/class-wp-customize-manager.php 811 - 807 812 message: '#^Parameter \#1 \$month of function wp_checkdate expects int, \(string\|false\) given\.$#' 813 identifier: argument.type 814 count: 1 815 path: ../../../src/wp-includes/class-wp-customize-manager.php 816 - 817 message: '#^Parameter \#1 \$postarr of function wp_insert_post expects array\{ID\?\: int, post_author\?\: int, post_date\?\: string, post_date_gmt\?\: string, post_content\?\: string, post_content_filtered\?\: string, post_title\?\: string, post_excerpt\?\: string, \.\.\., \.\.\.\}, array\<int\|string\|false\> given\.$#' 808 818 identifier: argument.type 809 819 count: 1 … … 845 855 path: ../../../src/wp-includes/class-wp-duotone.php 846 856 - 857 message: '#^Parameter \#1 \$postarr of function wp_insert_post expects array\{ID\?\: int, post_author\?\: int, post_date\?\: string, post_date_gmt\?\: string, post_content\?\: string, post_content_filtered\?\: string, post_title\?\: string, post_excerpt\?\: string, \.\.\., \.\.\.\}, array\<string\> given\.$#' 858 identifier: argument.type 859 count: 2 860 path: ../../../src/wp-includes/class-wp-embed.php 861 - 847 862 message: '#^Parameter \#3 \$priority of function _wp_filter_build_unique_id expects int, false given\.$#' 848 863 identifier: argument.type … … 940 955 path: ../../../src/wp-includes/class-wp-xmlrpc-server.php 941 956 - 957 message: '#^Parameter \#1 \$postarr of function wp_insert_post expects array\{ID\?\: int, post_author\?\: int, post_date\?\: string, post_date_gmt\?\: string, post_content\?\: string, post_content_filtered\?\: string, post_title\?\: string, post_excerpt\?\: string, \.\.\., \.\.\.\}, array\{post_author\: int, post_date\: int\|string, post_date_gmt\: int\|string, post_content\: string, post_title\: string, post_category\: array\<string\>\|string, post_status\: ''draft''\|''publish''\} given\.$#' 958 identifier: argument.type 959 count: 1 960 path: ../../../src/wp-includes/class-wp-xmlrpc-server.php 961 - 942 962 message: '#^Parameter \#1 \$term_id of method wp_xmlrpc_server\:\:get_term_custom_fields\(\) expects int, string given\.$#' 943 963 identifier: argument.type … … 1235 1255 path: ../../../src/wp-includes/nav-menu.php 1236 1256 - 1257 message: '#^Parameter \#1 \$postarr of function wp_insert_post expects array\{ID\?\: int, post_author\?\: int, post_date\?\: string, post_date_gmt\?\: string, post_content\?\: string, post_content_filtered\?\: string, post_title\?\: string, post_excerpt\?\: string, \.\.\., \.\.\.\}, array\{menu_order\: mixed, ping_status\: 0, post_content\: mixed, post_excerpt\: mixed, post_parent\: int\|string\|WP_Error\|null, post_title\: mixed, post_type\: ''nav_menu_item'', post_date\?\: non\-falsy\-string, \.\.\.\} given\.$#' 1258 identifier: argument.type 1259 count: 1 1260 path: ../../../src/wp-includes/nav-menu.php 1261 - 1237 1262 message: '#^Parameter \#2 \$value of function setcookie expects string, int\<1, max\> given\.$#' 1238 1263 identifier: argument.type … … 1275 1300 path: ../../../src/wp-includes/pluggable.php 1276 1301 - 1302 message: '#^Parameter \#1 \$args of function get_pages expects array\{child_of\?\: int, sort_order\?\: string, sort_column\?\: string, hierarchical\?\: bool, exclude\?\: array\<int\>, include\?\: array\<int\>, meta_key\?\: string, meta_value\?\: string, \.\.\., \.\.\.\}\|string, non\-empty\-array given\.$#' 1303 identifier: argument.type 1304 count: 1 1305 path: ../../../src/wp-includes/post-template.php 1306 - 1277 1307 message: '#^Parameter \#1 \$attachment of function is_attachment expects array\<int\|string\>\|int\|string, WP_Post given\.$#' 1278 1308 identifier: argument.type … … 1382 1412 message: '#^Parameter \#1 \$data_object of method WP_REST_Controller\:\:update_additional_fields_for_object\(\) expects object, array given\.$#' 1383 1413 identifier: argument.type 1384 count: 21414 count: 1 1385 1415 path: ../../../src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php 1386 1416 - 1417 message: '#^Parameter \#1 \$data_object of method WP_REST_Controller\:\:update_additional_fields_for_object\(\) expects object, array\<string, int\|string\|null\> given\.$#' 1418 identifier: argument.type 1419 count: 1 1420 path: ../../../src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php 1421 - 1387 1422 message: '#^Parameter \#1 \$comment_id of function get_comment_type expects int\|WP_Comment, string given\.$#' 1388 1423 identifier: argument.type … … 1575 1610 path: ../../../src/wp-includes/user.php 1576 1611 - 1612 message: '#^Parameter \#1 \$args of function register_sidebar expects array\{name\?\: string, id\?\: string, description\?\: string, class\?\: string, before_widget\?\: string, after_widget\?\: string, before_title\?\: string, after_title\?\: string, \.\.\., \.\.\.\}\|string, non\-empty\-array\<int\|string, array\<mixed\>\|string\> given\.$#' 1613 identifier: argument.type 1614 count: 1 1615 path: ../../../src/wp-includes/widgets.php 1616 - 1577 1617 message: '#^Parameter \#3 \$control_callback of function wp_register_widget_control expects callable\(\)\: mixed, '''' given\.$#' 1578 1618 identifier: argument.type -
trunk/tests/phpstan/baselines/assign.propertyType.neon
r63404 r63420 145 145 path: ../../../src/wp-includes/taxonomy.php 146 146 - 147 message: '#^Static property WP_Widget_Media\:\:\$l10n_defaults \(array\<string\>\) does not accept array\<string, array\ |string\>\.$#'147 message: '#^Static property WP_Widget_Media\:\:\$l10n_defaults \(array\<string\>\) does not accept array\<string, array\<int\|string, string\|null\>\|string\>\.$#' 148 148 identifier: assign.propertyType 149 149 count: 1 -
trunk/tests/phpstan/baselines/offsetAccess.notFound.neon
r63022 r63420 20 20 ignoreErrors: 21 21 - 22 message: '#^Offset float does not exist on list\ .$#'22 message: '#^Offset float does not exist on list\<float\>\.$#' 23 23 identifier: offsetAccess.notFound 24 24 count: 1
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)