PluginProbe
Gutenberg / trunk
Gutenberg vtrunk
24.1.0 24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 All 404 releases
gutenberg / lib / global-styles-and-settings.php

global-styles-and-settings.php in Gutenberg trunk, at lib/global-styles-and-settings.php

444 lines 15.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * API to interact with global settings & styles.
4 *
5 * @package gutenberg
6 */
7
8 /**
9 * Returns the stylesheet resulting of merging core, theme, and user data.
10 *
11 * @param array $types Types of styles to load. Optional.
12 * See {@see 'WP_Theme_JSON::get_stylesheet'} for all valid types.
13 * If empty, will load: 'variables', 'presets', 'styles'.
14 *
15 * @return string Stylesheet.
16 */
17 function gutenberg_get_global_stylesheet( $types = array() ) {
18 // Ignore cache when `WP_DEBUG` is enabled, so it doesn't interfere with the theme developers workflow.
19 $can_use_cached = empty( $types ) && ! WP_DEBUG;
20 $cache_key = 'gutenberg_get_global_stylesheet';
21 $cache_group = 'theme_json';
22 if ( $can_use_cached ) {
23 $cached = wp_cache_get( $cache_key, $cache_group );
24 if ( $cached ) {
25 return $cached;
26 }
27 }
28 $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data();
29 $tree = WP_Theme_JSON_Resolver_Gutenberg::resolve_theme_file_uris( $tree );
30
31 if ( empty( $types ) ) {
32 $types = array( 'variables', 'presets', 'styles' );
33 }
34
35 /*
36 * Enable base layout styles only mode for classic themes without theme.json.
37 * This skips alignment styles that target .wp-site-blocks which is only used by block themes.
38 */
39 $options = array();
40 if ( ! wp_is_block_theme() && ! wp_theme_has_theme_json() ) {
41 $options['base_layout_styles'] = true;
42 }
43
44 /*
45 * If variables are part of the stylesheet,
46 * we add them.
47 *
48 * This is so themes without a theme.json still work as before 5.9:
49 * they can override the default presets.
50 * See https://core.trac.wordpress.org/ticket/54782
51 */
52 $styles_variables = '';
53 if ( in_array( 'variables', $types, true ) ) {
54 /*
55 * We only use the default, theme, and custom origins.
56 * This is because styles for blocks origin are added
57 * at a later phase (render cycle) so we only render the ones in use.
58 * @see wp_add_global_styles_for_blocks
59 */
60 $origins = array( 'default', 'theme', 'custom' );
61 $styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins, $options );
62 $types = array_diff( $types, array( 'variables' ) );
63 }
64
65 /*
66 * For the remaining types (presets, styles), we do consider origins:
67 *
68 * - themes without theme.json: only the classes for the presets defined by core
69 * - themes with theme.json: the presets and styles classes, both from core and the theme
70 */
71 $styles_rest = '';
72 if ( ! empty( $types ) ) {
73 /*
74 * We only use the default, theme, and custom origins.
75 * This is because styles for blocks origin are added
76 * at a later phase (render cycle) so we only render the ones in use.
77 * @see wp_add_global_styles_for_blocks
78 */
79 $origins = array( 'default', 'theme', 'custom' );
80 $styles_rest = $tree->get_stylesheet( $types, $origins, $options );
81 }
82 $stylesheet = $styles_variables . $styles_rest;
83 if ( $can_use_cached ) {
84 wp_cache_set( $cache_key, $stylesheet, $cache_group );
85 }
86 return $stylesheet;
87 }
88
89 /**
90 * Function to get the settings resulting of merging core, theme, and user data.
91 *
92 * @param array $path Path to the specific setting to retrieve. Optional.
93 * If empty, will return all settings.
94 * @param array $context {
95 * Metadata to know where to retrieve the $path from. Optional.
96 *
97 * @type string $block_name Which block to retrieve the settings from.
98 * If empty, it'll return the settings for the global context.
99 * @type string $origin Which origin to take data from.
100 * Valid values are 'all' (core, theme, and user) or 'base' (core and theme).
101 * If empty or unknown, 'all' is used.
102 * }
103 *
104 * @return array The settings to retrieve.
105 */
106 function gutenberg_get_global_settings( $path = array(), $context = array() ) {
107 if ( ! empty( $context['block_name'] ) ) {
108 $new_path = array( 'blocks', $context['block_name'] );
109 foreach ( $path as $subpath ) {
110 $new_path[] = $subpath;
111 }
112 $path = $new_path;
113 }
114
115 // This is the default value when no origin is provided or when it is 'all'.
116 $origin = 'custom';
117 if ( isset( $context['origin'] ) && 'base' === $context['origin'] ) {
118 $origin = 'theme';
119 }
120
121 $cache_group = 'theme_json';
122 $cache_key = 'gutenberg_get_global_settings_' . $origin;
123 $settings = wp_cache_get( $cache_key, $cache_group );
124
125 if ( false === $settings || WP_DEBUG ) {
126 $settings = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $origin )->get_settings();
127 wp_cache_set( $cache_key, $settings, $cache_group );
128 }
129
130 return _wp_array_get( $settings, $path, $settings );
131 }
132
133 /**
134 * Returns CSS media queries for responsive viewport style states.
135 *
136 * @param mixed $viewport_settings Viewport settings from theme.json.
137 * @param array $options Options for generating media queries.
138 * @return array Responsive media queries.
139 */
140 function gutenberg_get_viewport_media_queries( $viewport_settings = null, $options = array() ) {
141 return WP_Theme_JSON_Gutenberg::get_viewport_media_queries(
142 $viewport_settings,
143 $options
144 );
145 }
146
147 /**
148 * Gets the global styles custom css from theme.json.
149 *
150 * @deprecated Gutenberg 18.6.0 Use {@see 'gutenberg_get_global_stylesheet'} instead for top-level custom CSS, or {@see 'WP_Theme_JSON_Gutenberg::get_styles_for_block'} for block-level custom CSS.
151 *
152 * @return string
153 */
154 function gutenberg_get_global_styles_custom_css() {
155 _deprecated_function( __FUNCTION__, 'Gutenberg 18.6.0', 'gutenberg_get_global_stylesheet' );
156 // Ignore cache when `WP_DEBUG` is enabled, so it doesn't interfere with the theme developers workflow.
157 $can_use_cached = ! WP_DEBUG;
158 $cache_key = 'gutenberg_get_global_custom_css';
159 $cache_group = 'theme_json';
160 if ( $can_use_cached ) {
161 $cached = wp_cache_get( $cache_key, $cache_group );
162 if ( $cached ) {
163 return $cached;
164 }
165 }
166
167 $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data();
168 $stylesheet = $tree->get_custom_css();
169
170 if ( $can_use_cached ) {
171 wp_cache_set( $cache_key, $stylesheet, $cache_group );
172 }
173
174 return $stylesheet;
175 }
176
177 /**
178 * Gets the global styles base custom CSS from theme.json.
179 *
180 * @since 6.6.0
181 *
182 * @return string The global base custom CSS.
183 */
184 function gutenberg_get_global_styles_base_custom_css() {
185 _deprecated_function( __FUNCTION__, 'Gutenberg 18.6.0', 'gutenberg_get_global_stylesheet' );
186
187 $can_use_cached = ! WP_DEBUG;
188
189 $cache_key = 'gutenberg_get_global_styles_base_custom_css';
190 $cache_group = 'theme_json';
191 if ( $can_use_cached ) {
192 $cached = wp_cache_get( $cache_key, $cache_group );
193 if ( $cached ) {
194 return $cached;
195 }
196 }
197
198 $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data();
199 $stylesheet = $tree->get_base_custom_css();
200
201 if ( $can_use_cached ) {
202 wp_cache_set( $cache_key, $stylesheet, $cache_group );
203 }
204
205 return $stylesheet;
206 }
207
208 /**
209 * Adds the global styles per-block custom CSS from theme.json
210 * to the inline style for each block.
211 *
212 * @since 6.6.0
213 *
214 * @global WP_Styles $wp_styles
215 */
216 function gutenberg_add_global_styles_block_custom_css() {
217 _deprecated_function( __FUNCTION__, 'Gutenberg 18.6.0', 'gutenberg_add_global_styles_for_blocks' );
218 global $wp_styles;
219
220 if ( ! wp_should_load_separate_core_block_assets() ) {
221 return;
222 }
223
224 $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data();
225 $block_nodes = $tree->get_block_custom_css_nodes();
226
227 foreach ( $block_nodes as $metadata ) {
228 $block_css = $tree->get_block_custom_css( $metadata['css'], $metadata['selector'] );
229
230 $stylesheet_handle = 'global-styles';
231
232 /*
233 * When `wp_should_load_separate_core_block_assets()` is true, follow a similar
234 * logic to the one in `gutenberg_add_global_styles_for_blocks` to add the custom
235 * css only when the block is rendered.
236 */
237 if ( isset( $metadata['name'] ) ) {
238 if ( str_starts_with( $metadata['name'], 'core/' ) ) {
239 $block_name = str_replace( 'core/', '', $metadata['name'] );
240 $block_handle = 'wp-block-' . $block_name;
241 if ( in_array( $block_handle, $wp_styles->queue, true ) ) {
242 wp_add_inline_style( $stylesheet_handle, $block_css );
243 }
244 } else {
245 wp_add_inline_style( $stylesheet_handle, $block_css );
246 }
247 }
248 }
249 }
250
251
252 /**
253 * Adds global style rules to the inline style for each block.
254 *
255 * @global WP_Styles $wp_styles
256 *
257 * @return void
258 */
259 function gutenberg_add_global_styles_for_blocks() {
260 global $wp_styles;
261 $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data();
262 $tree = WP_Theme_JSON_Resolver_Gutenberg::resolve_theme_file_uris( $tree );
263 $block_nodes = $tree->get_styles_block_nodes();
264
265 $can_use_cached = ! wp_is_development_mode( 'theme' );
266 $update_cache = false;
267
268 if ( $can_use_cached ) {
269 // Hash the merged WP_Theme_JSON data to bust cache on settings or styles change.
270 $cache_hash = md5( wp_json_encode( $tree->get_raw_data() ) );
271 $cache_key = 'wp_styles_for_blocks';
272 $cached = get_transient( $cache_key );
273
274 // Reset the cached data if there is no value or if the hash has changed.
275 if ( ! is_array( $cached ) || $cached['hash'] !== $cache_hash ) {
276 $cached = array(
277 'hash' => $cache_hash,
278 'blocks' => array(),
279 );
280
281 // Update the cache if the hash has changed.
282 $update_cache = true;
283 }
284 }
285
286 foreach ( $block_nodes as $metadata ) {
287 if ( $can_use_cached ) {
288 // Generate a unique cache key based on the full metadata to ensure pseudo-selectors and other variations get unique keys.
289 $cache_node_key = md5( wp_json_encode( $metadata ) );
290
291 if ( isset( $cached['blocks'][ $cache_node_key ] ) ) {
292 $block_css = $cached['blocks'][ $cache_node_key ];
293 } else {
294 $block_css = $tree->get_styles_for_block( $metadata );
295 $cached['blocks'][ $cache_node_key ] = $block_css;
296
297 // Update the cache if the cache contents have changed.
298 $update_cache = true;
299 }
300 } else {
301 $block_css = $tree->get_styles_for_block( $metadata );
302 }
303
304 if ( ! wp_should_load_separate_core_block_assets() ) {
305 wp_add_inline_style( 'global-styles', $block_css );
306 continue;
307 }
308
309 $stylesheet_handle = 'global-styles';
310
311 /*
312 * When `wp_should_load_separate_core_block_assets()` is true, block styles are
313 * enqueued for each block on the page in class WP_Block's render function.
314 * This means there will be a handle in the styles queue for each of those blocks.
315 * Block-specific global styles should be attached to the global-styles handle, but
316 * only for blocks on the page, thus we check if the block's handle is in the queue
317 * before adding the inline style.
318 * This conditional loading only applies to core blocks.
319 */
320 if ( isset( $metadata['name'] ) ) {
321 if ( str_starts_with( $metadata['name'], 'core/' ) ) {
322 $block_name = str_replace( 'core/', '', $metadata['name'] );
323 $block_handle = 'wp-block-' . $block_name;
324 if ( in_array( $block_handle, $wp_styles->queue, true ) ) {
325 wp_add_inline_style( $stylesheet_handle, $block_css );
326 }
327 } else {
328 wp_add_inline_style( $stylesheet_handle, $block_css );
329 }
330 }
331
332 // The likes of block element styles from theme.json do not have $metadata['name'] set.
333 if ( ! isset( $metadata['name'] ) && ! empty( $metadata['path'] ) ) {
334 $block_name = wp_get_block_name_from_theme_json_path( $metadata['path'] );
335 if ( $block_name ) {
336 if ( str_starts_with( $block_name, 'core/' ) ) {
337 $block_name = str_replace( 'core/', '', $block_name );
338 $block_handle = 'wp-block-' . $block_name;
339 if ( in_array( $block_handle, $wp_styles->queue, true ) ) {
340 wp_add_inline_style( $stylesheet_handle, $block_css );
341 }
342 } else {
343 wp_add_inline_style( $stylesheet_handle, $block_css );
344 }
345 }
346 }
347 }
348
349 if ( $update_cache ) {
350 set_transient( $cache_key, $cached );
351 }
352 }
353
354 /**
355 * Private function to clean the caches used by the theme JSON APIs.
356 *
357 * @access private
358 */
359 function _gutenberg_clean_theme_json_caches() {
360 wp_cache_delete( 'wp_theme_has_theme_json', 'theme_json' );
361 wp_cache_delete( 'gutenberg_get_global_stylesheet', 'theme_json' );
362 wp_cache_delete( 'gutenberg_get_global_settings_custom', 'theme_json' );
363 wp_cache_delete( 'gutenberg_get_global_settings_theme', 'theme_json' );
364 wp_cache_delete( 'gutenberg_get_global_styles_custom', 'theme_json' );
365 wp_cache_delete( 'gutenberg_get_global_styles_custom_resolved', 'theme_json' );
366 wp_cache_delete( 'gutenberg_get_global_styles_theme', 'theme_json' );
367 wp_cache_delete( 'gutenberg_get_global_styles_theme_resolved', 'theme_json' );
368 wp_cache_delete( 'gutenberg_get_global_custom_css', 'theme_json' );
369 wp_cache_delete( 'gutenberg_get_global_styles_base_custom_css', 'theme_json' );
370 WP_Theme_JSON_Resolver_Gutenberg::clean_cached_data();
371 }
372 add_action( 'start_previewing_theme', '_gutenberg_clean_theme_json_caches' );
373 add_action( 'switch_theme', '_gutenberg_clean_theme_json_caches' );
374
375 /**
376 * Gets the styles resulting of merging core, theme, and user data.
377 *
378 * @since 5.9.0
379 * @since 6.3.0 the internal link format "var:preset|color|secondary" is resolved
380 * to "var(--wp--preset--font-size--small)" so consumers don't have to.
381 * @since 6.3.0 `transforms` is now usable in the `context` parameter. In case [`transforms`]['resolve_variables']
382 * is defined, variables are resolved to their value in the styles.
383 *
384 * @param array $path Path to the specific style to retrieve. Optional.
385 * If empty, will return all styles.
386 * @param array $context {
387 * Metadata to know where to retrieve the $path from. Optional.
388 *
389 * @type string $block_name Which block to retrieve the styles from.
390 * If empty, it'll return the styles for the global context.
391 * @type string $origin Which origin to take data from.
392 * Valid values are 'all' (core, theme, and user) or 'base' (core and theme).
393 * If empty or unknown, 'all' is used.
394 * @type array $transforms Which transformation(s) to apply.
395 * Valid value is array( 'resolve-variables' ).
396 * If defined, variables are resolved to their value in the styles.
397 * }
398 * @return array The styles to retrieve.
399 */
400 function gutenberg_get_global_styles( $path = array(), $context = array() ) {
401 if ( ! empty( $context['block_name'] ) ) {
402 $path = array_merge( array( 'blocks', $context['block_name'] ), $path );
403 }
404
405 $origin = 'custom';
406 if ( isset( $context['origin'] ) && 'base' === $context['origin'] ) {
407 $origin = 'theme';
408 }
409
410 $resolve_variables = isset( $context['transforms'] )
411 && is_array( $context['transforms'] )
412 && in_array( 'resolve-variables', $context['transforms'], true );
413
414 $cache_group = 'theme_json';
415 $cache_key = 'gutenberg_get_global_styles_' . $origin;
416 if ( $resolve_variables ) {
417 $cache_key .= '_resolved';
418 }
419 $styles = wp_cache_get( $cache_key, $cache_group );
420
421 if ( false === $styles || WP_DEBUG ) {
422 $merged_data = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $origin );
423 if ( $resolve_variables ) {
424 $merged_data = WP_Theme_JSON_Gutenberg::resolve_variables( $merged_data );
425 }
426 $styles = $merged_data->get_raw_data()['styles'];
427 wp_cache_set( $cache_key, $styles, $cache_group );
428 }
429
430 return _wp_array_get( $styles, $path, $styles );
431 }
432
433 /**
434 * Returns the current theme's wanted patterns (slugs) to be
435 * registered from Pattern Directory.
436 *
437 * @since 6.3.0
438 *
439 * @return string[]
440 */
441 function gutenberg_get_theme_directory_pattern_slugs() {
442 return WP_Theme_JSON_Resolver_Gutenberg::get_theme_data( array(), array( 'with_supports' => false ) )->get_patterns();
443 }
444