Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
20 changes: 20 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,23 @@ parameters:
message: '#^PHPDoc tag @throws with type InvalidArgumentException\|ValueError is not subtype of Throwable$#'
path: src/wp-includes/compat.php
reportUnmatched: false

# Level 5:
# substr_compare()'s $length has always accepted null, meaning "compare the full length". PHP 7.4 spelled
# that `int $length = null`, where the null default makes the parameter implicitly nullable, and PHP 8.0
# only made it explicit as `?int $length = null`. The behavior never differed: verified on PHP 7.4 that
# passing null compares the whole string rather than coercing to a length of 0.
#
# PHPStan reads the two spellings from two sources and only one is right. Its PHP 8 stub carries `?int`,
# but the pre-8.0 `resources/functionMap.php` records the type as plain `int`, having dropped the implicit
# nullability when it was transcribed from the old manual. Because `phpVersion.min` is 70400, the legacy
# map wins and null is reported as invalid.
#
# This lives here rather than in a baseline because a baseline entry records work still to be done, and
# there is none: the call is correct on every version WordPress supports, and the fault is in PHPStan's
# data. Passing an explicit length purely to satisfy it would change working code to suit a tooling bug.
# `reportUnmatched: false` so this entry lapses quietly if PHPStan corrects the map.
-
message: '#^Parameter \#4 \$length of function substr_compare expects int, null given\.$#'
path: src/wp-includes/html-api/class-wp-html-tag-processor.php
reportUnmatched: false
2 changes: 1 addition & 1 deletion src/wp-admin/includes/class-wp-posts-list-table.php
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ protected function _column_title( $post, $classes, $data, $primary ) {
* @return string The post title, or 'no title' if no title.
*/
protected function get_primary_column_aria_label( $item ) {
return isset( $item->post_title ) && ! empty( $item->post_title ) ? $item->post_title : __( 'no title' );
return ! empty( $item->post_title ) ? $item->post_title : __( 'no title' );
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/wp-admin/users.php
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,11 @@

<ul>
<?php
/**
* @global wpdb $wpdb WordPress database abstraction object.
*/
global $wpdb;

$go_delete = 0;
$users_have_content = false;

Expand Down
2 changes: 1 addition & 1 deletion src/wp-includes/block-supports/states.php
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ function wp_add_block_state_style_rule( &$css_rules, $state, $selector, $style,
*/
function wp_get_block_state_style_rules( $state_styles, $block_type, $rules_group = null ) {
$css_rules = array();
$block_selectors = isset( $block_type->selectors ) && is_array( $block_type->selectors )
$block_selectors = is_array( $block_type->selectors )
? $block_type->selectors
: array();

Expand Down
5 changes: 3 additions & 2 deletions src/wp-includes/class-wp-block-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,9 @@ public function __construct( $block_type, $args = array() ) {
* @since 6.1.0
*
* @param string $name Deprecated property name.
* @return string|string[]|null The value read from the new property if the first item in the array provided,
* null when value not found or when unknown property name provided.
* @return string|string[]|array[]|null The value read from the new property if the first item in the array
* provided, the variations or uses context arrays for those two names,
* null when value not found or when unknown property name provided.
*/
public function __get( $name ) {
if ( 'variations' === $name ) {
Expand Down
14 changes: 8 additions & 6 deletions src/wp-includes/class-wp-comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,24 @@ final class WP_Comment {
/**
* Comment ID.
*
* A numeric string, for compatibility reasons.
* A numeric string, for compatibility reasons. Note that {@see get_comment_to_edit()}
* replaces it with an integer in place.
*
* @since 4.4.0
* @var string
* @phpstan-var numeric-string
* @var string|int
* @phpstan-var numeric-string|int
*/
public $comment_ID;

/**
* ID of the post the comment is associated with.
*
* A numeric string, for compatibility reasons.
* A numeric string, for compatibility reasons. Note that {@see get_comment_to_edit()}
* replaces it with an integer in place.
*
* @since 4.4.0
* @var string
* @phpstan-var numeric-string
* @var string|int
* @phpstan-var numeric-string|int
*/
public $comment_post_ID = '0';

Expand Down
5 changes: 3 additions & 2 deletions src/wp-includes/class-wp-theme-json.php
Original file line number Diff line number Diff line change
Expand Up @@ -5672,8 +5672,9 @@ protected static function get_block_element_selectors( $root_selector ) {
*
* @since 6.3.0
*
* @param object $metadata The related block metadata containing selectors.
* @param object $node A merged theme.json node for block or variation.
* @param array $metadata The related block metadata containing selectors.
* @param array $node A merged theme.json node for block or variation. Features
* promoted to their own selector are removed from it.
* @return array The style declarations for the node's features with custom
* selectors.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/wp-includes/class-wp-view-config-data.php
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ private function merge_properties( $current, $incoming, $replace_lists ) {
// A non-empty list only lands where a list (or nothing) lives, under
// merge() and replace() alike. An empty array is shape-ambiguous and
// exempt, so replace() with an empty list can still clear a list.
if ( array() !== $incoming && is_array( $current ) && ! array_is_list( $current ) && array() !== $current ) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Why remove that? I don't see how this is not a different behavior.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The && array() !== $current condition is flagged as a PHPStan error:

phpstan: Strict comparison using !== between array{} and non-empty-array<mixed, mixed> will always evaluate to true.

Thinking this through: If $current is an array but it is not a list, then is it guaranteed to never be an empty array as PHPStan has identified?

@westonruter westonruter Aug 28, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Because if it is not a list, then it must have a non-sequential key, and this implies that the array is not empty?

Also per Claude Opus 5:

Not a behavior change — array_is_list( array() ) returns true, so an empty array is already excluded by ! array_is_list( $current ). Anything reaching that point is necessarily a non-empty array, which is why PHPStan flags the array() !== $current comparison as always true. The array() !== $incoming check earlier in the condition is still load-bearing; only the $current half was redundant.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

yeah agreed, can follow you on that now. was not fully aware of array_is_list()

if ( array() !== $incoming && is_array( $current ) && ! array_is_list( $current ) ) {
_doing_it_wrong(
__METHOD__,
esc_html__( 'A view configuration patch value must match the shape of the value it patches: a list merges into a list, and an associative array into an associative array.' ),
Expand Down
2 changes: 1 addition & 1 deletion src/wp-includes/taxonomy.php
Original file line number Diff line number Diff line change
Expand Up @@ -1598,7 +1598,7 @@ function unregister_term_meta( $taxonomy, $meta_key ) {
* @phpstan-return (
* $term is null ? null : (
* $term is 0 ? 0 : (
* $taxonomy is '' ? int|null : (
* $taxonomy is '' ? numeric-string|null : (
* array{
* term_id: numeric-string,
* term_taxonomy_id: numeric-string,
Expand Down
Loading
Loading