Changeset 56500 for trunk/src/wp-includes/script-loader.php
- Timestamp:
- 08/31/2023 09:47:40 PM (18 months ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/script-loader.php
r56496 r56500 3225 3225 3226 3226 /** 3227 * Runs the theme.json webfonts handler.3228 *3229 * Using `WP_Theme_JSON_Resolver`, it gets the fonts defined3230 * in the `theme.json` for the current selection and style3231 * variations, validates the font-face properties, generates3232 * the '@font-face' style declarations, and then enqueues the3233 * styles for both the editor and front-end.3234 *3235 * Design Notes:3236 * This is not a public API, but rather an internal handler.3237 * A future public Webfonts API will replace this stopgap code.3238 *3239 * This code design is intentional.3240 * a. It hides the inner-workings.3241 * b. It does not expose API ins or outs for consumption.3242 * c. It only works with a theme's `theme.json`.3243 *3244 * Why?3245 * a. To avoid backwards-compatibility issues when3246 * the Webfonts API is introduced in Core.3247 * b. To make `fontFace` declarations in `theme.json` work.3248 *3249 * @link https://github.com/WordPress/gutenberg/issues/404723250 *3251 * @since 6.0.03252 * @access private3253 */3254 function _wp_theme_json_webfonts_handler() {3255 // Block themes are unavailable during installation.3256 if ( wp_installing() ) {3257 return;3258 }3259 3260 if ( ! wp_theme_has_theme_json() ) {3261 return;3262 }3263 3264 // Webfonts to be processed.3265 $registered_webfonts = array();3266 3267 /**3268 * Gets the webfonts from theme.json.3269 *3270 * @since 6.0.03271 *3272 * @return array Array of defined webfonts.3273 */3274 $fn_get_webfonts_from_theme_json = static function() {3275 // Get settings from theme.json.3276 $settings = WP_Theme_JSON_Resolver::get_merged_data()->get_settings();3277 3278 // If in the editor, add webfonts defined in variations.3279 if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {3280 $variations = WP_Theme_JSON_Resolver::get_style_variations();3281 foreach ( $variations as $variation ) {3282 // Skip if fontFamilies are not defined in the variation.3283 if ( empty( $variation['settings']['typography']['fontFamilies'] ) ) {3284 continue;3285 }3286 3287 // Initialize the array structure.3288 if ( empty( $settings['typography'] ) ) {3289 $settings['typography'] = array();3290 }3291 if ( empty( $settings['typography']['fontFamilies'] ) ) {3292 $settings['typography']['fontFamilies'] = array();3293 }3294 if ( empty( $settings['typography']['fontFamilies']['theme'] ) ) {3295 $settings['typography']['fontFamilies']['theme'] = array();3296 }3297 3298 // Combine variations with settings. Remove duplicates.3299 $settings['typography']['fontFamilies']['theme'] = array_merge( $settings['typography']['fontFamilies']['theme'], $variation['settings']['typography']['fontFamilies']['theme'] );3300 $settings['typography']['fontFamilies'] = array_unique( $settings['typography']['fontFamilies'] );3301 }3302 }3303 3304 // Bail out early if there are no settings for webfonts.3305 if ( empty( $settings['typography']['fontFamilies'] ) ) {3306 return array();3307 }3308 3309 $webfonts = array();3310 3311 // Look for fontFamilies.3312 foreach ( $settings['typography']['fontFamilies'] as $font_families ) {3313 foreach ( $font_families as $font_family ) {3314 3315 // Skip if fontFace is not defined.3316 if ( empty( $font_family['fontFace'] ) ) {3317 continue;3318 }3319 3320 // Skip if fontFace is not an array of webfonts.3321 if ( ! is_array( $font_family['fontFace'] ) ) {3322 continue;3323 }3324 3325 $webfonts = array_merge( $webfonts, $font_family['fontFace'] );3326 }3327 }3328 3329 return $webfonts;3330 };3331 3332 /**3333 * Transforms each 'src' into an URI by replacing 'file:./'3334 * placeholder from theme.json.3335 *3336 * The absolute path to the webfont file(s) cannot be defined in3337 * theme.json. `file:./` is the placeholder which is replaced by3338 * the theme's URL path to the theme's root.3339 *3340 * @since 6.0.03341 *3342 * @param array $src Webfont file(s) `src`.3343 * @return array Webfont's `src` in URI.3344 */3345 $fn_transform_src_into_uri = static function( array $src ) {3346 foreach ( $src as $key => $url ) {3347 // Tweak the URL to be relative to the theme root.3348 if ( ! str_starts_with( $url, 'file:./' ) ) {3349 continue;3350 }3351 3352 $src[ $key ] = get_theme_file_uri( str_replace( 'file:./', '', $url ) );3353 }3354 3355 return $src;3356 };3357 3358 /**3359 * Converts the font-face properties (i.e. keys) into kebab-case.3360 *3361 * @since 6.0.03362 *3363 * @param array $font_face Font face to convert.3364 * @return array Font faces with each property in kebab-case format.3365 */3366 $fn_convert_keys_to_kebab_case = static function( array $font_face ) {3367 foreach ( $font_face as $property => $value ) {3368 $kebab_case = _wp_to_kebab_case( $property );3369 $font_face[ $kebab_case ] = $value;3370 if ( $kebab_case !== $property ) {3371 unset( $font_face[ $property ] );3372 }3373 }3374 3375 return $font_face;3376 };3377 3378 /**3379 * Validates a webfont.3380 *3381 * @since 6.0.03382 *3383 * @param array $webfont The webfont arguments.3384 * @return array|false The validated webfont arguments, or false if the webfont is invalid.3385 */3386 $fn_validate_webfont = static function( $webfont ) {3387 $webfont = wp_parse_args(3388 $webfont,3389 array(3390 'font-family' => '',3391 'font-style' => 'normal',3392 'font-weight' => '400',3393 'font-display' => 'fallback',3394 'src' => array(),3395 )3396 );3397 3398 // Check the font-family.3399 if ( empty( $webfont['font-family'] ) || ! is_string( $webfont['font-family'] ) ) {3400 trigger_error( __( 'Webfont font family must be a non-empty string.' ) );3401 3402 return false;3403 }3404 3405 // Check that the `src` property is defined and a valid type.3406 if ( empty( $webfont['src'] ) || ( ! is_string( $webfont['src'] ) && ! is_array( $webfont['src'] ) ) ) {3407 trigger_error( __( 'Webfont src must be a non-empty string or an array of strings.' ) );3408 3409 return false;3410 }3411 3412 // Validate the `src` property.3413 foreach ( (array) $webfont['src'] as $src ) {3414 if ( ! is_string( $src ) || '' === trim( $src ) ) {3415 trigger_error( __( 'Each webfont src must be a non-empty string.' ) );3416 3417 return false;3418 }3419 }3420 3421 // Check the font-weight.3422 if ( ! is_string( $webfont['font-weight'] ) && ! is_int( $webfont['font-weight'] ) ) {3423 trigger_error( __( 'Webfont font weight must be a properly formatted string or integer.' ) );3424 3425 return false;3426 }3427 3428 // Check the font-display.3429 if ( ! in_array( $webfont['font-display'], array( 'auto', 'block', 'fallback', 'optional', 'swap' ), true ) ) {3430 $webfont['font-display'] = 'fallback';3431 }3432 3433 $valid_props = array(3434 'ascend-override',3435 'descend-override',3436 'font-display',3437 'font-family',3438 'font-stretch',3439 'font-style',3440 'font-weight',3441 'font-variant',3442 'font-feature-settings',3443 'font-variation-settings',3444 'line-gap-override',3445 'size-adjust',3446 'src',3447 'unicode-range',3448 );3449 3450 foreach ( $webfont as $prop => $value ) {3451 if ( ! in_array( $prop, $valid_props, true ) ) {3452 unset( $webfont[ $prop ] );3453 }3454 }3455 3456 return $webfont;3457 };3458 3459 /**3460 * Registers webfonts declared in theme.json.3461 *3462 * @since 6.0.03463 *3464 * @uses $registered_webfonts To access and update the registered webfonts registry (passed by reference).3465 * @uses $fn_get_webfonts_from_theme_json To run the function that gets the webfonts from theme.json.3466 * @uses $fn_convert_keys_to_kebab_case To run the function that converts keys into kebab-case.3467 * @uses $fn_validate_webfont To run the function that validates each font-face (webfont) from theme.json.3468 */3469 $fn_register_webfonts = static function() use ( &$registered_webfonts, $fn_get_webfonts_from_theme_json, $fn_convert_keys_to_kebab_case, $fn_validate_webfont, $fn_transform_src_into_uri ) {3470 $registered_webfonts = array();3471 3472 foreach ( $fn_get_webfonts_from_theme_json() as $webfont ) {3473 if ( ! is_array( $webfont ) ) {3474 continue;3475 }3476 3477 $webfont = $fn_convert_keys_to_kebab_case( $webfont );3478 3479 $webfont = $fn_validate_webfont( $webfont );3480 3481 $webfont['src'] = $fn_transform_src_into_uri( (array) $webfont['src'] );3482 3483 // Skip if not valid.3484 if ( empty( $webfont ) ) {3485 continue;3486 }3487 3488 $registered_webfonts[] = $webfont;3489 }3490 };3491 3492 /**3493 * Orders 'src' items to optimize for browser support.3494 *3495 * @since 6.0.03496 *3497 * @param array $webfont Webfont to process.3498 * @return array Ordered `src` items.3499 */3500 $fn_order_src = static function( array $webfont ) {3501 $src = array();3502 $src_ordered = array();3503 3504 foreach ( $webfont['src'] as $url ) {3505 // Add data URIs first.3506 if ( str_starts_with( trim( $url ), 'data:' ) ) {3507 $src_ordered[] = array(3508 'url' => $url,3509 'format' => 'data',3510 );3511 continue;3512 }3513 $format = pathinfo( $url, PATHINFO_EXTENSION );3514 $src[ $format ] = $url;3515 }3516 3517 // Add woff2.3518 if ( ! empty( $src['woff2'] ) ) {3519 $src_ordered[] = array(3520 'url' => sanitize_url( $src['woff2'] ),3521 'format' => 'woff2',3522 );3523 }3524 3525 // Add woff.3526 if ( ! empty( $src['woff'] ) ) {3527 $src_ordered[] = array(3528 'url' => sanitize_url( $src['woff'] ),3529 'format' => 'woff',3530 );3531 }3532 3533 // Add ttf.3534 if ( ! empty( $src['ttf'] ) ) {3535 $src_ordered[] = array(3536 'url' => sanitize_url( $src['ttf'] ),3537 'format' => 'truetype',3538 );3539 }3540 3541 // Add eot.3542 if ( ! empty( $src['eot'] ) ) {3543 $src_ordered[] = array(3544 'url' => sanitize_url( $src['eot'] ),3545 'format' => 'embedded-opentype',3546 );3547 }3548 3549 // Add otf.3550 if ( ! empty( $src['otf'] ) ) {3551 $src_ordered[] = array(3552 'url' => sanitize_url( $src['otf'] ),3553 'format' => 'opentype',3554 );3555 }3556 $webfont['src'] = $src_ordered;3557 3558 return $webfont;3559 };3560 3561 /**3562 * Compiles the 'src' into valid CSS.3563 *3564 * @since 6.0.03565 * @since 6.2.0 Removed local() CSS.3566 *3567 * @param string $font_family Font family.3568 * @param array $value Value to process.3569 * @return string The CSS.3570 */3571 $fn_compile_src = static function( $font_family, array $value ) {3572 $src = '';3573 3574 foreach ( $value as $item ) {3575 $src .= ( 'data' === $item['format'] )3576 ? ", url({$item['url']})"3577 : ", url('{$item['url']}') format('{$item['format']}')";3578 }3579 3580 $src = ltrim( $src, ', ' );3581 3582 return $src;3583 };3584 3585 /**3586 * Compiles the font variation settings.3587 *3588 * @since 6.0.03589 *3590 * @param array $font_variation_settings Array of font variation settings.3591 * @return string The CSS.3592 */3593 $fn_compile_variations = static function( array $font_variation_settings ) {3594 $variations = '';3595 3596 foreach ( $font_variation_settings as $key => $value ) {3597 $variations .= "$key $value";3598 }3599 3600 return $variations;3601 };3602 3603 /**3604 * Builds the font-family's CSS.3605 *3606 * @since 6.0.03607 *3608 * @uses $fn_compile_src To run the function that compiles the src.3609 * @uses $fn_compile_variations To run the function that compiles the variations.3610 *3611 * @param array $webfont Webfont to process.3612 * @return string This font-family's CSS.3613 */3614 $fn_build_font_face_css = static function( array $webfont ) use ( $fn_compile_src, $fn_compile_variations ) {3615 $css = '';3616 3617 // Wrap font-family in quotes if it contains spaces.3618 if (3619 str_contains( $webfont['font-family'], ' ' ) &&3620 ! str_contains( $webfont['font-family'], '"' ) &&3621 ! str_contains( $webfont['font-family'], "'" )3622 ) {3623 $webfont['font-family'] = '"' . $webfont['font-family'] . '"';3624 }3625 3626 foreach ( $webfont as $key => $value ) {3627 /*3628 * Skip "provider", since it's for internal API use,3629 * and not a valid CSS property.3630 */3631 if ( 'provider' === $key ) {3632 continue;3633 }3634 3635 // Compile the "src" parameter.3636 if ( 'src' === $key ) {3637 $value = $fn_compile_src( $webfont['font-family'], $value );3638 }3639 3640 // If font-variation-settings is an array, convert it to a string.3641 if ( 'font-variation-settings' === $key && is_array( $value ) ) {3642 $value = $fn_compile_variations( $value );3643 }3644 3645 if ( ! empty( $value ) ) {3646 $css .= "$key:$value;";3647 }3648 }3649 3650 return $css;3651 };3652 3653 /**3654 * Gets the '@font-face' CSS styles for locally-hosted font files.3655 *3656 * @since 6.0.03657 *3658 * @uses $registered_webfonts To access and update the registered webfonts registry (passed by reference).3659 * @uses $fn_order_src To run the function that orders the src.3660 * @uses $fn_build_font_face_css To run the function that builds the font-face CSS.3661 *3662 * @return string The `@font-face` CSS.3663 */3664 $fn_get_css = static function() use ( &$registered_webfonts, $fn_order_src, $fn_build_font_face_css ) {3665 $css = '';3666 3667 foreach ( $registered_webfonts as $webfont ) {3668 // Order the webfont's `src` items to optimize for browser support.3669 $webfont = $fn_order_src( $webfont );3670 3671 // Build the @font-face CSS for this webfont.3672 $css .= '@font-face{' . $fn_build_font_face_css( $webfont ) . '}';3673 }3674 3675 return $css;3676 };3677 3678 /**3679 * Generates and enqueues webfonts styles.3680 *3681 * @since 6.0.03682 *3683 * @uses $fn_get_css To run the function that gets the CSS.3684 */3685 $fn_generate_and_enqueue_styles = static function() use ( $fn_get_css ) {3686 // Generate the styles.3687 $styles = $fn_get_css();3688 3689 // Bail out if there are no styles to enqueue.3690 if ( '' === $styles ) {3691 return;3692 }3693 3694 // Enqueue the stylesheet.3695 wp_register_style( 'wp-webfonts', '' );3696 wp_enqueue_style( 'wp-webfonts' );3697 3698 // Add the styles to the stylesheet.3699 wp_add_inline_style( 'wp-webfonts', $styles );3700 };3701 3702 /**3703 * Generates and enqueues editor styles.3704 *3705 * @since 6.0.03706 *3707 * @uses $fn_get_css To run the function that gets the CSS.3708 */3709 $fn_generate_and_enqueue_editor_styles = static function() use ( $fn_get_css ) {3710 // Generate the styles.3711 $styles = $fn_get_css();3712 3713 // Bail out if there are no styles to enqueue.3714 if ( '' === $styles ) {3715 return;3716 }3717 3718 wp_add_inline_style( 'wp-block-library', $styles );3719 };3720 3721 add_action( 'wp_loaded', $fn_register_webfonts );3722 add_action( 'wp_enqueue_scripts', $fn_generate_and_enqueue_styles );3723 add_action( 'admin_init', $fn_generate_and_enqueue_editor_styles );3724 }3725 3726 /**3727 3227 * Loads classic theme styles on classic themes in the frontend. 3728 3228 *
Note: See TracChangeset
for help on using the changeset viewer.