Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c2eb50e
Build/Test Tools: Allow WP_DEBUG_LOG to be a string in PHPStan.
rahultank-spaceo Oct 6, 2026
cef7877
Merge remote-tracking branch 'origin/trunk' into 65817-wp-debug-log-type
rahultank-spaceo Oct 8, 2026
1ec8477
Build/Test Tools: Add types for all dynamic constants in PHPStan.
rahultank-spaceo Oct 9, 2026
01eef56
Merge branch 'trunk' into 65817-wp-debug-log-type
westonruter Oct 21, 2026
c18f6bd
Cast COOKIEPATH and SITECOOKIEPATH defaults to string
westonruter Oct 21, 2026
28f0552
Narrow dynamic constant types to what core guarantees
westonruter Oct 21, 2026
131ba04
Note wider real-world values for dynamic constants
westonruter Oct 21, 2026
1720c0a
Explain the role of bootstrap defines for typed dynamic constants
westonruter Oct 21, 2026
c8bf63f
Avoid naming a specific framework in dynamic constant comments
westonruter Oct 21, 2026
67fc41b
Narrow EMPTY_TRASH_DAYS to a non-negative int
westonruter Oct 21, 2026
4a412ad
Narrow salt and key constants to non-empty strings
westonruter Oct 21, 2026
4f64676
Narrow WP_POST_REVISIONS to its documented values
westonruter Oct 21, 2026
5c2a25b
Revert "Narrow WP_POST_REVISIONS to its documented values"
westonruter Oct 21, 2026
f9d9af6
Note wider values for NOBLOGREDIRECT and WP_DEVELOPMENT_MODE
westonruter Oct 21, 2026
33d5187
Clarify when a bootstrap define is needed for a dynamic constant
westonruter Oct 21, 2026
1c5af48
Add FORCE_SSL_ADMIN and COOKIE_DOMAIN to the dynamic constants
westonruter Oct 21, 2026
a23110f
Correct two dynamic constant comments
westonruter Oct 21, 2026
079b1dd
Qualify the EMPTY_TRASH_DAYS fatal as PHP 8 only
westonruter Oct 21, 2026
aa367fb
Say when to widen a dynamic constant type
westonruter Oct 21, 2026
ce5a5fc
Add the cookie constants to the dynamic constants
westonruter Oct 21, 2026
d439b2c
Add the path and URL constants to the dynamic constants
westonruter Oct 21, 2026
125a9ea
Add the limit, timeout, and mode constants to the dynamic constants
westonruter Oct 21, 2026
ea1a601
Drop bootstrap comments justifying now-ignored values
westonruter Oct 21, 2026
c901a1e
Define the test memory limits as strings
westonruter Oct 21, 2026
e3c157b
Type the remaining salt and key constants
westonruter Oct 21, 2026
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
2 changes: 1 addition & 1 deletion src/wp-admin/press-this.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function wp_load_press_this() {
403
);
} elseif ( is_plugin_active( $plugin_file ) ) {
include WP_PLUGIN_DIR . '/press-this/class-wp-press-this-plugin.php'; // @phpstan-ignore include.fileNotFound
include WP_PLUGIN_DIR . '/press-this/class-wp-press-this-plugin.php';
$wp_press_this = new WP_Press_This_Plugin(); // @phpstan-ignore class.notFound
$wp_press_this->html();
} elseif ( current_user_can( 'activate_plugins' ) ) {
Expand Down
6 changes: 3 additions & 3 deletions src/wp-includes/default-constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -301,14 +301,14 @@ function wp_cookie_constants() {
* @since 1.2.0
*/
if ( ! defined( 'COOKIEPATH' ) ) {
define( 'COOKIEPATH', preg_replace( '|https?://[^/]+|i', '', get_option( 'home' ) . '/' ) );
define( 'COOKIEPATH', (string) preg_replace( '|https?://[^/]+|i', '', get_option( 'home' ) . '/' ) );
}

/**
* @since 1.5.0
*/
if ( ! defined( 'SITECOOKIEPATH' ) ) {
define( 'SITECOOKIEPATH', preg_replace( '|https?://[^/]+|i', '', get_option( 'siteurl' ) . '/' ) );
define( 'SITECOOKIEPATH', (string) preg_replace( '|https?://[^/]+|i', '', get_option( 'siteurl' ) . '/' ) );
}

/**
Expand All @@ -322,7 +322,7 @@ function wp_cookie_constants() {
* @since 2.6.0
*/
if ( ! defined( 'PLUGINS_COOKIE_PATH' ) ) {
define( 'PLUGINS_COOKIE_PATH', preg_replace( '|https?://[^/]+|i', '', WP_PLUGIN_URL ) );
define( 'PLUGINS_COOKIE_PATH', (string) preg_replace( '|https?://[^/]+|i', '', WP_PLUGIN_URL ) );
}

/**
Expand Down
195 changes: 143 additions & 52 deletions tests/phpstan/base.neon
Original file line number Diff line number Diff line change
Expand Up @@ -112,59 +112,150 @@ parameters:
inferPrivatePropertyTypeFromConstructor: true

# Constants whose values may differ depending on the install.
#
# Each type is what core expects: the values core defines and documents, and that its reads are designed for.
# Where sites, plugins, or themes are known to define wider values, a comment notes them. Many configs pass
# getenv() through unconverted, which yields false when the variable is unset and otherwise a string (where
# 'false' and 'off' are truthy). When core starts handling a value outside a type, widen the type along with it.
dynamicConstantNames:
- ALLOW_SUBDIRECTORY_INSTALL
- AUTH_SALT
- AUTOMATIC_UPDATER_DISABLED
- BACKGROUND_COLOR
- BACKGROUND_IMAGE
- COOKIEPATH
- CUSTOM_TAGS
- DB_COLLATE
- DISALLOW_FILE_EDIT
- DISALLOW_UNFILTERED_HTML
- DOING_AJAX
- DOING_CRON
- EMPTY_TRASH_DAYS
- FORCE_SSL_LOGIN
- HEADER_IMAGE
- HEADER_IMAGE_HEIGHT
- HEADER_IMAGE_WIDTH
- HEADER_TEXTCOLOR
- LOGGED_IN_KEY
- LOGGED_IN_SALT
- MEDIA_TRASH
- MULTISITE
- NO_HEADER_TEXT
- NOBLOGREDIRECT
- REST_REQUEST
- SAVEQUERIES
- SCRIPT_DEBUG
- SECRET_KEY
- SECRET_SALT
- SHORTINIT
- SITECOOKIEPATH
- UPLOADBLOGSDIR
- WP_ALLOW_MULTISITE
- WP_CACHE
- WP_DEBUG
- WP_DEBUG_DISPLAY
- WP_DEBUG_LOG
- WP_DEVELOPMENT_MODE
- WP_INSTALLING
- WP_INSTALLING_NETWORK
- WP_LANG_DIR
- WP_NETWORK_ADMIN
- WP_POST_REVISIONS
- WP_REPAIRING
- WP_SITEURL
- WP_USE_THEMES
- WP_USER_ADMIN
- WPLANG
- WPMU_ACCEL_REDIRECT
- WPMU_PLUGIN_DIR
- WPMU_SENDFILE
- XMLRPC_REQUEST
ABSPATH: 'non-empty-string'
ADMIN_COOKIE_PATH: 'string'
ALLOW_SUBDIRECTORY_INSTALL: 'bool'
AUTH_COOKIE: 'non-empty-string'
# Some configs define '', and ones that read environment variables also yield false or null when unset. Core
# treats a falsy value as missing.
AUTH_KEY: 'non-empty-string'
# Some configs define '', and ones that read environment variables also yield false or null when unset. Core
# treats a falsy value as missing.
AUTH_SALT: 'non-empty-string'
# Also defined from getenv() strings, and as 'minor' (confused with WP_AUTO_UPDATE_CORE). Core checks truthiness.
AUTOMATIC_UPDATER_DISABLED: 'bool'
# Also defined as numeric strings from options, and as 0 or false, which turn off autosaves in the block editor.
AUTOSAVE_INTERVAL: 'positive-int'
BACKGROUND_COLOR: 'string'
BACKGROUND_IMAGE: 'string'
BLOGUPLOADDIR: 'non-empty-string'
# Core defined false before r58011, and many configs still do. setcookie() treats it like ''.
COOKIE_DOMAIN: 'string'
# Raw getenv() configs also yield false when unset. Core only concatenates it, so false acts like ''.
COOKIEHASH: 'string'
COOKIEPATH: 'string'
CUSTOM_TAGS: 'bool'
# Raw getenv() configs also yield false. Core treats any falsy value as empty.
DB_COLLATE: 'string'
# Also defined as 1, 'true', and getenv() strings. Core checks truthiness.
DISALLOW_FILE_EDIT: 'bool'
DISALLOW_UNFILTERED_HTML: 'bool'
# Some plugins define 1. Core checks truthiness.
DOING_AJAX: 'bool'
# Some plugins define 1. Core checks truthiness.
DOING_CRON: 'bool'
# Also defined as false (treated like 0), -1, and getenv() strings. Where core multiplies it by DAY_IN_SECONDS,
# an empty string fatals on PHP 8 (on PHP 7.4 it acts like 0), and a negative number makes wp_scheduled_delete()
# purge all trash.
EMPTY_TRASH_DAYS: 'non-negative-int'
# Also defined as getenv() strings. force_ssl_admin() casts it with (bool).
FORCE_SSL_ADMIN: 'bool'
FORCE_SSL_LOGIN: 'bool'
FS_CHMOD_DIR: 'int'
FS_CHMOD_FILE: 'int'
# Also defined as numeric strings from options. On PHP 8, ftp_connect() throws for a value below 1.
FS_CONNECT_TIMEOUT: 'positive-int'
FS_TIMEOUT: 'positive-int'
HEADER_IMAGE: 'string'
# Some legacy themes define a numeric string, such as '200'. Core casts it with (int).
HEADER_IMAGE_HEIGHT: 'int'
# Some legacy themes define a numeric string, such as '940'. Core casts it with (int).
HEADER_IMAGE_WIDTH: 'int'
HEADER_TEXTCOLOR: 'string'
LOGGED_IN_COOKIE: 'non-empty-string'
# Some configs define '', and ones that read environment variables also yield false or null when unset. Core
# treats a falsy value as missing.
LOGGED_IN_KEY: 'non-empty-string'
# Some configs define '', and ones that read environment variables also yield false or null when unset. Core
# treats a falsy value as missing.
LOGGED_IN_SALT: 'non-empty-string'
MEDIA_TRASH: 'bool'
# Configs generated by some backup plugins define 1 or '1'. Core checks truthiness.
MULTISITE: 'bool'
MUPLUGINDIR: 'non-empty-string'
NO_HEADER_TEXT: 'bool'
# The WordPress MU sample config defined '', which disables the 404 redirect, but for an unknown site on a
# subdomain network core then sends an empty Location header.
NOBLOGREDIRECT: 'non-empty-string'
# Some configs define '', and ones that read environment variables also yield false or null when unset. Core
# treats a falsy value as missing.
NONCE_KEY: 'non-empty-string'
# Some configs define '', and ones that read environment variables also yield false or null when unset. Core
# treats a falsy value as missing.
NONCE_SALT: 'non-empty-string'
PASS_COOKIE: 'non-empty-string'
PLUGINDIR: 'non-empty-string'
PLUGINS_COOKIE_PATH: 'string'
RECOVERY_MODE_COOKIE: 'non-empty-string'
REST_REQUEST: 'bool'
# Also defined as 1 or 0, and as getenv() strings. Core checks truthiness.
SAVEQUERIES: 'bool'
# Also defined as getenv() strings. Core checks truthiness.
SCRIPT_DEBUG: 'bool'
SECRET_KEY: 'non-empty-string'
SECRET_SALT: 'non-empty-string'
SECURE_AUTH_COOKIE: 'non-empty-string'
# Some configs define '', and ones that read environment variables also yield false or null when unset. Core
# treats a falsy value as missing.
SECURE_AUTH_KEY: 'non-empty-string'
# Some configs define '', and ones that read environment variables also yield false or null when unset. Core
# treats a falsy value as missing.
SECURE_AUTH_SALT: 'non-empty-string'
# Some code defines 1 or 'true'. Core checks truthiness.
SHORTINIT: 'bool'
SITECOOKIEPATH: 'string'
STYLESHEETPATH: 'string'
TEMPLATEPATH: 'string'
TEST_COOKIE: 'non-empty-string'
UPLOADBLOGSDIR: 'non-empty-string'
USER_COOKIE: 'non-empty-string'
WP_ALLOW_MULTISITE: 'bool'
WP_CACHE: 'bool'
# Raw getenv() configs also yield false, which core concatenates as '', so that plugins load from /plugins.
WP_CONTENT_DIR: 'non-empty-string'
WP_CONTENT_URL: 'non-empty-string'
WP_CRON_LOCK_TIMEOUT: 'positive-int'
# Also defined as 0 or 1, and as getenv() strings. Core checks truthiness.
WP_DEBUG: 'bool'
# Also defined as 0, and as getenv() strings. Core checks truthiness, and null leaves display_errors as is.
WP_DEBUG_DISPLAY: 'bool|null'
# A bool or a log file path. getenv() configs also define '' or '0' (both disable logging) and 'false' (logs
# to a file named "false").
WP_DEBUG_LOG: 'bool|non-falsy-string'
# getenv() configs also yield '' or false, for which wp_get_theme() returns the active theme instead.
WP_DEFAULT_THEME: 'non-empty-string'
# Some sites define true. wp_get_development_mode() treats an unknown value as '', but Site Health shows it as is.
WP_DEVELOPMENT_MODE: '''core''|''plugin''|''theme''|''all''|'''''
WP_INSTALLING: 'bool'
WP_INSTALLING_NETWORK: 'bool'
WP_LANG_DIR: 'non-empty-string'
WP_MAX_MEMORY_LIMIT: 'string'
WP_MEMORY_LIMIT: 'string'
WP_NETWORK_ADMIN: 'bool'
WP_PLUGIN_DIR: 'non-empty-string'
WP_PLUGIN_URL: 'non-empty-string'
# Also defined as numeric strings, from options or getenv(). Core casts with (int).
WP_POST_REVISIONS: 'bool|int'
WP_REPAIRING: 'bool'
# Raw getenv() configs also yield false when unset.
WP_SITEURL: 'string'
WP_START_TIMESTAMP: 'float'
# Some code defines 0, or the string 'false' (which core treats as true).
WP_USE_THEMES: 'bool'
WP_USER_ADMIN: 'bool'
WPLANG: 'string'
WPMU_ACCEL_REDIRECT: 'bool'
WPMU_PLUGIN_DIR: 'non-empty-string'
WPMU_PLUGIN_URL: 'non-empty-string'
WPMU_SENDFILE: 'bool'
# Some plugins define 1. Core checks truthiness.
XMLRPC_REQUEST: 'bool'

# What directories and files should be scanned.
paths:
Expand Down
10 changes: 0 additions & 10 deletions tests/phpstan/baselines/function.impossibleType.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,8 @@

parameters:
ignoreErrors:
-
message: '#^Call to function is_string\(\) with bool will always evaluate to false\.$#'
identifier: function.impossibleType
count: 1
path: ../../../src/wp-admin/includes/class-wp-debug-data.php
-
message: '#^Call to function is_wp_error\(\) with 0\|0\.0\|''''\|''0''\|array\{\}\|false\|null will always evaluate to false\.$#'
identifier: function.impossibleType
count: 2
path: ../../../src/wp-admin/update.php
-
message: '#^Call to function is_string\(\) with bool will always evaluate to false\.$#'
identifier: function.impossibleType
count: 1
path: ../../../src/wp-includes/load.php
28 changes: 14 additions & 14 deletions tests/phpstan/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
* Mocks the constant initiation that would normally happen in wp-includes/wp-settings.php.
*
* Loaded as a `bootstrapFile` by PHPStan; see `base.neon`.
*
* For a constant listed under `dynamicConstantNames` in `base.neon`, a define() here only
* declares the constant, since PHPStan does not discover the ones core defines inside a
* function, and reports reads of them outside a defined() check as not found. Its value
* is ignored, and the type comes from `base.neon` instead.
*/

/*
* A fixed, fictional path rather than the real checkout location. PHPStan resolves
* no files through this constant, and deriving it from __DIR__ embeds the developer's
* own path in error messages, making output differ between machines.
*/
define( 'ABSPATH', '/var/www/html/' );

/** @see wp_initial_constants() */
Expand All @@ -23,7 +23,7 @@
define( 'EB_IN_BYTES', 1024 * PB_IN_BYTES );
define( 'ZB_IN_BYTES', 1024 * EB_IN_BYTES );
define( 'YB_IN_BYTES', 1024 * ZB_IN_BYTES );
define( 'WP_START_TIMESTAMP', 1700000000.0 ); // Fixed rather than microtime( true ), whose value would differ on every run.
define( 'WP_START_TIMESTAMP', 1700000000.0 );
define( 'WP_MEMORY_LIMIT', '40M' );
define( 'WP_MAX_MEMORY_LIMIT', '256M' );
define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' );
Expand Down Expand Up @@ -63,14 +63,14 @@

/** @see wp_cookie_constants() */
define( 'COOKIEHASH', '' );
define( 'USER_COOKIE', '' );
define( 'PASS_COOKIE', '' );
define( 'AUTH_COOKIE', '' );
define( 'SECURE_AUTH_COOKIE', '' );
define( 'LOGGED_IN_COOKIE', '' );
define( 'TEST_COOKIE', '' );
define( 'USER_COOKIE', 'wordpressuser_' );
define( 'PASS_COOKIE', 'wordpresspass_' );
define( 'AUTH_COOKIE', 'wordpress_' );
define( 'SECURE_AUTH_COOKIE', 'wordpress_sec_' );
define( 'LOGGED_IN_COOKIE', 'wordpress_logged_in_' );
define( 'TEST_COOKIE', 'wordpress_test_cookie' );
define( 'PLUGINS_COOKIE_PATH', '' );
define( 'RECOVERY_MODE_COOKIE', '' );
define( 'RECOVERY_MODE_COOKIE', 'wordpress_rec_' );

/** @see wp_ssl_constants() */
define( 'FORCE_SSL_LOGIN', false );
Expand All @@ -92,7 +92,7 @@
define( 'WPMU_ACCEL_REDIRECT', false );

/** @see ms_load_current_site_and_network() */
define( 'NOBLOGREDIRECT', '' );
define( 'NOBLOGREDIRECT', '%siteurl%' );

/** @see ms_upload_constants() */
define( 'UPLOADBLOGSDIR', 'wp-content/blogs.dir' );
Expand Down
4 changes: 2 additions & 2 deletions tests/phpunit/includes/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,8 @@
*/
define( 'DISABLE_WP_CRON', true );

define( 'WP_MEMORY_LIMIT', -1 );
define( 'WP_MAX_MEMORY_LIMIT', -1 );
define( 'WP_MEMORY_LIMIT', '-1' );
define( 'WP_MAX_MEMORY_LIMIT', '-1' );

define( 'REST_TESTS_IMPOSSIBLY_HIGH_NUMBER', 99999999 );

Expand Down
Loading