wp/wp-includes/update.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
--- a/wp/wp-includes/update.php	Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-includes/update.php	Fri Sep 05 18:40:08 2025 +0200
@@ -1,13 +1,13 @@
 <?php
 /**
- * A simple set of functions to check our version 1.0 update service.
+ * A simple set of functions to check the WordPress.org Version Update service.
  *
  * @package WordPress
  * @since 2.3.0
  */
 
 /**
- * Check WordPress version against the newest version.
+ * Checks WordPress version against the newest version.
  *
  * The WordPress version, PHP version, and locale is sent.
  *
@@ -21,7 +21,8 @@
  * @global string $wp_local_package Locale code of the package.
  *
  * @param array $extra_stats Extra statistics to report to the WordPress.org API.
- * @param bool  $force_check Whether to bypass the transient cache and force a fresh update check. Defaults to false, true if $extra_stats is set.
+ * @param bool  $force_check Whether to bypass the transient cache and force a fresh update check.
+ *                           Defaults to false, true if $extra_stats is set.
  */
 function wp_version_check( $extra_stats = array(), $force_check = false ) {
 	global $wpdb, $wp_local_package;
@@ -32,7 +33,7 @@
 
 	// Include an unmodified $wp_version.
 	require ABSPATH . WPINC . '/version.php';
-	$php_version = phpversion();
+	$php_version = PHP_VERSION;
 
 	$current      = get_site_transient( 'update_core' );
 	$translations = wp_get_installed_translations( 'core' );
@@ -43,7 +44,7 @@
 	}
 
 	if ( ! is_object( $current ) ) {
-		$current                  = new stdClass;
+		$current                  = new stdClass();
 		$current->updates         = array();
 		$current->version_checked = $wp_version;
 	}
@@ -73,7 +74,9 @@
 	$current->last_checked = time();
 	set_site_transient( 'update_core', $current );
 
