wp/wp-includes/class-wp-theme-json-schema.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
--- a/wp/wp-includes/class-wp-theme-json-schema.php	Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-includes/class-wp-theme-json-schema.php	Fri Sep 05 18:40:08 2025 +0200
@@ -17,6 +17,7 @@
  * @since 5.9.0
  * @access private
  */
+#[AllowDynamicProperties]
 class WP_Theme_JSON_Schema {
 
 	/**
@@ -34,20 +35,27 @@
 	 * Function that migrates a given theme.json structure to the last version.
 	 *
 	 * @since 5.9.0
+	 * @since 6.6.0 Migrate up to v3 and add $origin parameter.
 	 *
 	 * @param array $theme_json The structure to migrate.
-	 *
+	 * @param string $origin    Optional. What source of data this object represents.
+	 *                          One of 'blocks', 'default', 'theme', or 'custom'. Default 'theme'.
 	 * @return array The structure in the last version.
 	 */
-	public static function migrate( $theme_json ) {
+	public static function migrate( $theme_json, $origin = 'theme' ) {
 		if ( ! isset( $theme_json['version'] ) ) {
 			$theme_json = array(
 				'version' => WP_Theme_JSON::LATEST_SCHEMA,
 			);
 		}
 
-		if ( 1 === $theme_json['version'] ) {
-			$theme_json = self::migrate_v1_to_v2( $theme_json );
+		// Migrate each version in order starting with the current version.
+		switch ( $theme_json['version'] ) {
+			case 1:
+				$theme_json = self::migrate_v1_to_v2( $theme_json );
+				// Deliberate fall through. Once migrated to v2, also migrate to v3.
+			case 2:
+				$theme_json = self::migrate_v2_to_v3( $theme_json, $origin );
 		}
 
 		return $theme_json;
@@ -84,6 +92,77 @@
 	}
 
 	/**
+	 * Migrates from v2 to v3.
+	 *
+	 * - Sets settings.typography.defaultFontSizes to false if settings.typography.fontSizes are defined.
+	 * - Sets settings.spacing.defaultSpacingSizes to false if settings.spacing.spacingSizes are defined.
+	 * - Prevents settings.spacing.spacingSizes from merging with settings.spacing.spacingScale by
+	 *   unsetting spacingScale when spacingSizes are defined.
+	 *
+	 * @since 6.6.0
+	 *
+	 * @param array $old     Data to migrate.
+	 * @param string $origin What source of data this object represents.
+	 *                       One of 'blocks', 'default', 'theme', or 'custom'.
+	 * @return array Data with defaultFontSizes set to false.
+	 */
+	private static function migrate_v2_to_v3( $old, $origin ) {
+		// Copy everything.
+		$new = $old;
+
+		// Set the new version.
+		$new['version'] = 3;
+
+		/*
+		 * Remaining changes do not need to be applied to the custom origin,
+		 * as they should take on the value of the theme origin.
+		 */
+		if ( 'custom' === $origin ) {
+			return $new;
+		}
+
+		/*
+		 * Even though defaultFontSizes and defaultSpacingSizes are new
+		 * settings, we need to migrate them as they each control
+		 * PRESETS_METADATA prevent_override values which were previously
+		 * hardcoded to false. This only needs to happen when the theme provides
+		 * fontSizes or spacingSizes as they could match the default ones and
+		 * affect the generated CSS.
+		 */
+		if ( isset( $old['settings']['typography']['fontSizes'] ) ) {
+			$new['settings']['typography']['defaultFontSizes'] = false;
+		}
+
+		/*
+		 * Similarly to defaultFontSizes, we need to migrate defaultSpacingSizes
+		 * as it controls the PRESETS_METADATA prevent_override which was
+		 * previously hardcoded to false. This only needs to happen when the
+		 * theme provided spacing sizes via spacingSizes or spacingScale.
+		 */
+		if (
+			isset( $old['settings']['spacing']['spacingSizes'] ) ||
+			isset( $old['settings']['spacing']['spacingScale'] )
+		) {
+			$new['settings']['spacing']['defaultSpacingSizes'] = false;
+		}
+
+		/*
+		 * In v3 spacingSizes is merged with the generated spacingScale sizes
+		 * instead of completely replacing them. The v3 behavior is what was
+		 * documented for the v2 schema, but the code never actually did work
+		 * that way. Instead of surprising users with a behavior change two
+		 * years after the fact at the same time as a v3 update is introduced,
+		 * we'll continue using the "bugged" behavior for v2 themes. And treat
+		 * the "bug fix" as a breaking change for v3.
+		 */
+		if ( isset( $old['settings']['spacing']['spacingSizes'] ) ) {
+			unset( $new['settings']['spacing']['spacingScale'] );
+		}
+
+		return $new;
+	}
+
+	/**
 	 * Processes the settings subtree.
 	 *
 	 * @since 5.9.0
@@ -137,11 +216,9 @@
 	 *
 	 * @param array $settings Reference to the current settings array.
 	 * @param array $path Path to the property to be removed.
-	 *
-	 * @return void
 	 */
 	private static function unset_setting_by_path( &$settings, $path ) {
-		$tmp_settings = &$settings; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+		$tmp_settings = &$settings;
 		$last_key     = array_pop( $path );
 		foreach ( $path as $key ) {
 			$tmp_settings = &$tmp_settings[ $key ];