$awareness Serializable awareness state.
- * @return bool True on success, false on failure.
- */
- public function set_awareness_state( string $room, array $awareness ): bool;
-}
diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php
index 0f102d1ea80ee..5395997ecd0ef 100644
--- a/src/wp-includes/comment.php
+++ b/src/wp-includes/comment.php
@@ -2806,7 +2806,7 @@ function wp_defer_comment_counting( $defer = null ) {
* @param int|null $post_id Post ID.
* @param bool $do_deferred Optional. Whether to process previously deferred
* post comment counts. Default false.
- * @return bool|void True on success, false on failure or if post with ID does
+ * @return bool|null True on success, false on failure or if post with ID does
* not exist.
*/
function wp_update_comment_count( $post_id, $do_deferred = false ) {
@@ -2831,6 +2831,7 @@ function wp_update_comment_count( $post_id, $do_deferred = false ) {
} elseif ( $post_id ) {
return wp_update_comment_count_now( $post_id );
}
+ return null;
}
/**
@@ -3313,13 +3314,13 @@ function privacy_ping_filter( $sites ) {
* @param string $title Title of post.
* @param string $excerpt Excerpt of post.
* @param int $post_id Post ID.
- * @return int|false|void Database query from update.
+ * @return int|false|null Database query from update.
*/
function trackback( $trackback_url, $title, $excerpt, $post_id ) {
global $wpdb;
if ( empty( $trackback_url ) ) {
- return;
+ return null;
}
$options = array();
@@ -3334,7 +3335,7 @@ function trackback( $trackback_url, $title, $excerpt, $post_id ) {
$response = wp_safe_remote_post( $trackback_url, $options );
if ( is_wp_error( $response ) ) {
- return;
+ return null;
}
$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET pinged = CONCAT(pinged, '\n', %s) WHERE ID = %d", $trackback_url, $post_id ) );
diff --git a/src/wp-includes/connectors.php b/src/wp-includes/connectors.php
index bdc585723aaf1..63e018074fd58 100644
--- a/src/wp-includes/connectors.php
+++ b/src/wp-includes/connectors.php
@@ -43,33 +43,39 @@ function wp_is_connector_registered( string $id ): bool {
* @type string $name The connector's display name.
* @type string $description The connector's description.
* @type string $logo_url Optional. URL to the connector's logo image.
- * @type string $type The connector type. Currently, only 'ai_provider' is supported.
+ * @type string $type The connector type, e.g. 'ai_provider'.
* @type array $authentication {
* Authentication configuration. When method is 'api_key', includes
- * credentials_url and setting_name. When 'none', only method is present.
+ * credentials_url, setting_name, and optionally constant_name and
+ * env_var_name. When 'none', only method is present.
*
* @type string $method The authentication method: 'api_key' or 'none'.
* @type string $credentials_url Optional. URL where users can obtain API credentials.
* @type string $setting_name Optional. The setting name for the API key.
+ * @type string $constant_name Optional. PHP constant name for the API key.
+ * @type string $env_var_name Optional. Environment variable name for the API key.
* }
* @type array $plugin {
* Optional. Plugin data for install/activate UI.
*
- * @type string $slug The WordPress.org plugin slug.
+ * @type string $file The plugin's main file path relative to the plugins
+ * directory (e.g. 'my-plugin/my-plugin.php' or 'hello.php').
* }
* }
* @phpstan-return ?array{
* name: non-empty-string,
* description: non-empty-string,
* logo_url?: non-empty-string,
- * type: 'ai_provider',
+ * type: non-empty-string,
* authentication: array{
* method: 'api_key'|'none',
* credentials_url?: non-empty-string,
- * setting_name?: non-empty-string
+ * setting_name?: non-empty-string,
+ * constant_name?: non-empty-string,
+ * env_var_name?: non-empty-string
* },
* plugin?: array{
- * slug: non-empty-string
+ * file: non-empty-string
* }
* }
*/
@@ -98,19 +104,23 @@ function wp_get_connector( string $id ): ?array {
* @type string $name The connector's display name.
* @type string $description The connector's description.
* @type string $logo_url Optional. URL to the connector's logo image.
- * @type string $type The connector type. Currently, only 'ai_provider' is supported.
+ * @type string $type The connector type, e.g. 'ai_provider'.
* @type array $authentication {
* Authentication configuration. When method is 'api_key', includes
- * credentials_url and setting_name. When 'none', only method is present.
+ * credentials_url, setting_name, and optionally constant_name and
+ * env_var_name. When 'none', only method is present.
*
* @type string $method The authentication method: 'api_key' or 'none'.
* @type string $credentials_url Optional. URL where users can obtain API credentials.
* @type string $setting_name Optional. The setting name for the API key.
+ * @type string $constant_name Optional. PHP constant name for the API key.
+ * @type string $env_var_name Optional. Environment variable name for the API key.
* }
* @type array $plugin {
* Optional. Plugin data for install/activate UI.
*
- * @type string $slug The WordPress.org plugin slug.
+ * @type string $file The plugin's main file path relative to the plugins
+ * directory (e.g. 'my-plugin/my-plugin.php' or 'hello.php').
* }
* }
* }
@@ -118,14 +128,16 @@ function wp_get_connector( string $id ): ?array {
* name: non-empty-string,
* description: non-empty-string,
* logo_url?: non-empty-string,
- * type: 'ai_provider',
+ * type: non-empty-string,
* authentication: array{
* method: 'api_key'|'none',
* credentials_url?: non-empty-string,
- * setting_name?: non-empty-string
+ * setting_name?: non-empty-string,
+ * constant_name?: non-empty-string,
+ * env_var_name?: non-empty-string
* },
* plugin?: array{
- * slug: non-empty-string
+ * file: non-empty-string
* }
* }>
*/
@@ -216,10 +228,10 @@ function _wp_connectors_init(): void {
* Example — overriding metadata on an auto-discovered connector:
*
* add_action( 'wp_connectors_init', function ( WP_Connector_Registry $registry ) {
- * if ( $registry->is_registered( 'openai' ) ) {
- * $connector = $registry->unregister( 'openai' );
- * $connector['description'] = __( 'Custom description for OpenAI.', 'my-plugin' );
- * $registry->register( 'openai', $connector );
+ * if ( $registry->is_registered( 'anthropic' ) ) {
+ * $connector = $registry->unregister( 'anthropic' );
+ * $connector['description'] = __( 'Custom description for Anthropic.', 'my-plugin' );
+ * $registry->register( 'anthropic', $connector );
* }
* } );
*
@@ -246,7 +258,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re
'description' => __( 'Text generation with Claude.' ),
'type' => 'ai_provider',
'plugin' => array(
- 'slug' => 'ai-provider-for-anthropic',
+ 'file' => 'ai-provider-for-anthropic/plugin.php',
),
'authentication' => array(
'method' => 'api_key',
@@ -258,7 +270,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re
'description' => __( 'Text and image generation with Gemini and Imagen.' ),
'type' => 'ai_provider',
'plugin' => array(
- 'slug' => 'ai-provider-for-google',
+ 'file' => 'ai-provider-for-google/plugin.php',
),
'authentication' => array(
'method' => 'api_key',
@@ -270,7 +282,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re
'description' => __( 'Text and image generation with GPT and Dall-E.' ),
'type' => 'ai_provider',
'plugin' => array(
- 'slug' => 'ai-provider-for-openai',
+ 'file' => 'ai-provider-for-openai/plugin.php',
),
'authentication' => array(
'method' => 'api_key',
@@ -335,6 +347,26 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re
// Register all default connectors directly on the registry.
foreach ( $defaults as $id => $args ) {
+ if ( 'api_key' === $args['authentication']['method'] ) {
+ $sanitized_id = str_replace( '-', '_', $id );
+
+ if ( ! isset( $args['authentication']['setting_name'] ) ) {
+ $args['authentication']['setting_name'] = "connectors_ai_{$sanitized_id}_api_key";
+ }
+
+ // All AI providers use the {CONSTANT_CASE_ID}_API_KEY naming convention.
+ if ( ! isset( $args['authentication']['constant_name'] ) || ! isset( $args['authentication']['env_var_name'] ) ) {
+ $constant_case_key = strtoupper( preg_replace( '/([a-z])([A-Z])/', '$1_$2', $sanitized_id ) ) . '_API_KEY';
+
+ if ( ! isset( $args['authentication']['constant_name'] ) ) {
+ $args['authentication']['constant_name'] = $constant_case_key;
+ }
+
+ if ( ! isset( $args['authentication']['env_var_name'] ) ) {
+ $args['authentication']['env_var_name'] = $constant_case_key;
+ }
+ }
+ }
$registry->register( $id, $args );
}
}
@@ -357,35 +389,32 @@ function _wp_connectors_mask_api_key( string $key ): string {
}
/**
- * Determines the source of an API key for a given provider.
+ * Determines the source of an API key for a given connector.
*
* Checks in order: environment variable, PHP constant, database.
- * Uses the same naming convention as the WP AI Client ProviderRegistry.
+ * Environment variable and constant are only checked when their
+ * respective names are provided.
*
* @since 7.0.0
* @access private
*
- * @param string $provider_id The provider ID (e.g., 'openai', 'anthropic', 'google').
- * @param string $setting_name The option name for the API key (e.g., 'connectors_ai_openai_api_key').
+ * @param string $setting_name The option name for the API key (e.g., 'connectors_spam_filtering_my_plugin_api_key').
+ * @param string $env_var_name Optional. Environment variable name to check (e.g., 'MY_PLUGIN_API_KEY').
+ * @param string $constant_name Optional. PHP constant name to check (e.g., 'MY_PLUGIN_API_KEY').
* @return string The key source: 'env', 'constant', 'database', or 'none'.
*/
-function _wp_connectors_get_api_key_source( string $provider_id, string $setting_name ): string {
- // Convert provider ID to CONSTANT_CASE for env var name.
- // e.g., 'openai' -> 'OPENAI', 'anthropic' -> 'ANTHROPIC'.
- $constant_case_id = strtoupper(
- preg_replace( '/([a-z])([A-Z])/', '$1_$2', str_replace( '-', '_', $provider_id ) )
- );
- $env_var_name = "{$constant_case_id}_API_KEY";
-
+function _wp_connectors_get_api_key_source( string $setting_name, string $env_var_name = '', string $constant_name = '' ): string {
// Check environment variable first.
- $env_value = getenv( $env_var_name );
- if ( false !== $env_value && '' !== $env_value ) {
- return 'env';
+ if ( '' !== $env_var_name ) {
+ $env_value = getenv( $env_var_name );
+ if ( false !== $env_value && '' !== $env_value ) {
+ return 'env';
+ }
}
// Check PHP constant.
- if ( defined( $env_var_name ) ) {
- $const_value = constant( $env_var_name );
+ if ( '' !== $constant_name && defined( $constant_name ) ) {
+ $const_value = constant( $constant_name );
if ( is_string( $const_value ) && '' !== $const_value ) {
return 'constant';
}
@@ -470,7 +499,7 @@ function _wp_connectors_rest_settings_dispatch( WP_REST_Response $response, WP_R
foreach ( wp_get_connectors() as $connector_id => $connector_data ) {
$auth = $connector_data['authentication'];
- if ( 'ai_provider' !== $connector_data['type'] || 'api_key' !== $auth['method'] || empty( $auth['setting_name'] ) ) {
+ if ( 'api_key' !== $auth['method'] || empty( $auth['setting_name'] ) ) {
continue;
}
@@ -481,8 +510,9 @@ function _wp_connectors_rest_settings_dispatch( WP_REST_Response $response, WP_R
$value = $data[ $setting_name ];
- // On update, validate the key before masking.
- if ( $is_update && is_string( $value ) && '' !== $value ) {
+ // On update, validate AI provider keys before masking.
+ // Non-AI connectors accept keys as-is; the service plugin handles its own validation.
+ if ( $is_update && is_string( $value ) && '' !== $value && 'ai_provider' === $connector_data['type'] ) {
if ( true !== _wp_connectors_is_ai_api_key_valid( $value, $connector_id ) ) {
update_option( $setting_name, '' );
$data[ $setting_name ] = '';
@@ -508,16 +538,22 @@ function _wp_connectors_rest_settings_dispatch( WP_REST_Response $response, WP_R
* @access private
*/
function _wp_register_default_connector_settings(): void {
- $ai_registry = AiClient::defaultRegistry();
+ $ai_registry = AiClient::defaultRegistry();
+ $registered_settings = get_registered_settings();
foreach ( wp_get_connectors() as $connector_id => $connector_data ) {
$auth = $connector_data['authentication'];
- if ( 'ai_provider' !== $connector_data['type'] || 'api_key' !== $auth['method'] || empty( $auth['setting_name'] ) ) {
+ if ( 'api_key' !== $auth['method'] || empty( $auth['setting_name'] ) ) {
+ continue;
+ }
+
+ // Skip if the setting is already registered (e.g. by an owning plugin).
+ if ( isset( $registered_settings[ $auth['setting_name'] ] ) ) {
continue;
}
- // Skip registering the setting if the provider is not in the registry.
- if ( ! $ai_registry->hasProvider( $connector_id ) ) {
+ // For AI providers, skip if the provider is not in the AI Client registry.
+ if ( 'ai_provider' === $connector_data['type'] && ! $ai_registry->hasProvider( $connector_id ) ) {
continue;
}
@@ -527,13 +563,13 @@ function _wp_register_default_connector_settings(): void {
array(
'type' => 'string',
'label' => sprintf(
- /* translators: %s: AI provider name. */
+ /* translators: %s: Connector name. */
__( '%s API Key' ),
$connector_data['name']
),
'description' => sprintf(
- /* translators: %s: AI provider name. */
- __( 'API key for the %s AI provider.' ),
+ /* translators: %s: Connector name. */
+ __( 'API key for the %s connector.' ),
$connector_data['name']
),
'default' => '',
@@ -569,7 +605,7 @@ function _wp_connectors_pass_default_keys_to_ai_client(): void {
}
// Skip if the key is already provided via env var or constant.
- $key_source = _wp_connectors_get_api_key_source( $connector_id, $auth['setting_name'] );
+ $key_source = _wp_connectors_get_api_key_source( $auth['setting_name'], $auth['env_var_name'] ?? '', $auth['constant_name'] ?? '' );
if ( 'env' === $key_source || 'constant' === $key_source ) {
continue;
}
@@ -602,15 +638,9 @@ function _wp_connectors_pass_default_keys_to_ai_client(): void {
function _wp_connectors_get_connector_script_module_data( array $data ): array {
$registry = AiClient::defaultRegistry();
- // Build a slug-to-file map for plugin installation status.
- if ( ! function_exists( 'get_plugins' ) ) {
+ if ( ! function_exists( 'is_plugin_active' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
- $plugin_files_by_slug = array();
- foreach ( array_keys( get_plugins() ) as $plugin_file ) {
- $slug = str_contains( $plugin_file, '/' ) ? dirname( $plugin_file ) : str_replace( '.php', '', $plugin_file );
- $plugin_files_by_slug[ $slug ] = $plugin_file;
- }
$connectors = array();
foreach ( wp_get_connectors() as $connector_id => $connector_data ) {
@@ -620,11 +650,17 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array {
if ( 'api_key' === $auth['method'] ) {
$auth_out['settingName'] = $auth['setting_name'] ?? '';
$auth_out['credentialsUrl'] = $auth['credentials_url'] ?? null;
- $auth_out['keySource'] = _wp_connectors_get_api_key_source( $connector_id, $auth['setting_name'] ?? '' );
- try {
- $auth_out['isConnected'] = $registry->hasProvider( $connector_id ) && $registry->isProviderConfigured( $connector_id );
- } catch ( Exception $e ) {
- $auth_out['isConnected'] = false;
+ $key_source = _wp_connectors_get_api_key_source( $auth['setting_name'] ?? '', $auth['env_var_name'] ?? '', $auth['constant_name'] ?? '' );
+ $auth_out['keySource'] = $key_source;
+
+ if ( 'ai_provider' === $connector_data['type'] ) {
+ try {
+ $auth_out['isConnected'] = $registry->hasProvider( $connector_id ) && $registry->isProviderConfigured( $connector_id );
+ } catch ( Exception $e ) {
+ $auth_out['isConnected'] = false;
+ }
+ } else {
+ $auth_out['isConnected'] = 'none' !== $key_source;
}
}
@@ -636,15 +672,13 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array {
'authentication' => $auth_out,
);
- if ( ! empty( $connector_data['plugin']['slug'] ) ) {
- $plugin_slug = $connector_data['plugin']['slug'];
- $plugin_file = $plugin_files_by_slug[ $plugin_slug ] ?? null;
-
- $is_installed = null !== $plugin_file;
- $is_activated = $is_installed && is_plugin_active( $plugin_file );
+ if ( ! empty( $connector_data['plugin']['file'] ) ) {
+ $file = $connector_data['plugin']['file'];
+ $is_installed = file_exists( wp_normalize_path( WP_PLUGIN_DIR . '/' . $file ) );
+ $is_activated = $is_installed && is_plugin_active( $file );
$connector_out['plugin'] = array(
- 'slug' => $plugin_slug,
+ 'file' => $file,
'isInstalled' => $is_installed,
'isActivated' => $is_activated,
);
diff --git a/src/wp-includes/css/media-views.css b/src/wp-includes/css/media-views.css
index 1b3c6edd7678f..f78a946c260f7 100644
--- a/src/wp-includes/css/media-views.css
+++ b/src/wp-includes/css/media-views.css
@@ -56,7 +56,7 @@
.media-frame a:focus {
border-radius: 2px;
box-shadow: 0 0 0 var(--wp-admin-border-width-focus, 1.5px) var(--wp-admin-theme-color, #3858e9);
- color: #043959;
+ color: var(--wp-admin-theme-color-darker-20, #183ad6);
/* Only visible in Windows High Contrast mode */
outline: 2px solid transparent;
}
@@ -244,13 +244,13 @@
.media-modal-close:hover,
.media-modal-close:active {
- color: #135e96;
+ color: var(--wp-admin-theme-color, #3858e9);
}
.media-modal-close:focus {
- color: #135e96;
- border-color: #4f94d4;
- box-shadow: 0 0 3px rgba(34, 113, 177, 0.8);
+ color: var(--wp-admin-theme-color, #3858e9);
+ border-color: var(--wp-admin-theme-color, #3858e9);
+ box-shadow: 0 0 3px rgba(var(--wp-admin-theme-color--rgb, 56, 88, 233), 0.8);
/* Only visible in Windows High Contrast mode */
outline: 2px solid transparent;
}
@@ -673,7 +673,7 @@
font-size: 14px;
line-height: 1.28571428;
background: transparent;
- color: #2271b1;
+ color: var(--wp-admin-theme-color, #3858e9);
text-align: left;
text-decoration: none;
cursor: pointer;
@@ -684,7 +684,7 @@
}
.media-menu .media-menu-item:active {
- color: #2271b1;
+ color: var(--wp-admin-theme-color, #3858e9);
outline: none;
}
@@ -696,7 +696,7 @@
.media-menu .media-menu-item:focus {
box-shadow: 0 0 0 var(--wp-admin-border-width-focus, 1.5px) var(--wp-admin-theme-color, #3858e9);
- color: #043959;
+ color: var(--wp-admin-theme-color-darker-20, #183ad6);
/* Only visible in Windows High Contrast mode */
outline: 2px solid transparent;
}
@@ -739,7 +739,7 @@
.media-router .media-menu-item:hover,
.media-router .media-menu-item:active {
- color: #2271b1;
+ color: var(--wp-admin-theme-color, #3858e9);
}
.media-router .active,
@@ -749,7 +749,7 @@
.media-router .media-menu-item:focus {
box-shadow: 0 0 0 var(--wp-admin-border-width-focus, 1.5px) var(--wp-admin-theme-color, #3858e9);
- color: #043959;
+ color: var(--wp-admin-theme-color-darker-20, #183ad6);
/* Only visible in Windows High Contrast mode */
outline: 2px solid transparent;
z-index: 1;
@@ -1321,8 +1321,8 @@
}
.uploader-inline .close:focus {
- outline: 1px solid #4f94d4;
- box-shadow: 0 0 3px rgba(34, 113, 177, 0.8);
+ outline: 1px solid var(--wp-admin-theme-color, #3858e9);
+ box-shadow: 0 0 3px rgba(var(--wp-admin-theme-color--rgb, 56, 88, 233), 0.8);
}
.attachments-browser.hide-sidebar .attachments,
@@ -1409,7 +1409,7 @@
height: 10px;
min-width: 20px;
width: 0;
- background: #2271b1;
+ background: var(--wp-admin-theme-color, #3858e9);
border-radius: 10px;
transition: width 300ms;
}
@@ -1527,7 +1527,7 @@
.uploader-window,
.wp-editor-wrap .uploader-editor.droppable {
- background: rgba(10, 75, 120, 0.9);
+ background-color: rgba(var(--wp-admin-theme-color--rgb, 56, 88, 233), 0.9);
}
.uploader-window-content,
@@ -1688,13 +1688,13 @@
margin: 1px 8px 1px -8px;
line-height: 1.4;
border-right: 1px solid #dcdcde;
- color: #2271b1;
+ color: var(--wp-admin-theme-color, #3858e9);
text-decoration: none;
}
.media-selection .button-link:hover,
.media-selection .button-link:focus {
- color: #135e96;
+ color: var(--wp-admin-theme-color-darker-20, #183ad6);
}
.media-selection .button-link:last-child {
@@ -1752,7 +1752,7 @@
.wp-core-ui .media-selection .attachment.details:focus {
box-shadow:
0 0 0 1px #fff,
- 0 0 2px 3px #4f94d4;
+ 0 0 2px 3px var(--wp-admin-theme-color, #3858e9);
/* Only visible in Windows High Contrast mode */
outline: 2px solid transparent;
}
@@ -1764,7 +1764,7 @@
.wp-core-ui .media-selection .attachment.details {
box-shadow:
0 0 0 1px #fff,
- 0 0 0 3px #2271b1;
+ 0 0 0 3px var(--wp-admin-theme-color, #3858e9);
}
.media-selection:after {
@@ -2044,7 +2044,7 @@
margin: 0;
padding: 0;
background: transparent;
- color: #2271b1;
+ color: var(--wp-admin-theme-color, #3858e9);
font-size: 20px;
line-height: 1;
cursor: pointer;
@@ -2053,9 +2053,9 @@
}
.wp-core-ui.media-modal .image-editor .imgedit-help-toggle:focus {
- color: #2271b1;
- border-color: #2271b1;
- box-shadow: 0 0 0 1px #2271b1;
+ color: var(--wp-admin-theme-color, #3858e9);
+ border-color: var(--wp-admin-theme-color, #3858e9);
+ box-shadow: 0 0 0 1px var(--wp-admin-theme-color, #3858e9);
/* Only visible in Windows High Contrast mode */
outline: 2px solid transparent;
}
diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php
index 4b6d9de25fa11..17c1695e6d72d 100644
--- a/src/wp-includes/default-filters.php
+++ b/src/wp-includes/default-filters.php
@@ -454,6 +454,7 @@
add_action( 'importer_scheduled_cleanup', 'wp_delete_attachment' );
add_action( 'upgrader_scheduled_cleanup', 'wp_delete_attachment' );
add_action( 'delete_expired_transients', 'delete_expired_transients' );
+add_action( 'wp_delete_old_collaboration_data', 'wp_delete_old_collaboration_data' );
// Navigation menu actions.
add_action( 'delete_post', '_wp_delete_post_menu_item' );
diff --git a/src/wp-includes/embed.php b/src/wp-includes/embed.php
index dd21b6cf22fe1..3fb8968c7c62c 100644
--- a/src/wp-includes/embed.php
+++ b/src/wp-includes/embed.php
@@ -739,10 +739,13 @@ function get_oembed_response_data_rich( $data, $post, $width, $height ) {
}
if ( $thumbnail_id ) {
- list( $thumbnail_url, $thumbnail_width, $thumbnail_height ) = wp_get_attachment_image_src( $thumbnail_id, array( $width, 0 ) );
- $data['thumbnail_url'] = $thumbnail_url;
- $data['thumbnail_width'] = $thumbnail_width;
- $data['thumbnail_height'] = $thumbnail_height;
+ $thumbnail_src = wp_get_attachment_image_src( $thumbnail_id, array( $width, 0 ) );
+
+ if ( is_array( $thumbnail_src ) ) {
+ $data['thumbnail_url'] = $thumbnail_src[0];
+ $data['thumbnail_width'] = $thumbnail_src[1];
+ $data['thumbnail_height'] = $thumbnail_src[2];
+ }
}
return $data;
diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php
index 2b32b5aafb05d..498d676f5c20f 100644
--- a/src/wp-includes/formatting.php
+++ b/src/wp-includes/formatting.php
@@ -6235,7 +6235,7 @@ function url_shorten( $url, $length = 35 ) {
* @since 3.4.0
*
* @param string $color
- * @return string|void
+ * @return string|null The sanitized hex color, or null if invalid.
*/
function sanitize_hex_color( $color ) {
if ( '' === $color ) {
@@ -6246,6 +6246,7 @@ function sanitize_hex_color( $color ) {
if ( preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) {
return $color;
}
+ return null;
}
/**
diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php
index 262b069e6da22..7d71c8c56963d 100644
--- a/src/wp-includes/functions.php
+++ b/src/wp-includes/functions.php
@@ -3004,7 +3004,7 @@ function wp_upload_bits( $name, $deprecated, $bits, $time = null ) {
* @since 2.5.0
*
* @param string $ext The extension to search.
- * @return string|void The file type, example: audio, video, document, spreadsheet, etc.
+ * @return string|null The file type, example: audio, video, document, spreadsheet, etc.
*/
function wp_ext2type( $ext ) {
$ext = strtolower( $ext );
@@ -3015,6 +3015,7 @@ function wp_ext2type( $ext ) {
return $type;
}
}
+ return null;
}
/**
@@ -3774,9 +3775,9 @@ function wp_nonce_ays( $action ) {
* is a WP_Error.
* @type bool $exit Whether to exit the process after completion. Default true.
* }
- * @return never|void Returns void if `$args['exit']` is false, otherwise exits.
+ * @return void Never returns if `$args['exit']` is true (the default), otherwise returns void.
*
- * @phpstan-return ( $args['exit'] is false ? void : never )
+ * @phpstan-return ( $args is array{exit: false} ? void : never )
*/
function wp_die( $message = '', $title = '', $args = array() ) {
global $wp_query;
@@ -3974,14 +3975,14 @@ function _default_wp_die_handler( $message, $title = '', $args = array() ) {
font-size: 14px ;
}
a {
- color: #2271b1;
+ color: #3858e9;
}
a:hover,
a:active {
- color: #135e96;
+ color: #183ad6;
}
a:focus {
- color: #043959;
+ color: #183ad6;
box-shadow: 0 0 0 var(--wp-admin-border-width-focus, 1.5px) var(--wp-admin-theme-color, #3858e9);
outline: 2px solid transparent;
}
@@ -5105,7 +5106,7 @@ function _wp_array_get( $input_array, $path, $default_value = null ) {
}
if ( is_string( $path_element )
- || is_integer( $path_element )
+ || is_int( $path_element )
|| null === $path_element
) {
/*
@@ -5182,7 +5183,7 @@ function _wp_array_set( &$input_array, $path, $value = null ) {
foreach ( $path as $path_element ) {
if (
- ! is_string( $path_element ) && ! is_integer( $path_element ) &&
+ ! is_string( $path_element ) && ! is_int( $path_element ) &&
! is_null( $path_element )
) {
return;
@@ -5511,6 +5512,8 @@ function wp_ob_end_flush_all() {
* @since 2.3.2
*
* @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @return never
*/
function dead_db() {
global $wpdb;
@@ -8578,7 +8581,7 @@ function wp_get_default_update_php_url() {
* @param string $after Markup to output after the annotation. Default ``.
* @param bool $display Whether to echo or return the markup. Default `true` for echo.
*
- * @return string|void
+ * @return string|null Update PHP page annotation if available and $display is false, null otherwise.
*/
function wp_update_php_annotation( $before = '', $after = '
', $display = true ) {
$annotation = wp_get_update_php_annotation();
@@ -8590,6 +8593,7 @@ function wp_update_php_annotation( $before = '', $after =
return $before . $annotation . $after;
}
}
+ return null;
}
/**
diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php
index 7ff250413ad2b..8f6ec1cef4e26 100644
--- a/src/wp-includes/media.php
+++ b/src/wp-includes/media.php
@@ -972,12 +972,15 @@ function wp_get_registered_image_subsizes() {
* @type int $2 Image height in pixels.
* @type bool $3 Whether the image is a resized image.
* }
+ * @phpstan-return array{ 0: string, 1: int, 2: int, 3: bool }|false
*/
function wp_get_attachment_image_src( $attachment_id, $size = 'thumbnail', $icon = false ) {
// Get a thumbnail or intermediate image if there is one.
$image = image_downsize( $attachment_id, $size );
if ( ! $image ) {
- $src = false;
+ $src = false;
+ $width = 0;
+ $height = 0;
if ( $icon ) {
$src = wp_mime_type_icon( $attachment_id, '.svg' );
@@ -988,7 +991,11 @@ function wp_get_attachment_image_src( $attachment_id, $size = 'thumbnail', $icon
$src_file = $icon_dir . '/' . wp_basename( $src );
- list( $width, $height ) = wp_getimagesize( $src_file );
+ $image_size = wp_getimagesize( $src_file );
+ if ( is_array( $image_size ) ) {
+ $width = $image_size[0];
+ $height = $image_size[1];
+ }
$ext = strtolower( substr( $src_file, -4 ) );
@@ -997,7 +1004,11 @@ function wp_get_attachment_image_src( $attachment_id, $size = 'thumbnail', $icon
$width = 48;
$height = 64;
} else {
- list( $width, $height ) = wp_getimagesize( $src_file );
+ $image_size = wp_getimagesize( $src_file );
+ if ( is_array( $image_size ) ) {
+ $width = $image_size[0];
+ $height = $image_size[1];
+ }
}
}
}
@@ -1024,7 +1035,16 @@ function wp_get_attachment_image_src( $attachment_id, $size = 'thumbnail', $icon
* an array of width and height values in pixels (in that order).
* @param bool $icon Whether the image should be treated as an icon.
*/
- return apply_filters( 'wp_get_attachment_image_src', $image, $attachment_id, $size, $icon );
+ $source = apply_filters( 'wp_get_attachment_image_src', $image, $attachment_id, $size, $icon );
+ if ( is_array( $source ) && isset( $source[0] ) && is_string( $source[0] ) ) {
+ return array(
+ $source[0],
+ (int) ( $source[1] ?? 0 ),
+ (int) ( $source[2] ?? 0 ),
+ (bool) ( $source[3] ?? false ),
+ );
+ }
+ return false;
}
/**
@@ -3230,10 +3250,23 @@ function wp_playlist_shortcode( $attr ) {
if ( $atts['images'] ) {
$thumb_id = get_post_thumbnail_id( $attachment->ID );
if ( ! empty( $thumb_id ) ) {
- list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'full' );
- $track['image'] = compact( 'src', 'width', 'height' );
- list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'thumbnail' );
- $track['thumb'] = compact( 'src', 'width', 'height' );
+ $image_src_full = wp_get_attachment_image_src( $thumb_id, 'full' );
+ if ( is_array( $image_src_full ) ) {
+ $track['image'] = array(
+ 'src' => $image_src_full[0],
+ 'width' => $image_src_full[1],
+ 'height' => $image_src_full[2],
+ );
+ }
+
+ $image_src_thumb = wp_get_attachment_image_src( $thumb_id, 'thumbnail' );
+ if ( is_array( $image_src_thumb ) ) {
+ $track['thumb'] = array(
+ 'src' => $image_src_thumb[0],
+ 'width' => $image_src_thumb[1],
+ 'height' => $image_src_thumb[2],
+ );
+ }
} else {
$src = wp_mime_type_icon( $attachment->ID, '.svg' );
$width = 48;
@@ -3389,7 +3422,7 @@ function wp_get_attachment_id3_keys( $attachment, $context = 'display' ) {
* @type string $style The 'style' attribute for the `