-	if ( method_exists( $wpdb, 'db_version' ) ) {
+	if ( method_exists( $wpdb, 'db_server_info' ) ) {
+		$mysql_version = $wpdb->db_server_info();
+	} elseif ( method_exists( $wpdb, 'db_version' ) ) {
 		$mysql_version = preg_replace( '/[^0-9.].*/', '', $wpdb->db_version() );
 	} else {
 		$mysql_version = 'N/A';
@@ -89,6 +92,8 @@
 		$wp_install        = home_url( '/' );
 	}
 
+	$extensions = get_loaded_extensions();
+	sort( $extensions, SORT_STRING | SORT_FLAG_CASE );
 	$query = array(
 		'version'            => $wp_version,
 		'php'                => $php_version,
@@ -99,8 +104,42 @@
 		'users'              => get_user_count(),
 		'multisite_enabled'  => $multisite_enabled,
 		'initial_db_version' => get_site_option( 'initial_db_version' ),
+		'extensions'         => array_combine( $extensions, array_map( 'phpversion', $extensions ) ),
+		'platform_flags'     => array(
+			'os'   => PHP_OS,
+			'bits' => PHP_INT_SIZE === 4 ? 32 : 64,
+		),
+		'image_support'      => array(),
 	);
 
+	if ( function_exists( 'gd_info' ) ) {
+		$gd_info = gd_info();
+		// Filter to supported values.
+		$gd_info = array_filter( $gd_info );
+
+		// Add data for GD WebP and AVIF support.
+		$query['image_support']['gd'] = array_keys(
+			array_filter(
+				array(
+					'webp' => isset( $gd_info['WebP Support'] ),
+					'avif' => isset( $gd_info['AVIF Support'] ),
+				)
+			)
+		);
+	}
+
+	if ( class_exists( 'Imagick' ) ) {
+		// Add data for Imagick WebP and AVIF support.
+		$query['image_support']['imagick'] = array_keys(
+			array_filter(
+				array(
+					'webp' => ! empty( Imagick::queryFormats( 'WEBP' ) ),
+					'avif' => ! empty( Imagick::queryFormats( 'AVIF' ) ),
+				)
+			)
+		);
+	}
+
 	/**
 	 * Filters the query arguments sent as part of the core version check.
 	 *
@@ -163,7 +202,8 @@
 	$response = wp_remote_post( $url, $options );
 
 	if ( $ssl && is_wp_error( $response ) ) {
-		trigger_error(
+		wp_trigger_error(
+			__FUNCTION__,
 			sprintf(
 				/* translators: %s: Support forums URL. */
 				__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
@@ -290,10 +330,10 @@
 	$current = get_site_transient( 'update_plugins' );
 
 	if ( ! is_object( $current ) ) {
-		$current = new stdClass;
+		$current = new stdClass();
 	}
 
-	$updates               = new stdClass;
+	$updates               = new stdClass();
 	$updates->last_checked = time();
 	$updates->response     = array();
 	$updates->translations = array();
@@ -363,13 +403,13 @@
 	 * @since 3.7.0
 	 * @since 4.5.0 The default value of the `$locales` parameter changed to include all locales.
 	 *
-	 * @param array $locales Plugin locales. Default is all available locales of the site.
+	 * @param string[] $locales Plugin locales. Default is all available locales of the site.
 	 */
 	$locales = apply_filters( 'plugins_update_check_locales', $locales );
 	$locales = array_unique( $locales );
 
 	if ( $doing_cron ) {
-		$timeout = 30;
+		$timeout = 30; // 30 seconds.
 	} else {
 		// Three seconds, plus one extra second for every 10 plugins.
 		$timeout = 3 + (int) ( count( $plugins ) / 10 );
@@ -401,7 +441,8 @@
 	$raw_response = wp_remote_post( $url, $options );
 
 	if ( $ssl && is_wp_error( $raw_response ) ) {
-		trigger_error(
+		wp_trigger_error(
+			__FUNCTION__,
 			sprintf(
 				/* translators: %s: Support forums URL. */
 				__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
@@ -430,7 +471,7 @@
 			continue;
 		}
 
-		$hostname = wp_parse_url( esc_url_raw( $plugin_data['UpdateURI'] ), PHP_URL_HOST );
+		$hostname = wp_parse_url( sanitize_url( $plugin_data['UpdateURI'] ), PHP_URL_HOST );
 
 		/**
 		 * Filters the update response for a given plugin hostname.
@@ -469,7 +510,7 @@
 		 * }
 		 * @param array       $plugin_data      Plugin headers.
 		 * @param string      $plugin_file      Plugin filename.
-		 * @param array       $locales          Installed locales to look translations for.
+		 * @param string[]    $locales          Installed locales to look up translations for.
 		 */
 		$update = apply_filters( "update_plugins_{$hostname}", false, $plugin_data, $plugin_file, $locales );
 
@@ -514,7 +555,7 @@
 		}
 	}
 
-	$sanitize_plugin_update_payload = static function( &$item ) {
+	$sanitize_plugin_update_payload = static function ( &$item ) {
 		$item = (object) $item;
 
 		unset( $item->translations, $item->compatibility );
@@ -558,7 +599,7 @@
 	$last_update = get_site_transient( 'update_themes' );
 
 	if ( ! is_object( $last_update ) ) {
-		$last_update = new stdClass;
+		$last_update = new stdClass();
 	}
 
 	$themes  = array();
@@ -577,6 +618,7 @@
 			'Version'    => $theme->get( 'Version' ),
 			'Author'     => $theme->get( 'Author' ),
 			'Author URI' => $theme->get( 'AuthorURI' ),
+			'UpdateURI'  => $theme->get( 'UpdateURI' ),
 			'Template'   => $theme->get_template(),
 			'Stylesheet' => $theme->get_stylesheet(),
 		);
@@ -644,13 +686,13 @@
 	 * @since 3.7.0
 	 * @since 4.5.0 The default value of the `$locales` parameter changed to include all locales.
 	 *
-	 * @param array $locales Theme locales. Default is all available locales of the site.
+	 * @param string[] $locales Theme locales. Default is all available locales of the site.
 	 */
 	$locales = apply_filters( 'themes_update_check_locales', $locales );
 	$locales = array_unique( $locales );
 
 	if ( $doing_cron ) {
-		$timeout = 30;
+		$timeout = 30; // 30 seconds.
 	} else {
 		// Three seconds, plus one extra second for every 10 themes.
 		$timeout = 3 + (int) ( count( $themes ) / 10 );
@@ -681,7 +723,8 @@
 	$raw_response = wp_remote_post( $url, $options );
 
 	if ( $ssl && is_wp_error( $raw_response ) ) {
-		trigger_error(
+		wp_trigger_error(
+			__FUNCTION__,
 			sprintf(
 				/* translators: %s: Support forums URL. */
 				__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
@@ -696,7 +739,7 @@
 		return;
 	}
 
-	$new_update               = new stdClass;
+	$new_update               = new stdClass();
 	$new_update->last_checked = time();
 	$new_update->checked      = $checked;
 
@@ -708,6 +751,92 @@
 		$new_update->translations = $response['translations'];
 	}
 
+	// Support updates for any themes using the `Update URI` header field.
+	foreach ( $themes as $theme_stylesheet => $theme_data ) {
+		if ( ! $theme_data['UpdateURI'] || isset( $new_update->response[ $theme_stylesheet ] ) ) {
+			continue;
+		}
+
+		$hostname = wp_parse_url( sanitize_url( $theme_data['UpdateURI'] ), PHP_URL_HOST );
+
+		/**
+		 * Filters the update response for a given theme hostname.
+		 *
+		 * The dynamic portion of the hook name, `$hostname`, refers to the hostname
+		 * of the URI specified in the `Update URI` header field.
+		 *
+		 * @since 6.1.0
+		 *
+		 * @param array|false $update {
+		 *     The theme update data with the latest details. Default false.
+		 *
+		 *     @type string $id           Optional. ID of the theme for update purposes, should be a URI
+		 *                                specified in the `Update URI` header field.
+		 *     @type string $theme        Directory name of the theme.
+		 *     @type string $version      The version of the theme.
+		 *     @type string $url          The URL for details of the theme.
+		 *     @type string $package      Optional. The update ZIP for the theme.
+		 *     @type string $tested       Optional. The version of WordPress the theme is tested against.
+		 *     @type string $requires_php Optional. The version of PHP which the theme requires.
+		 *     @type bool   $autoupdate   Optional. Whether the theme should automatically update.
+		 *     @type array  $translations {
+		 *         Optional. List of translation updates for the theme.
+		 *
+		 *         @type string $language   The language the translation update is for.
+		 *         @type string $version    The version of the theme this translation is for.
+		 *                                  This is not the version of the language file.
+		 *         @type string $updated    The update timestamp of the translation file.
+		 *                                  Should be a date in the `YYYY-MM-DD HH:MM:SS` format.
+		 *         @type string $package    The ZIP location containing the translation update.
+		 *         @type string $autoupdate Whether the translation should be automatically installed.
+		 *     }
+		 * }
+		 * @param array       $theme_data       Theme headers.
+		 * @param string      $theme_stylesheet Theme stylesheet.
+		 * @param string[]    $locales          Installed locales to look up translations for.
+		 */
+		$update = apply_filters( "update_themes_{$hostname}", false, $theme_data, $theme_stylesheet, $locales );
+
+		if ( ! $update ) {
+			continue;
+		}
+
+		$update = (object) $update;
+
+		// Is it valid? We require at least a version.
+		if ( ! isset( $update->version ) ) {
+			continue;
+		}
+
+		// This should remain constant.
+		$update->id = $theme_data['UpdateURI'];
+
+		// WordPress needs the version field specified as 'new_version'.
+		if ( ! isset( $update->new_version ) ) {
+			$update->new_version = $update->version;
+		}
+
+		// Handle any translation updates.
+		if ( ! empty( $update->translations ) ) {
+			foreach ( $update->translations as $translation ) {
+				if ( isset( $translation['language'], $translation['package'] ) ) {
+					$translation['type'] = 'theme';
+					$translation['slug'] = isset( $update->theme ) ? $update->theme : $update->id;
+
+					$new_update->translations[] = $translation;
+				}
+			}
+		}
+
+		unset( $new_update->no_update[ $theme_stylesheet ], $new_update->response[ $theme_stylesheet ] );
+
+		if ( version_compare( $update->new_version, $theme_data['Version'], '>' ) ) {
+			$new_update->response[ $theme_stylesheet ] = (array) $update;
+		} else {
+			$new_update->no_update[ $theme_stylesheet ] = (array) $update;
+		}
+	}
+
 	set_site_transient( 'update_themes', $new_update );
 }
 
@@ -719,10 +848,10 @@
  * @since 3.7.0
  */
 function wp_maybe_auto_update() {
-	include_once ABSPATH . 'wp-admin/includes/admin.php';
+	require_once ABSPATH . 'wp-admin/includes/admin.php';
 	require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
 
-	$upgrader = new WP_Automatic_Updater;
+	$upgrader = new WP_Automatic_Updater();
 	$upgrader->run();
 }
 
@@ -757,7 +886,7 @@
 }
 
 /**
- * Collect counts and UI strings for available updates
+ * Collects counts and UI strings for available updates.
  *
  * @since 3.3.0
  *
@@ -875,7 +1004,7 @@
 	wp_version_check();
 }
 /**
- * Check the last time plugins were run before checking plugin versions.
+ * Checks the last time plugins were run before checking plugin versions.
  *
  * This might have been backported to WordPress 2.6.1 for performance reasons.
  * This is used for the wp-admin to check only so often instead of every page
@@ -897,7 +1026,7 @@
 }
 
 /**
- * Check themes versions only after a duration of time.
+ * Checks themes versions only after a duration of time.
  *
  * This is for performance reasons to make sure that on the theme version
  * checker is not run on every page load.
@@ -918,7 +1047,7 @@
 }
 
 /**
- * Schedule core, theme, and plugin update checks.
+ * Schedules core, theme, and plugin update checks.
  *
  * @since 3.1.0
  */
@@ -937,7 +1066,7 @@
 }
 
 /**
- * Clear existing update caches for plugins, themes, and core.
+ * Clears existing update caches for plugins, themes, and core.
  *
  * @since 4.1.0
  */
@@ -953,6 +1082,73 @@
 	delete_site_transient( 'update_core' );
 }
 
+/**
+ * Schedules the removal of all contents in the temporary backup directory.
+ *
+ * @since 6.3.0
+ */
+function wp_delete_all_temp_backups() {
+	/*
+	 * Check if there is a lock, or if currently performing an Ajax request,
+	 * in which case there is a chance an update is running.
+	 * Reschedule for an hour from now and exit early.
+	 */
+	if ( get_option( 'core_updater.lock' ) || get_option( 'auto_updater.lock' ) || wp_doing_ajax() ) {
+		wp_schedule_single_event( time() + HOUR_IN_SECONDS, 'wp_delete_temp_updater_backups' );
+		return;
+	}
+
+	// This action runs on shutdown to make sure there are no plugin updates currently running.
+	add_action( 'shutdown', '_wp_delete_all_temp_backups' );
+}
+
+/**
+ * Deletes all contents in the temporary backup directory.
+ *
+ * @since 6.3.0
+ *
+ * @access private
+ *
+ * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
+ *
+ * @return void|WP_Error Void on success, or a WP_Error object on failure.
+ */
+function _wp_delete_all_temp_backups() {
+	global $wp_filesystem;
+
+	if ( ! function_exists( 'WP_Filesystem' ) ) {
+		require_once ABSPATH . '/wp-admin/includes/file.php';
+	}
+
+	ob_start();
+	$credentials = request_filesystem_credentials( '' );
+	ob_end_clean();
+
+	if ( false === $credentials || ! WP_Filesystem( $credentials ) ) {
+		return new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) );
+	}
+
+	if ( ! $wp_filesystem->wp_content_dir() ) {
+		return new WP_Error(
+			'fs_no_content_dir',
+			/* translators: %s: Directory name. */
+			sprintf( __( 'Unable to locate WordPress content directory (%s).' ), 'wp-content' )
+		);
+	}
+
+	$temp_backup_dir = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/';
+	$dirlist         = $wp_filesystem->dirlist( $temp_backup_dir );
+	$dirlist         = $dirlist ? $dirlist : array();
+
+	foreach ( array_keys( $dirlist ) as $dir ) {
+		if ( '.' === $dir || '..' === $dir ) {
+			continue;
+		}
+
+		$wp_filesystem->delete( $temp_backup_dir . $dir, true );
+	}
+}
+
 if ( ( ! is_main_site() && ! is_network_admin() ) || wp_doing_ajax() ) {
 	return;
 }
@@ -977,3 +1173,5 @@
 add_action( 'wp_maybe_auto_update', 'wp_maybe_auto_update' );
 
 add_action( 'init', 'wp_schedule_update_checks' );
+
+add_action( 'wp_delete_temp_updater_backups', 'wp_delete_all_temp_backups' );