From cef117add408f492751b3f4731eca5da66b3e3b0 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Fri, 6 Aug 2021 13:02:12 +0300 Subject: [PATCH 01/94] 1st Webfonts API implementation - to change. Bringing this implementation in as a starting point to capture the knowledge. This implementation will be changed in this branch / PR. --- .../class-wp-fonts-provider-google.php | 149 ++++++++++ .../class-wp-fonts-provider-local.php | 72 +++++ src/wp-includes/class-wp-fonts-provider.php | 257 ++++++++++++++++++ src/wp-includes/functions.php | 7 + src/wp-includes/functions.wp-webfonts.php | 227 ++++++++++++++++ src/wp-includes/script-loader.php | 6 + .../rest-api/rest-attachments-controller.php | 1 + tests/qunit/fixtures/wp-api-generated.js | 3 +- 8 files changed, 721 insertions(+), 1 deletion(-) create mode 100644 src/wp-includes/class-wp-fonts-provider-google.php create mode 100644 src/wp-includes/class-wp-fonts-provider-local.php create mode 100644 src/wp-includes/class-wp-fonts-provider.php create mode 100644 src/wp-includes/functions.wp-webfonts.php diff --git a/src/wp-includes/class-wp-fonts-provider-google.php b/src/wp-includes/class-wp-fonts-provider-google.php new file mode 100644 index 0000000000000..de642eaa9668f --- /dev/null +++ b/src/wp-includes/class-wp-fonts-provider-google.php @@ -0,0 +1,149 @@ + 'https://fonts.gstatic.com', + 'crossorigin' => true, + ), + array( + 'href' => 'https://fonts.googleapis.com', + 'crossorigin' => false, + ), + ); + + /** + * The provider's root URL. + * + * @access protected + * @since 5.9.0 + * @var string + */ + protected $root_url = 'https://fonts.googleapis.com/css2'; + + /** + * An array of API parameters which will not be added to the @font-face. + * + * @access protected + * @since 5.9.0 + * @var array + */ + protected $api_params = array( + 'subset', + 'text', + 'effect', + ); + + /** + * Build the API URL from the query args. + * + * @access protected + * @since 5.9.0 + * @return string + */ + protected function build_api_url() { + $query_args = array( + 'family' => $this->params['font-family'], + 'display' => $this->params['font-display'], + ); + + if ( 'italic' === $this->params['font-style'] ) { + $query_args['family'] .= ':ital,wght@1,' . $this->params['font-weight']; + } else { + $query_args['family'] .= ':wght@' . $this->params['font-weight']; + } + + if ( ! empty( $this->params['subset'] ) ) { + $query_args['subset'] = implode( ',', (array) $this->params['subset'] ); + } + + if ( ! empty( $this->params['text'] ) ) { + $query_args['text'] = $this->params['text']; + } + + if ( ! empty( $this->params['effect'] ) ) { + $query_args['effect'] = implode( '|', (array) $this->params['effect'] ); + } + + return add_query_arg( $query_args, $this->root_url ); + } + + /** + * Get the CSS for the font. + * + * @access public + * @since 5.9.0 + * @return string + */ + public function get_css() { + $remote_url = $this->build_api_url(); + $transient_name = 'google_fonts_' . md5( $remote_url ); + $css = get_site_transient( $transient_name ); + + // Get remote response and cache the CSS if it hasn't been cached already. + if ( false === $css ) { + // Get the remote URL contents. + $response = wp_remote_get( + $remote_url, + array( + // Use a modern user-agent, to get woff2 files. + 'user-agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0', + ) + ); + + // Early return if the request failed. + if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { + set_site_transient( $transient_name, '', 60 ); + return ''; + } + + // Get the response body. + $css = wp_remote_retrieve_body( $response ); + + // Cache the CSS for a month. + set_site_transient( $transient_name, $css, MONTH_IN_SECONDS ); + } + + // If there are additional props not included in the CSS provided by the API, add them to the final CSS. + $additional_props = array_diff( + array_keys( $this->params ), + array( 'font-family', 'font-style', 'font-weight', 'font-display', 'src', 'unicode-range' ) + ); + foreach ( $additional_props as $prop ) { + $css = str_replace( + '@font-face {', + '@font-face {' . $prop . ':' . $this->params[ $prop ] . ';', + $css + ); + } + + return $css; + } +} diff --git a/src/wp-includes/class-wp-fonts-provider-local.php b/src/wp-includes/class-wp-fonts-provider-local.php new file mode 100644 index 0000000000000..cbe885264de32 --- /dev/null +++ b/src/wp-includes/class-wp-fonts-provider-local.php @@ -0,0 +1,72 @@ +params['font-family'] ) ) { + return ''; + } + + $css = '@font-face{'; + foreach ( $this->params as $key => $value ) { + + // Skip the "preload" parameter. + if ( 'preload' === $key ) { + continue; + } + + // Compile the "src" parameter. + if ( 'src' === $key ) { + $src = "local({$this->params['font-family']})"; + foreach ( $value as $item ) { + $src .= ( 'data' === $item['format'] ) + ? ", url({$item['url']})" + : ", url('{$item['url']}') format('{$item['format']}')"; + } + $value = $src; + } + + // If font-variation-settings is an array, convert it to a string. + if ( 'font-variation-settings' === $key && is_array( $value ) ) { + $variations = array(); + foreach ( $value as $key => $val ) { + $variations[] = "$key $val"; + } + $value = implode( ', ', $variations ); + } + + if ( ! empty( $value ) ) { + $css .= "$key:$value;"; + } + } + $css .= '}'; + + return $css; + } +} diff --git a/src/wp-includes/class-wp-fonts-provider.php b/src/wp-includes/class-wp-fonts-provider.php new file mode 100644 index 0000000000000..c6e6a048164f6 --- /dev/null +++ b/src/wp-includes/class-wp-fonts-provider.php @@ -0,0 +1,257 @@ +id; + } + + /** + * Get the root URL for the provider. + * + * @access public + * @return string + */ + public function get_root_url() { + return $this->root_url; + } + + /** + * Get the array of URLs to preconnect to. + * + * @access public + * @return array + */ + public function get_preconnect_urls() { + return $this->preconnect_urls; + } + + /** + * Set the object's params. + * + * @access public + * @since 5.9.0 + * @param array $params The webfont's parameters. + * @return void + */ + public function set_params( $params ) { + // Default values. + $defaults = array( + 'font-weight' => '400', + 'font-style' => 'normal', + 'font-display' => 'fallback', + 'src' => array(), + ); + + // Merge defaults with passed params. + $params = wp_parse_args( $params, $defaults ); + + // Whitelisted params. + $whitelist = array_merge( $this->valid_font_face_properties, $this->api_params ); + + // Only allow whitelisted properties. + foreach ( $params as $key => $value ) { + if ( ! in_array( $key, $whitelist, true ) ) { + unset( $params[ $key ] ); + } + } + + // Order $src items to optimize for browser support. + if ( ! empty( $params['src'] ) ) { + $params['src'] = (array) $params['src']; + $src = array(); + $src_ordered = array(); + + foreach ( $params['src'] as $url ) { + // Add data URIs first. + if ( 0 === strpos( trim( $url ), 'data:' ) ) { + $src_ordered[] = array( + 'url' => $url, + 'format' => 'data', + ); + continue; + } + $format = pathinfo( $url, PATHINFO_EXTENSION ); + $src[ $format ] = $url; + } + + // Add woff2. + if ( ! empty( $src['woff2'] ) ) { + $src_ordered[] = array( + 'url' => $src['woff2'], + 'format' => 'woff2', + ); + } + + // Add woff. + if ( ! empty( $src['woff'] ) ) { + $src_ordered[] = array( + 'url' => $src['woff'], + 'format' => 'woff', + ); + } + + // Add ttf. + if ( ! empty( $src['ttf'] ) ) { + $src_ordered[] = array( + 'url' => $src['ttf'], + 'format' => 'truetype', + ); + } + + // Add eot. + if ( ! empty( $src['eot'] ) ) { + $src_ordered[] = array( + 'url' => $src['eot'], + 'format' => 'embedded-opentype', + ); + } + + // Add otf. + if ( ! empty( $src['otf'] ) ) { + $src_ordered[] = array( + 'url' => $src['otf'], + 'format' => 'opentype', + ); + } + $params['src'] = $src_ordered; + } + + // Only allow valid font-display values. + if ( + ! empty( $params['font-display'] ) && + ! in_array( $params['font-display'], array( 'auto', 'block', 'swap', 'fallback' ), true ) + ) { + $params['font-display'] = 'fallback'; + } + + // Only allow valid font-style values. + if ( + ! empty( $params['font-style'] ) && + ! in_array( $params['font-style'], array( 'normal', 'italic', 'oblique' ), true ) && + ! preg_match( '/^oblique\s+(\d+)%/', $params['font-style'], $matches ) + ) { + $params['font-style'] = 'normal'; + } + + // Only allow valid font-weight values. + if ( + ! empty( $params['font-weight'] ) && + ! in_array( $params['font-weight'], array( 'normal', 'bold', 'bolder', 'lighter', 'inherit' ), true ) && + ! preg_match( '/^(\d+)$/', $params['font-weight'], $matches ) && + ! preg_match( '/^(\d+)\s+(\d+)$/', $params['font-weight'], $matches ) + ) { + $params['font-weight'] = 'normal'; + } + + $this->params = $params; + } + + /** + * Get the object's params. + * + * @access public + * @since 5.9.0 + * @return array + */ + public function get_params() { + return $this->params; + } + + /** + * Get the CSS for the font. + * + * @access public + * @since 5.9.0 + * @return string + */ + abstract public function get_css(); +} diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 2417bcdf76b4a..07b70283adb4a 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -3268,6 +3268,7 @@ function wp_get_image_mime( $file ) { * @since 4.2.0 Support was added for GIMP (.xcf) files. * @since 4.9.2 Support was added for Flac (.flac) files. * @since 4.9.6 Support was added for AAC (.aac) files. + * @since 5.9.0 Support was added for webfont (.woff2, .woff, .ttf, .eot, .otf) files. * * @return string[] Array of mime types keyed by the file extension regex corresponding to those types. */ @@ -3332,6 +3333,12 @@ function wp_get_mime_types() { 'wma' => 'audio/x-ms-wma', 'wax' => 'audio/x-ms-wax', 'mka' => 'audio/x-matroska', + // Webfonts formats. + 'woff2' => 'font/woff2', + 'woff' => 'font/woff', + 'ttf' => 'font/ttf', + 'eot' => 'application/vnd.ms-fontobject', + 'otf' => 'application/x-font-opentype', // Misc application formats. 'rtf' => 'application/rtf', 'js' => 'application/javascript', diff --git a/src/wp-includes/functions.wp-webfonts.php b/src/wp-includes/functions.wp-webfonts.php new file mode 100644 index 0000000000000..fd8a888076704 --- /dev/null +++ b/src/wp-includes/functions.wp-webfonts.php @@ -0,0 +1,227 @@ +set_params( $params ); + // Get the CSS. + return $provider->get_css(); +} + +/** + * Add preconnect links to for enqueued webfonts. + * + * @since 5.9.0 + * + * @param array $params The webfont parameters. + * + * @return void + */ +function _wp_webfont_add_preconnect_links( $params ) { + + $provider = isset( $params['provider'] ) ? $params['provider'] : new WP_Fonts_Provider_Local(); + $provider->set_params( $params ); + + // Store a static var to avoid adding the same preconnect links multiple times. + static $preconnect_urls_added_from_api = array(); + // Add preconnect links. + add_action( + 'wp_head', + function() use ( $provider, &$preconnect_urls_added_from_api ) { + + // Early exit if the provider has already added preconnect links. + if ( in_array( $provider->get_id(), $preconnect_urls_added_from_api ) ) { + return; + } + + // Add the preconnect links. + $preconnect_urls = $provider->get_preconnect_urls(); + foreach ( $preconnect_urls as $preconnection ) { + echo ' $value ) { + if ( 'href' === $key ) { + echo ' href="' . esc_url( $value ) . '"'; + } elseif ( true === $value || false === $value ) { + echo $value ? ' ' . esc_attr( $key ) : ''; + } else { + echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"'; + } + } + echo '>' . "\n"; + } + $preconnect_urls_added_from_api[] = $provider->get_id(); + } + ); +} diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 972b4497a6f23..589c6d12bdeb4 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -34,6 +34,12 @@ /** WordPress Styles Functions */ require ABSPATH . WPINC . '/functions.wp-styles.php'; +/** WordPress Webfonts Functions */ +require ABSPATH . WPINC . '/class-wp-fonts-provider.php'; +require ABSPATH . WPINC . '/class-wp-fonts-provider-local.php'; +require ABSPATH . WPINC . '/class-wp-fonts-provider-google.php'; +require ABSPATH . WPINC . '/functions.wp-webfonts.php'; + /** * Registers TinyMCE scripts. * diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index fae3bbb8b397e..77cb1dc509980 100644 --- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php @@ -220,6 +220,7 @@ public function test_registered_query_params() { ); if ( ! is_multisite() ) { $media_types[] = 'text'; + $media_types[] = 'font'; } $this->assertSameSets( $media_types, $data['endpoints'][0]['args']['media_type']['enum'] ); } diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index 3c6f4906af686..1b9952082ef0a 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -2858,7 +2858,8 @@ mockedApiResponse.Schema = { "video", "text", "application", - "audio" + "audio", + "font" ], "required": false }, From 9facf393745ff8e7299a93d75997c4d29d57d211 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 6 Oct 2021 10:34:11 +0300 Subject: [PATCH 02/94] Register collections instead of individual fonts --- src/wp-includes/functions.wp-webfonts.php | 234 +++++++++------------- 1 file changed, 99 insertions(+), 135 deletions(-) diff --git a/src/wp-includes/functions.wp-webfonts.php b/src/wp-includes/functions.wp-webfonts.php index fd8a888076704..9e211823e8980 100644 --- a/src/wp-includes/functions.wp-webfonts.php +++ b/src/wp-includes/functions.wp-webfonts.php @@ -9,157 +9,55 @@ */ /** - * Register a webfont's stylesheet and generate CSS rules for it. - * - * @see WP_Dependencies::add() - * @link https://www.w3.org/TR/CSS2/media.html#media-types List of CSS media types. + * Registers a font-collection. * * @since 5.9.0 * - * @param string $handle Name of the webfont. Should be unique. - * @param string|bool $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory. - * @param array $params Optional. An array of parameters. Default empty array. - * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL - * as a query string for cache busting purposes. If version is set to false, a version - * number is automatically added equal to current installed WordPress version. - * If set to null, no version is added. - * @param string $media Optional. The media for which this stylesheet has been defined. - * Default 'screen'. Accepts media types like 'all', 'print' and 'screen', or media queries like - * '(orientation: portrait)' and '(max-width: 640px)'. - * @return bool Whether the style has been registered. True on success, false on failure. + * @param array $fonts An array of fonts to be registered. */ -function wp_register_webfont( $handle = '', $src = '', $params = array(), $ver = null, $media = 'screen' ) { - - // Generate handle if not provided. - if ( empty( $handle && ! empty( $params ) ) ) { - $handle = md5( json_encode( $params ) ); - } - - // Early return if there is no handle. - if ( empty( $handle ) ) { - return; +function wp_register_font_collection( $fonts ) { + // Get the stylesheet handle. + $stylesheet_handle = 'webfonts'; + $hook = 'wp_enqueue_scripts'; + if ( did_action( 'wp_enqueue_scripts' ) ) { + $stylesheet_handle = 'webfonts-footer'; + $hook = 'wp_print_footer_scripts'; } - // Register the stylesheet. - $result = wp_register_style( "webfont-$handle", $src, array(), $ver, $media ); - - // Add inline styles for generated @font-face styles. - $inline_styles = wp_webfont_generate_styles( $params ); - if ( $inline_styles ) { - wp_add_inline_style( "webfont-$handle", $inline_styles ); - } + add_action( + $hook, + function() use ( $stylesheet_handle, $fonts ) { + // Generate the styles. + $styles = wp_webfonts_collection_generate_styles( $fonts ); - // Add preconnect links for external webfonts. - _wp_webfont_add_preconnect_links( $params ); + // Enqueue the stylesheet. + wp_register_style( $stylesheet_handle, '' ); + wp_enqueue_style( $stylesheet_handle ); - return $result; + // Add the styles to the stylesheet. + wp_add_inline_style( $stylesheet_handle, $styles ); + } + ); } /** - * Remove a registered webfont. - * - * @see WP_Dependencies::remove() + * Generate styles for a webfonts collection. * * @since 5.9.0 * - * @param string $handle Name of the webfont to be removed. - */ -function wp_deregister_webfont( $handle ) { - wp_deregister_style( "webfont-$handle" ); -} - -/** - * Enqueue a webfont's CSS stylesheet and generate CSS rules for it. - * - * Registers the style if source provided (does NOT overwrite) and enqueues. - * - * @see WP_Dependencies::add() - * @see WP_Dependencies::enqueue() - * @link https://www.w3.org/TR/CSS2/media.html#media-types List of CSS media types. + * @param array $fonts An array of webfonts. * - * @since 5.9.0 - * - * @param string $handle Name of the webfont. Should be unique. - * @param string $src Full URL of the stylesheet, or path of the stylesheet relative to the WordPress root directory. - * Default empty. - * @param array $params Optional. An array of parameters. Default empty array. - * @param string|bool|null $ver Optional. String specifying stylesheet version number, if it has one, which is added to the URL - * as a query string for cache busting purposes. If version is set to false, a version - * number is automatically added equal to current installed WordPress version. - * If set to null, no version is added. - * @param string $media Optional. The media for which this stylesheet has been defined. - * Default 'screen'. Accepts media types like 'all', 'print' and 'screen', or media queries like - * '(orientation: portrait)' and '(max-width: 640px)'. + * @return string The generated styles. */ -function wp_enqueue_webfont( $handle = '', $src = '', $params = array(), $ver = null, $media = 'screen' ) { - if ( $src || ! empty( $params ) ) { - wp_register_webfont( $handle, $src, $params, $ver, $media ); - } +function wp_webfonts_collection_generate_styles( $fonts ) { + $styles = ''; + foreach ( $fonts as $font ) { + $styles .= wp_webfont_generate_styles( $font ); - // Generate handle if not provided. - if ( empty( $handle && ! empty( $params ) ) ) { - $handle = md5( json_encode( $params ) ); + // Add preconnect links for external webfonts. + _wp_webfont_add_preconnect_links( $font ); } - - // Early return if there is no handle. - if ( empty( $handle ) ) { - return; - } - - return wp_enqueue_style( "webfont-$handle" ); -} - -/** - * Remove a previously enqueued webfont. - * - * @see WP_Dependencies::dequeue() - * - * @since 5.9.0 - * - * @param string $handle Name of the webfont to be removed. - */ -function wp_dequeue_webfont( $handle ) { - wp_dequeue_style( "webfont-$handle" ); -} - -/** - * Check whether a webfont's CSS stylesheet has been added to the queue. - * - * @since 5.9.0 - * - * @param string $handle Name of the webfont. - * @param string $list Optional. Status of the webfont to check. Default 'enqueued'. - * Accepts 'enqueued', 'registered', 'queue', 'to_do', and 'done'. - * @return bool Whether style is queued. - */ -function wp_webfont_is( $handle, $list = 'enqueued' ) { - return wp_style_is( "webfont-$handle", $list ); -} - -/** - * Add metadata to a CSS stylesheet. - * - * Works only if the stylesheet has already been added. - * - * Possible values for $key and $value: - * 'conditional' string Comments for IE 6, lte IE 7 etc. - * 'rtl' bool|string To declare an RTL stylesheet. - * 'suffix' string Optional suffix, used in combination with RTL. - * 'alt' bool For rel="alternate stylesheet". - * 'title' string For preferred/alternate stylesheets. - * - * @see WP_Dependencies::add_data() - * - * @since 5.9.0 - * - * @param string $handle Name of the stylesheet. - * @param string $key Name of data point for which we're storing a value. - * Accepts 'conditional', 'rtl' and 'suffix', 'alt' and 'title'. - * @param mixed $value String containing the CSS data to be added. - * @return bool True on success, false on failure. - */ -function wp_webfont_add_data( $handle, $key, $value ) { - return wp_style_add_data( "webfont-$handle", $key, $value ); + return $styles; } /** @@ -172,10 +70,19 @@ function wp_webfont_add_data( $handle, $key, $value ) { * @return string The generated styles. */ function wp_webfont_generate_styles( $params ) { + // Get the array of providers. + $providers = wp_get_webfont_providers(); + // Fallback to local provider if none is specified. - $provider = isset( $params['provider'] ) ? $params['provider'] : new WP_Fonts_Provider_Local(); + $provider_id = isset( $params['provider'] ) ? $params['provider'] : 'local'; + if ( ! isset( $providers[ $provider_id ] ) ) { + return ''; + } + $provider = $providers[ $provider_id ]; + // Set the $params to the object. $provider->set_params( $params ); + // Get the CSS. return $provider->get_css(); } @@ -191,11 +98,16 @@ function wp_webfont_generate_styles( $params ) { */ function _wp_webfont_add_preconnect_links( $params ) { - $provider = isset( $params['provider'] ) ? $params['provider'] : new WP_Fonts_Provider_Local(); + $provider_id = isset( $params['provider'] ) ? $params['provider'] : 'local'; + if ( ! isset( $providers[ $provider_id ] ) ) { + return; + } + $provider = $providers[ $provider_id ]; $provider->set_params( $params ); // Store a static var to avoid adding the same preconnect links multiple times. static $preconnect_urls_added_from_api = array(); + // Add preconnect links. add_action( 'wp_head', @@ -225,3 +137,55 @@ function() use ( $provider, &$preconnect_urls_added_from_api ) { } ); } + +/** + * Register a webfont provider. + * + * @since 5.9.0 + * + * @param string $id The provider ID. + * @param string $class The provider class name. + */ +function wp_register_webfont_provider( $id, $class ) { + global $wp_webfonts_providers; + if ( ! $wp_webfonts_providers ) { + $wp_webfonts_providers = array(); + } + $wp_webfonts_providers[ $id ] = $class; +} + +/** + * Get webfonts providers. + * + * @since 5.9.0 + * + * @return array + */ +function wp_get_webfont_providers() { + global $wp_webfonts_providers; + if ( ! $wp_webfonts_providers ) { + $wp_webfonts_providers = array( + 'local' => 'WP_Fonts_Provider_Local', + 'google' => 'WP_Fonts_Provider_Google', + ); + } + + $providers = array(); + foreach ( $wp_webfonts_providers as $id => $class_name ) { + if ( ! class_exists( $class_name ) ) { + continue; + } + $providers[ $id ] = new $class_name(); + } + + /** + * Filters the list of registered webfont providers. + * + * @since 5.9.0 + * + * @param array $wp_webfonts_providers An array of registered webfont providers. + * + * @return array + */ + return apply_filters( 'wp_webfonts_providers', $providers ); +} From 85e0fb9b4714c752cfe3ce2bdcc86b8163a14fd1 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Mon, 11 Oct 2021 05:40:21 -0500 Subject: [PATCH 03/94] New architecture scaffolding. --- src/wp-includes/functions.wp-webfonts.php | 191 -------------- src/wp-includes/script-loader.php | 5 +- src/wp-includes/webfonts-api.php | 60 +++++ .../class-wp-webfonts-controller.php | 128 ++++++++++ .../class-wp-webfonts-provider-registry.php | 115 +++++++++ .../class-wp-webfonts-registry.php | 232 ++++++++++++++++++ .../class-wp-webfonts-google-provider.php} | 9 +- .../class-wp-webfonts-local-provider.php} | 4 +- .../providers/class-wp-webfonts-provider.php} | 22 +- 9 files changed, 547 insertions(+), 219 deletions(-) delete mode 100644 src/wp-includes/functions.wp-webfonts.php create mode 100644 src/wp-includes/webfonts-api.php create mode 100644 src/wp-includes/webfonts-api/class-wp-webfonts-controller.php create mode 100644 src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php create mode 100644 src/wp-includes/webfonts-api/class-wp-webfonts-registry.php rename src/wp-includes/{class-wp-fonts-provider-google.php => webfonts-api/providers/class-wp-webfonts-google-provider.php} (94%) rename src/wp-includes/{class-wp-fonts-provider-local.php => webfonts-api/providers/class-wp-webfonts-local-provider.php} (92%) rename src/wp-includes/{class-wp-fonts-provider.php => webfonts-api/providers/class-wp-webfonts-provider.php} (94%) diff --git a/src/wp-includes/functions.wp-webfonts.php b/src/wp-includes/functions.wp-webfonts.php deleted file mode 100644 index 9e211823e8980..0000000000000 --- a/src/wp-includes/functions.wp-webfonts.php +++ /dev/null @@ -1,191 +0,0 @@ -set_params( $params ); - - // Get the CSS. - return $provider->get_css(); -} - -/** - * Add preconnect links to for enqueued webfonts. - * - * @since 5.9.0 - * - * @param array $params The webfont parameters. - * - * @return void - */ -function _wp_webfont_add_preconnect_links( $params ) { - - $provider_id = isset( $params['provider'] ) ? $params['provider'] : 'local'; - if ( ! isset( $providers[ $provider_id ] ) ) { - return; - } - $provider = $providers[ $provider_id ]; - $provider->set_params( $params ); - - // Store a static var to avoid adding the same preconnect links multiple times. - static $preconnect_urls_added_from_api = array(); - - // Add preconnect links. - add_action( - 'wp_head', - function() use ( $provider, &$preconnect_urls_added_from_api ) { - - // Early exit if the provider has already added preconnect links. - if ( in_array( $provider->get_id(), $preconnect_urls_added_from_api ) ) { - return; - } - - // Add the preconnect links. - $preconnect_urls = $provider->get_preconnect_urls(); - foreach ( $preconnect_urls as $preconnection ) { - echo ' $value ) { - if ( 'href' === $key ) { - echo ' href="' . esc_url( $value ) . '"'; - } elseif ( true === $value || false === $value ) { - echo $value ? ' ' . esc_attr( $key ) : ''; - } else { - echo ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"'; - } - } - echo '>' . "\n"; - } - $preconnect_urls_added_from_api[] = $provider->get_id(); - } - ); -} - -/** - * Register a webfont provider. - * - * @since 5.9.0 - * - * @param string $id The provider ID. - * @param string $class The provider class name. - */ -function wp_register_webfont_provider( $id, $class ) { - global $wp_webfonts_providers; - if ( ! $wp_webfonts_providers ) { - $wp_webfonts_providers = array(); - } - $wp_webfonts_providers[ $id ] = $class; -} - -/** - * Get webfonts providers. - * - * @since 5.9.0 - * - * @return array - */ -function wp_get_webfont_providers() { - global $wp_webfonts_providers; - if ( ! $wp_webfonts_providers ) { - $wp_webfonts_providers = array( - 'local' => 'WP_Fonts_Provider_Local', - 'google' => 'WP_Fonts_Provider_Google', - ); - } - - $providers = array(); - foreach ( $wp_webfonts_providers as $id => $class_name ) { - if ( ! class_exists( $class_name ) ) { - continue; - } - $providers[ $id ] = new $class_name(); - } - - /** - * Filters the list of registered webfont providers. - * - * @since 5.9.0 - * - * @param array $wp_webfonts_providers An array of registered webfont providers. - * - * @return array - */ - return apply_filters( 'wp_webfonts_providers', $providers ); -} diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 589c6d12bdeb4..fa8676f0f8a50 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -35,10 +35,7 @@ require ABSPATH . WPINC . '/functions.wp-styles.php'; /** WordPress Webfonts Functions */ -require ABSPATH . WPINC . '/class-wp-fonts-provider.php'; -require ABSPATH . WPINC . '/class-wp-fonts-provider-local.php'; -require ABSPATH . WPINC . '/class-wp-fonts-provider-google.php'; -require ABSPATH . WPINC . '/functions.wp-webfonts.php'; +require ABSPATH . WPINC . '/webfonts-api.php'; /** * Registers TinyMCE scripts. diff --git a/src/wp-includes/webfonts-api.php b/src/wp-includes/webfonts-api.php new file mode 100644 index 0000000000000..cd39926419b9c --- /dev/null +++ b/src/wp-includes/webfonts-api.php @@ -0,0 +1,60 @@ +init(); + } + + return $instance; +} + +/** + * Registers a webfont collection. + * + * @since 5.9.0 + * + * @param array[] $webfonts Webfonts to be registered. + */ +function wp_register_webfonts( array $webfonts ) { + wp_webfonts()->register_webfonts( $webfonts ); +} + +/** + * Register a webfont provider. + * + * @since 5.9.0 + * + * @param string $classname The provider class name. + */ +function wp_register_webfont_provider( $classname ) { + wp_webfonts()->register_provider( $classname ); +} + +/** + * Get webfonts providers. + * + * @since 5.9.0 + * + * @return WP_Webfonts_Provider[] Array of registered providers. + */ +function wp_get_webfont_providers() { + return wp_webfonts()->get_registered_providers(); +} diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php new file mode 100644 index 0000000000000..9d667ac3de483 --- /dev/null +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -0,0 +1,128 @@ +webfonts_registry = $webfonts_registry; + $this->providers_registry = $provider_registry; + } + + /** + * Initializes the controller. + * + * @since 5.9.0 + */ + public function init() { + $this->provider->init(); + + // Register enqueue callback to + if ( did_action( 'wp_enqueue_scripts' ) ) { + $this->stylesheet_handle = 'webfonts-footer'; + $hook = 'wp_print_footer_scripts'; + } else { + $this->stylesheet_handle = 'webfonts'; + $hook = 'wp_enqueue_scripts'; + } + add_action( $hook, array( $this, 'enqueue' ) ); + } + + /** + * Registers a webfont collection. + * + * @since 5.9.0 + * + * @param array[] $webfonts Webfonts to be registered. + */ + public function register_webfonts( array $webfonts ) { + array_walk( $webfonts, array( $this, 'register_webfont' ) ); + } + + + /** + * Registers a webfont. + * + * @param string[] $webfont Webfont definition. + */ + public function register_webfont( array $webfont ) { + $registration_key = $this->webfonts_registry->register( $webfont ); + if ( '' === $registration_key ) { + return; + } + + // Register the webfont's registration key to its provider. + $this->providers_registry->register_webfont( $webfont['provider'], $registration_key ); + } + + public function get_registered_providers() { + return $this->providers_registry->get_registry(); + } + + public function register_provider( $classname ) { + return $this->providers_registry->register( $classname ); + } + + public function enqueue() { + // Generate the styles. + $styles = $this->generate_styles(); + + // Enqueue the stylesheet. + wp_register_style( $this->stylesheet_handle, '' ); + wp_enqueue_style( $this->stylesheet_handle ); + + // Add the styles to the stylesheet. + wp_add_inline_style( $this->stylesheet_handle, $styles ); + } + + private function generate_styles() { + $styles = ''; + foreach ( $this->get_registered_providers() as $provider_id => $provider ) { + $registered_webfonts = $this->webfonts_registry->get_by_provider( $provider_id ); + + if ( empty( $registered_webfonts ) ) { + continue; + } + + add_action( 'wp_head', array( $this, 'add_preconnect_links' ) ); + + $provider->set_webfonts( $registered_webfonts ); + $styles .= $provider->get_css(); + } + return $styles; + } + + /** + * Add preconnect links to for enqueued webfonts. + * + * @since 5.9.0 + */ + public function add_preconnect_links() { + echo $this->providers_registry->generate_preconnect_links(); + } +} diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php new file mode 100644 index 0000000000000..ef074aeceffca --- /dev/null +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -0,0 +1,115 @@ +registry; + } + + /** + * Initializes the registry. + * + * @since 5.9.0 + */ + public function init() { + $this->register_core_providers(); + } + + /** + * Registers the core providers. + * + * @since 5.9.0 + */ + private function register_core_providers() { + // Load the abstract class into memory. + require_once __DIR__ . '/providers/class-wp-webfonts-provider.php'; + + // Register the Google Provider. + require_once __DIR__ . '/providers/class-wp-webfonts-google-provider.php'; + $this->register( WP_Webfonts_Google_Provider::class ); + + // Register the Local Provider. + require_once __DIR__ . '/providers/class-wp-webfonts-local-provider.php'; + $this->register( WP_Webfonts_Local_Provider::class ); + } + + /** + * Registers the given provider. + * + * @since 5.9.0 + * + * @param string $classname The provider class name. + * @return bool True when registered. False when provider does not exist. + */ + public function register( $classname ) { + if ( ! class_exists( $classname ) ) { + return ''; + } + + $provider = new $classname; + $id = $provider->get_id(); + + if ( ! isset( $this->providers[ $id ] ) ) { + $this->registry[ $id ] = $provider; + } + + return $id; + } + + public function get_preconnect_links() { + // Store a static var to avoid adding the same preconnect links multiple times. + static $generated = array(); + + $links = ''; + + foreach ( $this->registry as $provider_id => $provider ) { + // Skip if the provider already added preconnect links. + if ( isset( $generated[ $provider_id ] ) ) { + continue; + } + + $links .= $this->get_preconnect_link( $provider ); + + $added[ $provider_id ] = true; + } + + return $links; + } + + private function get_preconnect_link( $provider ) { + $link = ''; + + foreach ( $provider->get_preconnect_urls() as $preconnection ) { + $link .= ' $value ) { + if ( 'href' === $key ) { + $link .= ' href="' . esc_url( $value ) . '"'; + } elseif ( true === $value || false === $value ) { + $link .= $value ? ' ' . esc_attr( $key ) : ''; + } else { + $link .= ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"'; + } + } + $link .= '>' . PHP_EOL; + } + + return $link; + } +} diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php new file mode 100644 index 0000000000000..56b25df138977 --- /dev/null +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -0,0 +1,232 @@ +registry; + } + + /** + * Gets the registered webfonts for the given provider. + * + * @since 5.9.0 + * + * @param string $provider_id Provider ID to fetch. + * @return array[] Registered webfonts. + */ + public function get_by_provider( $provider_id ) { + if ( ! isset( $this->registry_by_provider[ $provider_id ] ) ) { + return array(); + } + + $webfonts = array(); + foreach ( $this->registry_by_provider[ $provider_id ] as $registration_key ) { + // Safeguard. Skip if not in registry. + if ( ! isset( $this->registry[ $registration_key ] ) ) { + continue; + } + + $webfonts[ $registration_key ] = $this->registry[ $registration_key ]; + } + + return $webfonts; + } + + /** + * Gets the registered webfonts for the given font-family. + * + * @since 5.9.0 + * + * @param string $font_family Family font to fetch. + * @return array[] Registered webfonts. + */ + public function get_by_font_family( $font_family ) { + if ( ! is_string( $font_family ) || '' === $font_family ) { + return array(); + } + + $webfonts = array(); + $font_family_key = $this->convert_font_family_into_key( $font_family ) . '.'; + $last_char = strlen( $font_family_key ); + + foreach ( $this->registry as $registration_key => $webfont ) { + // Skip if webfont's family font does not match. + if ( ! substr( $registration_key, 0, $last_char ) !== $font_family_key ) { + continue; + } + + $webfonts[ $registration_key ] = $webfont; + } + + return $webfonts; + } + + /** + * Registers the given webfont if its schema is valid. + * + * @since 5.9.0 + * + * @param string[] $webfont Webfont definition. + * @return string Registration key. + */ + public function register( array $webfont ) { + // Validate schema. + if ( ! $this->is_schema_valid( $webfont ) ) { + return ''; + } + + // Add to registry. + $registration_key = $this->generate_registration_key( $webfont ); + if ( ! isset( $this->registry[ $registration_key ] ) ) { + $this->registry[ $registration_key ] = $webfont; + $this->registry_by_provider[ $webfont['provider'] ][] = $registration_key; + } + + return $registration_key; + } + + /** + * Generates the registration key. + * + * Format: fontFamily.fontStyle.fontWeight + * For example: `'open-sans.normal.400'`. + * + * @since 5.9.0 + * + * @param string[] $webfont Webfont definition. + * @return string Registration key. + */ + private function generate_registration_key( array $webfont ) { + $key = $this->convert_font_family_into_key( $webfont['fontFamily'] ); + $key .= trim( $webfont['fontStyle'] ); + $key .= trim( $webfont['fontWeight'] ); + + return $key; + } + + /** + * Converts the given font family into a key. + * + * For example: 'Open Sans' becomes 'open-sans'. + * + * @since 5.9.0 + * + * @param string $font_family Font family to convert into a key. + * @return string + */ + private function convert_font_family_into_key( $font_family ) { + if ( ! is_string( $font_family ) || '' === $font_family ) { + return ''; + } + + return sanitize_title( $font_family ); + } + + /** + * Checks if the given webfont schema is validate. + * + * @since 5.9.0 + * + * @param string[] $webfont Webfont definition. + * @return bool True when valid. False when invalid. + */ + private function is_schema_valid( array $webfont ) { + if ( empty( $webfont['fontFamily'] ) || ! is_string( $webfont['fontFamily'] ) ) { + _doing_it_wrong( + 'register_webfonts', + __( 'Webfont must define a string "fontFamily".' ), + '5.9.0' + ); + return false; + } + + if ( empty( $webfont['fontStyle'] ) || ! is_string( $webfont['fontStyle'] ) ) { + _doing_it_wrong( + 'register_webfonts', + __( 'Webfont must define a string "fontStyle".' ), + '5.9.0' + ); + return false; + } + + if ( ! $this->is_valid_font_style( $webfont['fontStyle'] ) ) { + _doing_it_wrong( + 'register_webfonts', + sprintf( + /* translators: 1: Slant angle, 2: Given font style. */ + __( 'Webfont font style must be normal, italic, oblique, or oblique %1$s. Given: %2$s.' ), + '', + $webfont['fontStyle'] + ), + '5.9.0' + ); + return false; + } + + if ( empty( $webfont['fontWeight'] ) || ! is_string( $webfont['fontWeight'] ) ) { + _doing_it_wrong( + 'register_webfonts', + __( 'Webfont must define a string "fontWeight".' ), + '5.9.0' + ); + return false; + } + + // @todo check if provider is registered. + if ( empty( $webfont['provider'] ) || ! is_string( $webfont['provider'] ) ) { + _doing_it_wrong( + 'register_webfonts', + __( 'Webfont must define a string "provider".' ), + '5.9.0' + ); + return false; + } + + return true; + } + + private function is_valid_font_style( $font_style ) { + if ( in_array( $font_style, $this->valid_font_style, true ) ) { + return true; + } + + // @todo Check for oblique . + + } +} diff --git a/src/wp-includes/class-wp-fonts-provider-google.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php similarity index 94% rename from src/wp-includes/class-wp-fonts-provider-google.php rename to src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index de642eaa9668f..e6619ae0b1c11 100644 --- a/src/wp-includes/class-wp-fonts-provider-google.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -10,12 +10,11 @@ /** * Webfonts API provider for Google Fonts. */ -final class WP_Fonts_Provider_Google extends WP_Fonts_Provider { +final class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { /** * The provider's unique ID. * - * @access protected * @since 5.9.0 * @var string */ @@ -24,7 +23,6 @@ final class WP_Fonts_Provider_Google extends WP_Fonts_Provider { /** * An array of URLs to preconnect to. * - * @access protected * @since 5.9.0 * @var array */ @@ -42,7 +40,6 @@ final class WP_Fonts_Provider_Google extends WP_Fonts_Provider { /** * The provider's root URL. * - * @access protected * @since 5.9.0 * @var string */ @@ -51,7 +48,6 @@ final class WP_Fonts_Provider_Google extends WP_Fonts_Provider { /** * An array of API parameters which will not be added to the @font-face. * - * @access protected * @since 5.9.0 * @var array */ @@ -64,7 +60,6 @@ final class WP_Fonts_Provider_Google extends WP_Fonts_Provider { /** * Build the API URL from the query args. * - * @access protected * @since 5.9.0 * @return string */ @@ -98,8 +93,8 @@ protected function build_api_url() { /** * Get the CSS for the font. * - * @access public * @since 5.9.0 + * * @return string */ public function get_css() { diff --git a/src/wp-includes/class-wp-fonts-provider-local.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php similarity index 92% rename from src/wp-includes/class-wp-fonts-provider-local.php rename to src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index cbe885264de32..2c4cbfd0bee1e 100644 --- a/src/wp-includes/class-wp-fonts-provider-local.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -10,12 +10,11 @@ /** * Webfonts API provider for locally-hosted fonts. */ -final class WP_Fonts_Provider_Local extends WP_Fonts_Provider { +final class WP_Webfonts_Local_Provider extends WP_Fonts_Provider { /** * The provider's unique ID. * - * @access protected * @since 5.9.0 * @var string */ @@ -24,7 +23,6 @@ final class WP_Fonts_Provider_Local extends WP_Fonts_Provider { /** * Get the CSS for the font. * - * @access public * @since 5.9.0 * @return string */ diff --git a/src/wp-includes/class-wp-fonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php similarity index 94% rename from src/wp-includes/class-wp-fonts-provider.php rename to src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index c6e6a048164f6..4a29d298954df 100644 --- a/src/wp-includes/class-wp-fonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -12,12 +12,11 @@ /** * Abstract class for webfonts API providers. */ -abstract class WP_Fonts_Provider { +abstract class WP_Webfonts_Provider { /** * The provider's unique ID. * - * @access protected * @since 5.9.0 * @var string */ @@ -26,7 +25,6 @@ abstract class WP_Fonts_Provider { /** * An array of URLs to preconnect to. * - * @access protected * @since 5.9.0 * @var array */ @@ -35,7 +33,6 @@ abstract class WP_Fonts_Provider { /** * The provider's root URL. * - * @access protected * @since 5.9.0 * @var string */ @@ -44,7 +41,6 @@ abstract class WP_Fonts_Provider { /** * Webfont parameters. * - * @access protected * @since 5.9.0 * @var array */ @@ -53,7 +49,6 @@ abstract class WP_Fonts_Provider { /** * An array of valid CSS properties for @font-face. * - * @access protected * @since 5.9.0 * @var array */ @@ -77,7 +72,6 @@ abstract class WP_Fonts_Provider { /** * An array of API parameters which will not be added to the @font-face. * - * @access protected * @since 5.9.0 * @var array */ @@ -86,7 +80,6 @@ abstract class WP_Fonts_Provider { /** * Get the provider's unique ID. * - * @access public * @since 5.9.0 * @return string */ @@ -97,7 +90,8 @@ public function get_id() { /** * Get the root URL for the provider. * - * @access public + * @since 5.9.0 + * * @return string */ public function get_root_url() { @@ -107,7 +101,8 @@ public function get_root_url() { /** * Get the array of URLs to preconnect to. * - * @access public + * @since 5.9.0 + * * @return array */ public function get_preconnect_urls() { @@ -117,10 +112,9 @@ public function get_preconnect_urls() { /** * Set the object's params. * - * @access public * @since 5.9.0 + * * @param array $params The webfont's parameters. - * @return void */ public function set_params( $params ) { // Default values. @@ -238,8 +232,8 @@ public function set_params( $params ) { /** * Get the object's params. * - * @access public * @since 5.9.0 + * * @return array */ public function get_params() { @@ -249,8 +243,8 @@ public function get_params() { /** * Get the CSS for the font. * - * @access public * @since 5.9.0 + * * @return string */ abstract public function get_css(); From 5ee49bb8b2b4e6cb43bb200494932dce58bd28d1 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Mon, 11 Oct 2021 14:38:12 +0300 Subject: [PATCH 04/94] Add get_validated_params method --- .../providers/class-wp-webfonts-provider.php | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index 4a29d298954df..62f34d3122958 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -110,13 +110,15 @@ public function get_preconnect_urls() { } /** - * Set the object's params. + * Validate the $params array. * * @since 5.9.0 * - * @param array $params The webfont's parameters. + * @param array $params The parameters to validate. + * + * @return array */ - public function set_params( $params ) { + public function get_validated_params( $params ) { // Default values. $defaults = array( 'font-weight' => '400', @@ -226,6 +228,18 @@ public function set_params( $params ) { $params['font-weight'] = 'normal'; } + return $params; + } + + /** + * Set the object's params. + * + * @since 5.9.0 + * + * @param array $params The webfont's parameters. + */ + public function set_params( $params ) { + $params = $this->get_validated_params( $params ); $this->params = $params; } From a1f34f065fe3718f1b4da5aff5760e9138c84055 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Mon, 11 Oct 2021 14:39:46 +0300 Subject: [PATCH 05/94] Tweak validation for local fonts --- .../class-wp-webfonts-local-provider.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index 2c4cbfd0bee1e..0fff01df5b10b 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -20,6 +20,24 @@ final class WP_Webfonts_Local_Provider extends WP_Fonts_Provider { */ protected $id = 'local'; + /** + * Get validated params. + * + * @access public + * @since 5.9.0 + * @param array $params The webfont's parameters. + * @return array + */ + public function get_validated_params( $params ) { + $params = parent::get_validated_params( $params ); + + // Wrap font-family in quotes if it contains spaces. + if ( false !== strpos( $params['font-family'], ' ' ) && false === strpos( $params['font-family'], '"' ) && false === strpos( $params['font-family'], "'" ) ) { + $params['font-family'] = '"' . $params['font-family'] . '"'; + } + return $params; + } + /** * Get the CSS for the font. * From d5ac8dfc4cb3a9cfad8ad1c3d74eb23259867239 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Mon, 11 Oct 2021 14:47:49 +0300 Subject: [PATCH 06/94] fix paths --- src/wp-includes/webfonts-api.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/webfonts-api.php b/src/wp-includes/webfonts-api.php index cd39926419b9c..0821079027185 100644 --- a/src/wp-includes/webfonts-api.php +++ b/src/wp-includes/webfonts-api.php @@ -12,9 +12,9 @@ function wp_webfonts() { static $instance; if ( ! $instance instanceof WP_Webfonts ) { - require_once __DIR__ . '/class-wp-webfonts-registry.php'; - require_once __DIR__ . '/class-wp-webfonts-provider-registry.php'; - require_once __DIR__ . '/class-wp-webfonts-controller.php'; + require_once __DIR__ . '/webfonts-api/class-wp-webfonts-registry.php'; + require_once __DIR__ . '/webfonts-api/class-wp-webfonts-provider-registry.php'; + require_once __DIR__ . '/webfonts-api/class-wp-webfonts-controller.php'; $instance = new WP_Webfonts_Controller( new WP_Webfonts_Registry(), From 6531b6677cef8b863701b8ba49d8e46680f82af7 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Mon, 11 Oct 2021 15:01:28 +0300 Subject: [PATCH 07/94] typo --- .../webfonts-api/providers/class-wp-webfonts-local-provider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index 0fff01df5b10b..db70559ba9b8e 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -10,7 +10,7 @@ /** * Webfonts API provider for locally-hosted fonts. */ -final class WP_Webfonts_Local_Provider extends WP_Fonts_Provider { +final class WP_Webfonts_Local_Provider extends WP_Webfonts_Provider { /** * The provider's unique ID. From f99faf407a9f362b0cacfecce9c5a7fc6ab2bc83 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Mon, 11 Oct 2021 07:53:37 -0500 Subject: [PATCH 08/94] Adds tests and fixes for WP_Webfonts_Registry. --- .../class-wp-webfonts-registry.php | 60 +++- .../tests/webfonts-api/wpWebfontsRegistry.php | 292 ++++++++++++++++++ 2 files changed, 339 insertions(+), 13 deletions(-) create mode 100644 tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index 56b25df138977..e4342e81dca7c 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -1,16 +1,34 @@ registry; @@ -48,7 +66,7 @@ public function get_registry() { * @since 5.9.0 * * @param string $provider_id Provider ID to fetch. - * @return array[] Registered webfonts. + * @return string[][] Registered webfonts. */ public function get_by_provider( $provider_id ) { if ( ! isset( $this->registry_by_provider[ $provider_id ] ) ) { @@ -74,7 +92,7 @@ public function get_by_provider( $provider_id ) { * @since 5.9.0 * * @param string $font_family Family font to fetch. - * @return array[] Registered webfonts. + * @return string[][] Registered webfonts. */ public function get_by_font_family( $font_family ) { if ( ! is_string( $font_family ) || '' === $font_family ) { @@ -87,7 +105,7 @@ public function get_by_font_family( $font_family ) { foreach ( $this->registry as $registration_key => $webfont ) { // Skip if webfont's family font does not match. - if ( ! substr( $registration_key, 0, $last_char ) !== $font_family_key ) { + if ( substr( $registration_key, 0, $last_char ) !== $font_family_key ) { continue; } @@ -133,11 +151,12 @@ public function register( array $webfont ) { * @return string Registration key. */ private function generate_registration_key( array $webfont ) { - $key = $this->convert_font_family_into_key( $webfont['fontFamily'] ); - $key .= trim( $webfont['fontStyle'] ); - $key .= trim( $webfont['fontWeight'] ); - - return $key; + return sprintf( + '%s.%s.%s', + $this->convert_font_family_into_key( $webfont['fontFamily'] ), + trim( $webfont['fontStyle'] ), + trim( $webfont['fontWeight'] ) + ); } /** @@ -173,6 +192,7 @@ private function is_schema_valid( array $webfont ) { __( 'Webfont must define a string "fontFamily".' ), '5.9.0' ); + return false; } @@ -182,6 +202,7 @@ private function is_schema_valid( array $webfont ) { __( 'Webfont must define a string "fontStyle".' ), '5.9.0' ); + return false; } @@ -189,22 +210,25 @@ private function is_schema_valid( array $webfont ) { _doing_it_wrong( 'register_webfonts', sprintf( - /* translators: 1: Slant angle, 2: Given font style. */ + /* translators: 1: Slant angle, 2: Given font style. */ __( 'Webfont font style must be normal, italic, oblique, or oblique %1$s. Given: %2$s.' ), '', $webfont['fontStyle'] ), '5.9.0' ); + return false; } + // @todo validate the value. if ( empty( $webfont['fontWeight'] ) || ! is_string( $webfont['fontWeight'] ) ) { _doing_it_wrong( 'register_webfonts', __( 'Webfont must define a string "fontWeight".' ), '5.9.0' ); + return false; } @@ -215,12 +239,21 @@ private function is_schema_valid( array $webfont ) { __( 'Webfont must define a string "provider".' ), '5.9.0' ); + return false; } return true; } + /** + * Checks if the given font-style is valid. + * + * @since 5.9.0 + * + * @param string $font_style Font style to validate. + * @return bool True when font-style is valid. + */ private function is_valid_font_style( $font_style ) { if ( in_array( $font_style, $this->valid_font_style, true ) ) { return true; @@ -228,5 +261,6 @@ private function is_valid_font_style( $font_style ) { // @todo Check for oblique . + return false; } } diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php new file mode 100644 index 0000000000000..f5055aa74bd87 --- /dev/null +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -0,0 +1,292 @@ +setExpectedIncorrectUsage( 'register_webfonts' ); + + $registry = new WP_Webfonts_Registry(); + + $this->assertSame( '', $registry->register( $webfont ) ); + } + + /** + * Data Provider. + * + * return @array + */ + public function data_register_with_invalid_schema() { + return array( + 'empty array - no schema' => array( + array(), + ), + 'provider: not set' => array( + array( + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + ), + 'provider: empty string' => array( + array( + 'provider' => '', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + ), + 'provider: invalid type' => array( + array( + 'provider' => null, + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + ), + 'font family: not set' => array( + array( + 'provider' => 'local', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + ), + 'font family: empty string' => array( + array( + 'provider' => 'local', + 'fontFamily' => '', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + ), + 'font family: invalid type' => array( + array( + 'provider' => 'local', + 'fontFamily' => null, + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + ), + 'font style: not set' => array( + array( + 'provider' => 'local', + 'fontFamily' => 'Open Sans', + 'fontWeight' => '400', + ), + ), + 'font style: empty string' => array( + array( + 'provider' => 'local', + 'fontFamily' => 'Open Sans', + 'fontStyle' => '', + 'fontWeight' => '400', + ), + ), + 'font style: invalid type' => array( + array( + 'provider' => 'local', + 'fontFamily' => 'Open Sans', + 'fontStyle' => null, + 'fontWeight' => '400', + ), + ), + 'font style: invalid value' => array( + array( + 'provider' => 'local', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'invalid', + 'fontWeight' => '400', + ), + ), + 'font weight: not set' => array( + array( + 'provider' => 'local', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + ), + ), + 'font wegith: empty string' => array( + array( + 'provider' => 'local', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '', + ), + ), + 'font weight: invalid type' => array( + array( + 'provider' => 'local', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => null, + ), + ), + /* @todo uncomment once value validation is added. + 'font weight: invalid value' => array( + array( + 'provider' => 'local', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => 'invalid', + ), + ), + */ + ); + } + + /** + * @covers WP_Webfonts_Registry::register + * + * @dataProvider data_register_with_valid_schema + * + * @param array Webfonts input. + * @param string Expected registration key. + */ + public function test_register_with_valid_schema( array $webfont, $expected ) { + $registry = new WP_Webfonts_Registry(); + + $this->assertSame( $expected, $registry->register( $webfont ) ); + } + + public function data_register_with_valid_schema() { + return array( + 'Open Sans; normal; 400' => array( + 'webfonts' => array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + 'expected' => 'open-sans.normal.400', + ), + 'Open Sans; italic; 900' => array( + 'webfonts' => array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'italic', + 'fontWeight' => '900', + ), + 'expected' => 'open-sans.italic.900', + ), + ); + } + + /** + * @covers WP_Webfonts_Registry::get_registry + */ + public function test_get_registry() { + $registry = new WP_Webfonts_Registry(); + $this->register_webfonts( $registry ); + + $this->assertSame( self::$webfonts, $registry->get_registry() ); + } + + /** + * @covers WP_Webfonts_Registry::get_by_font_family + */ + public function test_get_by_font_family() { + $registry = new WP_Webfonts_Registry(); + $this->register_webfonts( $registry ); + + $expected = array( + 'roboto.normal.400' => self::$webfonts['roboto.normal.400'], + 'roboto.normal.900' => self::$webfonts['roboto.normal.900'], + ); + $this->assertSame( $expected, $registry->get_by_font_family( 'Roboto' ) ); + } + + /** + * @covers WP_Webfonts_Registry::get_by_font_family + * + * @dataProvider data_get_by_font_family_with_invalid_input + * + * @param mixed Font family input. + */ + public function test_get_by_font_family_with_invalid_input( $font_family ) { + $registry = new WP_Webfonts_Registry(); + $this->register_webfonts( $registry ); + + $this->assertSame( array(), $registry->get_by_font_family( $font_family ) ); + } + + /** + * Data Provider. + * + * return @array + */ + public function data_get_by_font_family_with_invalid_input() { + return array( + 'not a string' => array( true ), + 'empty string' => array( '' ), + 'font family not registered' => array( 'Does not exist' ), + ); + } + + /** + * Register the webfonts helper function. + * + * @param WP_Webfonts_Registry $registry Instance of the registry. + */ + private function register_webfonts( $registry ) { + foreach ( self::$webfonts as $webfont ) { + $registry->register( $webfont ); + } + } + + /** + * Gets the webfonts collection. + * + * @return string[][] + */ + private static function get_webfonts() { + return array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + 'open-sans.normal.900' => array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '900', + ), + 'open-sans.italic.400' => array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'italic', + 'fontWeight' => '400', + ), + 'roboto.normal.400' => array( + 'provider' => 'google', + 'fontFamily' => 'Roboto', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + 'roboto.normal.900' => array( + 'provider' => 'google', + 'fontFamily' => 'Roboto', + 'fontStyle' => 'normal', + 'fontWeight' => '900', + ), + ); + } +} From 42bc934b98e070dec9ef5382dfecf7759abf7a78 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Mon, 11 Oct 2021 09:05:13 -0500 Subject: [PATCH 09/94] Adds tests amd fixes for WP_Webfonts_Provider_Registry. --- .../class-wp-webfonts-provider-registry.php | 32 +++- .../class-wp-webfonts-registry.php | 5 +- .../wpWebfontsProviderRegistry.php | 147 ++++++++++++++++++ .../tests/webfonts-api/wpWebfontsRegistry.php | 13 +- 4 files changed, 187 insertions(+), 10 deletions(-) create mode 100644 tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php index ef074aeceffca..7003913809398 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -1,9 +1,20 @@ get_preconnect_link( $provider ); + $links .= $this->generate_preconnect_link( $provider ); $added[ $provider_id ] = true; } @@ -92,7 +110,15 @@ public function get_preconnect_links() { return $links; } - private function get_preconnect_link( $provider ) { + /** + * Generate the preconnect links HTML for the given provider. + * + * @since 5.9.0 + * + * @param WP_Webfonts_Provider $provider Instance of the provider. + * @return string Preconnect links HTML for the provider. + */ + private function generate_preconnect_link( WP_Webfonts_Provider $provider ) { $link = ''; foreach ( $provider->get_preconnect_urls() as $preconnection ) { diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index e4342e81dca7c..d3e286c72e53e 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -2,11 +2,9 @@ /** * Webfonts API: Webfonts Registry * - * This is the main class integrating all other classes. - * * @package WordPress * @subpackage Webfonts - * @since 5.5.0 + * @since 5.9.0 */ /** @@ -15,6 +13,7 @@ * Handles schema validation, webfont registration, and query of webfonts. */ final class WP_Webfonts_Registry { + /** * Registered webfonts. * diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php new file mode 100644 index 0000000000000..86c909d8cd641 --- /dev/null +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php @@ -0,0 +1,147 @@ +assertSame( array(), $registry->get_registry() ); + } + + /** + * @covers WP_Webfonts_Provider_Registry::register + * @covers WP_Webfonts_Provider_Registry::get_registry + */ + public function test_register_with_invalid_class() { + $registry = new WP_Webfonts_Provider_Registry(); + $registry->register( 'DoesNotExist' ); + + $this->assertSame( array(), $registry->get_registry() ); + } + + /** + * @covers WP_Webfonts_Provider_Registry::register + * @covers WP_Webfonts_Provider_Registry::get_registry + */ + public function test_register_with_valid_class() { + $registry = new WP_Webfonts_Provider_Registry(); + $registry->register( My_Custom_Webfonts_Provider_Mock::class ); + + $providers = $registry->get_registry(); + + $this->assertIsArray( $providers ); + $this->assertCount( 1, $providers ); + $this->assertArrayHasKey( 'my-custom-provider', $providers ); + $this->assertInstanceOf( 'My_Custom_Webfonts_Provider_Mock', $providers['my-custom-provider'] ); + } + + /** + * @covers WP_Webfonts_Provider_Registry::init + * @covers WP_Webfonts_Provider_Registry::get_registry + */ + public function test_init() { + $registry = new WP_Webfonts_Provider_Registry(); + // Register the core providers. + $registry->init(); + + $providers = $registry->get_registry(); + + $expected = array( 'google', 'local' ); + $this->assertSame( $expected, array_keys( $providers ) ); + $this->assertInstanceOf( 'WP_Webfonts_Google_Provider', $providers['google'] ); + $this->assertInstanceOf( 'WP_Webfonts_Local_Provider', $providers['local'] ); + } + + /** + * @covers WP_Webfonts_Provider_Registry::register + * @covers WP_Webfonts_Provider_Registry::get_registry + */ + public function test_register_with_core_providers() { + $registry = new WP_Webfonts_Provider_Registry(); + // Register the core providers. + $registry->init(); + // Register a custom provider. + $registry->register( My_Custom_Webfonts_Provider_Mock::class ); + + $providers = $registry->get_registry(); + + $expected = array( 'google', 'local', 'my-custom-provider' ); + $this->assertSame( $expected, array_keys( $providers ) ); + } + + /** + * @covers WP_Webfonts_Provider_Registry::get_preconnect_links + * + * @dataProvider data_get_preconnect_links + * + * @param bool $register_custom When true, registers the custom provider. + * @param string $expected Expected HTML. + */ + public function test_get_preconnect_links( $register_custom, $expected ) { + $registry = new WP_Webfonts_Provider_Registry(); + // Register the core providers. + $registry->init(); + // Register a custom provider. + if ( $register_custom ) { + $registry->register( My_Custom_Webfonts_Provider_Mock::class ); + } + + $this->assertSame( $expected, $registry->get_preconnect_links() ); + } + + /** + * Data Provider. + * + * return @array + */ + public function data_get_preconnect_links() { + return array( + 'core providers' => array( + 'register_custom' => false, + 'expected' => << + + +LINKS + , + ), + 'core + custom providers' => array( + 'register_custom' => true, + 'expected' => << + + + +LINKS + , + ), + ); + } +} + +class My_Custom_Webfonts_Provider_Mock extends WP_Webfonts_Provider { + protected $id = 'my-custom-provider'; + + protected $preconnect_urls = array( + array( + 'href' => 'https://fonts.my-custom-api.com', + ), + ); + + public function get_css() { + return ''; + } +} diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index f5055aa74bd87..b7df56bfbae4e 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -14,7 +14,7 @@ public static function wpSetUpBeforeClass() { } /** - * @covers WP_Webfonts_Registry::register + * @covers WP_Webfonts_Registry::register * * @dataProvider data_register_with_invalid_schema * @@ -152,11 +152,11 @@ public function data_register_with_invalid_schema() { } /** - * @covers WP_Webfonts_Registry::register + * @covers WP_Webfonts_Registry::register * * @dataProvider data_register_with_valid_schema * - * @param array Webfonts input. + * @param array Webfonts input. * @param string Expected registration key. */ public function test_register_with_valid_schema( array $webfont, $expected ) { @@ -165,6 +165,11 @@ public function test_register_with_valid_schema( array $webfont, $expected ) { $this->assertSame( $expected, $registry->register( $webfont ) ); } + /** + * Data Provider. + * + * return @array + */ public function data_register_with_valid_schema() { return array( 'Open Sans; normal; 400' => array( @@ -213,7 +218,7 @@ public function test_get_by_font_family() { } /** - * @covers WP_Webfonts_Registry::get_by_font_family + * @covers WP_Webfonts_Registry::get_by_font_family * * @dataProvider data_get_by_font_family_with_invalid_input * From 1804d678682ecfda6a6086968a6803793716d775 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Mon, 11 Oct 2021 11:54:12 -0500 Subject: [PATCH 10/94] Adds tests and fixes for WP_Webfonts_Controller. --- src/wp-includes/webfonts-api.php | 22 +- .../class-wp-webfonts-controller.php | 86 +++++-- .../class-wp-webfonts-provider-registry.php | 1 - .../providers/class-wp-webfonts-provider.php | 10 +- ...class-my-custom-webfonts-provider-mock.php | 17 ++ .../webfonts-api/wpWebfontsController.php | 238 ++++++++++++++++++ .../wpWebfontsProviderRegistry.php | 17 +- .../tests/webfonts-api/wpWebfontsRegistry.php | 14 -- 8 files changed, 354 insertions(+), 51 deletions(-) create mode 100644 tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php create mode 100644 tests/phpunit/tests/webfonts-api/wpWebfontsController.php diff --git a/src/wp-includes/webfonts-api.php b/src/wp-includes/webfonts-api.php index 0821079027185..b08735bcf01d7 100644 --- a/src/wp-includes/webfonts-api.php +++ b/src/wp-includes/webfonts-api.php @@ -1,6 +1,6 @@ register_webfonts( $webfonts ); } +/** + * Registers a single webfont. + * + * @since 5.9.0 + * + * @param string[] $webfont Webfont to be registered. + */ +function wp_register_webfont( array $webfont ) { + wp_webfonts()->register_webfont( $webfont ); +} + /** * Register a webfont provider. * diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 9d667ac3de483..62a4fdece2301 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -29,7 +29,10 @@ class WP_Webfonts_Controller { * @param WP_Webfonts_Registry $webfonts_registry Instance of the webfonts registry. * @param WP_Webfonts_Provider_Registry $provider_registry Instance of the providers registry. */ - public function __construct( WP_Webfonts_Registry $webfonts_registry, WP_Webfonts_Provider_Registry $provider_registry ) { + public function __construct( + WP_Webfonts_Registry $webfonts_registry, + WP_Webfonts_Provider_Registry $provider_registry + ) { $this->webfonts_registry = $webfonts_registry; $this->providers_registry = $provider_registry; } @@ -40,9 +43,9 @@ public function __construct( WP_Webfonts_Registry $webfonts_registry, WP_Webfont * @since 5.9.0 */ public function init() { - $this->provider->init(); + $this->providers_registry->init(); - // Register enqueue callback to + // Register callback to generate and enqueue styles. if ( did_action( 'wp_enqueue_scripts' ) ) { $this->stylesheet_handle = 'webfonts-footer'; $hook = 'wp_print_footer_scripts'; @@ -50,7 +53,7 @@ public function init() { $this->stylesheet_handle = 'webfonts'; $hook = 'wp_enqueue_scripts'; } - add_action( $hook, array( $this, 'enqueue' ) ); + add_action( $hook, array( $this, 'generate_and_enqueue_styles' ) ); } /** @@ -58,37 +61,92 @@ public function init() { * * @since 5.9.0 * - * @param array[] $webfonts Webfonts to be registered. + * @param string[][] $webfonts Webfonts to be registered. */ public function register_webfonts( array $webfonts ) { + // Bail out if no webfonts collection was injected. + if ( empty( $webfonts ) ) { + return; + } + array_walk( $webfonts, array( $this, 'register_webfont' ) ); } - /** - * Registers a webfont. + * Registers the given webfont if its schema is valid. + * + * @since 5.9.0 * * @param string[] $webfont Webfont definition. */ public function register_webfont( array $webfont ) { - $registration_key = $this->webfonts_registry->register( $webfont ); - if ( '' === $registration_key ) { - return; - } + $this->webfonts_registry->register( $webfont ); + } + + /** + * Gets the registered webfonts. + * + * @since 5.9.0 + * + * @return string[][] Registered webfonts. + */ + public function get_webfonts() { + return $this->webfonts_registry->get_registry(); + } + + /** + * Gets the registered webfonts for the given provider. + * + * @since 5.9.0 + * + * @param string $provider_id Provider ID to fetch. + * @return string[][] Registered webfonts. + */ + public function get_webfonts_by_provider( $provider_id ) { + return $this->webfonts_registry->get_by_provider( $provider_id ); + } - // Register the webfont's registration key to its provider. - $this->providers_registry->register_webfont( $webfont['provider'], $registration_key ); + /** + * Gets the registered webfonts for the given font-family. + * + * @since 5.9.0 + * + * @param string $font_family Family font to fetch. + * @return string[][] Registered webfonts. + */ + public function get_webfonts_by_font_family( $font_family ) { + return $this->webfonts_registry->get_by_font_family( $font_family ); } + /** + * Gets the registered providers. + * + * @since 5.9.0 + * + * @return WP_Webfonts_Provider[] Registered providers. + */ public function get_registered_providers() { return $this->providers_registry->get_registry(); } + /** + * Registers the given provider. + * + * @since 5.9.0 + * + * @param string $classname The provider class name. + * @return bool True when registered. False when provider does not exist. + */ public function register_provider( $classname ) { return $this->providers_registry->register( $classname ); } - public function enqueue() { + /** + * Generate and enqueue webfonts styles. + * + * @since 5.9.0 + */ + public function generate_and_enqueue_styles() { // Generate the styles. $styles = $this->generate_styles(); diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php index 7003913809398..dbe77c2bf3023 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -10,7 +10,6 @@ /** * Provider Registry. */ - final class WP_Webfonts_Provider_Registry { /** diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index 62f34d3122958..bea6975a8e911 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -112,6 +112,8 @@ public function get_preconnect_urls() { /** * Validate the $params array. * + * @todo Move to validator.Validation should happen during webfont collection, i.e.during the schema validation. + * * @since 5.9.0 * * @param array $params The parameters to validate. @@ -236,11 +238,11 @@ public function get_validated_params( $params ) { * * @since 5.9.0 * - * @param array $params The webfont's parameters. + * @param string[][] $webfonts The webfont's parameters. */ - public function set_params( $params ) { - $params = $this->get_validated_params( $params ); - $this->params = $params; + public function set_webfonts( array $webfonts ) { + //$params = $this->get_validated_params( $params ); + $this->params = $webfonts; } /** diff --git a/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php b/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php new file mode 100644 index 0000000000000..3ca99b11edc62 --- /dev/null +++ b/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php @@ -0,0 +1,17 @@ + 'https://fonts.my-custom-api.com', + ), + ); + + public function get_css() { + return ''; + } +} diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php new file mode 100644 index 0000000000000..2b4d5c83f5dbc --- /dev/null +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -0,0 +1,238 @@ +init(); + + return $controller; + } + + /** + * @covers WP_Webfonts_Controller::get_webfonts + */ + public function test_get_webfonts_when_empty() { + $controller = $this->get_controller(); + + $this->assertSame( array(), $controller->get_webfonts() ); + } + + /** + * @covers WP_Webfonts_Controller::register_webfonts + */ + public function test_register_webfonts_with_empty_schema() { + $controller = $this->get_controller(); + $webfonts = array(); + + $controller->register_webfonts( $webfonts ); + + $this->assertSame( array(), $controller->get_webfonts() ); + } + + /** + * @covers WP_Webfonts_Controller::register_webfonts + * @covers WP_Webfonts_Controller::register_webfont + * + * @dataProvider data_register_webfonts_with_invalid_schema + * + * @param array $webfonts Webfonts input. + * @param array $expected Exptected registered webfonts. + */ + public function test_register_webfonts_with_invalid_schema( array $webfonts, array $expected ) { + $controller = $this->get_controller(); + + $this->setExpectedIncorrectUsage( 'register_webfonts' ); + + $controller->register_webfonts( $webfonts ); + + $this->assertSame( $expected, $controller->get_webfonts() ); + } + + /** + * Data Provider. + * + * See Tests_Webfonts_API_wpWebfontsRegistry::data_register_with_invalid_schema() + * for more complete test coverage. + * + * return @array + */ + public function data_register_webfonts_with_invalid_schema() { + return array( + 'provider: not set' => array( + 'webfonts' => array( + array( + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'italic', + 'fontWeight' => '700', + ), + ), + 'expected' => array( + 'open-sans.italic.700' => array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'italic', + 'fontWeight' => '700', + ), + ), + ), + 'font family: invalid key' => array( + 'webfonts' => array( + array( + 'provider' => 'google', + 'fontFamily' => 'Roboto', + 'fontStyle' => 'normal', + 'fontWeight' => '900', + ), + array( + 'provider' => 'google', + 'font_family' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + ), + 'expected' => array( + 'roboto.normal.900' => array( + 'provider' => 'google', + 'fontFamily' => 'Roboto', + 'fontStyle' => 'normal', + 'fontWeight' => '900', + ), + ), + ), + ); + } + + /** + * @covers WP_Webfonts_Controller::register_webfonts + * @covers WP_Webfonts_Controller::register_webfont + */ + public function test_register_webfonts_with_valid_schema() { + $controller = $this->get_controller(); + + $controller->register_webfonts( self::$webfonts ); + + $expected = array( + 'open-sans.normal.400' => self::$webfonts[0], + 'open-sans.italic.700' => self::$webfonts[1], + 'roboto.normal.900' => self::$webfonts[2], + ); + $this->assertSame( $expected, $controller->get_webfonts() ); + } + + /** + * @covers WP_Webfonts_Controller::get_registered_providers + */ + public function test_get_registered_providers_core_only() { + $controller = $this->get_controller(); + + $providers = $controller->get_registered_providers(); + + $expected = array( 'google', 'local' ); + $this->assertSame( $expected, array_keys( $providers ) ); + $this->assertInstanceOf( 'WP_Webfonts_Google_Provider', $providers['google'] ); + $this->assertInstanceOf( 'WP_Webfonts_Local_Provider', $providers['local'] ); + } + + /** + * @covers WP_Webfonts_Controller::register_provider + * @covers WP_Webfonts_Controller::get_registered_providers + */ + public function test_register_provider() { + $controller = $this->get_controller(); + + $controller->register_provider( My_Custom_Webfonts_Provider_Mock::class ); + + $providers = $controller->get_registered_providers(); + + $expected = array( 'google', 'local', 'my-custom-provider' ); + $this->assertSame( $expected, array_keys( $providers ) ); + } + + /** + * @covers WP_Webfonts_Controller::get_webfonts_by_font_family + */ + public function test_get_webfonts_by_font_family() { + $controller = $this->get_controller(); + $controller->register_webfonts( self::$webfonts ); + + $expected = array( + 'roboto.normal.900' => self::$webfonts[2], + ); + + $this->assertSame( $expected, $controller->get_webfonts_by_font_family( 'roboto' ) ); + } + + /** + * @covers WP_Webfonts_Controller::get_webfonts_by_font_family + * + * @dataProvider data_get_by_font_family_with_invalid_input + * + * @param mixed Font family input. + */ + public function test_get_webfonts_by_font_family_with_invalid_input( $font_family ) { + $controller = $this->get_controller(); + $controller->register_webfonts( self::$webfonts ); + + $this->assertSame( array(), $controller->get_webfonts_by_font_family( $font_family ) ); + } + + /** + * Data Provider. + * + * return @array + */ + public function data_get_by_font_family_with_invalid_input() { + return array( + 'not a string' => array( true ), + 'empty string' => array( '' ), + 'font family not registered' => array( 'Does not exist' ), + ); + } + + private static function get_webfonts() { + return array( + array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'italic', + 'fontWeight' => '700', + ), + array( + 'provider' => 'google', + 'fontFamily' => 'Roboto', + 'fontStyle' => 'normal', + 'fontWeight' => '900', + ), + ); + } +} diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php index 86c909d8cd641..e22a783a31abf 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php @@ -1,7 +1,5 @@ 'https://fonts.my-custom-api.com', - ), - ); - - public function get_css() { - return ''; - } -} diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index b7df56bfbae4e..4262392aae93f 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -84,13 +84,6 @@ public function data_register_with_invalid_schema() { 'fontWeight' => '400', ), ), - 'font style: not set' => array( - array( - 'provider' => 'local', - 'fontFamily' => 'Open Sans', - 'fontWeight' => '400', - ), - ), 'font style: empty string' => array( array( 'provider' => 'local', @@ -115,13 +108,6 @@ public function data_register_with_invalid_schema() { 'fontWeight' => '400', ), ), - 'font weight: not set' => array( - array( - 'provider' => 'local', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - ), - ), 'font wegith: empty string' => array( array( 'provider' => 'local', From a2331ae62b79053478817b28437cbad1d27207f8 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Mon, 11 Oct 2021 12:17:27 -0500 Subject: [PATCH 11/94] Moved schema validator to separate class. --- src/wp-includes/webfonts-api.php | 5 +- .../class-wp-webfonts-registry.php | 98 +----------- .../class-wp-webfonts-schema-validator.php | 144 ++++++++++++++++++ .../webfonts-api/wpWebfontsController.php | 5 +- .../tests/webfonts-api/wpWebfontsRegistry.php | 17 ++- 5 files changed, 172 insertions(+), 97 deletions(-) create mode 100644 src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php diff --git a/src/wp-includes/webfonts-api.php b/src/wp-includes/webfonts-api.php index b08735bcf01d7..43431ec6db225 100644 --- a/src/wp-includes/webfonts-api.php +++ b/src/wp-includes/webfonts-api.php @@ -19,12 +19,15 @@ function wp_webfonts() { static $instance; if ( ! $instance instanceof WP_Webfonts ) { + require_once __DIR__ . '/webfonts-api/class-wp-webfonts-schema-validator.php'; require_once __DIR__ . '/webfonts-api/class-wp-webfonts-registry.php'; require_once __DIR__ . '/webfonts-api/class-wp-webfonts-provider-registry.php'; require_once __DIR__ . '/webfonts-api/class-wp-webfonts-controller.php'; $instance = new WP_Webfonts_Controller( - new WP_Webfonts_Registry(), + new WP_Webfonts_Registry( + new WP_Webfonts_Schema_Validator() + ), new WP_Webfonts_Provider_Registry() ); $instance->init(); diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index d3e286c72e53e..17bf975bd3f8a 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -31,22 +31,15 @@ final class WP_Webfonts_Registry { private $registry_by_provider = array(); /** - * Valid font styles. + * Schema validator. * - * @since 5.9.0 - * - * @var string[] + * @var WP_Webfonts_Schema_Validator */ - private $valid_font_style = array( - 'normal', - 'italic', - 'oblique', - // Global values. - 'inherit', - 'initial', - 'revert', - 'unset', - ); + private $validator; + + public function __construct( WP_Webfonts_Schema_Validator $validator ) { + $this->validator = $validator; + } /** * Gets the webfont registry. @@ -185,81 +178,6 @@ private function convert_font_family_into_key( $font_family ) { * @return bool True when valid. False when invalid. */ private function is_schema_valid( array $webfont ) { - if ( empty( $webfont['fontFamily'] ) || ! is_string( $webfont['fontFamily'] ) ) { - _doing_it_wrong( - 'register_webfonts', - __( 'Webfont must define a string "fontFamily".' ), - '5.9.0' - ); - - return false; - } - - if ( empty( $webfont['fontStyle'] ) || ! is_string( $webfont['fontStyle'] ) ) { - _doing_it_wrong( - 'register_webfonts', - __( 'Webfont must define a string "fontStyle".' ), - '5.9.0' - ); - - return false; - } - - if ( ! $this->is_valid_font_style( $webfont['fontStyle'] ) ) { - _doing_it_wrong( - 'register_webfonts', - sprintf( - /* translators: 1: Slant angle, 2: Given font style. */ - __( 'Webfont font style must be normal, italic, oblique, or oblique %1$s. Given: %2$s.' ), - '', - $webfont['fontStyle'] - ), - '5.9.0' - ); - - return false; - } - - // @todo validate the value. - if ( empty( $webfont['fontWeight'] ) || ! is_string( $webfont['fontWeight'] ) ) { - _doing_it_wrong( - 'register_webfonts', - __( 'Webfont must define a string "fontWeight".' ), - '5.9.0' - ); - - return false; - } - - // @todo check if provider is registered. - if ( empty( $webfont['provider'] ) || ! is_string( $webfont['provider'] ) ) { - _doing_it_wrong( - 'register_webfonts', - __( 'Webfont must define a string "provider".' ), - '5.9.0' - ); - - return false; - } - - return true; - } - - /** - * Checks if the given font-style is valid. - * - * @since 5.9.0 - * - * @param string $font_style Font style to validate. - * @return bool True when font-style is valid. - */ - private function is_valid_font_style( $font_style ) { - if ( in_array( $font_style, $this->valid_font_style, true ) ) { - return true; - } - - // @todo Check for oblique . - - return false; + return $this->validator->is_schema_valid( $webfont ); } } diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php new file mode 100644 index 0000000000000..8bd94a24d3bc7 --- /dev/null +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -0,0 +1,144 @@ +webfont = $webfont; + + $is_valid = ( + $this->is_provider_valid() && + $this->is_font_family_valid() && + $this->is_font_style_valid() && + $this->is_font_weight_valid() + ); + + $this->webfont = array(); + + return $is_valid; + } + + private function is_provider_valid() { + // @todo check if provider is registered. + + if ( empty( $this->webfont['provider'] ) || ! is_string( $this->webfont['provider'] ) ) { + _doing_it_wrong( + 'register_webfonts', + __( 'Webfont must define a string "provider".' ), + '5.9.0' + ); + + return false; + } + + return true; + } + + private function is_font_family_valid() { + if ( empty( $this->webfont['fontFamily'] ) || ! is_string( $this->webfont['fontFamily'] ) ) { + _doing_it_wrong( + 'register_webfonts', + __( 'Webfont must define a string "fontFamily".' ), + '5.9.0' + ); + + return false; + } + + return true; + } + + private function is_font_style_valid() { + if ( empty( $this->webfont['fontStyle'] ) || ! is_string( $this->webfont['fontStyle'] ) ) { + _doing_it_wrong( + 'register_webfonts', + __( 'Webfont must define a string "fontStyle".' ), + '5.9.0' + ); + + return false; + } + + if ( ! $this->is_font_style_value_valid( $this->webfont['fontStyle'] ) ) { + _doing_it_wrong( + 'register_webfonts', + sprintf( + /* translators: 1: Slant angle, 2: Given font style. */ + __( 'Webfont font style must be normal, italic, oblique, or oblique %1$s. Given: %2$s.' ), + '', + $this->webfont['fontStyle'] + ), + '5.9.0' + ); + + return false; + } + + return true; + } + + /** + * Checks if the given font-style is valid. + * + * @since 5.9.0 + * + * @param string $font_style Font style to validate. + * @return bool True when font-style is valid. + */ + private function is_font_style_value_valid( $font_style ) { + if ( in_array( $font_style, $this->valid_font_style, true ) ) { + return true; + } + + // @todo Check for oblique . + + return false; + } + + private function is_font_weight_valid() { + // @todo validate the value. + if ( empty( $this->webfont['fontWeight'] ) || ! is_string( $this->webfont['fontWeight'] ) ) { + _doing_it_wrong( + 'register_webfonts', + __( 'Webfont must define a string "fontWeight".' ), + '5.9.0' + ); + + return false; + } + + return true; + } +} diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index 2b4d5c83f5dbc..aea44206e83de 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -8,6 +8,7 @@ class Tests_Webfonts_API_wpWebfontsController extends WP_UnitTestCase { private static $webfonts; public static function wpSetUpBeforeClass() { + require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-schema-validator.php'; require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-registry.php'; require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-provider-registry.php'; require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-controller.php'; @@ -18,7 +19,9 @@ public static function wpSetUpBeforeClass() { private function get_controller() { $controller = new WP_Webfonts_Controller( - new WP_Webfonts_Registry(), + new WP_Webfonts_Registry( + new WP_Webfonts_Schema_Validator() + ), new WP_Webfonts_Provider_Registry() ); $controller->init(); diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index 4262392aae93f..3e785a818d343 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -8,11 +8,18 @@ class Tests_Webfonts_API_wpWebfontsRegistry extends WP_UnitTestCase { private static $webfonts; public static function wpSetUpBeforeClass() { + require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-schema-validator.php'; require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-registry.php'; self::$webfonts = self::get_webfonts(); } + private function get_registry() { + return new WP_Webfonts_Registry( + new WP_Webfonts_Schema_Validator() + ); + } + /** * @covers WP_Webfonts_Registry::register * @@ -23,7 +30,7 @@ public static function wpSetUpBeforeClass() { public function test_register_with_invalid_schema( array $webfont ) { $this->setExpectedIncorrectUsage( 'register_webfonts' ); - $registry = new WP_Webfonts_Registry(); + $registry = $this->get_registry(); $this->assertSame( '', $registry->register( $webfont ) ); } @@ -146,7 +153,7 @@ public function data_register_with_invalid_schema() { * @param string Expected registration key. */ public function test_register_with_valid_schema( array $webfont, $expected ) { - $registry = new WP_Webfonts_Registry(); + $registry = $this->get_registry(); $this->assertSame( $expected, $registry->register( $webfont ) ); } @@ -183,7 +190,7 @@ public function data_register_with_valid_schema() { * @covers WP_Webfonts_Registry::get_registry */ public function test_get_registry() { - $registry = new WP_Webfonts_Registry(); + $registry = $this->get_registry(); $this->register_webfonts( $registry ); $this->assertSame( self::$webfonts, $registry->get_registry() ); @@ -193,7 +200,7 @@ public function test_get_registry() { * @covers WP_Webfonts_Registry::get_by_font_family */ public function test_get_by_font_family() { - $registry = new WP_Webfonts_Registry(); + $registry = $this->get_registry(); $this->register_webfonts( $registry ); $expected = array( @@ -211,7 +218,7 @@ public function test_get_by_font_family() { * @param mixed Font family input. */ public function test_get_by_font_family_with_invalid_input( $font_family ) { - $registry = new WP_Webfonts_Registry(); + $registry = $this->get_registry(); $this->register_webfonts( $registry ); $this->assertSame( array(), $registry->get_by_font_family( $font_family ) ); From ee9ea3b8917429b7e72a9d41ceba764a67cd3a66 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Mon, 11 Oct 2021 12:34:16 -0500 Subject: [PATCH 12/94] Made font weight and style optional. --- .../class-wp-webfonts-registry.php | 22 +++++++++++++++++++ .../webfonts-api/wpWebfontsController.php | 3 ++- .../tests/webfonts-api/wpWebfontsRegistry.php | 8 +++---- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index 17bf975bd3f8a..c7487dcb7a1db 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -116,6 +116,8 @@ public function get_by_font_family( $font_family ) { * @return string Registration key. */ public function register( array $webfont ) { + $webfont = $this->merge_optional_parameters( $webfont ); + // Validate schema. if ( ! $this->is_schema_valid( $webfont ) ) { return ''; @@ -131,6 +133,26 @@ public function register( array $webfont ) { return $registration_key; } + /** + * Merge optional parameters into webfont definition. + * + * @since 5.9.0 + * + * @param string[] $webfont Webfont definition. + * @return string[] Webfont with optional parameters. + */ + private function merge_optional_parameters( array $webfont ) { + if ( ! isset( $webfont['fontStyle'] ) ) { + $webfont['fontStyle'] = 'normal'; + } + + if ( ! isset( $webfont['fontWeight'] ) ) { + $webfont['fontWeight'] = '400'; + } + + return $webfont; + } + /** * Generates the registration key. * diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index aea44206e83de..50f81723190d7 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -79,9 +79,10 @@ public function test_register_webfonts_with_invalid_schema( array $webfonts, arr */ public function data_register_webfonts_with_invalid_schema() { return array( - 'provider: not set' => array( + 'provider: invalid type' => array( 'webfonts' => array( array( + 'provider' => null, 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => '400', diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index 3e785a818d343..022295cc0f3fe 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -62,7 +62,7 @@ public function data_register_with_invalid_schema() { ), 'provider: invalid type' => array( array( - 'provider' => null, + 'provider' => true, 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => '400', @@ -86,7 +86,7 @@ public function data_register_with_invalid_schema() { 'font family: invalid type' => array( array( 'provider' => 'local', - 'fontFamily' => null, + 'fontFamily' => true, 'fontStyle' => 'normal', 'fontWeight' => '400', ), @@ -103,7 +103,7 @@ public function data_register_with_invalid_schema() { array( 'provider' => 'local', 'fontFamily' => 'Open Sans', - 'fontStyle' => null, + 'fontStyle' => true, 'fontWeight' => '400', ), ), @@ -128,7 +128,7 @@ public function data_register_with_invalid_schema() { 'provider' => 'local', 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', - 'fontWeight' => null, + 'fontWeight' => true, ), ), /* @todo uncomment once value validation is added. From 0ad4f540b0bb28116bca1b00bf21b722573e6c07 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Tue, 12 Oct 2021 10:06:33 +0300 Subject: [PATCH 13/94] Fix method name --- src/wp-includes/webfonts-api/class-wp-webfonts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 62a4fdece2301..33908aed6a412 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -181,6 +181,6 @@ private function generate_styles() { * @since 5.9.0 */ public function add_preconnect_links() { - echo $this->providers_registry->generate_preconnect_links(); + echo $this->providers_registry->get_preconnect_links(); } } From 566fea7f7ea00a6e7e1cc7549ef64b51a8469580 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Tue, 12 Oct 2021 10:15:49 +0300 Subject: [PATCH 14/94] validation: should use kebab since it's 1 step before the CSS output --- .../providers/class-wp-webfonts-provider.php | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index bea6975a8e911..8a00d74071363 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -121,6 +121,13 @@ public function get_preconnect_urls() { * @return array */ public function get_validated_params( $params ) { + + // Convert camelCase to kebab-case. + $kebab = array(); + foreach ( $params as $key => $value ) { + $kebab[ $this->camel_to_kebab( $key ) ] = $value; + } + // Default values. $defaults = array( 'font-weight' => '400', @@ -130,7 +137,7 @@ public function get_validated_params( $params ) { ); // Merge defaults with passed params. - $params = wp_parse_args( $params, $defaults ); + $params = wp_parse_args( $kebab, $defaults ); // Whitelisted params. $whitelist = array_merge( $this->valid_font_face_properties, $this->api_params ); @@ -264,4 +271,17 @@ public function get_params() { * @return string */ abstract public function get_css(); + + /** + * Convert camelCase to kebab-case. + * + * @since 5.9.0 + * + * @param string $string The string to convert. + * + * @return string + */ + public function camel_to_kebab( $string ) { + return strtolower( preg_replace( '/(? Date: Tue, 12 Oct 2021 10:28:31 +0300 Subject: [PATCH 15/94] Fix styles generation --- .../class-wp-webfonts-google-provider.php | 124 ++++++++++-------- .../class-wp-webfonts-local-provider.php | 57 ++++---- .../providers/class-wp-webfonts-provider.php | 78 +++++++++++ 3 files changed, 174 insertions(+), 85 deletions(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index e6619ae0b1c11..b59e5c2d14b7e 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -58,85 +58,93 @@ final class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { ); /** - * Build the API URL from the query args. + * Build the API URL for a collection of fonts. * + * @access protected * @since 5.9.0 + * @param array $fonts * @return string */ - protected function build_api_url() { - $query_args = array( - 'family' => $this->params['font-family'], - 'display' => $this->params['font-display'], - ); - - if ( 'italic' === $this->params['font-style'] ) { - $query_args['family'] .= ':ital,wght@1,' . $this->params['font-weight']; - } else { - $query_args['family'] .= ':wght@' . $this->params['font-weight']; + protected function build_collection_api_urls( $fonts ) { + $font_families_urls = array(); + + + // Validate all fonts. + foreach ( $fonts as $key => $font ) { + $fonts[ $key ] = $this->get_validated_params( $font ); } - if ( ! empty( $this->params['subset'] ) ) { - $query_args['subset'] = implode( ',', (array) $this->params['subset'] ); + // Group by font-display. + // Each font-display will need to be a separate request. + $font_display_groups = array(); + foreach ( $fonts as $font ) { + $font['font-display'] = isset( $font['font-display'] ) ? $font['font-display'] : 'fallback'; + if ( ! isset( $font_display_groups[ $font['font-display'] ] ) ) { + $font_display_groups[ $font['font-display'] ] = array(); + } + $font_display_groups[ $font['font-display'] ][] = $font; } - if ( ! empty( $this->params['text'] ) ) { - $query_args['text'] = $this->params['text']; + // Iterate over each font-display group and group by font-family. + // Multiple font-families can be combined in the same request, but their params need to be grouped. + foreach ( $font_display_groups as $font_display => $font_display_group ) { + $font_families = array(); + foreach ( $font_display_group as $font ) { + if ( ! isset( $font_families[ $font['font-family'] ] ) ) { + $font_families[ $font['font-family'] ] = array(); + } + $font_families[ $font['font-family'] ][] = $font; + } + $font_display_groups[ $font_display ] = $font_families; } - if ( ! empty( $this->params['effect'] ) ) { - $query_args['effect'] = implode( '|', (array) $this->params['effect'] ); + // Iterate over each font-family group and build the API URL partial for that font-family. + foreach ( $font_display_groups as $font_display => $font_families ) { + $font_display_url_parts = array(); + foreach ( $font_families as $font_family => $fonts ) { + $normal_weights = array(); + $italic_weights = array(); + $url_part = urlencode( $font_family ); + + // Build an array of font-weights for italics and default styles. + foreach ( $fonts as $font ) { + if ( 'italic' === $font['font-style'] ) { + $italic_weights[] = $font['font-weight']; + } else { + $normal_weights[] = $font['font-weight']; + } + } + + if ( empty( $italic_weights ) && ! empty( $normal_weights ) ) { + $url_part .= ':wght@' . implode( ';', $normal_weights ); + } elseif ( ! empty( $italic_weights ) && empty( $normal_weights ) ) { + $url_part .= ':ital,wght@1,' . implode( ';', $normal_weights ); + } elseif ( ! empty( $italic_weights ) && ! empty( $normal_weights ) ) { + $url_part .= ':ital,wght@0,' . implode( ';0,', $normal_weights ) . ';1,' . implode( ';1,', $italic_weights ); + } + + $font_display_url_parts[] = $url_part; + } + + $font_families_urls[] = $this->root_url . '?family=' . implode( '&family=', $font_display_url_parts ) . '&display=' . $font_display; } - return add_query_arg( $query_args, $this->root_url ); + return $font_families_urls; } /** - * Get the CSS for the font. + * Get the CSS for a collection of fonts. * + * @access public * @since 5.9.0 - * * @return string */ public function get_css() { - $remote_url = $this->build_api_url(); - $transient_name = 'google_fonts_' . md5( $remote_url ); - $css = get_site_transient( $transient_name ); - - // Get remote response and cache the CSS if it hasn't been cached already. - if ( false === $css ) { - // Get the remote URL contents. - $response = wp_remote_get( - $remote_url, - array( - // Use a modern user-agent, to get woff2 files. - 'user-agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0', - ) - ); - - // Early return if the request failed. - if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { - set_site_transient( $transient_name, '', 60 ); - return ''; - } - - // Get the response body. - $css = wp_remote_retrieve_body( $response ); - - // Cache the CSS for a month. - set_site_transient( $transient_name, $css, MONTH_IN_SECONDS ); - } + $css = ''; + $urls = $this->build_collection_api_urls( $this->params ); - // If there are additional props not included in the CSS provided by the API, add them to the final CSS. - $additional_props = array_diff( - array_keys( $this->params ), - array( 'font-family', 'font-style', 'font-weight', 'font-display', 'src', 'unicode-range' ) - ); - foreach ( $additional_props as $prop ) { - $css = str_replace( - '@font-face {', - '@font-face {' . $prop . ':' . $this->params[ $prop ] . ';', - $css - ); + foreach ( $urls as $url ) { + $css .= $this->get_cached_remote_styles( 'google_fonts_' . md5( $url ), $url ); } return $css; diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index db70559ba9b8e..f89f7908f8251 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -39,49 +39,52 @@ public function get_validated_params( $params ) { } /** - * Get the CSS for the font. + * Get the CSS for a collection of fonts. * + * @access public * @since 5.9.0 * @return string */ public function get_css() { - if ( empty( $this->params['font-family'] ) ) { - return ''; - } + $css = ''; + foreach ( $this->params as $font ) { - $css = '@font-face{'; - foreach ( $this->params as $key => $value ) { + // Validate font params. + $font = $this->get_validated_params( $font ); - // Skip the "preload" parameter. - if ( 'preload' === $key ) { + if ( empty( $font['font-family'] ) ) { continue; } - // Compile the "src" parameter. - if ( 'src' === $key ) { - $src = "local({$this->params['font-family']})"; - foreach ( $value as $item ) { - $src .= ( 'data' === $item['format'] ) - ? ", url({$item['url']})" - : ", url('{$item['url']}') format('{$item['format']}')"; + $css .= '@font-face{'; + foreach ( $font as $key => $value ) { + + // Compile the "src" parameter. + if ( 'src' === $key ) { + $src = "local({$font['font-family']})"; + foreach ( $value as $item ) { + $src .= ( 'data' === $item['format'] ) + ? ", url({$item['url']})" + : ", url('{$item['url']}') format('{$item['format']}')"; + } + $value = $src; } - $value = $src; - } - // If font-variation-settings is an array, convert it to a string. - if ( 'font-variation-settings' === $key && is_array( $value ) ) { - $variations = array(); - foreach ( $value as $key => $val ) { - $variations[] = "$key $val"; + // If font-variation-settings is an array, convert it to a string. + if ( 'font-variation-settings' === $key && is_array( $value ) ) { + $variations = array(); + foreach ( $value as $key => $val ) { + $variations[] = "$key $val"; + } + $value = implode( ', ', $variations ); } - $value = implode( ', ', $variations ); - } - if ( ! empty( $value ) ) { - $css .= "$key:$value;"; + if ( ! empty( $value ) ) { + $css .= "$key:$value;"; + } } + $css .= '}'; } - $css .= '}'; return $css; } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index 8a00d74071363..a85ab07974412 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -272,6 +272,84 @@ public function get_params() { */ abstract public function get_css(); + /** + * Get cached styles from a remote URL. + * + * @access public + * @since 5.9.0 + * + * @param string $url The URL to fetch. + * @param string $id An ID used to cache the styles. + * @param array $args The arguments to pass to wp_remote_get(). + * @param array $additional_props Additional properties to add to the @font-face styles. + * + * @return string The styles. + */ + public function get_cached_remote_styles( $id, $url, $args = array(), $additional_props = array() ) { + $css = get_site_transient( $id ); + + // Get remote response and cache the CSS if it hasn't been cached already. + if ( false === $css ) { + $css = $this->get_remote_styles( $url, $args ); + + // Early return if the request failed. + // Cache an empty string for 60 seconds to avoid bottlenecks. + if ( empty( $css ) ) { + set_site_transient( $id, '', 60 ); + return ''; + } + + // Cache the CSS for a month. + set_site_transient( $id, $css, MONTH_IN_SECONDS ); + } + + // If there are additional props not included in the CSS provided by the API, add them to the final CSS. + foreach ( $additional_props as $prop ) { + $css = str_replace( + '@font-face {', + '@font-face {' . $prop . ':' . $this->params[ $prop ] . ';', + $css + ); + } + + return $css; + } + + /** + * Get styles from a remote URL. + * + * @access public + * @since 5.9.0 + * + * @param string $url The URL to fetch. + * @param string $id An ID used to cache the styles. + * @param array $args The arguments to pass to wp_remote_get(). + * @param array $additional_props Additional properties to add to the @font-face styles. + * + * @return string The styles. + */ + public function get_remote_styles( $url, $args = array() ) { + $args = wp_parse_args( + $args, + array( + // Use a modern user-agent, to get woff2 files. + 'user-agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0', + ) + ); + + // Get the remote URL contents. + $response = wp_remote_get( $url, $args ); + + // Early return if the request failed. + // Cache an empty string for 60 seconds to avoid bottlenecks. + if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { + return ''; + } + + // Get the response body. + return wp_remote_retrieve_body( $response ); + } + /** * Convert camelCase to kebab-case. * From 45c6f2169f0e43f0d778cb3025b554a992a94ec4 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Tue, 12 Oct 2021 10:53:31 +0300 Subject: [PATCH 16/94] remove blank line (CS) --- .../webfonts-api/providers/class-wp-webfonts-google-provider.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index b59e5c2d14b7e..5ad3652068d7f 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -68,7 +68,6 @@ final class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { protected function build_collection_api_urls( $fonts ) { $font_families_urls = array(); - // Validate all fonts. foreach ( $fonts as $key => $font ) { $fonts[ $key ] = $this->get_validated_params( $font ); From 78812ddbcd8e3fabc5a97e61bf825fdf5776a658 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 13 Oct 2021 10:09:07 +0300 Subject: [PATCH 17/94] Add editor styles for webfonts --- .../class-wp-webfonts-controller.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 33908aed6a412..e00c4a9884fd9 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -54,6 +54,9 @@ public function init() { $hook = 'wp_enqueue_scripts'; } add_action( $hook, array( $this, 'generate_and_enqueue_styles' ) ); + + // Enqueue webfonts in the block editor. + add_action( 'admin_init', array( $this, 'generate_and_enqueue_editor_styles' ) ); } /** @@ -158,6 +161,20 @@ public function generate_and_enqueue_styles() { wp_add_inline_style( $this->stylesheet_handle, $styles ); } + /** + * Generate and enqueue editor styles. + * + * @since 5.9.0 + */ + public function generate_and_enqueue_editor_styles() { + wp_add_inline_style( 'wp-block-library', $this->generate_styles() ); + } + + /** + * Generate styles for webfonts. + * + * @since 5.9.0 + */ private function generate_styles() { $styles = ''; foreach ( $this->get_registered_providers() as $provider_id => $provider ) { From 75c0bf9a2c6549784ce3a7cfa18c8ff16c36eae7 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 13 Oct 2021 12:35:15 +0300 Subject: [PATCH 18/94] Make function public. We'll need it for the theme.json implementation --- src/wp-includes/webfonts-api/class-wp-webfonts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index e00c4a9884fd9..1247a723087f0 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -175,7 +175,7 @@ public function generate_and_enqueue_editor_styles() { * * @since 5.9.0 */ - private function generate_styles() { + public function generate_styles() { $styles = ''; foreach ( $this->get_registered_providers() as $provider_id => $provider ) { $registered_webfonts = $this->webfonts_registry->get_by_provider( $provider_id ); From 228508d42b370eed76707ab9a42ba983c9fe0728 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 13 Oct 2021 12:35:15 +0300 Subject: [PATCH 19/94] Revert "Make function public. We'll need it for the theme.json implementation" This reverts commit 0aa0e4aebc47bc7be66a43eb7e05867c14aca517. --- src/wp-includes/webfonts-api/class-wp-webfonts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 1247a723087f0..e00c4a9884fd9 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -175,7 +175,7 @@ public function generate_and_enqueue_editor_styles() { * * @since 5.9.0 */ - public function generate_styles() { + private function generate_styles() { $styles = ''; foreach ( $this->get_registered_providers() as $provider_id => $provider ) { $registered_webfonts = $this->webfonts_registry->get_by_provider( $provider_id ); From 880178d22592cb0d2d85793357bef8a7963fff1d Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 13 Oct 2021 12:59:51 +0300 Subject: [PATCH 20/94] Account for "file:./" URLs defined in a theme.json file. URLs like that will be relative to the theme root since a theme.json file is always at the root of the theme. --- .../providers/class-wp-webfonts-local-provider.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index f89f7908f8251..aca94fc387178 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -63,6 +63,13 @@ public function get_css() { if ( 'src' === $key ) { $src = "local({$font['font-family']})"; foreach ( $value as $item ) { + + // If the URL starts with "file:./" then it originated in a theme.json file. + // Tweak the URL to be relative to the theme root. + if ( 0 === strpos( $item['url'], 'file:./' ) ) { + $item['url'] = wp_make_link_relative( get_theme_file_uri( str_replace( 'file:./', '', $item['url'] ) ) ); + } + $src .= ( 'data' === $item['format'] ) ? ", url({$item['url']})" : ", url('{$item['url']}') format('{$item['format']}')"; From 780cda657aa197a58ba04a8fdeeb65f21af11b36 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Wed, 13 Oct 2021 15:19:03 -0500 Subject: [PATCH 21/94] Update docblocks and remove final for testing. --- .../class-wp-webfonts-controller.php | 30 +++++++++++-- .../class-wp-webfonts-provider-registry.php | 2 +- .../class-wp-webfonts-registry.php | 8 +++- .../class-wp-webfonts-schema-validator.php | 42 ++++++++++++++++++- .../class-wp-webfonts-google-provider.php | 2 +- .../class-wp-webfonts-local-provider.php | 9 ++-- .../providers/class-wp-webfonts-provider.php | 20 +++++---- 7 files changed, 91 insertions(+), 22 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index e00c4a9884fd9..acb00a773a28b 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -1,10 +1,24 @@ set_webfonts( $registered_webfonts ); $styles .= $provider->get_css(); @@ -193,11 +215,11 @@ private function generate_styles() { } /** - * Add preconnect links to for enqueued webfonts. + * Renders preconnect links to for enqueued webfonts. * * @since 5.9.0 */ - public function add_preconnect_links() { + public function render_preconnect_links() { echo $this->providers_registry->get_preconnect_links(); } } diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php index dbe77c2bf3023..469dd1a5e3ed8 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -10,7 +10,7 @@ /** * Provider Registry. */ -final class WP_Webfonts_Provider_Registry { +class WP_Webfonts_Provider_Registry { /** * Registered providers. diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index c7487dcb7a1db..e43e29304838b 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -10,9 +10,9 @@ /** * Webfonts Registry. * - * Handles schema validation, webfont registration, and query of webfonts. + * Handles webfont registration and query of webfonts. */ -final class WP_Webfonts_Registry { +class WP_Webfonts_Registry { /** * Registered webfonts. @@ -26,6 +26,8 @@ final class WP_Webfonts_Registry { /** * Registration keys per provider. * + * @since 5.9.0 + * * @var string[] */ private $registry_by_provider = array(); @@ -33,6 +35,8 @@ final class WP_Webfonts_Registry { /** * Schema validator. * + * @since 5.9.0 + * * @var WP_Webfonts_Schema_Validator */ private $validator; diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 8bd94a24d3bc7..de2422e34e7c5 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -1,5 +1,17 @@ webfont['fontFamily'] ) || ! is_string( $this->webfont['fontFamily'] ) ) { _doing_it_wrong( @@ -80,6 +106,13 @@ private function is_font_family_valid() { return true; } + /** + * Checks if the font style is validate. + * + * @since 5.9.0 + * + * @return bool True if valid. False if invalid. + */ private function is_font_style_valid() { if ( empty( $this->webfont['fontStyle'] ) || ! is_string( $this->webfont['fontStyle'] ) ) { _doing_it_wrong( @@ -127,6 +160,13 @@ private function is_font_style_value_valid( $font_style ) { return false; } + /** + * Checks if the font weight is validate. + * + * @since 5.9.0 + * + * @return bool True if valid. False if invalid. + */ private function is_font_weight_valid() { // @todo validate the value. if ( empty( $this->webfont['fontWeight'] ) || ! is_string( $this->webfont['fontWeight'] ) ) { diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 5ad3652068d7f..3dc2cce97745b 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -10,7 +10,7 @@ /** * Webfonts API provider for Google Fonts. */ -final class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { +class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { /** * The provider's unique ID. diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index aca94fc387178..9fd7c9abf0244 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -1,6 +1,6 @@ Date: Wed, 13 Oct 2021 18:07:56 -0500 Subject: [PATCH 22/94] Replaces _doing_it_wrong() with trigger_error(). Per Ozz, this is an incorrect usage of doing it wrong. It should not be used for input validation. Use `trigger_error()` instead. --- .../class-wp-webfonts-schema-validator.php | 33 +++---------- .../webfonts-api/wpWebfontsController.php | 28 ++++++----- .../wpWebfontsProviderRegistry.php | 2 +- .../tests/webfonts-api/wpWebfontsRegistry.php | 49 ++++++++++++------- 4 files changed, 55 insertions(+), 57 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index de2422e34e7c5..5b81a66b72b1b 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -73,11 +73,7 @@ private function is_provider_valid() { // @todo check if provider is registered. if ( empty( $this->webfont['provider'] ) || ! is_string( $this->webfont['provider'] ) ) { - _doing_it_wrong( - 'register_webfonts', - __( 'Webfont must define a string "provider".' ), - '5.9.0' - ); + trigger_error( __( 'Webfont provider must be a non-empty string.' ) ); return false; } @@ -94,11 +90,7 @@ private function is_provider_valid() { */ private function is_font_family_valid() { if ( empty( $this->webfont['fontFamily'] ) || ! is_string( $this->webfont['fontFamily'] ) ) { - _doing_it_wrong( - 'register_webfonts', - __( 'Webfont must define a string "fontFamily".' ), - '5.9.0' - ); + trigger_error( __( 'Webfont font family must be a non-empty string.' ) ); return false; } @@ -115,25 +107,18 @@ private function is_font_family_valid() { */ private function is_font_style_valid() { if ( empty( $this->webfont['fontStyle'] ) || ! is_string( $this->webfont['fontStyle'] ) ) { - _doing_it_wrong( - 'register_webfonts', - __( 'Webfont must define a string "fontStyle".' ), - '5.9.0' - ); - + trigger_error( __( 'Webfont font style must be a non-empty string.' ) ); return false; } if ( ! $this->is_font_style_value_valid( $this->webfont['fontStyle'] ) ) { - _doing_it_wrong( - 'register_webfonts', + trigger_error( sprintf( - /* translators: 1: Slant angle, 2: Given font style. */ + /* translators: 1: Slant angle, 2: Given font style. */ __( 'Webfont font style must be normal, italic, oblique, or oblique %1$s. Given: %2$s.' ), '', $this->webfont['fontStyle'] - ), - '5.9.0' + ) ); return false; @@ -170,11 +155,7 @@ private function is_font_style_value_valid( $font_style ) { private function is_font_weight_valid() { // @todo validate the value. if ( empty( $this->webfont['fontWeight'] ) || ! is_string( $this->webfont['fontWeight'] ) ) { - _doing_it_wrong( - 'register_webfonts', - __( 'Webfont must define a string "fontWeight".' ), - '5.9.0' - ); + trigger_error( __( 'Webfont font weight must be a non-empty string.' ) ); return false; } diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index 50f81723190d7..831e9e9a7721d 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -7,7 +7,7 @@ class Tests_Webfonts_API_wpWebfontsController extends WP_UnitTestCase { private static $webfonts; - public static function wpSetUpBeforeClass() { + public static function set_up_before_class() { require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-schema-validator.php'; require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-registry.php'; require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-provider-registry.php'; @@ -51,19 +51,19 @@ public function test_register_webfonts_with_empty_schema() { } /** - * @covers WP_Webfonts_Controller::register_webfonts - * @covers WP_Webfonts_Controller::register_webfont + * @covers WP_Webfonts_Controller::register_webfonts + * @covers WP_Webfonts_Controller::register_webfont * * @dataProvider data_register_webfonts_with_invalid_schema * * @param array $webfonts Webfonts input. - * @param array $expected Exptected registered webfonts. + * @param array $expected Expected registered webfonts. */ - public function test_register_webfonts_with_invalid_schema( array $webfonts, array $expected ) { - $controller = $this->get_controller(); - - $this->setExpectedIncorrectUsage( 'register_webfonts' ); + public function test_register_webfonts_with_invalid_schema( array $webfonts, $expected_message, array $expected ) { + $this->expectNotice(); + $this->expectNoticeMessage( $expected_message ); + $controller = $this->get_controller(); $controller->register_webfonts( $webfonts ); $this->assertSame( $expected, $controller->get_webfonts() ); @@ -80,7 +80,7 @@ public function test_register_webfonts_with_invalid_schema( array $webfonts, arr public function data_register_webfonts_with_invalid_schema() { return array( 'provider: invalid type' => array( - 'webfonts' => array( + 'webfonts' => array( array( 'provider' => null, 'fontFamily' => 'Open Sans', @@ -94,7 +94,8 @@ public function data_register_webfonts_with_invalid_schema() { 'fontWeight' => '700', ), ), - 'expected' => array( + 'expected_message' => 'Webfont provider must be a non-empty string.', + 'expected' => array( 'open-sans.italic.700' => array( 'provider' => 'google', 'fontFamily' => 'Open Sans', @@ -104,7 +105,7 @@ public function data_register_webfonts_with_invalid_schema() { ), ), 'font family: invalid key' => array( - 'webfonts' => array( + 'webfonts' => array( array( 'provider' => 'google', 'fontFamily' => 'Roboto', @@ -118,7 +119,8 @@ public function data_register_webfonts_with_invalid_schema() { 'fontWeight' => '400', ), ), - 'expected' => array( + 'expected_message' => 'Webfont font family must be a non-empty string.', + 'expected' => array( 'roboto.normal.900' => array( 'provider' => 'google', 'fontFamily' => 'Roboto', @@ -191,7 +193,7 @@ public function test_get_webfonts_by_font_family() { } /** - * @covers WP_Webfonts_Controller::get_webfonts_by_font_family + * @covers WP_Webfonts_Controller::get_webfonts_by_font_family * * @dataProvider data_get_by_font_family_with_invalid_input * diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php index e22a783a31abf..c9501d5483d6e 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php @@ -6,7 +6,7 @@ */ class Tests_Webfonts_API_wpWebfontsProviderRegistry extends WP_UnitTestCase { - public static function wpSetUpBeforeClass() { + public static function set_up_before_class() { require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-provider-registry.php'; require_once __DIR__ . '/mocks/class-my-custom-webfonts-provider-mock.php'; } diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index 022295cc0f3fe..b0824fcec5656 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -27,8 +27,9 @@ private function get_registry() { * * @param array Webfonts input. */ - public function test_register_with_invalid_schema( array $webfont ) { - $this->setExpectedIncorrectUsage( 'register_webfonts' ); + public function test_register_with_invalid_schema( array $webfont, $expected_message ) { + $this->expectNotice(); + $this->expectNoticeMessage( $expected_message ); $registry = $this->get_registry(); @@ -43,102 +44,116 @@ public function test_register_with_invalid_schema( array $webfont ) { public function data_register_with_invalid_schema() { return array( 'empty array - no schema' => array( - array(), + 'webfont' => array(), + 'expected_message' => 'Webfont provider must be a non-empty string.', ), 'provider: not set' => array( - array( + 'webfont' => array( 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => '400', ), + 'expected_message' => 'Webfont provider must be a non-empty string.', ), 'provider: empty string' => array( - array( + 'webfont' => array( 'provider' => '', 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => '400', ), + 'expected_message' => 'Webfont provider must be a non-empty string.', ), 'provider: invalid type' => array( - array( + 'webfont' => array( 'provider' => true, 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => '400', ), + 'expected_message' => 'Webfont provider must be a non-empty string.', ), 'font family: not set' => array( - array( + 'webfont' => array( 'provider' => 'local', 'fontStyle' => 'normal', 'fontWeight' => '400', ), + 'expected_message' => 'Webfont font family must be a non-empty string.', ), 'font family: empty string' => array( - array( + 'webfont' => array( 'provider' => 'local', 'fontFamily' => '', 'fontStyle' => 'normal', 'fontWeight' => '400', ), + 'expected_message' => 'Webfont font family must be a non-empty string.', ), 'font family: invalid type' => array( - array( + 'webfont' => array( 'provider' => 'local', 'fontFamily' => true, 'fontStyle' => 'normal', 'fontWeight' => '400', ), + 'expected_message' => 'Webfont font family must be a non-empty string.', ), 'font style: empty string' => array( - array( + 'webfont' => array( 'provider' => 'local', 'fontFamily' => 'Open Sans', 'fontStyle' => '', 'fontWeight' => '400', ), + 'expected_message' => 'Webfont font style must be a non-empty string.', ), 'font style: invalid type' => array( - array( + 'webfont' => array( 'provider' => 'local', 'fontFamily' => 'Open Sans', 'fontStyle' => true, 'fontWeight' => '400', ), + 'expected_message' => 'Webfont font style must be a non-empty string.', ), 'font style: invalid value' => array( - array( + 'webfont' => array( 'provider' => 'local', 'fontFamily' => 'Open Sans', 'fontStyle' => 'invalid', 'fontWeight' => '400', ), + 'expected_message' => 'Webfont font style must be normal, italic, oblique, or oblique . Given: invalid.', ), - 'font wegith: empty string' => array( - array( + 'font weight: empty string' => array( + 'webfont' => array( 'provider' => 'local', 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => '', ), + 'expected_message' => 'Webfont font weight must be a non-empty string.', ), 'font weight: invalid type' => array( - array( + 'webfont' => array( 'provider' => 'local', 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => true, ), + 'expected_message' => 'Webfont font weight must be a non-empty string.', ), - /* @todo uncomment once value validation is added. + // @todo uncomment once value validation is added. + /* 'font weight: invalid value' => array( - array( + 'webfont' => array( 'provider' => 'local', 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => 'invalid', ), + 'expected_message' => 'Webfont font weight must be a non-empty string.', ), */ ); From a73bc1dd14202a2556ed35d345d39b738c982d00 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Wed, 13 Oct 2021 20:37:23 -0500 Subject: [PATCH 23/94] Moves validation from providers to Validator. Moves all of the validation logic from the providers to the Validator. Adds tests. --- .../class-wp-webfonts-registry.php | 35 +- .../class-wp-webfonts-schema-validator.php | 220 +++++--- .../class-wp-webfonts-google-provider.php | 20 +- .../class-wp-webfonts-local-provider.php | 223 ++++++-- .../providers/class-wp-webfonts-provider.php | 228 ++------ ...class-my-custom-webfonts-provider-mock.php | 21 +- .../providers/wpWebfontsGoogleProvider.php | 151 +++++ .../providers/wpWebfontsLocalProvider.php | 244 ++++++++ .../webfonts-api/wpWebfontsController.php | 412 ++++++++------ .../tests/webfonts-api/wpWebfontsRegistry.php | 524 ++++++++++++------ .../wpWebfontsSchemaValidator.php | 393 +++++++++++++ 11 files changed, 1823 insertions(+), 648 deletions(-) create mode 100644 tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php create mode 100644 tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php create mode 100644 tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index e43e29304838b..ff4bca0b9649f 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -88,7 +88,7 @@ public function get_by_provider( $provider_id ) { * @since 5.9.0 * * @param string $font_family Family font to fetch. - * @return string[][] Registered webfonts. + * @return array[] Registered webfonts. */ public function get_by_font_family( $font_family ) { if ( ! is_string( $font_family ) || '' === $font_family ) { @@ -116,17 +116,19 @@ public function get_by_font_family( $font_family ) { * * @since 5.9.0 * - * @param string[] $webfont Webfont definition. + * @param array $webfont Webfont definition. * @return string Registration key. */ public function register( array $webfont ) { - $webfont = $this->merge_optional_parameters( $webfont ); + $webfont = $this->convert_to_kabeb_case( $webfont ); // Validate schema. - if ( ! $this->is_schema_valid( $webfont ) ) { + if ( ! $this->validator->is_valid_schema( $webfont ) ) { return ''; } + $webfont = $this->validator->set_valid_properties( $webfont ); + // Add to registry. $registration_key = $this->generate_registration_key( $webfont ); if ( ! isset( $this->registry[ $registration_key ] ) ) { @@ -138,29 +140,24 @@ public function register( array $webfont ) { } /** - * Merge optional parameters into webfont definition. + * Convert camelCase parameters into kabeb_case. * * @since 5.9.0 * * @param string[] $webfont Webfont definition. - * @return string[] Webfont with optional parameters. + * @return array Webfont with kabeb_case parameters (keys). */ - private function merge_optional_parameters( array $webfont ) { - if ( ! isset( $webfont['fontStyle'] ) ) { - $webfont['fontStyle'] = 'normal'; - } - - if ( ! isset( $webfont['fontWeight'] ) ) { - $webfont['fontWeight'] = '400'; - } + private function convert_to_kabeb_case( array $webfont ) { + $kebab_case = preg_replace( '/(?convert_font_family_into_key( $webfont['fontFamily'] ), - trim( $webfont['fontStyle'] ), - trim( $webfont['fontWeight'] ) + $this->convert_font_family_into_key( $webfont['font-family'] ), + trim( $webfont['font-style'] ), + trim( $webfont['font-weight'] ) ); } diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 5b81a66b72b1b..055039249a1d5 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -21,45 +21,87 @@ class WP_Webfonts_Schema_Validator { * * @var string[] */ - private $valid_font_style = array( - 'normal', - 'italic', - 'oblique', - // Global values. - 'inherit', - 'initial', - 'revert', - 'unset', + protected $font_style = array( 'normal', 'italic', 'oblique', 'inherit', 'initial', 'revert', 'unset' ); + + /** + * Valid font weight values. + * + * @since 5.9.0 + * + * @var string[] + */ + protected $font_weight = array( 'normal', 'bold', 'bolder', 'lighter', 'inherit' ); + + /** + * Valid font display values. + * + * @since 5.9.0 + * + * @var string[] + */ + protected $font_display = array( 'auto', 'block', 'swap', 'fallback' ); + + /** + * An array of valid CSS properties for @font-face. + * + * @since 5.9.0 + * + * @var string[] + */ + protected $font_face_properties = array( + 'ascend-override', + 'descend-override', + 'font-display', + 'font-family', + 'font-stretch', + 'font-style', + 'font-weight', + 'font-variant', + 'font-feature-settings', + 'font-variation-settings', + 'line-gap-override', + 'size-adjust', + 'src', + 'unicode-range', + ); + + /** + * Basic schema structure. + * + * @since 5.9.0 + * + * @var array + */ + protected $basic_schema = array( + 'provider' => '', + 'font-family' => '', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', ); /** * Webfont being validated. * - * @var string[] + * Set as a property for performance. + * + * @var array */ private $webfont = array(); /** - * Checks if the given webfont schema is validate. + * Checks if the given webfont schema is valid. * * @since 5.9.0 * - * @param string[] $webfont Webfont definition. + * @param array $webfont Webfont to validate. * @return bool True when valid. False when invalid. */ - public function is_schema_valid( array $webfont ) { - $this->webfont = $webfont; - - $is_valid = ( - $this->is_provider_valid() && - $this->is_font_family_valid() && - $this->is_font_style_valid() && - $this->is_font_weight_valid() + public function is_valid_schema( array $webfont ) { + return ( + $this->is_valid_provider( $webfont ) && + $this->is_valid_font_family( $webfont ) ); - - $this->webfont = array(); - - return $is_valid; } /** @@ -67,12 +109,16 @@ public function is_schema_valid( array $webfont ) { * * @since 5.9.0 * + * @param array $webfont Webfont to valiate. * @return bool True if valid. False if invalid. */ - private function is_provider_valid() { + private function is_valid_provider( array $webfont ) { // @todo check if provider is registered. - if ( empty( $this->webfont['provider'] ) || ! is_string( $this->webfont['provider'] ) ) { + if ( + empty( $webfont['provider'] ) || + ! is_string( $webfont['provider'] ) + ) { trigger_error( __( 'Webfont provider must be a non-empty string.' ) ); return false; @@ -86,10 +132,14 @@ private function is_provider_valid() { * * @since 5.9.0 * + * @param array $webfont Webfont to validate. * @return bool True if valid. False if invalid. */ - private function is_font_family_valid() { - if ( empty( $this->webfont['fontFamily'] ) || ! is_string( $this->webfont['fontFamily'] ) ) { + private function is_valid_font_family( array $webfont ) { + if ( + empty( $webfont['font-family'] ) || + ! is_string( $webfont['font-family'] ) + ) { trigger_error( __( 'Webfont font family must be a non-empty string.' ) ); return false; @@ -99,67 +149,109 @@ private function is_font_family_valid() { } /** - * Checks if the font style is validate. + * Sets valid properties. * * @since 5.9.0 * - * @return bool True if valid. False if invalid. + * @param array $webfont Webfont definition. + * @return array Updated webfont. */ - private function is_font_style_valid() { - if ( empty( $this->webfont['fontStyle'] ) || ! is_string( $this->webfont['fontStyle'] ) ) { - trigger_error( __( 'Webfont font style must be a non-empty string.' ) ); - return false; - } + public function set_valid_properties( array $webfont ) { + $this->webfont = array_merge( $this->basic_schema, $webfont ); - if ( ! $this->is_font_style_value_valid( $this->webfont['fontStyle'] ) ) { - trigger_error( - sprintf( - /* translators: 1: Slant angle, 2: Given font style. */ - __( 'Webfont font style must be normal, italic, oblique, or oblique %1$s. Given: %2$s.' ), - '', - $this->webfont['fontStyle'] - ) - ); + $this->set_valid_font_face_property(); + $this->set_valid_font_style(); + $this->set_valid_font_weight(); + $this->set_valid_font_display(); - return false; - } + $webfont = $this->webfont; + $this->webfont = array(); // Reset property. - return true; + return $webfont; } /** - * Checks if the given font-style is valid. + * Checks if the CSS property is valid for @font-face. * * @since 5.9.0 - * - * @param string $font_style Font style to validate. - * @return bool True when font-style is valid. */ - private function is_font_style_value_valid( $font_style ) { - if ( in_array( $font_style, $this->valid_font_style, true ) ) { - return true; + private function set_valid_font_face_property() { + foreach ( array_keys( $this->webfont ) as $property ) { + /* + * Skip valid configuration parameters (these are configuring the webfont + * but are not @font-face properties. + */ + if ( 'provider' === $property ) { + continue; + } + + if ( ! in_array( $property, $this->font_face_properties, true ) ) { + unset( $this->webfont[ $property ] ); + } } + } - // @todo Check for oblique . + /** + * Checks if the font style is validate. + * + * @since 5.9.0 + */ + private function set_valid_font_style() { + // If empty or not a string, trigger an error and then set the default value. + if ( + empty( $this->webfont['font-style'] ) || + ! is_string( $this->webfont['font-style'] ) + ) { + trigger_error( __( 'Webfont font style must be a non-empty string.' ) ); + + } elseif ( // Bail out if the font-weight is a valid value. + in_array( $this->webfont['font-style'], $this->font_style, true ) || + preg_match( '/^oblique\s+(\d+)%/', $this->webfont['font-style'] ) + ) { + return; + } - return false; + $this->webfont['font-style'] = 'normal'; } /** - * Checks if the font weight is validate. + * Sets a default font weight if invalid. * * @since 5.9.0 - * - * @return bool True if valid. False if invalid. */ - private function is_font_weight_valid() { - // @todo validate the value. - if ( empty( $this->webfont['fontWeight'] ) || ! is_string( $this->webfont['fontWeight'] ) ) { + private function set_valid_font_weight() { + // If empty or not a string, trigger an error and then set the default value. + if ( + empty( $this->webfont['font-weight'] ) || + ! is_string( $this->webfont['font-weight'] ) + ) { trigger_error( __( 'Webfont font weight must be a non-empty string.' ) ); - return false; + } elseif ( // Bail out if the font-weight is a valid value. + in_array( $this->webfont['font-weight'], $this->font_weight, true ) || + preg_match( '/^(\d+)$/', $this->webfont['font-weight'], $matches ) || + preg_match( '/^(\d+)\s+(\d+)$/', $this->webfont['font-weight'], $matches ) + ) { + return; } - return true; + // Not valid. Set the default value. + $this->webfont['font-weight'] = '400'; + } + + /** + * Sets a default font display if invalid. + * + * @since 5.9.0 + */ + private function set_valid_font_display() { + if ( + ! empty( $this->webfont['font-display'] ) && + in_array( $this->webfont['font-display'], $this->font_display, true ) + ) { + return; + } + + $this->webfont['font-display'] = 'fallback'; } } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 3dc2cce97745b..c4065feb2da83 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -1,6 +1,6 @@ $font ) { - $fonts[ $key ] = $this->get_validated_params( $font ); - } - // Group by font-display. // Each font-display will need to be a separate request. $font_display_groups = array(); @@ -140,7 +136,7 @@ protected function build_collection_api_urls( $fonts ) { */ public function get_css() { $css = ''; - $urls = $this->build_collection_api_urls( $this->params ); + $urls = $this->build_collection_api_urls( $this->webfonts ); foreach ( $urls as $url ) { $css .= $this->get_cached_remote_styles( 'google_fonts_' . md5( $url ), $url ); diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index 9fd7c9abf0244..dc1da5cb8daf2 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -22,78 +22,205 @@ class WP_Webfonts_Local_Provider extends WP_Webfonts_Provider { protected $id = 'local'; /** - * Get validated params. + * Prepares the given webfont. * * @since 5.9.0 * - * @param array $params The webfont's parameters. + * @param array $webfont Webfont to validate. * @return array */ - public function get_validated_params( $params ) { - $params = parent::get_validated_params( $params ); + protected function prepare( array $webfont ) { + $webfont = parent::prepare( $webfont ); + $webfont = $this->order_src( $webfont ); // Wrap font-family in quotes if it contains spaces. - if ( false !== strpos( $params['font-family'], ' ' ) && false === strpos( $params['font-family'], '"' ) && false === strpos( $params['font-family'], "'" ) ) { - $params['font-family'] = '"' . $params['font-family'] . '"'; + if ( + false !== strpos( $webfont['font-family'], ' ' ) && + false === strpos( $webfont['font-family'], '"' ) && + false === strpos( $webfont['font-family'], "'" ) + ) { + $webfont['font-family'] = '"' . $webfont['font-family'] . '"'; } - return $params; + + return $webfont; + } + + /** + * Order `src` items to optimize for browser support. + * + * @since 5.9.0 + * + * @param string[] $webfont Webfont to process. + * @return string[] + */ + private function order_src( array $webfont ) { + if ( ! is_array( $webfont['src'] ) ) { + $webfont['src'] = (array) $webfont['src']; + } + + $src = array(); + $src_ordered = array(); + + foreach ( $webfont['src'] as $url ) { + // Add data URIs first. + if ( 0 === strpos( trim( $url ), 'data:' ) ) { + $src_ordered[] = array( + 'url' => $url, + 'format' => 'data', + ); + continue; + } + $format = pathinfo( $url, PATHINFO_EXTENSION ); + $src[ $format ] = $url; + } + + // Add woff2. + if ( ! empty( $src['woff2'] ) ) { + $src_ordered[] = array( + 'url' => $src['woff2'], + 'format' => 'woff2', + ); + } + + // Add woff. + if ( ! empty( $src['woff'] ) ) { + $src_ordered[] = array( + 'url' => $src['woff'], + 'format' => 'woff', + ); + } + + // Add ttf. + if ( ! empty( $src['ttf'] ) ) { + $src_ordered[] = array( + 'url' => $src['ttf'], + 'format' => 'truetype', + ); + } + + // Add eot. + if ( ! empty( $src['eot'] ) ) { + $src_ordered[] = array( + 'url' => $src['eot'], + 'format' => 'embedded-opentype', + ); + } + + // Add otf. + if ( ! empty( $src['otf'] ) ) { + $src_ordered[] = array( + 'url' => $src['otf'], + 'format' => 'opentype', + ); + } + $webfont['src'] = $src_ordered; + + return $webfont; } /** - * Get the CSS for a collection of fonts. + * Get the CSS for a collection of webfonts. * * @since 5.9.0 * - * @return string + * @return string The CSS. */ public function get_css() { $css = ''; - foreach ( $this->params as $font ) { - // Validate font params. - $font = $this->get_validated_params( $font ); + foreach ( $this->webfonts as $webfont ) { + $css .= "@font-face{\n" . $this->build_font_css( $webfont ) . "}\n"; + } - if ( empty( $font['font-family'] ) ) { - continue; + return $css; + } + + /** + * Builds the font-family's CSS. + * + * @since 5.9.0 + * + * @param array $webfont Webfont to process. + * @return string This font-family's CSS. + */ + private function build_font_css( array $webfont ) { + $css = ''; + foreach ( $webfont as $key => $value ) { + + // Compile the "src" parameter. + if ( 'src' === $key ) { + $value = $this->compile_src( $webfont['font-family'], $value ); + } + + // @todo Is this a needed configuration parameter? If yes, need to add to Validator; else will be stripped out. + // If font-variation-settings is an array, convert it to a string. + if ( 'font-variation-settings' === $key && is_array( $value ) ) { + $value = $this->compile_variations( $value ); } - $css .= '@font-face{'; - foreach ( $font as $key => $value ) { - - // Compile the "src" parameter. - if ( 'src' === $key ) { - $src = "local({$font['font-family']})"; - foreach ( $value as $item ) { - - // If the URL starts with "file:./" then it originated in a theme.json file. - // Tweak the URL to be relative to the theme root. - if ( 0 === strpos( $item['url'], 'file:./' ) ) { - $item['url'] = wp_make_link_relative( get_theme_file_uri( str_replace( 'file:./', '', $item['url'] ) ) ); - } - - $src .= ( 'data' === $item['format'] ) - ? ", url({$item['url']})" - : ", url('{$item['url']}') format('{$item['format']}')"; - } - $value = $src; - } - - // If font-variation-settings is an array, convert it to a string. - if ( 'font-variation-settings' === $key && is_array( $value ) ) { - $variations = array(); - foreach ( $value as $key => $val ) { - $variations[] = "$key $val"; - } - $value = implode( ', ', $variations ); - } - - if ( ! empty( $value ) ) { - $css .= "$key:$value;"; - } + if ( ! empty( $value ) ) { + $css .= "\t$key:$value;\n"; } - $css .= '}'; } return $css; } + + /** + * Compiles the `src` into valid CSS. + * + * @since 5.9.0 + * + * @param string $font_family Font family. + * @param array $value Value to process. + * @return string The CSS. + */ + private function compile_src( $font_family, array $value ) { + $src = "local($font_family)"; + + foreach ( $value as $item ) { + // If the URL starts with "file:./" then it originated in a theme.json file. + // Tweak the URL to be relative to the theme root. + if ( 0 === strpos( $item['url'], 'file:./' ) ) { + $item['url'] = $this->replace_url_temp_placeholder( $item['url'] ); + } + + $src .= ( 'data' === $item['format'] ) + ? ", url({$item['url']})" + : ", url('{$item['url']}') format('{$item['format']}')"; + } + return $src; + } + + /** + * Replace URL's temporary placeholder with theme path. + * + * @since 5.9.0 + * + * @param string $url URL with temporary placeholder. + * @return string URL to font file. + */ + private function replace_url_temp_placeholder( $url ) { + $url = str_replace( 'file:./', '', $url ); + + return wp_make_link_relative( get_theme_file_uri( $url ) ); + } + + /** + * Compiles the font variation settings. + * + * @since 5.9.0 + * + * @param array $font_variation_settings Array of font variation settings. + * @return string The CSS. + */ + private function compile_variations( array $font_variation_settings ) { + $variations = ''; + + foreach ( $font_variation_settings as $key => $value ) { + $variations .= "$key $value"; + } + + return $variations; + } } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index bee09cbfcd233..f8fb2553331be 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -42,37 +42,13 @@ abstract class WP_Webfonts_Provider { protected $root_url = ''; /** - * Webfont parameters. + * Webfonts to be processed. * * @since 5.9.0 * - * @var array - */ - protected $params = array(); - - /** - * An array of valid CSS properties for @font-face. - * - * @since 5.9.0 - * - * @var array + * @var array[] */ - protected $valid_font_face_properties = array( - 'ascend-override', - 'descend-override', - 'font-display', - 'font-family', - 'font-stretch', - 'font-style', - 'font-weight', - 'font-variant', - 'font-feature-settings', - 'font-variation-settings', - 'line-gap-override', - 'size-adjust', - 'src', - 'unicode-range', - ); + protected $webfonts = array(); /** * An array of API parameters which will not be added to the @font-face. @@ -81,7 +57,7 @@ abstract class WP_Webfonts_Provider { * * @var array */ - protected $api_params = array(); + protected $invalid_parameters = array(); /** * Get the provider's unique ID. @@ -117,157 +93,59 @@ public function get_preconnect_urls() { } /** - * Validate the $params array. + * Sets the webfonts. * - * @todo Move to validator.Validation should happen during webfont collection, i.e.during the schema validation. + * The webfonts have been validated, are in kebab_case, and + * are arranged by provider and then by font-family. * * @since 5.9.0 * - * @param array $params The parameters to validate. - * - * @return array + * @param array[] $webfonts The webfont's parameters. */ - public function get_validated_params( $params ) { - - // Convert camelCase to kebab-case. - $kebab = array(); - foreach ( $params as $key => $value ) { - $kebab[ $this->camel_to_kebab( $key ) ] = $value; - } - - // Default values. - $defaults = array( - 'font-weight' => '400', - 'font-style' => 'normal', - 'font-display' => 'fallback', - 'src' => array(), - ); - - // Merge defaults with passed params. - $params = wp_parse_args( $kebab, $defaults ); - - // Whitelisted params. - $whitelist = array_merge( $this->valid_font_face_properties, $this->api_params ); - - // Only allow whitelisted properties. - foreach ( $params as $key => $value ) { - if ( ! in_array( $key, $whitelist, true ) ) { - unset( $params[ $key ] ); - } - } - - // Order $src items to optimize for browser support. - if ( ! empty( $params['src'] ) ) { - $params['src'] = (array) $params['src']; - $src = array(); - $src_ordered = array(); - - foreach ( $params['src'] as $url ) { - // Add data URIs first. - if ( 0 === strpos( trim( $url ), 'data:' ) ) { - $src_ordered[] = array( - 'url' => $url, - 'format' => 'data', - ); - continue; - } - $format = pathinfo( $url, PATHINFO_EXTENSION ); - $src[ $format ] = $url; - } - - // Add woff2. - if ( ! empty( $src['woff2'] ) ) { - $src_ordered[] = array( - 'url' => $src['woff2'], - 'format' => 'woff2', - ); - } - - // Add woff. - if ( ! empty( $src['woff'] ) ) { - $src_ordered[] = array( - 'url' => $src['woff'], - 'format' => 'woff', - ); - } - - // Add ttf. - if ( ! empty( $src['ttf'] ) ) { - $src_ordered[] = array( - 'url' => $src['ttf'], - 'format' => 'truetype', - ); - } - - // Add eot. - if ( ! empty( $src['eot'] ) ) { - $src_ordered[] = array( - 'url' => $src['eot'], - 'format' => 'embedded-opentype', - ); - } - - // Add otf. - if ( ! empty( $src['otf'] ) ) { - $src_ordered[] = array( - 'url' => $src['otf'], - 'format' => 'opentype', - ); - } - $params['src'] = $src_ordered; - } - - // Only allow valid font-display values. - if ( - ! empty( $params['font-display'] ) && - ! in_array( $params['font-display'], array( 'auto', 'block', 'swap', 'fallback' ), true ) - ) { - $params['font-display'] = 'fallback'; - } - - // Only allow valid font-style values. - if ( - ! empty( $params['font-style'] ) && - ! in_array( $params['font-style'], array( 'normal', 'italic', 'oblique' ), true ) && - ! preg_match( '/^oblique\s+(\d+)%/', $params['font-style'], $matches ) - ) { - $params['font-style'] = 'normal'; - } + public function set_webfonts( array $webfonts ) { + $this->webfonts = $webfonts; - // Only allow valid font-weight values. - if ( - ! empty( $params['font-weight'] ) && - ! in_array( $params['font-weight'], array( 'normal', 'bold', 'bolder', 'lighter', 'inherit' ), true ) && - ! preg_match( '/^(\d+)$/', $params['font-weight'], $matches ) && - ! preg_match( '/^(\d+)\s+(\d+)$/', $params['font-weight'], $matches ) - ) { - $params['font-weight'] = 'normal'; + foreach ( $this->webfonts as $registered_key => $webfont ) { + $this->webfonts[ $registered_key ] = $this->prepare( $webfont ); } - - return $params; } /** - * Set the object's params. + * Prepares the given webfont. * * @since 5.9.0 * - * @param string[][] $webfonts The webfont's parameters. + * @param array $webfont Webfont to validate. + * @return array */ - public function set_webfonts( array $webfonts ) { - //$params = $this->get_validated_params( $params ); - $this->params = $webfonts; + protected function prepare( array $webfont ) { + if ( empty( $this->invalid_parameters ) ) { + return $webfont; + } + + return $this->remove_invalid_properties( $webfont ); } /** - * Get the object's params. + * Removes invalid properties from the webfont. * * @since 5.9.0 * - * @return array + * @param array $webfont Webfont to process. + * @return array Webfont with valid properties. */ - public function get_params() { - return $this->params; + private function remove_invalid_properties( array $webfont ) { + $invalid_parameters = $this->invalid_parameters; + + $webfont = array_filter( + $webfont, + static function( $key ) use ( $invalid_parameters ) { + return ! in_array( $key, $invalid_parameters, true ); + }, + ARRAY_FILTER_USE_KEY + ); + + return $webfont; } /** @@ -275,24 +153,22 @@ public function get_params() { * * @since 5.9.0 * - * @return string + * @return string Webfonts CSS. */ abstract public function get_css(); /** * Get cached styles from a remote URL. * - * @access public * @since 5.9.0 * - * @param string $url The URL to fetch. * @param string $id An ID used to cache the styles. + * @param string $url The URL to fetch. * @param array $args The arguments to pass to wp_remote_get(). * @param array $additional_props Additional properties to add to the @font-face styles. - * * @return string The styles. */ - public function get_cached_remote_styles( $id, $url, $args = array(), $additional_props = array() ) { + public function get_cached_remote_styles( $id, $url, array $args = array(), array $additional_props = array() ) { $css = get_site_transient( $id ); // Get remote response and cache the CSS if it hasn't been cached already. @@ -329,22 +205,16 @@ public function get_cached_remote_styles( $id, $url, $args = array(), $additiona * * @param string $url The URL to fetch. * @param array $args The arguments to pass to wp_remote_get(). - * @return string The styles. + * @return string The styles on success. Empty string on failure. */ - public function get_remote_styles( $url, $args = array() ) { - $args = wp_parse_args( - $args, - array( - // Use a modern user-agent, to get woff2 files. - 'user-agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0', - ) - ); + public function get_remote_styles( $url, array $args = array() ) { + // Use a modern user-agent, to get woff2 files. + $args['user-agent'] = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0'; // Get the remote URL contents. $response = wp_remote_get( $url, $args ); // Early return if the request failed. - // Cache an empty string for 60 seconds to avoid bottlenecks. if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return ''; } @@ -352,16 +222,4 @@ public function get_remote_styles( $url, $args = array() ) { // Get the response body. return wp_remote_retrieve_body( $response ); } - - /** - * Convert camelCase to kebab-case. - * - * @since 5.9.0 - * - * @param string $string The string to convert. - * @return string - */ - public function camel_to_kebab( $string ) { - return strtolower( preg_replace( '/(?provider = new WP_Webfonts_Google_Provider(); + } + + /** + * @covers WP_Webfonts_Google_Provider::set_webfonts + */ + public function test_set_webfonts() { + $webfonts = array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + 'open-sans.italic.700' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'italic', + 'font-weight' => '700', + 'font-display' => 'fallback', + ), + 'roboto.normal.900' => array( + 'provider' => 'google', + 'font-family' => 'Roboto', + 'font-style' => 'normal', + 'font-weight' => '900', + 'font-display' => 'fallback', + ), + ); + + $this->provider->set_webfonts( $webfonts ); + + $this->assertSame( $webfonts, $this->get_webfonts_property() ); + } + + /** + * @covers WP_Webfonts_Google_Provider::build_collection_api_urls + * + * @dataProvider data_build_collection_api_urls + * + * @since 5.9.0 + * + * @param array $webfonts Webfonts input. + * @param array $expected Expected urls. + */ + public function test_build_collection_api_urls( array $webfonts, array $expected ) { + $method = new ReflectionMethod( $this->provider, 'build_collection_api_urls' ); + $method->setAccessible( true ); + $actual = $method->invoke( $this->provider, $webfonts ); + + $this->assertSame( $expected, $actual ); + } + + /** + * Data Provider. + * + * @return array + */ + public function data_build_collection_api_urls() { + return array( + 'single font-family + single variation' => array( + 'webfonts' => array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + ), + 'expected' => array( + 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@400&display=fallback', + ), + ), + 'single font-family + multiple variations' => array( + 'webfonts' => array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + 'open-sans.italic.700' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'italic', + 'font-weight' => '700', + 'font-display' => 'fallback', + ), + ), + 'expected' => array( + 'https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;1,700&display=fallback', + ), + ), + 'multiple font-families and variations' => array( + 'webfonts' => array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + 'open-sans.italic.700' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'italic', + 'font-weight' => '700', + 'font-display' => 'fallback', + ), + 'roboto.normal.900' => array( + 'provider' => 'google', + 'font-family' => 'Roboto', + 'font-style' => 'normal', + 'font-weight' => '900', + 'font-display' => 'fallback', + ), + ), + 'expected' => array( + 'https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;1,700&family=Roboto:wght@900&display=fallback', + ), + ), + ); + } + + private function get_webfonts_property() { + $property = new ReflectionProperty( $this->provider, 'webfonts' ); + $property->setAccessible( true ); + + return $property->getValue( $this->provider ); + } +} diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php new file mode 100644 index 0000000000000..830d0d0c2f8dc --- /dev/null +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php @@ -0,0 +1,244 @@ +provider = new WP_Webfonts_Local_Provider(); + + $this->set_up_theme(); + } + + /** + * Local `src` paths to need to be relative to the theme. This method sets up the + * `wp-content/themes/` directory to ensure consistency when running tests. + */ + private function set_up_theme() { + $this->theme_root = realpath( DIR_TESTDATA . '/themedir1' ); + $this->orig_theme_dir = $GLOBALS['wp_theme_directories']; + $GLOBALS['wp_theme_directories'] = array( $this->theme_root ); + + $theme_root_callback = function () { + return $this->theme_root; + }; + add_filter( 'theme_root', $theme_root_callback ); + add_filter( 'stylesheet_root', $theme_root_callback ); + add_filter( 'template_root', $theme_root_callback ); + + // Clear caches. + wp_clean_themes_cache(); + unset( $GLOBALS['wp_themes'] ); + } + + function tear_down() { + // Restore the original theme directory setup. + $GLOBALS['wp_theme_directories'] = $this->orig_theme_dir; + wp_clean_themes_cache(); + unset( $GLOBALS['wp_themes'] ); + + parent::tear_down(); + } + + /** + * @covers WP_Webfonts_Local_Provider::set_webfonts + */ + public function test_set_webfonts() { + $webfonts = array( + 'source-serif-pro.normal.200 900' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'source-serif-pro.italic.200 900' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'italic', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', + ), + ); + + $expected = array( + 'source-serif-pro.normal.200 900' => array( + 'provider' => 'local', + 'font-family' => '"Source Serif Pro"', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => array( + array( + 'url' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'format' => 'woff2', + ), + ), + ), + 'source-serif-pro.italic.200 900' => array( + 'provider' => 'local', + 'font-family' => '"Source Serif Pro"', + 'font-style' => 'italic', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => array( + array( + 'url' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', + 'format' => 'woff2', + ), + ), + ), + ); + + $this->provider->set_webfonts( $webfonts ); + + $property = $this->get_webfonts_property(); + $this->assertSame( $expected, $property->getValue( $this->provider ) ); + } + + /** + * @covers WP_Webfonts_Local_Provider::get_css + * + * @dataProvider data_get_css + * + * @param array $webfonts Prepared webfonts (to store in WP_Webfonts_Local_Provider::$webfonts property) + * @param string $expected Expected CSS. + */ + public function test_get_css( array $webfonts, $expected ) { + $property = $this->get_webfonts_property(); + $property->setValue( $this->provider, $webfonts ); + + $this->assertSame( $expected, $this->provider->get_css() ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_css() { + return array( + 'URL to root assets dir' => array( + 'webfonts' => array( + 'open-sans.italic.400 900' => array( + 'provider' => 'local', + 'font-family' => '"Open Sans"', + 'font-style' => 'italic', + 'font-weight' => '400 900', + 'font-stretch' => 'normal', + 'src' => array( + array( + 'url' => 'http://example.org/assets/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf', + 'format' => 'ttf', + ), + ), + ), + 'open-sans.normal.400 900' => array( + 'provider' => 'local', + 'font-family' => '"Open Sans"', + 'font-style' => 'normal', + 'font-weight' => '400 900', + 'font-stretch' => 'normal', + 'src' => array( + array( + 'url' => 'http://example.org/assets/fonts/OpenSans-VariableFont_wdth,wght.ttf', + 'format' => 'ttf', + ), + ), + ), + ), + 'expected' => << array( + 'webfonts' => array( + 'source-serif-pro.normal.200 900' => array( + 'provider' => 'local', + 'font-family' => '"Source Serif Pro"', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => array( + array( + 'url' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'format' => 'woff2', + ), + ), + ), + 'source-serif-pro.italic.200 900' => array( + 'provider' => 'local', + 'font-family' => '"Source Serif Pro"', + 'font-style' => 'italic', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => array( + array( + 'url' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', + 'format' => 'woff2', + ), + ), + ), + ), + 'expected' => <<provider, 'webfonts' ); + $property->setAccessible( true ); + + return $property; + } +} diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index 831e9e9a7721d..97ef0cd8335c1 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -5,240 +5,340 @@ * @covers WP_Webfonts_Controller */ class Tests_Webfonts_API_wpWebfontsController extends WP_UnitTestCase { - private static $webfonts; + private $controller; + private $webfont_registry_mock; + private $provider_registry_mock; public static function set_up_before_class() { - require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-schema-validator.php'; require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-registry.php'; require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-provider-registry.php'; require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-controller.php'; require_once __DIR__ . '/mocks/class-my-custom-webfonts-provider-mock.php'; - - self::$webfonts = self::get_webfonts(); } - private function get_controller() { - $controller = new WP_Webfonts_Controller( - new WP_Webfonts_Registry( - new WP_Webfonts_Schema_Validator() - ), - new WP_Webfonts_Provider_Registry() - ); - $controller->init(); + public function set_up() { + parent::set_up(); - return $controller; + $this->webfont_registry_mock = $this->getMockBuilder( 'WP_Webfonts_Registry' ) + ->disableOriginalConstructor() + ->getMock(); + $this->provider_registry_mock = $this->getMockBuilder( 'WP_Webfonts_Provider_Registry' ) + ->getMock(); + $this->controller = new WP_Webfonts_Controller( + $this->webfont_registry_mock, + $this->provider_registry_mock + ); } /** - * @covers WP_Webfonts_Controller::get_webfonts + * @covers WP_Webfonts_Controller::init + * + * @dataProvider data_init + * + * @param string $hook Expected hook name. + * @param bool $did_action Whether the action fired or not. */ - public function test_get_webfonts_when_empty() { - $controller = $this->get_controller(); + public function test_init( $hook, $did_action ) { + $this->provider_registry_mock + ->expects( $this->once() ) + ->method( 'init' ); - $this->assertSame( array(), $controller->get_webfonts() ); + if ( $did_action ) { + do_action( 'wp_enqueue_scripts' ); + } + + $this->controller->init(); + + $this->assertSame( + 10, + has_action( $hook, array( $this->controller, 'generate_and_enqueue_styles' ) ) + ); + $this->assertSame( + 10, + has_action( 'admin_init', array( $this->controller, 'generate_and_enqueue_editor_styles' ) ) + ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_init() { + return array( + 'did_action fired' => array( + 'hook' => 'wp_print_footer_scripts', + 'did_action' => true, + ), + 'did_action did not fire' => array( + 'hook' => 'wp_enqueue_scripts', + 'did_action' => false, + ), + ); } /** * @covers WP_Webfonts_Controller::register_webfonts */ public function test_register_webfonts_with_empty_schema() { - $controller = $this->get_controller(); - $webfonts = array(); + $webfonts = array(); - $controller->register_webfonts( $webfonts ); + $this->webfont_registry_mock + ->expects( $this->never() ) + ->method( 'register' ); - $this->assertSame( array(), $controller->get_webfonts() ); + $this->controller->register_webfonts( $webfonts ); } /** * @covers WP_Webfonts_Controller::register_webfonts * @covers WP_Webfonts_Controller::register_webfont - * - * @dataProvider data_register_webfonts_with_invalid_schema - * - * @param array $webfonts Webfonts input. - * @param array $expected Expected registered webfonts. */ - public function test_register_webfonts_with_invalid_schema( array $webfonts, $expected_message, array $expected ) { - $this->expectNotice(); - $this->expectNoticeMessage( $expected_message ); + public function test_register_webfonts() { + $webfonts = array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + 'open-sans.italic.700' => array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'italic', + 'fontWeight' => '700', + ), + 'roboto.normal.900' => array( + 'provider' => 'google', + 'fontFamily' => 'Roboto', + 'fontStyle' => 'normal', + 'fontWeight' => '900', + ), + ); - $controller = $this->get_controller(); - $controller->register_webfonts( $webfonts ); + $this->mock_register_webfonts( $webfonts ); - $this->assertSame( $expected, $controller->get_webfonts() ); + $this->controller->register_webfonts( $webfonts ); } /** - * Data Provider. - * - * See Tests_Webfonts_API_wpWebfontsRegistry::data_register_with_invalid_schema() - * for more complete test coverage. - * - * return @array + * @covers WP_Webfonts_Controller::get_webfonts */ - public function data_register_webfonts_with_invalid_schema() { - return array( - 'provider: invalid type' => array( - 'webfonts' => array( - array( - 'provider' => null, - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - ), - array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'italic', - 'fontWeight' => '700', - ), - ), - 'expected_message' => 'Webfont provider must be a non-empty string.', - 'expected' => array( - 'open-sans.italic.700' => array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'italic', - 'fontWeight' => '700', - ), - ), + public function test_get_webfonts() { + $expected = array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + ), + 'open-sans.italic.700' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'italic', + 'fontWeight' => '700', ), - 'font family: invalid key' => array( - 'webfonts' => array( - array( - 'provider' => 'google', - 'fontFamily' => 'Roboto', - 'fontStyle' => 'normal', - 'fontWeight' => '900', - ), - array( - 'provider' => 'google', - 'font_family' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - ), - ), - 'expected_message' => 'Webfont font family must be a non-empty string.', - 'expected' => array( - 'roboto.normal.900' => array( - 'provider' => 'google', - 'fontFamily' => 'Roboto', - 'fontStyle' => 'normal', - 'fontWeight' => '900', - ), - ), + 'roboto.normal.900' => array( + 'provider' => 'google', + 'font-family' => 'Roboto', + 'font-style' => 'normal', + 'font-weight' => '900', ), ); + + $this->webfont_registry_mock + ->expects( $this->once() ) + ->method( 'get_registry' ) + ->willReturn( $expected ); + + $this->assertSame( $expected, $this->controller->get_webfonts() ); } /** - * @covers WP_Webfonts_Controller::register_webfonts - * @covers WP_Webfonts_Controller::register_webfont + * @covers WP_Webfonts_Controller::get_webfonts_by_provider */ - public function test_register_webfonts_with_valid_schema() { - $controller = $this->get_controller(); + public function test_get_webfonts_by_provider() { + $provider_id = 'google'; + $expected = array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + 'open-sans.italic.700' => array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'italic', + 'fontWeight' => '700', + ), + 'roboto.normal.900' => array( + 'provider' => 'google', + 'fontFamily' => 'Roboto', + 'fontStyle' => 'normal', + 'fontWeight' => '900', + ), + ); - $controller->register_webfonts( self::$webfonts ); + $this->webfont_registry_mock + ->expects( $this->once() ) + ->method( 'get_by_provider' ) + ->with( $this->equalTo( $provider_id ) ) + ->willReturn( $expected ); - $expected = array( - 'open-sans.normal.400' => self::$webfonts[0], - 'open-sans.italic.700' => self::$webfonts[1], - 'roboto.normal.900' => self::$webfonts[2], - ); - $this->assertSame( $expected, $controller->get_webfonts() ); + $this->assertSame( $expected, $this->controller->get_webfonts_by_provider( $provider_id ) ); } /** - * @covers WP_Webfonts_Controller::get_registered_providers + * @covers WP_Webfonts_Controller::get_webfonts_by_font_family */ - public function test_get_registered_providers_core_only() { - $controller = $this->get_controller(); + public function test_get_webfonts_by_font_family() { + $font_family = 'roboto'; + $expected = array( + 'roboto.normal.900' => array( + 'provider' => 'google', + 'font-family' => 'Roboto', + 'font-style' => 'normal', + 'font-weight' => '900', + ), + ); - $providers = $controller->get_registered_providers(); + $this->webfont_registry_mock + ->expects( $this->once() ) + ->method( 'get_by_font_family' ) + ->with( $this->equalTo( $font_family ) ) + ->willReturn( $expected ); - $expected = array( 'google', 'local' ); - $this->assertSame( $expected, array_keys( $providers ) ); - $this->assertInstanceOf( 'WP_Webfonts_Google_Provider', $providers['google'] ); - $this->assertInstanceOf( 'WP_Webfonts_Local_Provider', $providers['local'] ); + $this->assertSame( $expected, $this->controller->get_webfonts_by_font_family( $font_family ) ); } /** - * @covers WP_Webfonts_Controller::register_provider * @covers WP_Webfonts_Controller::get_registered_providers */ - public function test_register_provider() { - $controller = $this->get_controller(); + public function test_get_registered_providers() { + $expected = array( + 'my-custom-provider' => new My_Custom_Webfonts_Provider_Mock(), + ); - $controller->register_provider( My_Custom_Webfonts_Provider_Mock::class ); + $this->provider_registry_mock + ->expects( $this->once() ) + ->method( 'get_registry' ) + ->willReturn( $expected ); - $providers = $controller->get_registered_providers(); + $actual = $this->controller->get_registered_providers(); - $expected = array( 'google', 'local', 'my-custom-provider' ); - $this->assertSame( $expected, array_keys( $providers ) ); + $this->assertIsArray( $actual ); + $this->assertCount( 1, $actual ); + $this->assertArrayHasKey( 'my-custom-provider', $actual ); + $this->assertInstanceOf( 'My_Custom_Webfonts_Provider_Mock', $actual['my-custom-provider'] ); } /** - * @covers WP_Webfonts_Controller::get_webfonts_by_font_family + * @covers WP_Webfonts_Controller::register_provider */ - public function test_get_webfonts_by_font_family() { - $controller = $this->get_controller(); - $controller->register_webfonts( self::$webfonts ); + public function test_register_provider() { + $classname = My_Custom_Webfonts_Provider_Mock::class; - $expected = array( - 'roboto.normal.900' => self::$webfonts[2], - ); + $this->provider_registry_mock + ->expects( $this->once() ) + ->method( 'register' ) + ->with( $this->equalTo( $classname ) ) + ->willReturn( true ); - $this->assertSame( $expected, $controller->get_webfonts_by_font_family( 'roboto' ) ); + $this->assertTrue( $this->controller->register_provider( $classname ) ); } /** - * @covers WP_Webfonts_Controller::get_webfonts_by_font_family + * @covers WP_Webfonts_Controller::generate_and_enqueue_styles + * @covers WP_Webfonts_Controller::generate_and_enqueue_editor_styles * - * @dataProvider data_get_by_font_family_with_invalid_input + * @dataProvider data_generate_and_enqueue_editor_styles * - * @param mixed Font family input. + * @param string $stylestyle_handle Handle for the registered stylesheet. */ - public function test_get_webfonts_by_font_family_with_invalid_input( $font_family ) { - $controller = $this->get_controller(); - $controller->register_webfonts( self::$webfonts ); + public function test_generate_and_enqueue_editor_styles( $stylestyle_handle ) { + /* + * Set the stylesheet_handle property. + * This is set in WP_Webfonts_Controller::init(); however, init is not part + * of this test (as it has its own test). + */ + $property = new ReflectionProperty( $this->controller, 'stylesheet_handle' ); + $property->setAccessible( true ); + $property->setValue( $this->controller, $stylestyle_handle ); + + // Set up the provider mock. + $provider = new My_Custom_Webfonts_Provider_Mock(); + $providers = array( + 'my-custom-provider' => $provider, + ); + $this->provider_registry_mock + ->expects( $this->once() ) + ->method( 'get_registry' ) + ->willReturn( $providers ); - $this->assertSame( array(), $controller->get_webfonts_by_font_family( $font_family ) ); + // Set up the webfonts registry mock. + $webfonts = array( + 'source-serif-pro.normal.200 900' => array( + 'provider' => 'my-custom-provider', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + ), + 'source-serif-pro.italic.200 900' => array( + 'provider' => 'my-custom-provider', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'italic', + 'font-weight' => '200 900', + ), + ); + $this->webfont_registry_mock + ->expects( $this->once() ) + ->method( 'get_by_provider' ) + ->with( $this->equalTo( 'my-custom-provider' ) ) + ->willReturn( $webfonts ); + + // Fire the method being tested. + $this->controller->generate_and_enqueue_styles(); + + /* + * As this method adds an inline style, the test needs to print it. + * Print the webfont styles and test the output matches expectation. + */ + $expected = "\n"; + $this->expectOutputString( $expected ); + wp_print_styles( $stylestyle_handle ); } /** - * Data Provider. + * Data provider. * - * return @array + * @return array */ - public function data_get_by_font_family_with_invalid_input() { + public function data_generate_and_enqueue_editor_styles() { return array( - 'not a string' => array( true ), - 'empty string' => array( '' ), - 'font family not registered' => array( 'Does not exist' ), + 'for wp_enqueue_scripts' => array( 'webfonts' ), + 'for wp_print_footer_scripts' => array( 'webfonts-footer' ), ); } - private static function get_webfonts() { - return array( - array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - ), - array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'italic', - 'fontWeight' => '700', - ), - array( - 'provider' => 'google', - 'fontFamily' => 'Roboto', - 'fontStyle' => 'normal', - 'fontWeight' => '900', - ), - ); + /** + * Mocks WP_Webfonts_Provider::register(). + * + * @since 5.9.0 + * + * @param array $webfonts Webfonts to register. + */ + private function mock_register_webfonts( array $webfonts ) { + $register_webfonts = array(); + foreach ( $webfonts as $webfont ) { + $register_webfonts[] = array( $this->equalTo( $webfont ) ); + } + + $this->webfont_registry_mock + ->expects( $this->exactly( count( $webfonts ) ) ) + ->method( 'register' ) + ->withConsecutive( ...$register_webfonts ); } } diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index b0824fcec5656..3fab2f46e2ab5 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -5,19 +5,53 @@ * @covers WP_Webfonts_Registry */ class Tests_Webfonts_API_wpWebfontsRegistry extends WP_UnitTestCase { - private static $webfonts; + private $registry; + private $validator_mock; - public static function wpSetUpBeforeClass() { + public static function set_up_before_class() { require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-schema-validator.php'; require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-registry.php'; + } + + public function set_up() { + parent::set_up(); + + $this->validator_mock = $this->getMockBuilder( 'WP_Webfonts_Schema_Validator' ) + ->getMock(); - self::$webfonts = self::get_webfonts(); + $this->registry = new WP_Webfonts_Registry( $this->validator_mock ); } - private function get_registry() { - return new WP_Webfonts_Registry( - new WP_Webfonts_Schema_Validator() + /** + * @covers WP_Webfonts_Registry::get_registry + */ + public function test_get_registry() { + $expected = array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + 'roboto.normal.900' => array( + 'provider' => 'google', + 'font-family' => 'Robot', + 'font-style' => 'normal', + 'font-weight' => '900', + 'font-display' => 'fallback', + ), ); + + /* + * Set the registry property. + * This is set in WP_Webfonts_Registry::register(), which not part of this test. + */ + $property = new ReflectionProperty( $this->registry, 'registry' ); + $property->setAccessible( true ); + $property->setValue( $this->registry, $expected ); + + $this->assertSame( $expected, $this->registry->get_registry() ); } /** @@ -27,135 +61,81 @@ private function get_registry() { * * @param array Webfonts input. */ - public function test_register_with_invalid_schema( array $webfont, $expected_message ) { - $this->expectNotice(); - $this->expectNoticeMessage( $expected_message ); - - $registry = $this->get_registry(); + public function test_register_with_invalid_schema( array $webfont ) { + $this->validator_mock + ->expects( $this->once() ) + ->method( 'is_valid_schema' ) + ->willReturn( false ); + $this->validator_mock + ->expects( $this->never() ) + ->method( 'set_valid_properties' ); - $this->assertSame( '', $registry->register( $webfont ) ); + $this->assertSame( '', $this->registry->register( $webfont ) ); } /** * Data Provider. * - * return @array + * @return array */ public function data_register_with_invalid_schema() { return array( 'empty array - no schema' => array( - 'webfont' => array(), - 'expected_message' => 'Webfont provider must be a non-empty string.', + array(), ), - 'provider: not set' => array( - 'webfont' => array( - 'fontFamily' => 'Open Sans', + 'provider: not defined' => array( + array( + 'fontFamily' => 'Some Font', 'fontStyle' => 'normal', 'fontWeight' => '400', ), - 'expected_message' => 'Webfont provider must be a non-empty string.', ), 'provider: empty string' => array( - 'webfont' => array( - 'provider' => '', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '400', + array( + 'provider' => '', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', ), - 'expected_message' => 'Webfont provider must be a non-empty string.', ), - 'provider: invalid type' => array( - 'webfont' => array( - 'provider' => true, - 'fontFamily' => 'Open Sans', + 'provider: not a string' => array( + array( + 'provider' => null, + 'fontFamily' => 'Some Font', 'fontStyle' => 'normal', 'fontWeight' => '400', ), - 'expected_message' => 'Webfont provider must be a non-empty string.', ), - 'font family: not set' => array( - 'webfont' => array( + 'font family: not defined' => array( + array( 'provider' => 'local', 'fontStyle' => 'normal', 'fontWeight' => '400', ), - 'expected_message' => 'Webfont font family must be a non-empty string.', ), - 'font family: empty string' => array( - 'webfont' => array( - 'provider' => 'local', - 'fontFamily' => '', - 'fontStyle' => 'normal', - 'fontWeight' => '400', + 'font-family: not defined' => array( + array( + 'provider' => 'some-provider', + 'font-style' => 'normal', + 'font-weight' => '400', ), - 'expected_message' => 'Webfont font family must be a non-empty string.', ), - 'font family: invalid type' => array( - 'webfont' => array( - 'provider' => 'local', - 'fontFamily' => true, - 'fontStyle' => 'normal', - 'fontWeight' => '400', + 'font-family: empty string' => array( + array( + 'provider' => 'some-provider', + 'font-family' => '', + 'font-style' => 'normal', + 'font-weight' => '400', ), - 'expected_message' => 'Webfont font family must be a non-empty string.', ), - 'font style: empty string' => array( - 'webfont' => array( - 'provider' => 'local', - 'fontFamily' => 'Open Sans', - 'fontStyle' => '', - 'fontWeight' => '400', + 'font-family: not a string' => array( + array( + 'provider' => 'some-provider', + 'font-family' => null, + 'font-style' => 'normal', + 'font-weight' => '400', ), - 'expected_message' => 'Webfont font style must be a non-empty string.', ), - 'font style: invalid type' => array( - 'webfont' => array( - 'provider' => 'local', - 'fontFamily' => 'Open Sans', - 'fontStyle' => true, - 'fontWeight' => '400', - ), - 'expected_message' => 'Webfont font style must be a non-empty string.', - ), - 'font style: invalid value' => array( - 'webfont' => array( - 'provider' => 'local', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'invalid', - 'fontWeight' => '400', - ), - 'expected_message' => 'Webfont font style must be normal, italic, oblique, or oblique . Given: invalid.', - ), - 'font weight: empty string' => array( - 'webfont' => array( - 'provider' => 'local', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '', - ), - 'expected_message' => 'Webfont font weight must be a non-empty string.', - ), - 'font weight: invalid type' => array( - 'webfont' => array( - 'provider' => 'local', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => true, - ), - 'expected_message' => 'Webfont font weight must be a non-empty string.', - ), - // @todo uncomment once value validation is added. - /* - 'font weight: invalid value' => array( - 'webfont' => array( - 'provider' => 'local', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => 'invalid', - ), - 'expected_message' => 'Webfont font weight must be a non-empty string.', - ), - */ ); } @@ -164,13 +144,21 @@ public function data_register_with_invalid_schema() { * * @dataProvider data_register_with_valid_schema * - * @param array Webfonts input. - * @param string Expected registration key. + * @param array $webfont Webfont input. + * @param array $validated_webfont Webfont after being processed by the validator. + * @param string $expected Expected return value. */ - public function test_register_with_valid_schema( array $webfont, $expected ) { - $registry = $this->get_registry(); + public function test_register_with_valid_schema( array $webfont, array $validated_webfont, $expected ) { + $this->validator_mock + ->expects( $this->once() ) + ->method( 'is_valid_schema' ) + ->willReturn( true ); + $this->validator_mock + ->expects( $this->once() ) + ->method( 'set_valid_properties' ) + ->willReturn( $validated_webfont ); - $this->assertSame( $expected, $registry->register( $webfont ) ); + $this->assertSame( $expected, $this->registry->register( $webfont ) ); } /** @@ -180,63 +168,129 @@ public function test_register_with_valid_schema( array $webfont, $expected ) { */ public function data_register_with_valid_schema() { return array( - 'Open Sans; normal; 400' => array( - 'webfonts' => array( + 'camelCase schema' => array( + 'webfont' => array( 'provider' => 'google', 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => '400', ), - 'expected' => 'open-sans.normal.400', + 'validated_webfont' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + 'expected' => 'open-sans.normal.400', ), - 'Open Sans; italic; 900' => array( - 'webfonts' => array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'italic', - 'fontWeight' => '900', + 'kebab-case schema' => array( + 'webfont' => array( + 'provider' => 'google', + 'font-family' => 'Roboto', + 'font-style' => 'normal', + 'font-weight' => 'normal', + ), + 'validated_webfont' => array( + 'provider' => 'google', + 'font-family' => 'Roboto', + 'font-style' => 'normal', + 'font-weight' => 'normal', + 'font-display' => 'fallback', ), - 'expected' => 'open-sans.italic.900', + 'expected' => 'roboto.normal.normal', + ), + 'camelCase with src' => array( + 'webfont' => array( + 'provider' => 'local', + 'fontFamily' => 'Source Serif Pro', + 'fontStyle' => 'normal', + 'fontWeight' => '200 900', + 'fontStretch' => 'normal', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'validated_webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-display' => 'fallback', + 'font-stretch' => 'normal', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'expected' => 'source-serif-pro.normal.200 900', + ), + 'kebab-case with src' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'validated_webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-display' => 'fallback', + 'font-stretch' => 'normal', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'expected' => 'source-serif-pro.normal.200 900', ), ); } /** - * @covers WP_Webfonts_Registry::get_registry + * @covers WP_Webfonts_Registry::get_by_provider */ - public function test_get_registry() { - $registry = $this->get_registry(); - $this->register_webfonts( $registry ); + public function test_get_by_provider_when_does_not_exist() { + /* + * Set the `registry_by_provider` property. + * This is set in WP_Webfonts_Registry::register(), which not part of this test. + */ + $property = new ReflectionProperty( $this->registry, 'registry_by_provider' ); + $property->setAccessible( true ); + $property->setValue( $this->registry, array( 'google', 'local' ) ); - $this->assertSame( self::$webfonts, $registry->get_registry() ); + $this->assertSame( array(), $this->registry->get_by_provider( 'my-custom-provider' ) ); } /** - * @covers WP_Webfonts_Registry::get_by_font_family + * Data Provider. + * + * return @array */ - public function test_get_by_font_family() { - $registry = $this->get_registry(); - $this->register_webfonts( $registry ); - - $expected = array( - 'roboto.normal.400' => self::$webfonts['roboto.normal.400'], - 'roboto.normal.900' => self::$webfonts['roboto.normal.900'], + public function data_get_by_font_family_when_invalid_input() { + return array( + 'not a string' => array( null ), + 'empty string' => array( '' ), ); - $this->assertSame( $expected, $registry->get_by_font_family( 'Roboto' ) ); } /** - * @covers WP_Webfonts_Registry::get_by_font_family + * As there are many moving parts to getting by provider, this test is an integration + * test that does not mock. * - * @dataProvider data_get_by_font_family_with_invalid_input + * @covers WP_Webfonts_Registry::get_by_provider + * @covers WP_Webfonts_Registry::register * - * @param mixed Font family input. + * @dataProvider data_get_by_provider_integrated + * + * @param array $webfonts Given webfont to register. + * @param string $provider_id Provider ID to query. + * @param array $expected Expected return value. */ - public function test_get_by_font_family_with_invalid_input( $font_family ) { - $registry = $this->get_registry(); - $this->register_webfonts( $registry ); + public function test_get_by_provider_integrated( array $webfonts, $provider_id, $expected ) { + $registry = new WP_Webfonts_Registry( new WP_Webfonts_Schema_Validator() ); + + foreach ( $webfonts as $webfont ) { + $registry->register( $webfont ); + } - $this->assertSame( array(), $registry->get_by_font_family( $font_family ) ); + $this->assertSame( $expected, $registry->get_by_provider( $provider_id ) ); } /** @@ -244,61 +298,205 @@ public function test_get_by_font_family_with_invalid_input( $font_family ) { * * return @array */ - public function data_get_by_font_family_with_invalid_input() { + public function data_get_by_provider_integrated() { return array( - 'not a string' => array( true ), - 'empty string' => array( '' ), - 'font family not registered' => array( 'Does not exist' ), + 'no webfonts for requested provider' => array( + 'webfonts' => array( + array( + 'provider' => 'google', + 'fontFamily' => 'Lato', + 'fontStyle' => 'italic', + 'fontWeight' => '400', + ), + ), + 'provider_id' => 'local', + 'expected' => array(), + ), + 'with one provider' => array( + 'webfonts' => array( + array( + 'provider' => 'google', + 'fontFamily' => 'Lato', + 'fontStyle' => 'italic', + 'fontWeight' => '400', + ), + array( + 'provider' => 'google', + 'fontFamily' => 'Roboto', + 'fontStyle' => 'normal', + 'fontWeight' => '900', + ), + ), + 'provider_id' => 'google', + 'expected' => array( + 'lato.italic.400' => array( + 'provider' => 'google', + 'font-family' => 'Lato', + 'font-style' => 'italic', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + 'roboto.normal.900' => array( + 'provider' => 'google', + 'font-family' => 'Roboto', + 'font-style' => 'normal', + 'font-weight' => '900', + 'font-display' => 'fallback', + ), + ), + ), + 'with multiple providers' => array( + 'webfonts' => array( + array( + 'provider' => 'google', + 'fontFamily' => 'Open Sans', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + ), + array( + 'provider' => 'local', + 'fontFamily' => 'Source Serif Pro', + 'fontStyle' => 'normal', + 'fontWeight' => '200 900', + 'fontStretch' => 'normal', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + array( + 'provider' => 'google', + 'fontFamily' => 'Roboto', + 'fontStyle' => 'normal', + 'fontWeight' => '900', + ), + ), + 'provider_id' => 'local', + 'expected' => array( + 'source-serif-pro.normal.200 900' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-display' => 'fallback', + 'font-stretch' => 'normal', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + ), + ), ); } /** - * Register the webfonts helper function. + * @covers WP_Webfonts_Registry::get_by_font_family + * + * @dataProvider data_get_by_font_family_when_invalid_input + * + * @param string $font_family Given font-family for the query. + */ + public function test_get_by_font_family_when_invalid_input( $font_family ) { + $this->assertSame( array(), $this->registry->get_by_font_family( $font_family ) ); + } + + /** + * As there are many moving parts to getting by font-family, this test is an integration + * test that does not mock. + * + * @covers WP_Webfonts_Registry::get_by_font_family + * @covers WP_Webfonts_Registry::register * - * @param WP_Webfonts_Registry $registry Instance of the registry. + * @dataProvider data_get_by_font_family_integrated + * + * @param array $webfonts Given webfont to register. + * @param string $font_family Font family to query. + * @param array $expected Expected return value. */ - private function register_webfonts( $registry ) { - foreach ( self::$webfonts as $webfont ) { + public function test_get_by_font_family_integrated( array $webfonts, $font_family, $expected ) { + $registry = new WP_Webfonts_Registry( new WP_Webfonts_Schema_Validator() ); + + foreach ( $webfonts as $webfont ) { $registry->register( $webfont ); } + + $this->assertSame( $expected, $registry->get_by_font_family( $font_family ) ); } /** - * Gets the webfonts collection. + * Data Provider. * - * @return string[][] + * return @array */ - private static function get_webfonts() { - return array( - 'open-sans.normal.400' => array( + public function data_get_by_font_family_integrated() { + $webfonts = array( + array( 'provider' => 'google', 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => '400', ), - 'open-sans.normal.900' => array( + array( 'provider' => 'google', 'fontFamily' => 'Open Sans', 'fontStyle' => 'normal', 'fontWeight' => '900', ), - 'open-sans.italic.400' => array( + array( + 'provider' => 'google', + 'fontFamily' => 'Roboto', + 'fontStyle' => 'normal', + 'fontWeight' => '900', + ), + array( 'provider' => 'google', 'fontFamily' => 'Open Sans', 'fontStyle' => 'italic', 'fontWeight' => '400', ), - 'roboto.normal.400' => array( - 'provider' => 'google', - 'fontFamily' => 'Roboto', - 'fontStyle' => 'normal', - 'fontWeight' => '400', + ); + + $expected = array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', ), - 'roboto.normal.900' => array( - 'provider' => 'google', - 'fontFamily' => 'Roboto', - 'fontStyle' => 'normal', - 'fontWeight' => '900', + 'open-sans.normal.900' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '900', + 'font-display' => 'fallback', + ), + 'open-sans.italic.400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'italic', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + ); + + return array( + 'no webfonts for requested font-family' => array( + 'webfonts' => array( + array( + 'provider' => 'google', + 'fontFamily' => 'Lato', + 'fontStyle' => 'italic', + 'fontWeight' => '400', + ), + ), + 'font-family' => 'Open Sans', + 'expected' => array(), + ), + 'given proper font family' => array( + 'webfonts' => $webfonts, + 'font-family' => 'Open Sans', + 'expected' => $expected, + ), + 'given font family slug' => array( + 'webfonts' => $webfonts, + 'font-family' => 'open-sans', + 'expected' => $expected, ), ); } diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php new file mode 100644 index 0000000000000..e4f1ca1171d9e --- /dev/null +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -0,0 +1,393 @@ +assertTrue( self::$validator->is_valid_schema( $webfont ) ); + } + + /** + * Data Provider. + * + * return @array + */ + public function data_is_valid_schema_with_valid() { + return array( + 'basic schema' => array( + array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + ), + ), + 'only provider and font-family' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + ), + ), + ); + } + + /** + * @covers WP_Webfonts_Registry::is_valid_schema + * + * @dataProvider data_is_valid_schema_with_invalid + * + * @param array $webfont Webfont input. + * @param string $expected_message Expected notice message. + */ + public function test_is_valid_schema_with_invalid( array $webfont, $expected_message ) { + $this->expectNotice(); + $this->expectNoticeMessage( $expected_message ); + + $this->assertFalse( self::$validator->is_valid_schema( $webfont ) ); + } + + /** + * Data Provider. + * + * return @array + */ + public function data_is_valid_schema_with_invalid() { + return array( + 'empty array - no schema' => array( + 'webfont' => array(), + 'expected_message' => 'Webfont provider must be a non-empty string.', + ), + 'provider: not defined' => array( + 'webfont' => array( + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + ), + 'expected_message' => 'Webfont provider must be a non-empty string.', + ), + 'provider: empty string' => array( + 'webfont' => array( + 'provider' => '', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + ), + 'expected_message' => 'Webfont provider must be a non-empty string.', + ), + 'provider: not a string' => array( + 'webfont' => array( + 'provider' => null, + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + ), + 'expected_message' => 'Webfont provider must be a non-empty string.', + ), + 'font-family: not defined' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-style' => 'normal', + 'font-weight' => '400', + ), + 'expected_message' => 'Webfont font family must be a non-empty string.', + ), + 'font-family: empty string' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => '', + 'font-style' => 'normal', + 'font-weight' => '400', + ), + 'expected_message' => 'Webfont font family must be a non-empty string.', + ), + 'font-family: not a string' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => null, + 'font-style' => 'normal', + 'font-weight' => '400', + ), + 'expected_message' => 'Webfont font family must be a non-empty string.', + ), + ); + } + + /** + * @covers WP_Webfonts_Registry::set_valid_properties + * + * @dataProvider data_set_valid_properties_with_valid_input + * + * @param array $webfont Webfont input. + * @param array $expected Expected updated webfont. + */ + public function test_set_valid_properties_with_valid_input( array $webfont, array $expected ) { + $this->assertSame( $expected, self::$validator->set_valid_properties( $webfont ) ); + } + + /** + * Data Provider. + * + * return @array + */ + public function data_set_valid_properties_with_valid_input() { + return array( + 'basic schema' => array( + 'webfont' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + ), + 'expected' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + ), + 'basic schema in opposite order' => array( + 'webfont' => array( + 'font-weight' => '400', + 'font-style' => 'normal', + 'font-family' => 'Open Sans', + 'provider' => 'google', + ), + 'expected' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + ), + 'with src' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'expected' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-display' => 'fallback', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + ), + 'with font-stretch' => array( + 'webfont' => array( + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'provider' => 'local', + ), + 'expected' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-display' => 'fallback', + 'font-stretch' => 'normal', + 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + ), + ), + ); + } + + /** + * @covers WP_Webfonts_Registry::set_valid_properties + * + * @dataProvider data_set_valid_properties_with_invalid_input + * + * @param array $webfont Webfont input. + * @param array $expected Expected updated webfont. + */ + public function test_set_valid_properties_with_invalid_input( array $webfont, array $expected ) { + $this->assertSame( $expected, self::$validator->set_valid_properties( $webfont ) ); + } + + /** + * Data Provider. + * + * return @array + */ + public function data_set_valid_properties_with_invalid_input() { + return array( + 'empty array - no schema' => array( + 'webfont' => array(), + 'expected' => array( + 'provider' => '', + 'font-family' => '', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + ), + 'with invalid @font-face property' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'invalid' => 'should remove it', + ), + 'expected' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + ), + 'font-style: invalid value' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-style' => 'invalid', + ), + 'expected' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + ), + 'font-weight: invalid value' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-weight' => 'invalid', + ), + 'expected' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + ), + 'font-display: invalid value' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-display' => 'invalid', + ), + 'expected' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + ), + ); + } + + /** + * @covers WP_Webfonts_Registry::set_valid_properties + * + * @dataProvider data_set_valid_properties_with_invalid_and_error + * + * @param array $webfont Webfont input. + * @param array $expected Expected updated webfont. + * @param string $expected_message Expected notice message. + */ + public function test_set_valid_properties_with_invalid_and_error( array $webfont, array $expected, $expected_message ) { + $this->expectNotice(); + $this->expectNoticeMessage( $expected_message ); + + $this->assertSame( $expected, self::$validator->set_valid_properties( $webfont ) ); + } + + /** + * Data Provider. + * + * return @array + */ + public function data_set_valid_properties_with_invalid_and_error() { + return array( + 'font-style: empty value' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-style' => '', + ), + 'expected' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Webfont font style must be a non-empty string.', + ), + 'font-weight: not a string' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-weight' => null, + ), + 'expected' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Webfont font style must be a non-empty string.', + ), + 'font-weight: empty value' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-weight' => '', + ), + 'expected' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Webfont font weight must be a non-empty string.', + ), + 'font-weight: not a string' => array( + 'webfont' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-weight' => true, + ), + 'expected' => array( + 'provider' => 'some-provider', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + 'expected_message' => 'Webfont font weight must be a non-empty string.', + ), + ); + } +} From 9966b1935b83a26c91983cdd1462fdfb3ce7fb6c Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Fri, 15 Oct 2021 15:37:37 -0500 Subject: [PATCH 24/94] Adds src validation to Validator. --- .../class-wp-webfonts-schema-validator.php | 104 +++++++++++++-- .../wpWebfontsSchemaValidator.php | 126 ++++++++++++++++-- 2 files changed, 206 insertions(+), 24 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 055039249a1d5..054401543343f 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -21,25 +21,25 @@ class WP_Webfonts_Schema_Validator { * * @var string[] */ - protected $font_style = array( 'normal', 'italic', 'oblique', 'inherit', 'initial', 'revert', 'unset' ); + const VALID_FONT_STYLE = array( 'normal', 'italic', 'oblique', 'inherit', 'initial', 'revert', 'unset' ); /** - * Valid font weight values. + * Valid font display values. * * @since 5.9.0 * * @var string[] */ - protected $font_weight = array( 'normal', 'bold', 'bolder', 'lighter', 'inherit' ); + const VALID_FONT_DISPLAY = array( 'auto', 'block', 'fallback', 'swap' ); /** - * Valid font display values. + * Valid font weight values. * * @since 5.9.0 * * @var string[] */ - protected $font_display = array( 'auto', 'block', 'swap', 'fallback' ); + const VALID_FONT_WEIGHT = array( 'normal', 'bold', 'bolder', 'lighter', 'inherit' ); /** * An array of valid CSS properties for @font-face. @@ -98,10 +98,20 @@ class WP_Webfonts_Schema_Validator { * @return bool True when valid. False when invalid. */ public function is_valid_schema( array $webfont ) { - return ( + $is_valid = ( $this->is_valid_provider( $webfont ) && $this->is_valid_font_family( $webfont ) ); + + if ( ! $is_valid ) { + return false; + } + + if ( 'local' === $webfont['provider'] || array_key_exists( 'src', $webfont ) ) { + $is_valid = $this->is_src_valid( $webfont ); + } + + return $is_valid; } /** @@ -109,7 +119,7 @@ public function is_valid_schema( array $webfont ) { * * @since 5.9.0 * - * @param array $webfont Webfont to valiate. + * @param array $webfont Webfont to validate. * @return bool True if valid. False if invalid. */ private function is_valid_provider( array $webfont ) { @@ -133,7 +143,7 @@ private function is_valid_provider( array $webfont ) { * @since 5.9.0 * * @param array $webfont Webfont to validate. - * @return bool True if valid. False if invalid. + * @return bool True when valid. False when invalid. */ private function is_valid_font_family( array $webfont ) { if ( @@ -148,6 +158,75 @@ private function is_valid_font_family( array $webfont ) { return true; } + /** + * Checks if the "src" value is valid. + * + * @since 5.9.0 + * + * @param array $webfont Webfont to validate. + * @return bool True if valid. False if invalid. + */ + private function is_src_valid( $webfont ) { + if ( + empty( $webfont['src'] ) || + ( + ! is_string( $webfont['src'] ) && ! is_array( $webfont['src'] ) + ) + ) { + trigger_error( __( 'Webfont src must be a non-empty string or an array of strings.' ) ); + + return false; + } + + foreach ( (array) $webfont['src'] as $src ) { + if ( ! is_string( $src ) ) { + trigger_error( __( 'Each webfont src must be a non-empty string.' ) ); + + return false; + } + + if ( ! $this->is_src_value_valid( $src ) ) { + trigger_error( __( 'Webfont src must be a valid URL or a data URI.' ) ); + + return false; + } + } + + return true; + } + + /** + * Checks if the given `src` value is valid. + * + * @since 5.9.0 + * + * @param string $src Source to validate. + * @return bool True when valid. False when invalid. + */ + private function is_src_value_valid( $src ) { + // Validate data URLs. + if ( preg_match( '/^data:.+;base64/', $src ) ) { + return true; + } + + // Validate URLs. + if ( filter_var( $src, FILTER_VALIDATE_URL ) ) { + return true; + } + + // Check if it's a URL starting with "//" (omitted protocol). + if ( 0 === strpos( $src, '//' ) ) { + return true; + } + + // Check if it's a relative URL. + if ( 0 === strpos( $src, 'file:./' ) ) { + return true; + } + + return false; + } + /** * Sets valid properties. * @@ -205,7 +284,7 @@ private function set_valid_font_style() { trigger_error( __( 'Webfont font style must be a non-empty string.' ) ); } elseif ( // Bail out if the font-weight is a valid value. - in_array( $this->webfont['font-style'], $this->font_style, true ) || + in_array( $this->webfont['font-style'], self::VALID_FONT_STYLE, true ) || preg_match( '/^oblique\s+(\d+)%/', $this->webfont['font-style'] ) ) { return; @@ -228,8 +307,11 @@ private function set_valid_font_weight() { trigger_error( __( 'Webfont font weight must be a non-empty string.' ) ); } elseif ( // Bail out if the font-weight is a valid value. - in_array( $this->webfont['font-weight'], $this->font_weight, true ) || + // Check if value is a single font-weight, formatted as a number. + in_array( $this->webfont['font-weight'], self::VALID_FONT_WEIGHT, true ) || + // Check if value is a single font-weight, formatted as a number. preg_match( '/^(\d+)$/', $this->webfont['font-weight'], $matches ) || + // Check if value is a range of font-weights, formatted as a number range. preg_match( '/^(\d+)\s+(\d+)$/', $this->webfont['font-weight'], $matches ) ) { return; @@ -247,7 +329,7 @@ private function set_valid_font_weight() { private function set_valid_font_display() { if ( ! empty( $this->webfont['font-display'] ) && - in_array( $this->webfont['font-display'], $this->font_display, true ) + in_array( $this->webfont['font-display'], self::VALID_FONT_DISPLAY, true ) ) { return; } diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index e4f1ca1171d9e..46ec230dbcf71 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -14,7 +14,7 @@ public static function set_up_before_class() { } /** - * @covers WP_Webfonts_Registry::is_valid_schema + * @covers WP_Webfonts_Registry::is_valid_schema * * @dataProvider data_is_valid_schema_with_valid * @@ -49,7 +49,7 @@ public function data_is_valid_schema_with_valid() { } /** - * @covers WP_Webfonts_Registry::is_valid_schema + * @covers WP_Webfonts_Registry::is_valid_schema * * @dataProvider data_is_valid_schema_with_invalid * @@ -70,11 +70,11 @@ public function test_is_valid_schema_with_invalid( array $webfont, $expected_mes */ public function data_is_valid_schema_with_invalid() { return array( - 'empty array - no schema' => array( + 'empty array - no schema' => array( 'webfont' => array(), 'expected_message' => 'Webfont provider must be a non-empty string.', ), - 'provider: not defined' => array( + 'provider: not defined' => array( 'webfont' => array( 'font-family' => 'Some Font', 'font-style' => 'normal', @@ -82,7 +82,7 @@ public function data_is_valid_schema_with_invalid() { ), 'expected_message' => 'Webfont provider must be a non-empty string.', ), - 'provider: empty string' => array( + 'provider: empty string' => array( 'webfont' => array( 'provider' => '', 'font-family' => 'Some Font', @@ -91,7 +91,7 @@ public function data_is_valid_schema_with_invalid() { ), 'expected_message' => 'Webfont provider must be a non-empty string.', ), - 'provider: not a string' => array( + 'provider: not a string' => array( 'webfont' => array( 'provider' => null, 'font-family' => 'Some Font', @@ -100,7 +100,7 @@ public function data_is_valid_schema_with_invalid() { ), 'expected_message' => 'Webfont provider must be a non-empty string.', ), - 'font-family: not defined' => array( + 'font-family: not defined' => array( 'webfont' => array( 'provider' => 'some-provider', 'font-style' => 'normal', @@ -108,7 +108,7 @@ public function data_is_valid_schema_with_invalid() { ), 'expected_message' => 'Webfont font family must be a non-empty string.', ), - 'font-family: empty string' => array( + 'font-family: empty string' => array( 'webfont' => array( 'provider' => 'some-provider', 'font-family' => '', @@ -117,7 +117,7 @@ public function data_is_valid_schema_with_invalid() { ), 'expected_message' => 'Webfont font family must be a non-empty string.', ), - 'font-family: not a string' => array( + 'font-family: not a string' => array( 'webfont' => array( 'provider' => 'some-provider', 'font-family' => null, @@ -126,11 +126,60 @@ public function data_is_valid_schema_with_invalid() { ), 'expected_message' => 'Webfont font family must be a non-empty string.', ), + 'src: not defined' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + ), + 'expected_message' => 'Webfont src must be a non-empty string or an array of strings.', + ), + 'src: type is invalid' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'src' => null, + ), + 'expected_message' => 'Webfont src must be a non-empty string or an array of strings.', + ), + 'src: individual src is not a string' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'src' => array( null ), + ), + 'expected_message' => 'Each webfont src must be a non-empty string.', + ), + 'src: invalid url' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'src' => '/assets/fonts/font.woff2', + ), + 'expected_message' => 'Webfont src must be a valid URL or a data URI.', + ), + 'src: invalid data uri' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'src' => 'data:text/plain', + ), + 'expected_message' => 'Webfont src must be a valid URL or a data URI.', + ), ); } /** - * @covers WP_Webfonts_Registry::set_valid_properties + * @covers WP_Webfonts_Registry::set_valid_properties * * @dataProvider data_set_valid_properties_with_valid_input * @@ -178,7 +227,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-display' => 'fallback', ), ), - 'with src' => array( + 'src: file::./ relative' => array( 'webfont' => array( 'provider' => 'local', 'font-family' => 'Source Serif Pro', @@ -195,6 +244,57 @@ public function data_set_valid_properties_with_valid_input() { 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), ), + 'src: with protocol' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'src' => 'http://example.org/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'expected' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-display' => 'fallback', + 'src' => 'http://example.org/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2', + ), + ), + 'src: without protocol' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'src' => '//example.org/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2', + ), + 'expected' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-display' => 'fallback', + 'src' => '//example.org/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2', + ), + ), + 'src: data:' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'src' => 'data:font/opentype; base64, SGVsbG8sIFdvcmxkIQ==', + ), + 'expected' => array( + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-display' => 'fallback', + 'src' => 'data:font/opentype; base64, SGVsbG8sIFdvcmxkIQ==', + ), + ), 'with font-stretch' => array( 'webfont' => array( 'font-family' => 'Source Serif Pro', @@ -218,7 +318,7 @@ public function data_set_valid_properties_with_valid_input() { } /** - * @covers WP_Webfonts_Registry::set_valid_properties + * @covers WP_Webfonts_Registry::set_valid_properties * * @dataProvider data_set_valid_properties_with_invalid_input * @@ -306,7 +406,7 @@ public function data_set_valid_properties_with_invalid_input() { } /** - * @covers WP_Webfonts_Registry::set_valid_properties + * @covers WP_Webfonts_Registry::set_valid_properties * * @dataProvider data_set_valid_properties_with_invalid_and_error * From 56ebaa79aaef68f019640b4a76f33a9c8313a745 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Mon, 18 Oct 2021 13:58:32 +0300 Subject: [PATCH 25/94] Remove invalid_parameters & related method --- .../class-wp-webfonts-google-provider.php | 12 ------- .../class-wp-webfonts-local-provider.php | 1 - .../providers/class-wp-webfonts-provider.php | 35 ------------------- 3 files changed, 48 deletions(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index c4065feb2da83..dc81f9e1c944b 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -46,18 +46,6 @@ class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { */ protected $root_url = 'https://fonts.googleapis.com/css2'; - /** - * An array of API parameters which will not be added to the @font-face. - * - * @since 5.9.0 - * @var array - */ - protected $invalid_parameters = array( - 'subset', - 'text', - 'effect', - ); - /** * Build the API URL for a collection of fonts. * diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index dc1da5cb8daf2..669f90938a4e5 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -30,7 +30,6 @@ class WP_Webfonts_Local_Provider extends WP_Webfonts_Provider { * @return array */ protected function prepare( array $webfont ) { - $webfont = parent::prepare( $webfont ); $webfont = $this->order_src( $webfont ); // Wrap font-family in quotes if it contains spaces. diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index f8fb2553331be..5b7c9249fe2c7 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -50,15 +50,6 @@ abstract class WP_Webfonts_Provider { */ protected $webfonts = array(); - /** - * An array of API parameters which will not be added to the @font-face. - * - * @since 5.9.0 - * - * @var array - */ - protected $invalid_parameters = array(); - /** * Get the provider's unique ID. * @@ -119,32 +110,6 @@ public function set_webfonts( array $webfonts ) { * @return array */ protected function prepare( array $webfont ) { - if ( empty( $this->invalid_parameters ) ) { - return $webfont; - } - - return $this->remove_invalid_properties( $webfont ); - } - - /** - * Removes invalid properties from the webfont. - * - * @since 5.9.0 - * - * @param array $webfont Webfont to process. - * @return array Webfont with valid properties. - */ - private function remove_invalid_properties( array $webfont ) { - $invalid_parameters = $this->invalid_parameters; - - $webfont = array_filter( - $webfont, - static function( $key ) use ( $invalid_parameters ) { - return ! in_array( $key, $invalid_parameters, true ); - }, - ARRAY_FILTER_USE_KEY - ); - return $webfont; } From 0c0354bccd02ffc7d3db22acd67e13bdd553adb8 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Tue, 19 Oct 2021 16:13:08 -0500 Subject: [PATCH 26/94] Improves readability, usability, and performance. Readability improvements: - Renamed `get_registry()` to `get_all_registered()` - Added and refined comments - Moved the organizational logic in Google Fonts Provider to private method - Moved the store in "query by" containers to a private method Performance: - Changed `get_by_font_family()` query to use the same registered key strategy as the `get_by_provider()`. Both are now a O(1) lookup. Usability: - Allows a custom provider to set `` attributes for a single `` (vs a strict array of arrays) --- .../class-wp-webfonts-controller.php | 32 ++-- .../class-wp-webfonts-provider-registry.php | 153 +++++++++++++----- .../class-wp-webfonts-registry.php | 115 +++++++++---- .../class-wp-webfonts-google-provider.php | 118 +++++++++----- .../providers/class-wp-webfonts-provider.php | 26 +-- ...class-my-custom-webfonts-provider-mock.php | 7 +- .../providers/wpWebfontsGoogleProvider.php | 8 +- .../webfonts-api/wpWebfontsController.php | 6 +- .../wpWebfontsProviderRegistry.php | 35 ++-- .../tests/webfonts-api/wpWebfontsRegistry.php | 16 +- 10 files changed, 337 insertions(+), 179 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index acb00a773a28b..d6f9e69d61ee1 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -24,13 +24,13 @@ class WP_Webfonts_Controller { private $webfonts_registry; /** - * Instance of the provider registry. + * Instance of the provider's registry. * * @since 5.9.0 * * @var WP_Webfonts_Provider_Registry */ - private $providers_registry; + private $providers; /** * Stylesheet handle. @@ -53,8 +53,8 @@ public function __construct( WP_Webfonts_Registry $webfonts_registry, WP_Webfonts_Provider_Registry $provider_registry ) { - $this->webfonts_registry = $webfonts_registry; - $this->providers_registry = $provider_registry; + $this->webfonts_registry = $webfonts_registry; + $this->providers = $provider_registry; } /** @@ -63,7 +63,7 @@ public function __construct( * @since 5.9.0 */ public function init() { - $this->providers_registry->init(); + $this->providers->init(); // Register callback to generate and enqueue styles. if ( did_action( 'wp_enqueue_scripts' ) ) { @@ -111,19 +111,19 @@ public function register_webfont( array $webfont ) { * * @since 5.9.0 * - * @return string[][] Registered webfonts. + * @return array[] Registered webfonts. */ public function get_webfonts() { - return $this->webfonts_registry->get_registry(); + return $this->webfonts_registry->get_all_registered(); } /** - * Gets the registered webfonts for the given provider. + * Gets the registered webfonts for the given provider organized by font-family. * * @since 5.9.0 * * @param string $provider_id Provider ID to fetch. - * @return string[][] Registered webfonts. + * @return array[] Registered webfonts. */ public function get_webfonts_by_provider( $provider_id ) { return $this->webfonts_registry->get_by_provider( $provider_id ); @@ -135,7 +135,7 @@ public function get_webfonts_by_provider( $provider_id ) { * @since 5.9.0 * * @param string $font_family Family font to fetch. - * @return string[][] Registered webfonts. + * @return array[] Registered webfonts. */ public function get_webfonts_by_font_family( $font_family ) { return $this->webfonts_registry->get_by_font_family( $font_family ); @@ -149,7 +149,7 @@ public function get_webfonts_by_font_family( $font_family ) { * @return WP_Webfonts_Provider[] Registered providers. */ public function get_registered_providers() { - return $this->providers_registry->get_registry(); + return $this->providers->get_all_registered(); } /** @@ -161,7 +161,7 @@ public function get_registered_providers() { * @return bool True when registered. False when provider does not exist. */ public function register_provider( $classname ) { - return $this->providers_registry->register( $classname ); + return $this->providers->register( $classname ); } /** @@ -206,7 +206,7 @@ private function generate_styles() { continue; } - add_action( 'wp_head', array( $this, 'render_preconnect_links' ) ); + add_action( 'wp_head', array( $this, 'render_links' ) ); $provider->set_webfonts( $registered_webfonts ); $styles .= $provider->get_css(); @@ -215,11 +215,11 @@ private function generate_styles() { } /** - * Renders preconnect links to for enqueued webfonts. + * Renders the HTML `` for each provider into `` for enqueued webfonts. * * @since 5.9.0 */ - public function render_preconnect_links() { - echo $this->providers_registry->get_preconnect_links(); + public function render_links() { + echo $this->providers->get_links(); } } diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php index 469dd1a5e3ed8..baa5365ac433e 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -9,27 +9,41 @@ /** * Provider Registry. + * + * This registry exists to handle all providers. + * + * It handles the following within the API: + * - loads the bundled provider files into memory; + * - registers each provider with the API by: + * 1. creating an instance (object); + * 2. storing it in-memory (by its unique provider ID) for use with the API; + * - handles generating the linked resources `` for all providers. */ class WP_Webfonts_Provider_Registry { /** - * Registered providers. + * An in-memory storage container that holds all registered providers + * for use within the API. + * + * Keyed by the respective provider's unique provider ID: + * + * @type string $provider_id => @type WP_Webfonts_Provider Provider instance. * * @since 5.9.0 * * @var WP_Webfonts_Provider[] */ - private $registry = array(); + private $registered = array(); /** - * Gets the provider registry. + * Gets all registered providers. * * @since 5.9.0 * - * @return WP_Webfonts_Provider[] Registered providers. + * @return WP_Webfonts_Provider[] All registered providers each keyed by their unique provider ID. */ - public function get_registry() { - return $this->registry; + public function get_all_registered() { + return $this->registered; } /** @@ -44,6 +58,9 @@ public function init() { /** * Registers the core providers. * + * Loads each bundled provider's file into memory and + * then registers it for use with the API. + * * @since 5.9.0 */ private function register_core_providers() { @@ -64,77 +81,129 @@ private function register_core_providers() { * * @since 5.9.0 * - * @param string $classname The provider class name. + * @param string $classname The provider's class name. * @return bool True when registered. False when provider does not exist. */ public function register( $classname ) { + // If the class does not exist in memory, bail out. if ( ! class_exists( $classname ) ) { - return ''; + return false; } + /* + * Create an instance of the provider. + * This API uses one instance of each provider. + */ $provider = new $classname; $id = $provider->get_id(); - if ( ! isset( $this->providers[ $id ] ) ) { - $this->registry[ $id ] = $provider; + // Store the provider's instance by its unique provider ID. + if ( ! isset( $this->registered[ $id ] ) ) { + $this->registered[ $id ] = $provider; } - return $id; + return true; } /** - * Get the preconnect links HTML. + * Gets the HTML `` for each provider. * * @since 5.9.0 * - * @return string Preconnect links HTML. + * @return string HTML links for each provider. */ - public function get_preconnect_links() { - // Store a static var to avoid adding the same preconnect links multiple times. - static $generated = array(); - - $links = ''; - - foreach ( $this->registry as $provider_id => $provider ) { - // Skip if the provider already added preconnect links. - if ( isset( $generated[ $provider_id ] ) ) { + public function get_links() { + /* + * Store each `` by its provider ID. Why? + * To ensure only one link is created per provider. + */ + static $links = array(); + + foreach ( $this->get_all_registered() as $provider_id => $provider ) { + // Skip if the provider already added the link. + if ( isset( $links[ $provider_id ] ) ) { continue; } - $links .= $this->generate_preconnect_link( $provider ); - - $added[ $provider_id ] = true; + $links[ $provider_id ] = $this->generate_links( $provider ); } - return $links; + // Put each `` on a newline and then return them as a string. + return implode( '', $links ); } /** - * Generate the preconnect links HTML for the given provider. + * Generate the ` element(s) for the given provider. * * @since 5.9.0 * * @param WP_Webfonts_Provider $provider Instance of the provider. - * @return string Preconnect links HTML for the provider. + * @return string The `` element(s). */ - private function generate_preconnect_link( WP_Webfonts_Provider $provider ) { + private function generate_links( WP_Webfonts_Provider $provider ) { + $link_attributes = $provider->get_link_attributes(); + + /* + * Bail out if there are no attributes for this provider + * (i.e. no `` is needed). + */ + if ( ! is_array( $link_attributes ) || empty( $link_attributes ) ) { + return ''; + } + + /* + * This provider needs multiple `` elements. + * Loop through each array and pass its attributes + * to create each of its `` elements. + */ + if ( is_array( current( $link_attributes ) ) ) { + $links = ''; + foreach ( $link_attributes as $attributes ) { + $links .= $this->create_link_element( $attributes ); + } + + return $links; + } + + /* + * This provider needs one `` element. + * Pass its attributes to create its `` element. + */ + return $this->create_link_element( $link_attributes ); + } + + /** + * Creates the `` element and populates with the given attributes. + * + * @since 5.9.0 + * + * @param string[] $attributes An array of attributes => values. + * @return string The `` element. + */ + private function create_link_element( array $attributes ) { $link = ''; - foreach ( $provider->get_preconnect_urls() as $preconnection ) { - $link .= ' $value ) { - if ( 'href' === $key ) { - $link .= ' href="' . esc_url( $value ) . '"'; - } elseif ( true === $value || false === $value ) { - $link .= $value ? ' ' . esc_attr( $key ) : ''; - } else { - $link .= ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"'; - } + foreach ( $attributes as $attribute => $value ) { + // Checks if attribute is a nonempty string. If no, skip it. + if ( ! is_string( $attribute ) || '' === $attribute ) { + continue; + } + + if ( 'href' === $attribute ) { + $link .= ' href="' . esc_url( $value ) . '"'; + } elseif ( is_bool( $value ) ) { + $link .= $value + ? ' ' . esc_attr( $attribute ) + : ''; + } else { + $link .= ' ' . esc_attr( $attribute ) . '="' . esc_attr( $value ) . '"'; } - $link .= '>' . PHP_EOL; } - return $link; + if ( '' === $link ) { + return ''; + } + + return '' . "\n"; } } diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index ff4bca0b9649f..4873a517f03db 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -10,28 +10,53 @@ /** * Webfonts Registry. * - * Handles webfont registration and query of webfonts. + * This registry exists to handle all webfonts. + * + * It handles the following within the API: + * - loads the bundled provider files into memory; + * - registers each provider with the API by: + * 1. creating an instance (object); + * 2. storing it in-memory (by its unique provider ID) for use with the API; + * - handles generating the linked resources `` for all providers. */ class WP_Webfonts_Registry { /** - * Registered webfonts. + * An in-memory storage container that holds all registered webfonts + * for use within the API. + * + * Keyed by font-family.font-style.font-weight: + * + * @type string $key => @type array Webfont. * * @since 5.9.0 * - * @var string[][] + * @var array[] */ - private $registry = array(); + private $registered = array(); /** * Registration keys per provider. * + * Provides a O(1) lookup when querying by provider. + * * @since 5.9.0 * * @var string[] */ private $registry_by_provider = array(); + /** + * Registration keys per font-family. + * + * Provides a O(1) lookup when querying by provider. + * + * @since 5.9.0 + * + * @var string[] + */ + private $registry_by_font_family = array(); + /** * Schema validator. * @@ -46,14 +71,14 @@ public function __construct( WP_Webfonts_Schema_Validator $validator ) { } /** - * Gets the webfont registry. + * Gets all registered webfonts. * * @since 5.9.0 * - * @return string[][] Registered webfonts. + * @return array[] Registered webfonts each keyed by font-family.font-style.font-weight. */ - public function get_registry() { - return $this->registry; + public function get_all_registered() { + return $this->registered; } /** @@ -62,7 +87,7 @@ public function get_registry() { * @since 5.9.0 * * @param string $provider_id Provider ID to fetch. - * @return string[][] Registered webfonts. + * @return array[] Registered webfonts. */ public function get_by_provider( $provider_id ) { if ( ! isset( $this->registry_by_provider[ $provider_id ] ) ) { @@ -71,12 +96,12 @@ public function get_by_provider( $provider_id ) { $webfonts = array(); foreach ( $this->registry_by_provider[ $provider_id ] as $registration_key ) { - // Safeguard. Skip if not in registry. - if ( ! isset( $this->registry[ $registration_key ] ) ) { + // Skip if not registered. + if ( ! isset( $this->registered[ $registration_key ] ) ) { continue; } - $webfonts[ $registration_key ] = $this->registry[ $registration_key ]; + $webfonts[ $registration_key ] = $this->registered[ $registration_key ]; } return $webfonts; @@ -95,17 +120,21 @@ public function get_by_font_family( $font_family ) { return array(); } - $webfonts = array(); - $font_family_key = $this->convert_font_family_into_key( $font_family ) . '.'; - $last_char = strlen( $font_family_key ); + $font_family_key = $this->convert_font_family_into_key( $font_family ); + + // If the font family is not registered, bail out. + if ( ! isset( $this->registry_by_font_family[ $font_family_key ] ) ) { + return array(); + } - foreach ( $this->registry as $registration_key => $webfont ) { - // Skip if webfont's family font does not match. - if ( substr( $registration_key, 0, $last_char ) !== $font_family_key ) { + $webfonts = array(); + foreach ( $this->registry_by_font_family[ $font_family_key ] as $registration_key ) { + // Safeguard. Skip if not in registry. + if ( ! isset( $this->registered[ $registration_key ] ) ) { continue; } - $webfonts[ $registration_key ] = $webfont; + $webfonts[ $registration_key ] = $this->registered[ $registration_key ]; } return $webfonts; @@ -131,14 +160,44 @@ public function register( array $webfont ) { // Add to registry. $registration_key = $this->generate_registration_key( $webfont ); - if ( ! isset( $this->registry[ $registration_key ] ) ) { - $this->registry[ $registration_key ] = $webfont; - $this->registry_by_provider[ $webfont['provider'] ][] = $registration_key; + if ( isset( $this->registered[ $registration_key ] ) ) { + return $registration_key; } + $this->registered[ $registration_key ] = $webfont; + $this->store_in_query_by_containers( $webfont, $registration_key ); + return $registration_key; } + /** + * Store the webfont into each query by container. + * + * These containers provide a performant way to quickly query webfonts by + * provider or font-family. The registration keys are stored in each for + * O(1) lookup. + * + * @since 5.9.0 + * + * @param array $webfont Webfont definition. + * @param string $registration_key Webfont's registration key. + */ + private function store_in_query_by_containers( array $webfont, $registration_key ) { + $font_family = $this->convert_font_family_into_key( $webfont['font-family'] ); + $provider = $webfont['provider']; + + // Initialize the arrays if they do not exist. + if ( ! isset( $this->registry_by_provider[ $provider ] ) ) { + $this->registry_by_provider[ $provider ] = array(); + } + if ( ! isset( $this->registry_by_font_family[ $font_family ] ) ) { + $this->registry_by_font_family[ $font_family ] = array(); + } + + $this->registry_by_provider[ $provider ][] = $registration_key; + $this->registry_by_font_family[ $font_family ][] = $registration_key; + } + /** * Convert camelCase parameters into kabeb_case. * @@ -191,16 +250,4 @@ private function convert_font_family_into_key( $font_family ) { return sanitize_title( $font_family ); } - - /** - * Checks if the given webfont schema is validate. - * - * @since 5.9.0 - * - * @param string[] $webfont Webfont definition. - * @return bool True when valid. False when invalid. - */ - private function is_schema_valid( array $webfont ) { - return $this->validator->is_schema_valid( $webfont ); - } } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index dc81f9e1c944b..826285be0c272 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -2,9 +2,9 @@ /** * Webfonts API: Google Fonts provider. * - * @package WordPress - * @subpackage WebFonts * @since 5.9.0 + * @subpackage WebFonts + * @package WordPress */ /** @@ -22,12 +22,21 @@ class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { protected $id = 'google'; /** - * An array of URLs to preconnect to. + * The `` element's attributes for each linked resource. * * @since 5.9.0 - * @var array + * + * @var array[] { + * An array of linked resources. + * + * @type array() { + * An array of attributes for this linked resource. + * + * @type string $attribute => @type string $attribute_value + * } + * } */ - protected $preconnect_urls = array( + protected $link_attributes = array( array( 'href' => 'https://fonts.gstatic.com', 'crossorigin' => true, @@ -46,51 +55,44 @@ class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { */ protected $root_url = 'https://fonts.googleapis.com/css2'; + /** + * Get the CSS for a collection of fonts. + * + * @access public + * @since 5.9.0 + * @return string + */ + public function get_css() { + $css = ''; + $urls = $this->build_collection_api_urls(); + + foreach ( $urls as $url ) { + $css .= $this->get_cached_remote_styles( 'google_fonts_' . md5( $url ), $url ); + } + + return $css; + } + /** * Build the API URL for a collection of fonts. * * @since 5.9.0 * - * @param array $fonts Registered webfonts. * @return array Collection by font-family urls. */ - protected function build_collection_api_urls( array $fonts ) { + protected function build_collection_api_urls() { $font_families_urls = array(); - // Group by font-display. - // Each font-display will need to be a separate request. - $font_display_groups = array(); - foreach ( $fonts as $font ) { - $font['font-display'] = isset( $font['font-display'] ) ? $font['font-display'] : 'fallback'; - if ( ! isset( $font_display_groups[ $font['font-display'] ] ) ) { - $font_display_groups[ $font['font-display'] ] = array(); - } - $font_display_groups[ $font['font-display'] ][] = $font; - } - - // Iterate over each font-display group and group by font-family. - // Multiple font-families can be combined in the same request, but their params need to be grouped. - foreach ( $font_display_groups as $font_display => $font_display_group ) { - $font_families = array(); - foreach ( $font_display_group as $font ) { - if ( ! isset( $font_families[ $font['font-family'] ] ) ) { - $font_families[ $font['font-family'] ] = array(); - } - $font_families[ $font['font-family'] ][] = $font; - } - $font_display_groups[ $font_display ] = $font_families; - } - // Iterate over each font-family group and build the API URL partial for that font-family. - foreach ( $font_display_groups as $font_display => $font_families ) { + foreach ( $this->organize_webfonts() as $font_display => $font_families ) { $font_display_url_parts = array(); - foreach ( $font_families as $font_family => $fonts ) { + foreach ( $font_families as $font_family => $webfonts ) { $normal_weights = array(); $italic_weights = array(); $url_part = urlencode( $font_family ); // Build an array of font-weights for italics and default styles. - foreach ( $fonts as $font ) { + foreach ( $webfonts as $font ) { if ( 'italic' === $font['font-style'] ) { $italic_weights[] = $font['font-weight']; } else { @@ -116,20 +118,48 @@ protected function build_collection_api_urls( array $fonts ) { } /** - * Get the CSS for a collection of fonts. + * Organizes the webfonts by font-display. * - * @access public - * @since 5.9.0 - * @return string + * @since 5.8.0 + * + * @return array Sorted by font-display. */ - public function get_css() { - $css = ''; - $urls = $this->build_collection_api_urls( $this->webfonts ); + private function organize_webfonts() { + $font_display_groups = array(); - foreach ( $urls as $url ) { - $css .= $this->get_cached_remote_styles( 'google_fonts_' . md5( $url ), $url ); + /* + * Group by font-display. + * Each font-display will need to be a separate request. + */ + foreach ( $this->webfonts as $webfont ) { + if ( ! isset( $font['font-display'] ) ) { + $webfont['font-display'] = 'fallback'; + } + + if ( ! isset( $font_display_groups[ $webfont['font-display'] ] ) ) { + $font_display_groups[ $webfont['font-display'] ] = array(); + } + $font_display_groups[ $webfont['font-display'] ][] = $webfont; } - return $css; + /* + * Iterate over each font-display group and group by font-family. + * Multiple font-families can be combined in the same request, + * but their params need to be grouped. + */ + foreach ( $font_display_groups as $font_display => $font_display_group ) { + $font_families = array(); + + foreach ( $font_display_group as $webfont ) { + if ( ! isset( $font_families[ $webfont['font-family'] ] ) ) { + $font_families[ $webfont['font-family'] ] = array(); + } + $font_families[ $webfont['font-family'] ][] = $webfont; + } + + $font_display_groups[ $font_display ] = $font_families; + } + + return $font_display_groups; } } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index 5b7c9249fe2c7..354fdae03c9c4 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -24,13 +24,21 @@ abstract class WP_Webfonts_Provider { protected $id; /** - * An array of URLs to preconnect to. + * The `` element's attributes for each linked resource. * * @since 5.9.0 * - * @var array + * @var array[] { + * An array of linked resources. + * + * @type array() { + * An array of attributes for this linked resource. + * + * @type string $attribute => @type string $attribute_value + * } + * } */ - protected $preconnect_urls = array(); + protected $link_attributes = array(); /** * The provider's root URL. @@ -73,25 +81,25 @@ public function get_root_url() { } /** - * Get the array of URLs to preconnect to. + * Get the `` attributes. * * @since 5.9.0 * - * @return array + * @return array[] */ - public function get_preconnect_urls() { - return $this->preconnect_urls; + public function get_link_attributes() { + return $this->link_attributes; } /** * Sets the webfonts. * * The webfonts have been validated, are in kebab_case, and - * are arranged by provider and then by font-family. + * are arranged by provider. * * @since 5.9.0 * - * @param array[] $webfonts The webfont's parameters. + * @param array[] $webfonts Registered webfonts. */ public function set_webfonts( array $webfonts ) { $this->webfonts = $webfonts; diff --git a/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php b/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php index 778b3a3a475b2..0a32275aa0ca3 100644 --- a/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php +++ b/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php @@ -5,10 +5,9 @@ class My_Custom_Webfonts_Provider_Mock extends WP_Webfonts_Provider { protected $id = 'my-custom-provider'; - protected $preconnect_urls = array( - array( - 'href' => 'https://fonts.my-custom-api.com', - ), + protected $link_attributes = array( + 'href' => 'https://fonts.my-custom-api.com', + 'crossorigin' => true, ); public function set_webfonts( array $webfonts ) { diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php index 080b9df360d0a..ce1351b568bb4 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php @@ -56,15 +56,19 @@ public function test_set_webfonts() { * * @dataProvider data_build_collection_api_urls * - * @since 5.9.0 + * @since 5.9.0 * * @param array $webfonts Webfonts input. * @param array $expected Expected urls. */ public function test_build_collection_api_urls( array $webfonts, array $expected ) { + $property = new ReflectionProperty( $this->provider, 'webfonts' ); + $property->setAccessible( true ); + $property->setValue( $this->provider, $webfonts ); + $method = new ReflectionMethod( $this->provider, 'build_collection_api_urls' ); $method->setAccessible( true ); - $actual = $method->invoke( $this->provider, $webfonts ); + $actual = $method->invoke( $this->provider ); $this->assertSame( $expected, $actual ); } diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index 97ef0cd8335c1..11a266993fa90 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -148,7 +148,7 @@ public function test_get_webfonts() { $this->webfont_registry_mock ->expects( $this->once() ) - ->method( 'get_registry' ) + ->method( 'get_all_registered' ) ->willReturn( $expected ); $this->assertSame( $expected, $this->controller->get_webfonts() ); @@ -222,7 +222,7 @@ public function test_get_registered_providers() { $this->provider_registry_mock ->expects( $this->once() ) - ->method( 'get_registry' ) + ->method( 'get_all_registered' ) ->willReturn( $expected ); $actual = $this->controller->get_registered_providers(); @@ -273,7 +273,7 @@ public function test_generate_and_enqueue_editor_styles( $stylestyle_handle ) { ); $this->provider_registry_mock ->expects( $this->once() ) - ->method( 'get_registry' ) + ->method( 'get_all_registered' ) ->willReturn( $providers ); // Set up the webfonts registry mock. diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php index c9501d5483d6e..641c8dc4ad46f 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php @@ -12,34 +12,34 @@ public static function set_up_before_class() { } /** - * @covers WP_Webfonts_Provider_Registry::get_registry + * @covers WP_Webfonts_Provider_Registry::get_all_registered */ - public function test_get_registry_when_empty() { + public function test_get_all_registered_when_empty() { $registry = new WP_Webfonts_Provider_Registry(); - $this->assertSame( array(), $registry->get_registry() ); + $this->assertSame( array(), $registry->get_all_registered() ); } /** * @covers WP_Webfonts_Provider_Registry::register - * @covers WP_Webfonts_Provider_Registry::get_registry + * @covers WP_Webfonts_Provider_Registry::get_all_registered */ public function test_register_with_invalid_class() { $registry = new WP_Webfonts_Provider_Registry(); $registry->register( 'DoesNotExist' ); - $this->assertSame( array(), $registry->get_registry() ); + $this->assertSame( array(), $registry->get_all_registered() ); } /** * @covers WP_Webfonts_Provider_Registry::register - * @covers WP_Webfonts_Provider_Registry::get_registry + * @covers WP_Webfonts_Provider_Registry::get_all_registered */ public function test_register_with_valid_class() { $registry = new WP_Webfonts_Provider_Registry(); $registry->register( My_Custom_Webfonts_Provider_Mock::class ); - $providers = $registry->get_registry(); + $providers = $registry->get_all_registered(); $this->assertIsArray( $providers ); $this->assertCount( 1, $providers ); @@ -49,14 +49,14 @@ public function test_register_with_valid_class() { /** * @covers WP_Webfonts_Provider_Registry::init - * @covers WP_Webfonts_Provider_Registry::get_registry + * @covers WP_Webfonts_Provider_Registry::get_all_registered */ public function test_init() { $registry = new WP_Webfonts_Provider_Registry(); // Register the core providers. $registry->init(); - $providers = $registry->get_registry(); + $providers = $registry->get_all_registered(); $expected = array( 'google', 'local' ); $this->assertSame( $expected, array_keys( $providers ) ); @@ -66,7 +66,7 @@ public function test_init() { /** * @covers WP_Webfonts_Provider_Registry::register - * @covers WP_Webfonts_Provider_Registry::get_registry + * @covers WP_Webfonts_Provider_Registry::get_all_registered */ public function test_register_with_core_providers() { $registry = new WP_Webfonts_Provider_Registry(); @@ -75,21 +75,21 @@ public function test_register_with_core_providers() { // Register a custom provider. $registry->register( My_Custom_Webfonts_Provider_Mock::class ); - $providers = $registry->get_registry(); + $providers = $registry->get_all_registered(); $expected = array( 'google', 'local', 'my-custom-provider' ); $this->assertSame( $expected, array_keys( $providers ) ); } /** - * @covers WP_Webfonts_Provider_Registry::get_preconnect_links + * @covers WP_Webfonts_Provider_Registry::get_links * - * @dataProvider data_get_preconnect_links + * @dataProvider data_get_links * * @param bool $register_custom When true, registers the custom provider. * @param string $expected Expected HTML. */ - public function test_get_preconnect_links( $register_custom, $expected ) { + public function test_get_links( $register_custom, $expected ) { $registry = new WP_Webfonts_Provider_Registry(); // Register the core providers. $registry->init(); @@ -98,7 +98,8 @@ public function test_get_preconnect_links( $register_custom, $expected ) { $registry->register( My_Custom_Webfonts_Provider_Mock::class ); } - $this->assertSame( $expected, $registry->get_preconnect_links() ); + $actual = $registry->get_links(); + $this->assertSame( $expected, $actual ); } /** @@ -106,7 +107,7 @@ public function test_get_preconnect_links( $register_custom, $expected ) { * * return @array */ - public function data_get_preconnect_links() { + public function data_get_links() { return array( 'core providers' => array( 'register_custom' => false, @@ -122,7 +123,7 @@ public function data_get_preconnect_links() { 'expected' => << - + LINKS , diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index 3fab2f46e2ab5..ef80df2f04367 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -23,9 +23,9 @@ public function set_up() { } /** - * @covers WP_Webfonts_Registry::get_registry + * @covers WP_Webfonts_Registry::get_all_registered */ - public function test_get_registry() { + public function get_all_registered() { $expected = array( 'open-sans.normal.400' => array( 'provider' => 'google', @@ -51,11 +51,11 @@ public function test_get_registry() { $property->setAccessible( true ); $property->setValue( $this->registry, $expected ); - $this->assertSame( $expected, $this->registry->get_registry() ); + $this->assertSame( $expected, $this->registry->get_all_registered() ); } /** - * @covers WP_Webfonts_Registry::register + * @covers WP_Webfonts_Registry::register * * @dataProvider data_register_with_invalid_schema * @@ -140,7 +140,7 @@ public function data_register_with_invalid_schema() { } /** - * @covers WP_Webfonts_Registry::register + * @covers WP_Webfonts_Registry::register * * @dataProvider data_register_with_valid_schema * @@ -274,8 +274,8 @@ public function data_get_by_font_family_when_invalid_input() { * As there are many moving parts to getting by provider, this test is an integration * test that does not mock. * - * @covers WP_Webfonts_Registry::get_by_provider - * @covers WP_Webfonts_Registry::register + * @covers WP_Webfonts_Registry::get_by_provider + * @covers WP_Webfonts_Registry::register * * @dataProvider data_get_by_provider_integrated * @@ -385,7 +385,7 @@ public function data_get_by_provider_integrated() { } /** - * @covers WP_Webfonts_Registry::get_by_font_family + * @covers WP_Webfonts_Registry::get_by_font_family * * @dataProvider data_get_by_font_family_when_invalid_input * From 2e541a1a36678d810f7bda9c484a562b69628974 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 20 Oct 2021 08:19:27 +0300 Subject: [PATCH 27/94] Remove file:./ implementation --- .../class-wp-webfonts-schema-validator.php | 5 ----- .../class-wp-webfonts-local-provider.php | 19 ------------------- 2 files changed, 24 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 054401543343f..15d9e1fca6b55 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -219,11 +219,6 @@ private function is_src_value_valid( $src ) { return true; } - // Check if it's a relative URL. - if ( 0 === strpos( $src, 'file:./' ) ) { - return true; - } - return false; } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index 669f90938a4e5..bec44418c60c4 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -178,11 +178,6 @@ private function compile_src( $font_family, array $value ) { $src = "local($font_family)"; foreach ( $value as $item ) { - // If the URL starts with "file:./" then it originated in a theme.json file. - // Tweak the URL to be relative to the theme root. - if ( 0 === strpos( $item['url'], 'file:./' ) ) { - $item['url'] = $this->replace_url_temp_placeholder( $item['url'] ); - } $src .= ( 'data' === $item['format'] ) ? ", url({$item['url']})" @@ -191,20 +186,6 @@ private function compile_src( $font_family, array $value ) { return $src; } - /** - * Replace URL's temporary placeholder with theme path. - * - * @since 5.9.0 - * - * @param string $url URL with temporary placeholder. - * @return string URL to font file. - */ - private function replace_url_temp_placeholder( $url ) { - $url = str_replace( 'file:./', '', $url ); - - return wp_make_link_relative( get_theme_file_uri( $url ) ); - } - /** * Compiles the font variation settings. * From 1d928440d1b7f3c7beddfafee0e6966039e80bbd Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 20 Oct 2021 08:44:51 +0300 Subject: [PATCH 28/94] update tests --- .../providers/wpWebfontsLocalProvider.php | 58 ++----------------- .../tests/webfonts-api/wpWebfontsRegistry.php | 12 ++-- .../wpWebfontsSchemaValidator.php | 21 +------ 3 files changed, 12 insertions(+), 79 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php index 830d0d0c2f8dc..3e6b324bfef35 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php @@ -63,7 +63,7 @@ public function test_set_webfonts() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-stretch' => 'normal', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), ), 'source-serif-pro.italic.200 900' => array( 'provider' => 'local', @@ -71,7 +71,7 @@ public function test_set_webfonts() { 'font-style' => 'italic', 'font-weight' => '200 900', 'font-stretch' => 'normal', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ), ), ); @@ -84,7 +84,7 @@ public function test_set_webfonts() { 'font-stretch' => 'normal', 'src' => array( array( - 'url' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'url' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), 'format' => 'woff2', ), ), @@ -97,7 +97,7 @@ public function test_set_webfonts() { 'font-stretch' => 'normal', 'src' => array( array( - 'url' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', + 'url' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ), 'format' => 'woff2', ), ), @@ -179,56 +179,6 @@ public function data_get_css() { src:local("Open Sans"), url('http://example.org/assets/fonts/OpenSans-VariableFont_wdth,wght.ttf') format('ttf'); } -CSS - , - ), - 'with file:./' => array( - 'webfonts' => array( - 'source-serif-pro.normal.200 900' => array( - 'provider' => 'local', - 'font-family' => '"Source Serif Pro"', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => array( - array( - 'url' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'format' => 'woff2', - ), - ), - ), - 'source-serif-pro.italic.200 900' => array( - 'provider' => 'local', - 'font-family' => '"Source Serif Pro"', - 'font-style' => 'italic', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => array( - array( - 'url' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', - 'format' => 'woff2', - ), - ), - ), - ), - 'expected' => << 'normal', 'fontWeight' => '200 900', 'fontStretch' => 'normal', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ) ), 'validated_webfont' => array( 'provider' => 'local', @@ -216,7 +216,7 @@ public function data_register_with_valid_schema() { 'font-weight' => '200 900', 'font-display' => 'fallback', 'font-stretch' => 'normal', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), ), 'expected' => 'source-serif-pro.normal.200 900', ), @@ -227,7 +227,7 @@ public function data_register_with_valid_schema() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-stretch' => 'normal', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), ), 'validated_webfont' => array( 'provider' => 'local', @@ -236,7 +236,7 @@ public function data_register_with_valid_schema() { 'font-weight' => '200 900', 'font-display' => 'fallback', 'font-stretch' => 'normal', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), ), 'expected' => 'source-serif-pro.normal.200 900', ), @@ -359,7 +359,7 @@ public function data_get_by_provider_integrated() { 'fontStyle' => 'normal', 'fontWeight' => '200 900', 'fontStretch' => 'normal', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ) ), array( 'provider' => 'google', @@ -377,7 +377,7 @@ public function data_get_by_provider_integrated() { 'font-weight' => '200 900', 'font-display' => 'fallback', 'font-stretch' => 'normal', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), ), ), ), diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index 46ec230dbcf71..6281c97de2381 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -227,23 +227,6 @@ public function data_set_valid_properties_with_valid_input() { 'font-display' => 'fallback', ), ), - 'src: file::./ relative' => array( - 'webfont' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - ), - 'expected' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-display' => 'fallback', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - ), - ), 'src: with protocol' => array( 'webfont' => array( 'provider' => 'local', @@ -301,7 +284,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-stretch' => 'normal', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), 'provider' => 'local', ), 'expected' => array( @@ -311,7 +294,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-weight' => '200 900', 'font-display' => 'fallback', 'font-stretch' => 'normal', - 'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), ), ), ); From e5ca831d8750c6d519b8be50bc4801800866ef23 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 20 Oct 2021 08:50:13 +0300 Subject: [PATCH 29/94] CS --- tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index ea537eb97161f..c71ddde3414cd 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -207,7 +207,7 @@ public function data_register_with_valid_schema() { 'fontStyle' => 'normal', 'fontWeight' => '200 900', 'fontStretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ) + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), ), 'validated_webfont' => array( 'provider' => 'local', @@ -359,7 +359,7 @@ public function data_get_by_provider_integrated() { 'fontStyle' => 'normal', 'fontWeight' => '200 900', 'fontStretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ) + 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), ), array( 'provider' => 'google', From 33872b8bea077133d6314c054ffbd1452c44f777 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 20 Oct 2021 09:11:04 +0300 Subject: [PATCH 30/94] more tests fixes --- .../mocks/class-my-custom-webfonts-provider-mock.php | 4 ++-- .../providers/wpWebfontsLocalProvider.php | 8 ++++---- .../tests/webfonts-api/wpWebfontsRegistry.php | 12 ++++++------ .../tests/webfonts-api/wpWebfontsSchemaValidator.php | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php b/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php index 0a32275aa0ca3..f1b3e8ce319bf 100644 --- a/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php +++ b/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php @@ -21,14 +21,14 @@ public function get_css() { font-weight: 200 900; font-style: normal; font-stretch: normal; - src: url('" . get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ) . "') format('woff2'); + src: url('https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2') format('woff2'); } @font-face{ font-family: 'Source Serif Pro'; font-weight: 200 900; font-style: italic; font-stretch: normal; - src: url('" . get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ) . "') format('woff2'); + src: url('https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2') format('woff2'); } "; } diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php index 3e6b324bfef35..8f190ccbc0e56 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php @@ -63,7 +63,7 @@ public function test_set_webfonts() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-stretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), 'source-serif-pro.italic.200 900' => array( 'provider' => 'local', @@ -71,7 +71,7 @@ public function test_set_webfonts() { 'font-style' => 'italic', 'font-weight' => '200 900', 'font-stretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ), + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', ), ); @@ -84,7 +84,7 @@ public function test_set_webfonts() { 'font-stretch' => 'normal', 'src' => array( array( - 'url' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + 'url' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', 'format' => 'woff2', ), ), @@ -97,7 +97,7 @@ public function test_set_webfonts() { 'font-stretch' => 'normal', 'src' => array( array( - 'url' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ), + 'url' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', 'format' => 'woff2', ), ), diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index c71ddde3414cd..122a9da8aedd2 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -207,7 +207,7 @@ public function data_register_with_valid_schema() { 'fontStyle' => 'normal', 'fontWeight' => '200 900', 'fontStretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), 'validated_webfont' => array( 'provider' => 'local', @@ -216,7 +216,7 @@ public function data_register_with_valid_schema() { 'font-weight' => '200 900', 'font-display' => 'fallback', 'font-stretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), 'expected' => 'source-serif-pro.normal.200 900', ), @@ -227,7 +227,7 @@ public function data_register_with_valid_schema() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-stretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), 'validated_webfont' => array( 'provider' => 'local', @@ -236,7 +236,7 @@ public function data_register_with_valid_schema() { 'font-weight' => '200 900', 'font-display' => 'fallback', 'font-stretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), 'expected' => 'source-serif-pro.normal.200 900', ), @@ -359,7 +359,7 @@ public function data_get_by_provider_integrated() { 'fontStyle' => 'normal', 'fontWeight' => '200 900', 'fontStretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), array( 'provider' => 'google', @@ -377,7 +377,7 @@ public function data_get_by_provider_integrated() { 'font-weight' => '200 900', 'font-display' => 'fallback', 'font-stretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), ), ), diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index 6281c97de2381..d24c5d6816cfc 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -284,7 +284,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-stretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', 'provider' => 'local', ), 'expected' => array( @@ -294,7 +294,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-weight' => '200 900', 'font-display' => 'fallback', 'font-stretch' => 'normal', - 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), ), ); From 551adeeb0a82cfe971879e36a7237c83d0cf6137 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 20 Oct 2021 10:29:37 +0300 Subject: [PATCH 31/94] Make links relative if URL is on-site --- .../providers/class-wp-webfonts-local-provider.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index bec44418c60c4..e004bb8075d9c 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -179,6 +179,10 @@ private function compile_src( $font_family, array $value ) { foreach ( $value as $item ) { + if ( 0 === strpos( $item['url'], get_site_url() ) ) { + $item['url'] = wp_make_link_relative( $item['url'] ); + } + $src .= ( 'data' === $item['format'] ) ? ", url({$item['url']})" : ", url('{$item['url']}') format('{$item['format']}')"; From 846e8605393e6ce71d3d0bdb953f77b81c110c6c Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 20 Oct 2021 10:43:28 +0300 Subject: [PATCH 32/94] Update tests for local URLs CSS generation --- .../tests/webfonts-api/providers/wpWebfontsLocalProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php index 8f190ccbc0e56..f27a3eebffe7b 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php @@ -168,7 +168,7 @@ public function data_get_css() { font-style:italic; font-weight:400 900; font-stretch:normal; - src:local("Open Sans"), url('http://example.org/assets/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf') format('ttf'); + src:local("Open Sans"), url('/assets/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf') format('ttf'); } @font-face{ provider:local; @@ -176,7 +176,7 @@ public function data_get_css() { font-style:normal; font-weight:400 900; font-stretch:normal; - src:local("Open Sans"), url('http://example.org/assets/fonts/OpenSans-VariableFont_wdth,wght.ttf') format('ttf'); + src:local("Open Sans"), url('/assets/fonts/OpenSans-VariableFont_wdth,wght.ttf') format('ttf'); } CSS From dab75cc73e67ebbc6302a5c456170fc7bace6450 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 21 Oct 2021 09:43:13 +0300 Subject: [PATCH 33/94] Add check to verify the provider is registered. --- .../class-wp-webfonts-controller.php | 22 +++++++++++++++++++ .../class-wp-webfonts-schema-validator.php | 7 +++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index d6f9e69d61ee1..4cf063fb066fa 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -222,4 +222,26 @@ private function generate_styles() { public function render_links() { echo $this->providers->get_links(); } + + /** + * Get the webfonts registry. + * + * @since 5.9.0 + * + * @return WP_Webfonts_Registry + */ + public function get_webfonts_registry() { + return $this->webfonts_registry; + } + + /** + * Get the providers registry. + * + * @since 5.9.0 + * + * @return WP_Webfonts_Provider_Registry + */ + public function get_providers() { + return $this->providers; + } } diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 15d9e1fca6b55..0c359a8bec7e9 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -123,7 +123,6 @@ public function is_valid_schema( array $webfont ) { * @return bool True if valid. False if invalid. */ private function is_valid_provider( array $webfont ) { - // @todo check if provider is registered. if ( empty( $webfont['provider'] ) || @@ -134,6 +133,12 @@ private function is_valid_provider( array $webfont ) { return false; } + // Check if provider is registered. + $providers = wp_webfonts()->get_providers()->get_all_registered(); + if ( ! array_key_exists( $webfont['provider'], $providers ) ) { + return false; + } + return true; } From d14aa9e66a0440c781cbd45256b439a3097e5083 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 21 Oct 2021 09:45:35 +0300 Subject: [PATCH 34/94] This was already done --- .../webfonts-api/providers/class-wp-webfonts-local-provider.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index e004bb8075d9c..bae1e565cce69 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -151,7 +151,6 @@ private function build_font_css( array $webfont ) { $value = $this->compile_src( $webfont['font-family'], $value ); } - // @todo Is this a needed configuration parameter? If yes, need to add to Validator; else will be stripped out. // If font-variation-settings is an array, convert it to a string. if ( 'font-variation-settings' === $key && is_array( $value ) ) { $value = $this->compile_variations( $value ); From d87a3b8ce2a5ff78cc62bc1607966f0e4ae2056a Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 21 Oct 2021 09:57:45 +0300 Subject: [PATCH 35/94] test unregistered provider --- .../wpWebfontsSchemaValidator.php | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index d24c5d6816cfc..b699926979afa 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -24,6 +24,17 @@ public function test_is_valid_schema_with_valid( array $webfont ) { $this->assertTrue( self::$validator->is_valid_schema( $webfont ) ); } + /** + * @covers WP_Webfonts_Registry::is_valid_schema + * + * @dataProvider data_is_unregistered_schema_provider + * + * @param array $webfont Webfont input. + */ + public function test_is_unregistered_schema_provider( array $webfont ) { + $this->assertFalse( self::$validator->is_valid_schema( $webfont ) ); + } + /** * Data Provider. * @@ -38,8 +49,18 @@ public function data_is_valid_schema_with_valid() { 'font-style' => 'normal', 'font-weight' => '400', ), - ), - 'only provider and font-family' => array( + ) + ); + } + + /** + * Data Provider. + * + * return @array + */ + public function data_is_unregistered_schema_provider() { + return array( + 'unregistered provider' => array( 'webfont' => array( 'provider' => 'some-provider', 'font-family' => 'Some Font', From 646c9c0fa043ecf309dd7c92a879cb40d3c84277 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 21 Oct 2021 09:58:09 +0300 Subject: [PATCH 36/94] cs --- tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index b699926979afa..a7d2f1a6dc8c2 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -42,7 +42,7 @@ public function test_is_unregistered_schema_provider( array $webfont ) { */ public function data_is_valid_schema_with_valid() { return array( - 'basic schema' => array( + 'basic schema' => array( array( 'provider' => 'google', 'font-family' => 'Open Sans', From fe29ea7e164a7a63f858e5711d3491715d35c7dc Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 21 Oct 2021 10:04:11 +0300 Subject: [PATCH 37/94] Trigger error when provider is not registered --- .../webfonts-api/class-wp-webfonts-schema-validator.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 0c359a8bec7e9..60856811cd1e8 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -136,6 +136,8 @@ private function is_valid_provider( array $webfont ) { // Check if provider is registered. $providers = wp_webfonts()->get_providers()->get_all_registered(); if ( ! array_key_exists( $webfont['provider'], $providers ) ) { + trigger_error( __( 'Webfont provider is not registered.' ) ); + return false; } From 01fd8442ba80b3a808d2cddb1070e821422ea26e Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 21 Oct 2021 10:05:17 +0300 Subject: [PATCH 38/94] CS fix --- tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index a7d2f1a6dc8c2..9c3a447dc3127 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -49,7 +49,7 @@ public function data_is_valid_schema_with_valid() { 'font-style' => 'normal', 'font-weight' => '400', ), - ) + ), ); } From 27116bc7dc804c30a45ac72fc6aef419e4d0d992 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 21 Oct 2021 10:24:41 +0300 Subject: [PATCH 39/94] Fix some failing tests --- .../webfonts-api/wpWebfontsSchemaValidator.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index 9c3a447dc3127..681dc248b558b 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -119,19 +119,28 @@ public function data_is_valid_schema_with_invalid() { 'font-style' => 'normal', 'font-weight' => '400', ), - 'expected_message' => 'Webfont provider must be a non-empty string.', + 'expected_message' => array( + 'Webfont provider must be a non-empty string.', ), - 'font-family: not defined' => array( + 'provider: not registered' => array( 'webfont' => array( 'provider' => 'some-provider', 'font-style' => 'normal', 'font-weight' => '400', ), + 'expected_message' => 'Webfont provider is not registered.', + ), + 'font-family: not defined' => array( + 'webfont' => array( + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '400', + ), 'expected_message' => 'Webfont font family must be a non-empty string.', ), 'font-family: empty string' => array( 'webfont' => array( - 'provider' => 'some-provider', + 'provider' => 'local', 'font-family' => '', 'font-style' => 'normal', 'font-weight' => '400', @@ -140,7 +149,7 @@ public function data_is_valid_schema_with_invalid() { ), 'font-family: not a string' => array( 'webfont' => array( - 'provider' => 'some-provider', + 'provider' => 'local', 'font-family' => null, 'font-style' => 'normal', 'font-weight' => '400', From 4be94a3a0c242a26b66f045c143e5bb73686a5a8 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 21 Oct 2021 10:30:25 +0300 Subject: [PATCH 40/94] meh, copy/pasta typo-error --- .../phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index 681dc248b558b..ee7b93f778c23 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -119,10 +119,9 @@ public function data_is_valid_schema_with_invalid() { 'font-style' => 'normal', 'font-weight' => '400', ), - 'expected_message' => array( - 'Webfont provider must be a non-empty string.', + 'expected_message' => 'Webfont provider must be a non-empty string.', ), - 'provider: not registered' => array( + 'provider: not registered' => array( 'webfont' => array( 'provider' => 'some-provider', 'font-style' => 'normal', From d2f1474372fc949396d25b7c581193891d86bc2d Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 21 Oct 2021 10:46:07 +0300 Subject: [PATCH 41/94] Fix failing test for unregistered provider --- tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index ee7b93f778c23..e6c93e3f30294 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -32,6 +32,8 @@ public function test_is_valid_schema_with_valid( array $webfont ) { * @param array $webfont Webfont input. */ public function test_is_unregistered_schema_provider( array $webfont ) { + $this->expectNotice(); + $this->expectNoticeMessage( 'Webfont provider is not registered.' ); $this->assertFalse( self::$validator->is_valid_schema( $webfont ) ); } From dc5ed3f8fd1039c53d156b3ef622c65179c37274 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Mon, 25 Oct 2021 14:26:46 +0300 Subject: [PATCH 42/94] minor improvements to the validator --- .../class-wp-webfonts-schema-validator.php | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 60856811cd1e8..40b27444bcd86 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -80,6 +80,17 @@ class WP_Webfonts_Schema_Validator { 'font-display' => 'fallback', ); + /** + * Configuration parameters. + * + * These are configuring the webfont but are not @font-face properties. + * + * @since 5.9.0 + * + * @var array + */ + protected $configuration_params = array( 'provider' ); + /** * Webfont being validated. * @@ -186,7 +197,7 @@ private function is_src_valid( $webfont ) { } foreach ( (array) $webfont['src'] as $src ) { - if ( ! is_string( $src ) ) { + if ( empty( $src ) || ! is_string( $src ) ) { trigger_error( __( 'Each webfont src must be a non-empty string.' ) ); return false; @@ -211,18 +222,14 @@ private function is_src_valid( $webfont ) { * @return bool True when valid. False when invalid. */ private function is_src_value_valid( $src ) { - // Validate data URLs. - if ( preg_match( '/^data:.+;base64/', $src ) ) { - return true; - } - - // Validate URLs. - if ( filter_var( $src, FILTER_VALIDATE_URL ) ) { - return true; - } - - // Check if it's a URL starting with "//" (omitted protocol). - if ( 0 === strpos( $src, '//' ) ) { + if ( + // Validate data URLs. + preg_match( '/^data:.+;base64/', $src ) || + // Validate URLs. + filter_var( $src, FILTER_VALIDATE_URL ) || + // Check if it's a URL starting with "//" (omitted protocol). + 0 === strpos( $src, '//' ) + ) { return true; } @@ -259,10 +266,10 @@ public function set_valid_properties( array $webfont ) { private function set_valid_font_face_property() { foreach ( array_keys( $this->webfont ) as $property ) { /* - * Skip valid configuration parameters (these are configuring the webfont - * but are not @font-face properties. + * Skip valid configuration parameters + * (these are configuring the webfont but are not @font-face properties). */ - if ( 'provider' === $property ) { + if ( in_array( $property, $this->configuration_params, true ) ) { continue; } @@ -330,12 +337,10 @@ private function set_valid_font_weight() { */ private function set_valid_font_display() { if ( - ! empty( $this->webfont['font-display'] ) && - in_array( $this->webfont['font-display'], self::VALID_FONT_DISPLAY, true ) + empty( $this->webfont['font-display'] ) || + ! in_array( $this->webfont['font-display'], self::VALID_FONT_DISPLAY, true ) ) { - return; + $this->webfont['font-display'] = 'fallback'; } - - $this->webfont['font-display'] = 'fallback'; } } From 291e4a831d49d9100d2854b7a9a36f8bc0af11e0 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Tue, 26 Oct 2021 09:28:09 +0300 Subject: [PATCH 43/94] fix docs & typos --- .../class-wp-webfonts-provider-registry.php | 2 +- .../webfonts-api/class-wp-webfonts-registry.php | 8 ++++---- .../class-wp-webfonts-schema-validator.php | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php index baa5365ac433e..32a0e681d701d 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -128,7 +128,7 @@ public function get_links() { $links[ $provider_id ] = $this->generate_links( $provider ); } - // Put each `` on a newline and then return them as a string. + // Combine `` elements and return them as a string. return implode( '', $links ); } diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index 4873a517f03db..15ee6d5511053 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -149,7 +149,7 @@ public function get_by_font_family( $font_family ) { * @return string Registration key. */ public function register( array $webfont ) { - $webfont = $this->convert_to_kabeb_case( $webfont ); + $webfont = $this->convert_to_kebab_case( $webfont ); // Validate schema. if ( ! $this->validator->is_valid_schema( $webfont ) ) { @@ -199,14 +199,14 @@ private function store_in_query_by_containers( array $webfont, $registration_key } /** - * Convert camelCase parameters into kabeb_case. + * Convert camelCase parameters into kebab_case. * * @since 5.9.0 * * @param string[] $webfont Webfont definition. - * @return array Webfont with kabeb_case parameters (keys). + * @return array Webfont with kebab_case parameters (keys). */ - private function convert_to_kabeb_case( array $webfont ) { + private function convert_to_kebab_case( array $webfont ) { $kebab_case = preg_replace( '/(? Date: Tue, 26 Oct 2021 09:43:53 +0300 Subject: [PATCH 44/94] another typo fix --- .../providers/class-wp-webfonts-google-provider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 826285be0c272..8146a0c41abca 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -78,7 +78,7 @@ public function get_css() { * * @since 5.9.0 * - * @return array Collection by font-family urls. + * @return array Collection of font-family urls. */ protected function build_collection_api_urls() { $font_families_urls = array(); From 88fb4afed25689df07bf2541691407badc75e14f Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Tue, 26 Oct 2021 10:18:46 +0300 Subject: [PATCH 45/94] Don't check if provider is registered --- .../class-wp-webfonts-schema-validator.php | 8 ---- .../wpWebfontsSchemaValidator.php | 37 ------------------- 2 files changed, 45 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 0c97fef990c1c..8ebb2eb020747 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -144,14 +144,6 @@ private function is_valid_provider( array $webfont ) { return false; } - // Check if provider is registered. - $providers = wp_webfonts()->get_providers()->get_all_registered(); - if ( ! array_key_exists( $webfont['provider'], $providers ) ) { - trigger_error( __( 'Webfont provider is not registered.' ) ); - - return false; - } - return true; } diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index e6c93e3f30294..a143ea280e2aa 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -24,19 +24,6 @@ public function test_is_valid_schema_with_valid( array $webfont ) { $this->assertTrue( self::$validator->is_valid_schema( $webfont ) ); } - /** - * @covers WP_Webfonts_Registry::is_valid_schema - * - * @dataProvider data_is_unregistered_schema_provider - * - * @param array $webfont Webfont input. - */ - public function test_is_unregistered_schema_provider( array $webfont ) { - $this->expectNotice(); - $this->expectNoticeMessage( 'Webfont provider is not registered.' ); - $this->assertFalse( self::$validator->is_valid_schema( $webfont ) ); - } - /** * Data Provider. * @@ -55,22 +42,6 @@ public function data_is_valid_schema_with_valid() { ); } - /** - * Data Provider. - * - * return @array - */ - public function data_is_unregistered_schema_provider() { - return array( - 'unregistered provider' => array( - 'webfont' => array( - 'provider' => 'some-provider', - 'font-family' => 'Some Font', - ), - ), - ); - } - /** * @covers WP_Webfonts_Registry::is_valid_schema * @@ -123,14 +94,6 @@ public function data_is_valid_schema_with_invalid() { ), 'expected_message' => 'Webfont provider must be a non-empty string.', ), - 'provider: not registered' => array( - 'webfont' => array( - 'provider' => 'some-provider', - 'font-style' => 'normal', - 'font-weight' => '400', - ), - 'expected_message' => 'Webfont provider is not registered.', - ), 'font-family: not defined' => array( 'webfont' => array( 'provider' => 'local', From 699f96c996febc46adbaebe4a392baddece9ce9c Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 08:35:05 +0300 Subject: [PATCH 46/94] This is not necessary, the browser won't get a request for this URL. --- .../providers/class-wp-webfonts-google-provider.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 8146a0c41abca..589354171d052 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -40,11 +40,7 @@ class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { array( 'href' => 'https://fonts.gstatic.com', 'crossorigin' => true, - ), - array( - 'href' => 'https://fonts.googleapis.com', - 'crossorigin' => false, - ), + ) ); /** From e22ed3eee93b7d68f499f2731b0d19edb81980fe Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 09:59:39 +0300 Subject: [PATCH 47/94] CS --- .../providers/class-wp-webfonts-google-provider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 589354171d052..9fe59ea98e915 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -40,7 +40,7 @@ class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { array( 'href' => 'https://fonts.gstatic.com', 'crossorigin' => true, - ) + ), ); /** From 4757065c67a7128e0ca7a0f8c65c7f44bf485393 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 10:04:58 +0300 Subject: [PATCH 48/94] simplify configuration params --- .../class-wp-webfonts-schema-validator.php | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 8ebb2eb020747..28df9d53b6502 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -80,17 +80,6 @@ class WP_Webfonts_Schema_Validator { 'font-display' => 'fallback', ); - /** - * Configuration parameters. - * - * These are configuring the webfont but are not @font-face properties. - * - * @since 5.9.0 - * - * @var array - */ - protected $configuration_params = array( 'provider' ); - /** * Webfont being validated. * @@ -261,7 +250,7 @@ private function set_valid_font_face_property() { * Skip valid configuration parameters * (these are configuring the webfont but are not @font-face properties). */ - if ( in_array( $property, $this->configuration_params, true ) ) { + if ( 'provider' === $property || 'provider-params' === $property ) { continue; } From d2b2c918cdad2a14044cd50eec53fa72d388b673 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 10:31:46 +0300 Subject: [PATCH 49/94] Add docs to the register method --- .../class-wp-webfonts-registry.php | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index 15ee6d5511053..510997cfbd326 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -145,7 +145,34 @@ public function get_by_font_family( $font_family ) { * * @since 5.9.0 * - * @param array $webfont Webfont definition. + * @param array $webfont { + * Webfont definition. + * + * @type string $provider The provider ID (e.g. 'local', 'google'). + * @type string $fontFamily The @font-face font-family property. + * @type string $fontWeight The @font-face font-weight property. + * The font-weight can be a single value, or a range. + * If a single value, then the font-weight can either be + * a numeric value (400, 700, etc), or a word value (normal, bold, etc). + * If a range, then the font-weight can be a numeric range + * using 2 values, separated by a space ('100 700'). + * @type string $fontStyle The @font-face font-style property. + * The font-style can be a valid CSS value (normal, italic etc). + * @type string $fontDisplay The @font-face font-display property. + * Accepted values: 'auto', 'block', 'fallback', 'swap'. + * @type array|string $src The @font-face src property. + * The src can be a single URL, or an array of URLs. + * @type string $fontStretch The @font-face font-stretch property. + * @type string $fontVariant The @font-face font-variant property. + * @type string $fontFeatureSettings The @font-face font-feature-settings property. + * @type string $fontVariationSettings The @font-face font-variation-settings property. + * @type string $lineHeightOverride The @font-face line-gap-override property. + * @type string $sizeAdjust The @font-face size-adjust property. + * @type string $unicodeRange The @font-face unicode-range property. + * @type string $ascendOverride The @font-face ascend-override property. + * @type string $descendOverride The @font-face descend-override property. + * } + * * @return string Registration key. */ public function register( array $webfont ) { From 09742cac47bde940aaedc38c5090f06ec1be8d34 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 10:33:54 +0300 Subject: [PATCH 50/94] This was removed --- tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php index 641c8dc4ad46f..f69200db59bb4 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php @@ -113,7 +113,6 @@ public function data_get_links() { 'register_custom' => false, 'expected' => << - LINKS , @@ -122,7 +121,6 @@ public function data_get_links() { 'register_custom' => true, 'expected' => << - LINKS From 571c40dc9a58af7169f38a0718a7266e1d5e254f Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 10:50:28 +0300 Subject: [PATCH 51/94] Check if provider is a subclass of WP_Webfonts_Provider --- .../webfonts-api/class-wp-webfonts-provider-registry.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php index 32a0e681d701d..0d1695193855e 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -85,8 +85,8 @@ private function register_core_providers() { * @return bool True when registered. False when provider does not exist. */ public function register( $classname ) { - // If the class does not exist in memory, bail out. - if ( ! class_exists( $classname ) ) { + // If the class does not exist in memory, or is not a subclass of WP_Webfonts_Provider, bail out. + if ( ! class_exists( $classname ) || ! is_subclass_of( $classname, 'WP_Webfonts_Provider' ) ) { return false; } From 022467a6502c535dffa10e68433fe65e4dedbcb0 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 10:55:56 +0300 Subject: [PATCH 52/94] more docs --- src/wp-includes/webfonts-api.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/webfonts-api.php b/src/wp-includes/webfonts-api.php index 43431ec6db225..bad14058c05a2 100644 --- a/src/wp-includes/webfonts-api.php +++ b/src/wp-includes/webfonts-api.php @@ -41,7 +41,7 @@ function wp_webfonts() { * * @since 5.9.0 * - * @param string[][] $webfonts Webfonts to be registered. + * @param array $webfonts Webfonts to be registered. */ function wp_register_webfonts( array $webfonts ) { wp_webfonts()->register_webfonts( $webfonts ); @@ -52,7 +52,8 @@ function wp_register_webfonts( array $webfonts ) { * * @since 5.9.0 * - * @param string[] $webfont Webfont to be registered. + * @param array $webfont Webfont to be registered. + * See {@see WP_Webfonts_Registry::register()} for a list of supported arguments. */ function wp_register_webfont( array $webfont ) { wp_webfonts()->register_webfont( $webfont ); From 68f439a3d88ba11610d7207b71ab44f85aedcb69 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 10:58:05 +0300 Subject: [PATCH 53/94] more docs --- src/wp-includes/webfonts-api.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wp-includes/webfonts-api.php b/src/wp-includes/webfonts-api.php index bad14058c05a2..64ddab50ae55a 100644 --- a/src/wp-includes/webfonts-api.php +++ b/src/wp-includes/webfonts-api.php @@ -42,6 +42,8 @@ function wp_webfonts() { * @since 5.9.0 * * @param array $webfonts Webfonts to be registered. + * This contains ar array of webfonts to be registered. Each webfont is an array. + * See {@see WP_Webfonts_Registry::register()} for a list of supported arguments for each webfont. */ function wp_register_webfonts( array $webfonts ) { wp_webfonts()->register_webfonts( $webfonts ); From b2aef242e92e6779b645526fcc8f208ed5ce800b Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 11:07:16 +0300 Subject: [PATCH 54/94] rename the webfonts file. --- src/wp-includes/script-loader.php | 2 +- src/wp-includes/{webfonts-api.php => webfonts.php} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/wp-includes/{webfonts-api.php => webfonts.php} (100%) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index fa8676f0f8a50..47f1f53507117 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -35,7 +35,7 @@ require ABSPATH . WPINC . '/functions.wp-styles.php'; /** WordPress Webfonts Functions */ -require ABSPATH . WPINC . '/webfonts-api.php'; +require ABSPATH . WPINC . '/webfonts.php'; /** * Registers TinyMCE scripts. diff --git a/src/wp-includes/webfonts-api.php b/src/wp-includes/webfonts.php similarity index 100% rename from src/wp-includes/webfonts-api.php rename to src/wp-includes/webfonts.php From cf6ac7c8667e9538235df2be460a6b8380241a02 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 11:13:00 +0300 Subject: [PATCH 55/94] Remove get_by_font_family and related tests --- .../class-wp-webfonts-controller.php | 12 -- .../class-wp-webfonts-registry.php | 33 ----- .../webfonts-api/wpWebfontsController.php | 23 ---- .../tests/webfonts-api/wpWebfontsRegistry.php | 129 ------------------ 4 files changed, 197 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 4cf063fb066fa..2abee68e8c301 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -129,18 +129,6 @@ public function get_webfonts_by_provider( $provider_id ) { return $this->webfonts_registry->get_by_provider( $provider_id ); } - /** - * Gets the registered webfonts for the given font-family. - * - * @since 5.9.0 - * - * @param string $font_family Family font to fetch. - * @return array[] Registered webfonts. - */ - public function get_webfonts_by_font_family( $font_family ) { - return $this->webfonts_registry->get_by_font_family( $font_family ); - } - /** * Gets the registered providers. * diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index 510997cfbd326..4b44f4f549648 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -107,39 +107,6 @@ public function get_by_provider( $provider_id ) { return $webfonts; } - /** - * Gets the registered webfonts for the given font-family. - * - * @since 5.9.0 - * - * @param string $font_family Family font to fetch. - * @return array[] Registered webfonts. - */ - public function get_by_font_family( $font_family ) { - if ( ! is_string( $font_family ) || '' === $font_family ) { - return array(); - } - - $font_family_key = $this->convert_font_family_into_key( $font_family ); - - // If the font family is not registered, bail out. - if ( ! isset( $this->registry_by_font_family[ $font_family_key ] ) ) { - return array(); - } - - $webfonts = array(); - foreach ( $this->registry_by_font_family[ $font_family_key ] as $registration_key ) { - // Safeguard. Skip if not in registry. - if ( ! isset( $this->registered[ $registration_key ] ) ) { - continue; - } - - $webfonts[ $registration_key ] = $this->registered[ $registration_key ]; - } - - return $webfonts; - } - /** * Registers the given webfont if its schema is valid. * diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index 11a266993fa90..ede6653d376df 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -189,29 +189,6 @@ public function test_get_webfonts_by_provider() { $this->assertSame( $expected, $this->controller->get_webfonts_by_provider( $provider_id ) ); } - /** - * @covers WP_Webfonts_Controller::get_webfonts_by_font_family - */ - public function test_get_webfonts_by_font_family() { - $font_family = 'roboto'; - $expected = array( - 'roboto.normal.900' => array( - 'provider' => 'google', - 'font-family' => 'Roboto', - 'font-style' => 'normal', - 'font-weight' => '900', - ), - ); - - $this->webfont_registry_mock - ->expects( $this->once() ) - ->method( 'get_by_font_family' ) - ->with( $this->equalTo( $font_family ) ) - ->willReturn( $expected ); - - $this->assertSame( $expected, $this->controller->get_webfonts_by_font_family( $font_family ) ); - } - /** * @covers WP_Webfonts_Controller::get_registered_providers */ diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index 122a9da8aedd2..bfb50f824aae4 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -258,18 +258,6 @@ public function test_get_by_provider_when_does_not_exist() { $this->assertSame( array(), $this->registry->get_by_provider( 'my-custom-provider' ) ); } - /** - * Data Provider. - * - * return @array - */ - public function data_get_by_font_family_when_invalid_input() { - return array( - 'not a string' => array( null ), - 'empty string' => array( '' ), - ); - } - /** * As there are many moving parts to getting by provider, this test is an integration * test that does not mock. @@ -383,121 +371,4 @@ public function data_get_by_provider_integrated() { ), ); } - - /** - * @covers WP_Webfonts_Registry::get_by_font_family - * - * @dataProvider data_get_by_font_family_when_invalid_input - * - * @param string $font_family Given font-family for the query. - */ - public function test_get_by_font_family_when_invalid_input( $font_family ) { - $this->assertSame( array(), $this->registry->get_by_font_family( $font_family ) ); - } - - /** - * As there are many moving parts to getting by font-family, this test is an integration - * test that does not mock. - * - * @covers WP_Webfonts_Registry::get_by_font_family - * @covers WP_Webfonts_Registry::register - * - * @dataProvider data_get_by_font_family_integrated - * - * @param array $webfonts Given webfont to register. - * @param string $font_family Font family to query. - * @param array $expected Expected return value. - */ - public function test_get_by_font_family_integrated( array $webfonts, $font_family, $expected ) { - $registry = new WP_Webfonts_Registry( new WP_Webfonts_Schema_Validator() ); - - foreach ( $webfonts as $webfont ) { - $registry->register( $webfont ); - } - - $this->assertSame( $expected, $registry->get_by_font_family( $font_family ) ); - } - - /** - * Data Provider. - * - * return @array - */ - public function data_get_by_font_family_integrated() { - $webfonts = array( - array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - ), - array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '900', - ), - array( - 'provider' => 'google', - 'fontFamily' => 'Roboto', - 'fontStyle' => 'normal', - 'fontWeight' => '900', - ), - array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - ), - ); - - $expected = array( - 'open-sans.normal.400' => array( - 'provider' => 'google', - 'font-family' => 'Open Sans', - 'font-style' => 'normal', - 'font-weight' => '400', - 'font-display' => 'fallback', - ), - 'open-sans.normal.900' => array( - 'provider' => 'google', - 'font-family' => 'Open Sans', - 'font-style' => 'normal', - 'font-weight' => '900', - 'font-display' => 'fallback', - ), - 'open-sans.italic.400' => array( - 'provider' => 'google', - 'font-family' => 'Open Sans', - 'font-style' => 'italic', - 'font-weight' => '400', - 'font-display' => 'fallback', - ), - ); - - return array( - 'no webfonts for requested font-family' => array( - 'webfonts' => array( - array( - 'provider' => 'google', - 'fontFamily' => 'Lato', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - ), - ), - 'font-family' => 'Open Sans', - 'expected' => array(), - ), - 'given proper font family' => array( - 'webfonts' => $webfonts, - 'font-family' => 'Open Sans', - 'expected' => $expected, - ), - 'given font family slug' => array( - 'webfonts' => $webfonts, - 'font-family' => 'open-sans', - 'expected' => $expected, - ), - ); - } } From 46c87d5a91b9f9347643b3275adf91d26d2851dd Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 11:39:26 +0300 Subject: [PATCH 56/94] Move font-family quoting to css-generation method --- .../class-wp-webfonts-local-provider.php | 23 +++++++++---------- .../providers/wpWebfontsLocalProvider.php | 4 ++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index bae1e565cce69..aa91b4d51d36a 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -30,18 +30,7 @@ class WP_Webfonts_Local_Provider extends WP_Webfonts_Provider { * @return array */ protected function prepare( array $webfont ) { - $webfont = $this->order_src( $webfont ); - - // Wrap font-family in quotes if it contains spaces. - if ( - false !== strpos( $webfont['font-family'], ' ' ) && - false === strpos( $webfont['font-family'], '"' ) && - false === strpos( $webfont['font-family'], "'" ) - ) { - $webfont['font-family'] = '"' . $webfont['font-family'] . '"'; - } - - return $webfont; + return $this->order_src( $webfont ); } /** @@ -144,6 +133,16 @@ public function get_css() { */ private function build_font_css( array $webfont ) { $css = ''; + + // Wrap font-family in quotes if it contains spaces. + if ( + false !== strpos( $webfont['font-family'], ' ' ) && + false === strpos( $webfont['font-family'], '"' ) && + false === strpos( $webfont['font-family'], "'" ) + ) { + $webfont['font-family'] = '"' . $webfont['font-family'] . '"'; + } + foreach ( $webfont as $key => $value ) { // Compile the "src" parameter. diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php index f27a3eebffe7b..72fb0327a609b 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php @@ -78,7 +78,7 @@ public function test_set_webfonts() { $expected = array( 'source-serif-pro.normal.200 900' => array( 'provider' => 'local', - 'font-family' => '"Source Serif Pro"', + 'font-family' => 'Source Serif Pro', 'font-style' => 'normal', 'font-weight' => '200 900', 'font-stretch' => 'normal', @@ -91,7 +91,7 @@ public function test_set_webfonts() { ), 'source-serif-pro.italic.200 900' => array( 'provider' => 'local', - 'font-family' => '"Source Serif Pro"', + 'font-family' => 'Source Serif Pro', 'font-style' => 'italic', 'font-weight' => '200 900', 'font-stretch' => 'normal', From 5bf5f66cd9de005373275e86c2ff070a3af98866 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 12:40:07 +0300 Subject: [PATCH 57/94] Simplify the controller --- .../class-wp-webfonts-controller.php | 78 +------- src/wp-includes/webfonts.php | 15 +- .../webfonts-api/wpWebfontsController.php | 167 ------------------ 3 files changed, 14 insertions(+), 246 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 2abee68e8c301..5d7fe8b805a16 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -79,79 +79,6 @@ public function init() { add_action( 'admin_init', array( $this, 'generate_and_enqueue_editor_styles' ) ); } - /** - * Registers a webfont collection. - * - * @since 5.9.0 - * - * @param string[][] $webfonts Webfonts to be registered. - */ - public function register_webfonts( array $webfonts ) { - // Bail out if no webfonts collection was injected. - if ( empty( $webfonts ) ) { - return; - } - - array_walk( $webfonts, array( $this, 'register_webfont' ) ); - } - - /** - * Registers the given webfont if its schema is valid. - * - * @since 5.9.0 - * - * @param string[] $webfont Webfont definition. - */ - public function register_webfont( array $webfont ) { - $this->webfonts_registry->register( $webfont ); - } - - /** - * Gets the registered webfonts. - * - * @since 5.9.0 - * - * @return array[] Registered webfonts. - */ - public function get_webfonts() { - return $this->webfonts_registry->get_all_registered(); - } - - /** - * Gets the registered webfonts for the given provider organized by font-family. - * - * @since 5.9.0 - * - * @param string $provider_id Provider ID to fetch. - * @return array[] Registered webfonts. - */ - public function get_webfonts_by_provider( $provider_id ) { - return $this->webfonts_registry->get_by_provider( $provider_id ); - } - - /** - * Gets the registered providers. - * - * @since 5.9.0 - * - * @return WP_Webfonts_Provider[] Registered providers. - */ - public function get_registered_providers() { - return $this->providers->get_all_registered(); - } - - /** - * Registers the given provider. - * - * @since 5.9.0 - * - * @param string $classname The provider class name. - * @return bool True when registered. False when provider does not exist. - */ - public function register_provider( $classname ) { - return $this->providers->register( $classname ); - } - /** * Generate and enqueue webfonts styles. * @@ -186,8 +113,9 @@ public function generate_and_enqueue_editor_styles() { * @return string $styles Generated styles. */ private function generate_styles() { - $styles = ''; - foreach ( $this->get_registered_providers() as $provider_id => $provider ) { + $styles = ''; + $providers = $this->get_providers()->get_all_registered(); + foreach ( $providers as $provider_id => $provider ) { $registered_webfonts = $this->webfonts_registry->get_by_provider( $provider_id ); if ( empty( $registered_webfonts ) ) { diff --git a/src/wp-includes/webfonts.php b/src/wp-includes/webfonts.php index 64ddab50ae55a..5473952af71db 100644 --- a/src/wp-includes/webfonts.php +++ b/src/wp-includes/webfonts.php @@ -46,7 +46,14 @@ function wp_webfonts() { * See {@see WP_Webfonts_Registry::register()} for a list of supported arguments for each webfont. */ function wp_register_webfonts( array $webfonts ) { - wp_webfonts()->register_webfonts( $webfonts ); + // Bail out if the webfonts collection is empty. + if ( empty( $webfonts ) ) { + return; + } + + foreach ( $webfonts as $webfont ) { + wp_webfonts()->get_webfonts_registry()->register( $webfont ); + } } /** @@ -58,7 +65,7 @@ function wp_register_webfonts( array $webfonts ) { * See {@see WP_Webfonts_Registry::register()} for a list of supported arguments. */ function wp_register_webfont( array $webfont ) { - wp_webfonts()->register_webfont( $webfont ); + wp_webfonts()->get_webfonts_registry()->register( $webfont ); } /** @@ -69,7 +76,7 @@ function wp_register_webfont( array $webfont ) { * @param string $classname The provider class name. */ function wp_register_webfont_provider( $classname ) { - wp_webfonts()->register_provider( $classname ); + wp_webfonts()->get_providers()->register( $classname ); } /** @@ -80,5 +87,5 @@ function wp_register_webfont_provider( $classname ) { * @return WP_Webfonts_Provider[] Array of registered providers. */ function wp_get_webfont_providers() { - return wp_webfonts()->get_registered_providers(); + return wp_webfonts()->get_providers()->get_all_registered(); } diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index ede6653d376df..4793543ff3b40 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -77,154 +77,6 @@ public function data_init() { ); } - /** - * @covers WP_Webfonts_Controller::register_webfonts - */ - public function test_register_webfonts_with_empty_schema() { - $webfonts = array(); - - $this->webfont_registry_mock - ->expects( $this->never() ) - ->method( 'register' ); - - $this->controller->register_webfonts( $webfonts ); - } - - /** - * @covers WP_Webfonts_Controller::register_webfonts - * @covers WP_Webfonts_Controller::register_webfont - */ - public function test_register_webfonts() { - $webfonts = array( - 'open-sans.normal.400' => array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - ), - 'open-sans.italic.700' => array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'italic', - 'fontWeight' => '700', - ), - 'roboto.normal.900' => array( - 'provider' => 'google', - 'fontFamily' => 'Roboto', - 'fontStyle' => 'normal', - 'fontWeight' => '900', - ), - ); - - $this->mock_register_webfonts( $webfonts ); - - $this->controller->register_webfonts( $webfonts ); - } - - /** - * @covers WP_Webfonts_Controller::get_webfonts - */ - public function test_get_webfonts() { - $expected = array( - 'open-sans.normal.400' => array( - 'provider' => 'google', - 'font-family' => 'Open Sans', - 'font-style' => 'normal', - 'font-weight' => '400', - ), - 'open-sans.italic.700' => array( - 'provider' => 'google', - 'font-family' => 'Open Sans', - 'font-style' => 'italic', - 'fontWeight' => '700', - ), - 'roboto.normal.900' => array( - 'provider' => 'google', - 'font-family' => 'Roboto', - 'font-style' => 'normal', - 'font-weight' => '900', - ), - ); - - $this->webfont_registry_mock - ->expects( $this->once() ) - ->method( 'get_all_registered' ) - ->willReturn( $expected ); - - $this->assertSame( $expected, $this->controller->get_webfonts() ); - } - - /** - * @covers WP_Webfonts_Controller::get_webfonts_by_provider - */ - public function test_get_webfonts_by_provider() { - $provider_id = 'google'; - $expected = array( - 'open-sans.normal.400' => array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - ), - 'open-sans.italic.700' => array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'italic', - 'fontWeight' => '700', - ), - 'roboto.normal.900' => array( - 'provider' => 'google', - 'fontFamily' => 'Roboto', - 'fontStyle' => 'normal', - 'fontWeight' => '900', - ), - ); - - $this->webfont_registry_mock - ->expects( $this->once() ) - ->method( 'get_by_provider' ) - ->with( $this->equalTo( $provider_id ) ) - ->willReturn( $expected ); - - $this->assertSame( $expected, $this->controller->get_webfonts_by_provider( $provider_id ) ); - } - - /** - * @covers WP_Webfonts_Controller::get_registered_providers - */ - public function test_get_registered_providers() { - $expected = array( - 'my-custom-provider' => new My_Custom_Webfonts_Provider_Mock(), - ); - - $this->provider_registry_mock - ->expects( $this->once() ) - ->method( 'get_all_registered' ) - ->willReturn( $expected ); - - $actual = $this->controller->get_registered_providers(); - - $this->assertIsArray( $actual ); - $this->assertCount( 1, $actual ); - $this->assertArrayHasKey( 'my-custom-provider', $actual ); - $this->assertInstanceOf( 'My_Custom_Webfonts_Provider_Mock', $actual['my-custom-provider'] ); - } - - /** - * @covers WP_Webfonts_Controller::register_provider - */ - public function test_register_provider() { - $classname = My_Custom_Webfonts_Provider_Mock::class; - - $this->provider_registry_mock - ->expects( $this->once() ) - ->method( 'register' ) - ->with( $this->equalTo( $classname ) ) - ->willReturn( true ); - - $this->assertTrue( $this->controller->register_provider( $classname ) ); - } - /** * @covers WP_Webfonts_Controller::generate_and_enqueue_styles * @covers WP_Webfonts_Controller::generate_and_enqueue_editor_styles @@ -299,23 +151,4 @@ public function data_generate_and_enqueue_editor_styles() { 'for wp_print_footer_scripts' => array( 'webfonts-footer' ), ); } - - /** - * Mocks WP_Webfonts_Provider::register(). - * - * @since 5.9.0 - * - * @param array $webfonts Webfonts to register. - */ - private function mock_register_webfonts( array $webfonts ) { - $register_webfonts = array(); - foreach ( $webfonts as $webfont ) { - $register_webfonts[] = array( $this->equalTo( $webfont ) ); - } - - $this->webfont_registry_mock - ->expects( $this->exactly( count( $webfonts ) ) ) - ->method( 'register' ) - ->withConsecutive( ...$register_webfonts ); - } } From e31dee58fefb3e78fe02f63c212473195917837a Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 12:44:51 +0300 Subject: [PATCH 58/94] Move require_once to script_loader.php --- src/wp-includes/script-loader.php | 8 ++++++-- src/wp-includes/webfonts.php | 5 ----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 47f1f53507117..ac5c3b8bf4638 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -34,8 +34,12 @@ /** WordPress Styles Functions */ require ABSPATH . WPINC . '/functions.wp-styles.php'; -/** WordPress Webfonts Functions */ -require ABSPATH . WPINC . '/webfonts.php'; +/** WordPress Webfonts Classes & Functions */ +require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-schema-validator.php'; +require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-registry.php'; +require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-provider-registry.php'; +require_once ABSPATH . WPINC . '/webfonts-api/class-wp-webfonts-controller.php'; +require_once ABSPATH . WPINC . '/webfonts.php'; /** * Registers TinyMCE scripts. diff --git a/src/wp-includes/webfonts.php b/src/wp-includes/webfonts.php index 5473952af71db..70b35432f2ee5 100644 --- a/src/wp-includes/webfonts.php +++ b/src/wp-includes/webfonts.php @@ -19,11 +19,6 @@ function wp_webfonts() { static $instance; if ( ! $instance instanceof WP_Webfonts ) { - require_once __DIR__ . '/webfonts-api/class-wp-webfonts-schema-validator.php'; - require_once __DIR__ . '/webfonts-api/class-wp-webfonts-registry.php'; - require_once __DIR__ . '/webfonts-api/class-wp-webfonts-provider-registry.php'; - require_once __DIR__ . '/webfonts-api/class-wp-webfonts-controller.php'; - $instance = new WP_Webfonts_Controller( new WP_Webfonts_Registry( new WP_Webfonts_Schema_Validator() From 9ae7f9ae1a5f2b7151b4ee8fb68efc0b101eff77 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 12:51:46 +0300 Subject: [PATCH 59/94] add missing "since" to classes --- src/wp-includes/webfonts-api/class-wp-webfonts-controller.php | 2 ++ .../webfonts-api/class-wp-webfonts-provider-registry.php | 2 ++ src/wp-includes/webfonts-api/class-wp-webfonts-registry.php | 2 ++ .../webfonts-api/class-wp-webfonts-schema-validator.php | 2 ++ .../providers/class-wp-webfonts-google-provider.php | 2 ++ .../webfonts-api/providers/class-wp-webfonts-local-provider.php | 2 ++ .../webfonts-api/providers/class-wp-webfonts-provider.php | 2 ++ 7 files changed, 14 insertions(+) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 5d7fe8b805a16..83359877564af 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -11,6 +11,8 @@ * Webfonts Controller. * * Receives the incoming requests and handles the processing. + * + * @since 5.9.0 */ class WP_Webfonts_Controller { diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php index 0d1695193855e..eef6a436276e7 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -18,6 +18,8 @@ * 1. creating an instance (object); * 2. storing it in-memory (by its unique provider ID) for use with the API; * - handles generating the linked resources `` for all providers. + * + * @since 5.9.0 */ class WP_Webfonts_Provider_Registry { diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index 4b44f4f549648..5cad6bd002373 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -18,6 +18,8 @@ * 1. creating an instance (object); * 2. storing it in-memory (by its unique provider ID) for use with the API; * - handles generating the linked resources `` for all providers. + * + * @since 5.9.0 */ class WP_Webfonts_Registry { diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 28df9d53b6502..50c172e8c3cfc 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -11,6 +11,8 @@ * Webfonts Schema Validator. * * Validates the webfont schema. + * + * @since 5.9.0 */ class WP_Webfonts_Schema_Validator { diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 9fe59ea98e915..22730d7e60494 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -9,6 +9,8 @@ /** * Webfonts API provider for Google Fonts. + * + * @since 5.9.0 */ class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index aa91b4d51d36a..1add40c0a1b41 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -9,6 +9,8 @@ /** * Webfonts API provider for locally-hosted fonts. + * + * @since 5.9.0 */ class WP_Webfonts_Local_Provider extends WP_Webfonts_Provider { diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index 354fdae03c9c4..52abc29d2da24 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -11,6 +11,8 @@ /** * Abstract class for Webfonts API providers. + * + * @since 5.9.0 */ abstract class WP_Webfonts_Provider { From 0883e97fe429b0551c6009540855f0db324fec98 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 13:32:15 +0300 Subject: [PATCH 60/94] Simplify the preconnect-URLs implementation --- .../class-wp-webfonts-controller.php | 11 -- .../class-wp-webfonts-provider-registry.php | 103 ------------------ .../class-wp-webfonts-registry.php | 1 - .../class-wp-webfonts-google-provider.php | 63 +++++++---- .../providers/class-wp-webfonts-provider.php | 28 ----- .../wpWebfontsProviderRegistry.php | 48 -------- 6 files changed, 43 insertions(+), 211 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 83359877564af..279560ac112fc 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -124,23 +124,12 @@ private function generate_styles() { continue; } - add_action( 'wp_head', array( $this, 'render_links' ) ); - $provider->set_webfonts( $registered_webfonts ); $styles .= $provider->get_css(); } return $styles; } - /** - * Renders the HTML `` for each provider into `` for enqueued webfonts. - * - * @since 5.9.0 - */ - public function render_links() { - echo $this->providers->get_links(); - } - /** * Get the webfonts registry. * diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php index eef6a436276e7..8727ff004a191 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -17,7 +17,6 @@ * - registers each provider with the API by: * 1. creating an instance (object); * 2. storing it in-memory (by its unique provider ID) for use with the API; - * - handles generating the linked resources `` for all providers. * * @since 5.9.0 */ @@ -106,106 +105,4 @@ public function register( $classname ) { return true; } - - /** - * Gets the HTML `` for each provider. - * - * @since 5.9.0 - * - * @return string HTML links for each provider. - */ - public function get_links() { - /* - * Store each `` by its provider ID. Why? - * To ensure only one link is created per provider. - */ - static $links = array(); - - foreach ( $this->get_all_registered() as $provider_id => $provider ) { - // Skip if the provider already added the link. - if ( isset( $links[ $provider_id ] ) ) { - continue; - } - - $links[ $provider_id ] = $this->generate_links( $provider ); - } - - // Combine `` elements and return them as a string. - return implode( '', $links ); - } - - /** - * Generate the ` element(s) for the given provider. - * - * @since 5.9.0 - * - * @param WP_Webfonts_Provider $provider Instance of the provider. - * @return string The `` element(s). - */ - private function generate_links( WP_Webfonts_Provider $provider ) { - $link_attributes = $provider->get_link_attributes(); - - /* - * Bail out if there are no attributes for this provider - * (i.e. no `` is needed). - */ - if ( ! is_array( $link_attributes ) || empty( $link_attributes ) ) { - return ''; - } - - /* - * This provider needs multiple `` elements. - * Loop through each array and pass its attributes - * to create each of its `` elements. - */ - if ( is_array( current( $link_attributes ) ) ) { - $links = ''; - foreach ( $link_attributes as $attributes ) { - $links .= $this->create_link_element( $attributes ); - } - - return $links; - } - - /* - * This provider needs one `` element. - * Pass its attributes to create its `` element. - */ - return $this->create_link_element( $link_attributes ); - } - - /** - * Creates the `` element and populates with the given attributes. - * - * @since 5.9.0 - * - * @param string[] $attributes An array of attributes => values. - * @return string The `` element. - */ - private function create_link_element( array $attributes ) { - $link = ''; - - foreach ( $attributes as $attribute => $value ) { - // Checks if attribute is a nonempty string. If no, skip it. - if ( ! is_string( $attribute ) || '' === $attribute ) { - continue; - } - - if ( 'href' === $attribute ) { - $link .= ' href="' . esc_url( $value ) . '"'; - } elseif ( is_bool( $value ) ) { - $link .= $value - ? ' ' . esc_attr( $attribute ) - : ''; - } else { - $link .= ' ' . esc_attr( $attribute ) . '="' . esc_attr( $value ) . '"'; - } - } - - if ( '' === $link ) { - return ''; - } - - return '' . "\n"; - } } diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index 5cad6bd002373..b61eb3902a69b 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -17,7 +17,6 @@ * - registers each provider with the API by: * 1. creating an instance (object); * 2. storing it in-memory (by its unique provider ID) for use with the API; - * - handles generating the linked resources `` for all providers. * * @since 5.9.0 */ diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 22730d7e60494..99f9c6b46e873 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -24,34 +24,23 @@ class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { protected $id = 'google'; /** - * The `` element's attributes for each linked resource. + * The provider's root URL. * * @since 5.9.0 - * - * @var array[] { - * An array of linked resources. - * - * @type array() { - * An array of attributes for this linked resource. - * - * @type string $attribute => @type string $attribute_value - * } - * } + * @var string */ - protected $link_attributes = array( - array( - 'href' => 'https://fonts.gstatic.com', - 'crossorigin' => true, - ), - ); + protected $root_url = 'https://fonts.googleapis.com/css2'; /** - * The provider's root URL. + * The object's constructor. * * @since 5.9.0 - * @var string */ - protected $root_url = 'https://fonts.googleapis.com/css2'; + public function __construct() { + + // Add preconnect links. + add_filter( 'wp_resource_hints', array( $this, 'add_preconnect_urls' ), 10, 2 ); + } /** * Get the CSS for a collection of fonts. @@ -160,4 +149,38 @@ private function organize_webfonts() { return $font_display_groups; } + + /** + * Adds preconnect URLs for webfonts providers. + * + * @since 5.9.0 + * + * @param array $urls { + * Array of resources and their attributes, or URLs to print for resource hints. + * + * @type array|string ...$0 { + * Array of resource attributes, or a URL string. + * + * @type string $href URL to include in resource hints. Required. + * @type string $as How the browser should treat the resource + * (`script`, `style`, `image`, `document`, etc). + * @type string $crossorigin Indicates the CORS policy of the specified resource. + * @type float $pr Expected probability that the resource hint will be used. + * @type string $type Type of the resource (`text/html`, `text/css`, etc). + * } + * } + * @param string $relation_type The relation type the URLs are printed for, + * e.g. 'preconnect' or 'prerender'. + */ + public function add_preconnect_urls( $urls, $relation_type ) { + if ( 'preconnect' !== $relation_type ) { + return $urls; + } + + $urls[] = array( + 'href' => 'https://fonts.gstatic.com', + 'crossorigin' => 'anonymous', + ); + return $urls; + } } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index 52abc29d2da24..f525476202b41 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -25,23 +25,6 @@ abstract class WP_Webfonts_Provider { */ protected $id; - /** - * The `` element's attributes for each linked resource. - * - * @since 5.9.0 - * - * @var array[] { - * An array of linked resources. - * - * @type array() { - * An array of attributes for this linked resource. - * - * @type string $attribute => @type string $attribute_value - * } - * } - */ - protected $link_attributes = array(); - /** * The provider's root URL. * @@ -82,17 +65,6 @@ public function get_root_url() { return $this->root_url; } - /** - * Get the `` attributes. - * - * @since 5.9.0 - * - * @return array[] - */ - public function get_link_attributes() { - return $this->link_attributes; - } - /** * Sets the webfonts. * diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php index f69200db59bb4..f5cc69270b153 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsProviderRegistry.php @@ -80,52 +80,4 @@ public function test_register_with_core_providers() { $expected = array( 'google', 'local', 'my-custom-provider' ); $this->assertSame( $expected, array_keys( $providers ) ); } - - /** - * @covers WP_Webfonts_Provider_Registry::get_links - * - * @dataProvider data_get_links - * - * @param bool $register_custom When true, registers the custom provider. - * @param string $expected Expected HTML. - */ - public function test_get_links( $register_custom, $expected ) { - $registry = new WP_Webfonts_Provider_Registry(); - // Register the core providers. - $registry->init(); - // Register a custom provider. - if ( $register_custom ) { - $registry->register( My_Custom_Webfonts_Provider_Mock::class ); - } - - $actual = $registry->get_links(); - $this->assertSame( $expected, $actual ); - } - - /** - * Data Provider. - * - * return @array - */ - public function data_get_links() { - return array( - 'core providers' => array( - 'register_custom' => false, - 'expected' => << - -LINKS - , - ), - 'core + custom providers' => array( - 'register_custom' => true, - 'expected' => << - - -LINKS - , - ), - ); - } } From 2d0274b145316ee1605217c0cbba75113ad61d42 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Wed, 27 Oct 2021 14:00:38 +0300 Subject: [PATCH 61/94] minify styles if not debugging scripts --- .../providers/class-wp-webfonts-local-provider.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index 1add40c0a1b41..b8ab2937ea740 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -158,7 +158,9 @@ private function build_font_css( array $webfont ) { } if ( ! empty( $value ) ) { - $css .= "\t$key:$value;\n"; + $css .= ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) + ? "\t$key:$value;\n" + : "$key:$value"; } } From 28445b32db02b17973fa2c99a122bd52ada15bc2 Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Wed, 27 Oct 2021 08:15:31 -0500 Subject: [PATCH 62/94] Removes store by font-family during register. Removes the property and storage logic for the query by font-family. --- .../class-wp-webfonts-registry.php | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index b61eb3902a69b..d06228cafd010 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -48,25 +48,21 @@ class WP_Webfonts_Registry { private $registry_by_provider = array(); /** - * Registration keys per font-family. - * - * Provides a O(1) lookup when querying by provider. + * Schema validator. * * @since 5.9.0 * - * @var string[] + * @var WP_Webfonts_Schema_Validator */ - private $registry_by_font_family = array(); + private $validator; /** - * Schema validator. + * Creates the registry. * * @since 5.9.0 * - * @var WP_Webfonts_Schema_Validator + * @param WP_Webfonts_Schema_Validator $validator Instance of the validator. */ - private $validator; - public function __construct( WP_Webfonts_Schema_Validator $validator ) { $this->validator = $validator; } @@ -160,37 +156,31 @@ public function register( array $webfont ) { } $this->registered[ $registration_key ] = $webfont; - $this->store_in_query_by_containers( $webfont, $registration_key ); + $this->store_for_query_by( $webfont, $registration_key ); return $registration_key; } /** - * Store the webfont into each query by container. + * Store the webfont for query by request. * - * These containers provide a performant way to quickly query webfonts by - * provider or font-family. The registration keys are stored in each for - * O(1) lookup. + * This container provides a performant way to quickly query webfonts by + * provider. The registration keys are stored for O(1) lookup. * * @since 5.9.0 * * @param array $webfont Webfont definition. * @param string $registration_key Webfont's registration key. */ - private function store_in_query_by_containers( array $webfont, $registration_key ) { - $font_family = $this->convert_font_family_into_key( $webfont['font-family'] ); - $provider = $webfont['provider']; + private function store_for_query_by( array $webfont, $registration_key ) { + $provider = $webfont['provider']; - // Initialize the arrays if they do not exist. + // Initialize the array if it does not exist. if ( ! isset( $this->registry_by_provider[ $provider ] ) ) { $this->registry_by_provider[ $provider ] = array(); } - if ( ! isset( $this->registry_by_font_family[ $font_family ] ) ) { - $this->registry_by_font_family[ $font_family ] = array(); - } - $this->registry_by_provider[ $provider ][] = $registration_key; - $this->registry_by_font_family[ $font_family ][] = $registration_key; + $this->registry_by_provider[ $provider ][] = $registration_key; } /** @@ -198,7 +188,7 @@ private function store_in_query_by_containers( array $webfont, $registration_key * * @since 5.9.0 * - * @param string[] $webfont Webfont definition. + * @param array $webfont Webfont definition. * @return array Webfont with kebab_case parameters (keys). */ private function convert_to_kebab_case( array $webfont ) { @@ -236,7 +226,7 @@ private function generate_registration_key( array $webfont ) { * @since 5.9.0 * * @param string $font_family Font family to convert into a key. - * @return string + * @return string Font-family as a key. */ private function convert_font_family_into_key( $font_family ) { if ( ! is_string( $font_family ) || '' === $font_family ) { From aff4233dbbbd5d16711e1aef97e7fb16b074e7c0 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Mon, 1 Nov 2021 10:07:59 +0200 Subject: [PATCH 63/94] Abstracts generating resources hints. --- .../class-wp-webfonts-controller.php | 44 ++++++++++++++++ .../class-wp-webfonts-google-provider.php | 51 ++++--------------- .../providers/class-wp-webfonts-provider.php | 24 +++++++++ 3 files changed, 79 insertions(+), 40 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 279560ac112fc..1ff1247800ff9 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -79,6 +79,9 @@ public function init() { // Enqueue webfonts in the block editor. add_action( 'admin_init', array( $this, 'generate_and_enqueue_editor_styles' ) ); + + // Add resources hints. + add_filter( 'wp_resource_hints', array( $this, 'get_resource_hints' ), 10, 2 ); } /** @@ -151,4 +154,45 @@ public function get_webfonts_registry() { public function get_providers() { return $this->providers; } + + /** + * Get the resource hints. + * + * @since 5.9.0 + * + * @param array $urls { + * Array of resources and their attributes, or URLs to print for resource hints. + * + * @type array|string ...$0 { + * Array of resource attributes, or a URL string. + * + * @type string $href URL to include in resource hints. Required. + * @type string $as How the browser should treat the resource + * (`script`, `style`, `image`, `document`, etc). + * @type string $crossorigin Indicates the CORS policy of the specified resource. + * @type float $pr Expected probability that the resource hint will be used. + * @type string $type Type of the resource (`text/html`, `text/css`, etc). + * } + * } + * @param string $relation_type The relation type the URLs are printed for, + * e.g. 'preconnect' or 'prerender'. + * + * @return array URLs to print for resource hints. + */ + public function get_resource_hints( $urls, $relation_type ) { + $providers = $this->get_providers()->get_all_registered(); + foreach ( $providers as $provider_id => $provider ) { + $hints = $provider->get_resource_hints(); + foreach ( $hints as $relation => $relation_hints ) { + if ( $relation !== $relation_type ) { + continue; + } + foreach ( $relation_hints as $hint ) { + $urls[] = $hint; + } + } + } + + return $urls; + } } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 99f9c6b46e873..9aceaab34837a 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -32,15 +32,20 @@ class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { protected $root_url = 'https://fonts.googleapis.com/css2'; /** - * The object's constructor. + * Array of resources hints. * * @since 5.9.0 + * + * @var array */ - public function __construct() { - - // Add preconnect links. - add_filter( 'wp_resource_hints', array( $this, 'add_preconnect_urls' ), 10, 2 ); - } + protected $resource_hints = array( + 'preconnect' => array( + array( + 'href' => 'https://fonts.gstatic.com', + 'crossorigin' => 'anonymous', + ), + ), + ); /** * Get the CSS for a collection of fonts. @@ -149,38 +154,4 @@ private function organize_webfonts() { return $font_display_groups; } - - /** - * Adds preconnect URLs for webfonts providers. - * - * @since 5.9.0 - * - * @param array $urls { - * Array of resources and their attributes, or URLs to print for resource hints. - * - * @type array|string ...$0 { - * Array of resource attributes, or a URL string. - * - * @type string $href URL to include in resource hints. Required. - * @type string $as How the browser should treat the resource - * (`script`, `style`, `image`, `document`, etc). - * @type string $crossorigin Indicates the CORS policy of the specified resource. - * @type float $pr Expected probability that the resource hint will be used. - * @type string $type Type of the resource (`text/html`, `text/css`, etc). - * } - * } - * @param string $relation_type The relation type the URLs are printed for, - * e.g. 'preconnect' or 'prerender'. - */ - public function add_preconnect_urls( $urls, $relation_type ) { - if ( 'preconnect' !== $relation_type ) { - return $urls; - } - - $urls[] = array( - 'href' => 'https://fonts.gstatic.com', - 'crossorigin' => 'anonymous', - ); - return $urls; - } } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index f525476202b41..efe47ebf717ae 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -43,6 +43,19 @@ abstract class WP_Webfonts_Provider { */ protected $webfonts = array(); + /** + * Array of resources hints. + * + * Keyed by relation-type: + * + * @type string $key => @type array resource hint. + * + * @since 5.9.0 + * + * @var array + */ + protected $resource_hints = array(); + /** * Get the provider's unique ID. * @@ -169,4 +182,15 @@ public function get_remote_styles( $url, array $args = array() ) { // Get the response body. return wp_remote_retrieve_body( $response ); } + + /** + * Get the provider's resource hints. + * + * @since 5.9.0 + * + * @return array + */ + public function get_resource_hints() { + return $this->resource_hints; + } } From 1ec9d692821950dfff124ba0df1de659b458f820 Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Wed, 3 Nov 2021 14:44:55 -0500 Subject: [PATCH 64/94] Fix method parameter name typo. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sören Wrede --- tests/phpunit/tests/webfonts-api/wpWebfontsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index 4793543ff3b40..cb53b5e9c2afe 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -85,7 +85,7 @@ public function data_init() { * * @param string $stylestyle_handle Handle for the registered stylesheet. */ - public function test_generate_and_enqueue_editor_styles( $stylestyle_handle ) { + public function test_generate_and_enqueue_editor_styles( $stylesheet_handle ) { /* * Set the stylesheet_handle property. * This is set in WP_Webfonts_Controller::init(); however, init is not part From 6b08c0aa4502af8429f7557f0f9c4943b8b23f75 Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Wed, 3 Nov 2021 14:47:40 -0500 Subject: [PATCH 65/94] Fix variable name typo. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sören Wrede --- .../providers/class-wp-webfonts-google-provider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 9aceaab34837a..6bed1c859c8ac 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -124,7 +124,7 @@ private function organize_webfonts() { * Each font-display will need to be a separate request. */ foreach ( $this->webfonts as $webfont ) { - if ( ! isset( $font['font-display'] ) ) { + if ( ! isset( $webfont['font-display'] ) ) { $webfont['font-display'] = 'fallback'; } From a9c62fc3e8512ee0b64469e475b60cc6a7380c58 Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Wed, 3 Nov 2021 14:49:19 -0500 Subject: [PATCH 66/94] Remove unused key variable from `foreach`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sören Wrede --- src/wp-includes/webfonts-api/class-wp-webfonts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 1ff1247800ff9..2f9237c05c9b9 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -181,7 +181,7 @@ public function get_providers() { */ public function get_resource_hints( $urls, $relation_type ) { $providers = $this->get_providers()->get_all_registered(); - foreach ( $providers as $provider_id => $provider ) { + foreach ( $providers as $provider ) { $hints = $provider->get_resource_hints(); foreach ( $hints as $relation => $relation_hints ) { if ( $relation !== $relation_type ) { From b0e02966553bd5d3a0756800fe22d14e7b69a09a Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Wed, 3 Nov 2021 15:17:09 -0500 Subject: [PATCH 67/94] Fixes to tests from code review. --- .../class-my-custom-webfonts-provider-mock.php | 10 +++++++--- .../tests/webfonts-api/wpWebfontsController.php | 6 +++--- .../webfonts-api/wpWebfontsSchemaValidator.php | 14 +++++++------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php b/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php index f1b3e8ce319bf..e12051a033333 100644 --- a/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php +++ b/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php @@ -5,9 +5,13 @@ class My_Custom_Webfonts_Provider_Mock extends WP_Webfonts_Provider { protected $id = 'my-custom-provider'; - protected $link_attributes = array( - 'href' => 'https://fonts.my-custom-api.com', - 'crossorigin' => true, + protected $resource_hints = array( + 'preconnect' => array( + array( + 'href' => 'https://fonts.my-custom-api.com', + 'crossorigin' => 'anonymous', + ), + ), ); public function set_webfonts( array $webfonts ) { diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index cb53b5e9c2afe..7da4728a1f3f8 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -93,7 +93,7 @@ public function test_generate_and_enqueue_editor_styles( $stylesheet_handle ) { */ $property = new ReflectionProperty( $this->controller, 'stylesheet_handle' ); $property->setAccessible( true ); - $property->setValue( $this->controller, $stylestyle_handle ); + $property->setValue( $this->controller, $stylesheet_handle ); // Set up the provider mock. $provider = new My_Custom_Webfonts_Provider_Mock(); @@ -133,11 +133,11 @@ public function test_generate_and_enqueue_editor_styles( $stylesheet_handle ) { * As this method adds an inline style, the test needs to print it. * Print the webfont styles and test the output matches expectation. */ - $expected = "\n"; $this->expectOutputString( $expected ); - wp_print_styles( $stylestyle_handle ); + wp_print_styles( $stylesheet_handle ); } /** diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index a143ea280e2aa..8b3a100eb22a9 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -14,7 +14,7 @@ public static function set_up_before_class() { } /** - * @covers WP_Webfonts_Registry::is_valid_schema + * @covers WP_Webfonts_Schema_Validator::is_valid_schema * * @dataProvider data_is_valid_schema_with_valid * @@ -43,7 +43,7 @@ public function data_is_valid_schema_with_valid() { } /** - * @covers WP_Webfonts_Registry::is_valid_schema + * @covers WP_Webfonts_Schema_Validator::is_valid_schema * * @dataProvider data_is_valid_schema_with_invalid * @@ -173,7 +173,7 @@ public function data_is_valid_schema_with_invalid() { } /** - * @covers WP_Webfonts_Registry::set_valid_properties + * @covers WP_Webfonts_Schema_Validator::set_valid_properties * * @dataProvider data_set_valid_properties_with_valid_input * @@ -295,7 +295,7 @@ public function data_set_valid_properties_with_valid_input() { } /** - * @covers WP_Webfonts_Registry::set_valid_properties + * @covers WP_Webfonts_Schema_Validator::set_valid_properties * * @dataProvider data_set_valid_properties_with_invalid_input * @@ -383,7 +383,7 @@ public function data_set_valid_properties_with_invalid_input() { } /** - * @covers WP_Webfonts_Registry::set_valid_properties + * @covers WP_Webfonts_Schema_Validator::set_valid_properties * * @dataProvider data_set_valid_properties_with_invalid_and_error * @@ -420,11 +420,11 @@ public function data_set_valid_properties_with_invalid_and_error() { ), 'expected_message' => 'Webfont font style must be a non-empty string.', ), - 'font-weight: not a string' => array( + 'font-style: not a string' => array( 'webfont' => array( 'provider' => 'some-provider', 'font-family' => 'Some Font', - 'font-weight' => null, + 'font-style' => null, ), 'expected' => array( 'provider' => 'some-provider', From febc81b88d3fa87b5d9e6aaa17a6b066d34a5ec9 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 4 Nov 2021 11:20:14 +0200 Subject: [PATCH 68/94] Skip "provider" in CSS generation in the local provider. --- .../providers/class-wp-webfonts-local-provider.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index b8ab2937ea740..a583995b61cd1 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -147,6 +147,11 @@ private function build_font_css( array $webfont ) { foreach ( $webfont as $key => $value ) { + // Skip "provider". + if ( 'provider' === $key ) { + continue; + } + // Compile the "src" parameter. if ( 'src' === $key ) { $value = $this->compile_src( $webfont['font-family'], $value ); From de653879fa1fe4180fa1a16e8f73375cd0643945 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 4 Nov 2021 11:43:57 +0200 Subject: [PATCH 69/94] Update src/wp-includes/webfonts-api/class-wp-webfonts-registry.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sören Wrede --- src/wp-includes/webfonts-api/class-wp-webfonts-registry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index d06228cafd010..ce0a29740818c 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -43,7 +43,7 @@ class WP_Webfonts_Registry { * * @since 5.9.0 * - * @var string[] + * @var array[] */ private $registry_by_provider = array(); From 00c54aa42dd9dd25ed46ed328c62f09513aeb50c Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 4 Nov 2021 11:48:58 +0200 Subject: [PATCH 70/94] Allow using a range of font-weights for google-fonts --- .../class-wp-webfonts-google-provider.php | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 6bed1c859c8ac..8489981af60b7 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -86,12 +86,15 @@ protected function build_collection_api_urls() { // Build an array of font-weights for italics and default styles. foreach ( $webfonts as $font ) { if ( 'italic' === $font['font-style'] ) { - $italic_weights[] = $font['font-weight']; + $italic_weights = array_merge( $italic_weights, $this->get_font_weights( $font['font-weight'] ) ); } else { - $normal_weights[] = $font['font-weight']; + $normal_weights = array_merge( $normal_weights, $this->get_font_weights( $font['font-weight'] ) ); } } + $normal_weights = array_unique( $normal_weights ); + $italic_weights = array_unique( $italic_weights ); + if ( empty( $italic_weights ) && ! empty( $normal_weights ) ) { $url_part .= ':wght@' . implode( ';', $normal_weights ); } elseif ( ! empty( $italic_weights ) && empty( $normal_weights ) ) { @@ -154,4 +157,26 @@ private function organize_webfonts() { return $font_display_groups; } + + /** + * Get an array of font-weights from the given font-weights if using a space delimited string. + * + * @since 5.9.0 + * + * @param string $font_weights The font-weights string. + * + * @return array The font-weights array. + */ + protected function get_font_weights( $font_weights ) { + if ( false !== strpos( $font_weights, ' ' ) ) { + $font_weights = explode( ' ', $font_weights ); + } + + // If there are 2 values, treat them as a range. + if ( 2 === count( $font_weights ) ) { + $font_weights = range( $font_weights[0], $font_weights[1], 100 ); + } + + return $font_weights; + } } From 0e75bcfe6a5b72369818b46c513d38fef46daf5a Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 4 Nov 2021 11:55:03 +0200 Subject: [PATCH 71/94] provider no longer printed in the CSS --- .../tests/webfonts-api/providers/wpWebfontsLocalProvider.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php index 72fb0327a609b..d693faad51350 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php @@ -163,7 +163,6 @@ public function data_get_css() { ), 'expected' => << Date: Thu, 4 Nov 2021 12:36:21 +0200 Subject: [PATCH 72/94] Require 'is-external' => true to load external API webfonts --- .../class-wp-webfonts-controller.php | 8 ++++++++ .../class-wp-webfonts-schema-validator.php | 3 ++- .../class-wp-webfonts-local-provider.php | 11 +++++++++- .../providers/class-wp-webfonts-provider.php | 20 +++++++++++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 2f9237c05c9b9..6741455fb378c 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -123,6 +123,14 @@ private function generate_styles() { foreach ( $providers as $provider_id => $provider ) { $registered_webfonts = $this->webfonts_registry->get_by_provider( $provider_id ); + if ( ! empty( $registered_webfonts ) && $provider->is_external() ) { + foreach ( $registered_webfonts as $key => $webfont ) { + if ( empty( $webfont['is-external'] ) || true !== $webfont['is-external'] ) { + unset( $registered_webfonts[ $key ] ); + } + } + } + if ( empty( $registered_webfonts ) ) { continue; } diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 50c172e8c3cfc..35f043499d3c7 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -80,6 +80,7 @@ class WP_Webfonts_Schema_Validator { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => false, ); /** @@ -252,7 +253,7 @@ private function set_valid_font_face_property() { * Skip valid configuration parameters * (these are configuring the webfont but are not @font-face properties). */ - if ( 'provider' === $property || 'provider-params' === $property ) { + if ( 'provider' === $property || 'provider-params' === $property || 'is-external' === $property ) { continue; } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index a583995b61cd1..e755acabc1322 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -23,6 +23,15 @@ class WP_Webfonts_Local_Provider extends WP_Webfonts_Provider { */ protected $id = 'local'; + /** + * Whether the provider fetches external resources or not. + * + * @since 5.9.0 + * + * @var bool + */ + protected $is_external = false; + /** * Prepares the given webfont. * @@ -148,7 +157,7 @@ private function build_font_css( array $webfont ) { foreach ( $webfont as $key => $value ) { // Skip "provider". - if ( 'provider' === $key ) { + if ( 'provider' === $key || 'is-external' === $key ) { continue; } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index efe47ebf717ae..9a5ffd12a5640 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -56,6 +56,15 @@ abstract class WP_Webfonts_Provider { */ protected $resource_hints = array(); + /** + * Whether the provider fetches external resources or not. + * + * @since 5.9.0 + * + * @var bool + */ + protected $is_external = true; + /** * Get the provider's unique ID. * @@ -193,4 +202,15 @@ public function get_remote_styles( $url, array $args = array() ) { public function get_resource_hints() { return $this->resource_hints; } + + /** + * Whether the provider fetches external resources or not. + * + * @since 5.9.0 + * + * @return bool + */ + public function is_external() { + return $this->is_external; + } } From 2139913bdfa100ab0cbbf591bd8edaf767090f3c Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 4 Nov 2021 13:32:42 +0200 Subject: [PATCH 73/94] fix some (not all) test failures --- .../class-wp-webfonts-schema-validator.php | 2 +- .../providers/wpWebfontsGoogleProvider.php | 6 ++++++ .../tests/webfonts-api/wpWebfontsController.php | 2 ++ .../tests/webfonts-api/wpWebfontsRegistry.php | 3 +++ .../webfonts-api/wpWebfontsSchemaValidator.php | 13 +++++++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 35f043499d3c7..5d90dd4c74426 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -80,7 +80,7 @@ class WP_Webfonts_Schema_Validator { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => false, + 'is-external' => true, ); /** diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php index ce1351b568bb4..23b82f9a6f6c1 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php @@ -88,6 +88,7 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), ), 'expected' => array( @@ -102,6 +103,7 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), 'open-sans.italic.700' => array( 'provider' => 'google', @@ -109,6 +111,7 @@ public function data_build_collection_api_urls() { 'font-style' => 'italic', 'font-weight' => '700', 'font-display' => 'fallback', + 'is-external' => true, ), ), 'expected' => array( @@ -123,6 +126,7 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), 'open-sans.italic.700' => array( 'provider' => 'google', @@ -130,6 +134,7 @@ public function data_build_collection_api_urls() { 'font-style' => 'italic', 'font-weight' => '700', 'font-display' => 'fallback', + 'is-external' => true, ), 'roboto.normal.900' => array( 'provider' => 'google', @@ -137,6 +142,7 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '900', 'font-display' => 'fallback', + 'is-external' => true, ), ), 'expected' => array( diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index 7da4728a1f3f8..0725e6e938509 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -112,12 +112,14 @@ public function test_generate_and_enqueue_editor_styles( $stylesheet_handle ) { 'font-family' => 'Source Serif Pro', 'font-style' => 'normal', 'font-weight' => '200 900', + 'is-external' => true, ), 'source-serif-pro.italic.200 900' => array( 'provider' => 'my-custom-provider', 'font-family' => 'Source Serif Pro', 'font-style' => 'italic', 'font-weight' => '200 900', + 'is-external' => true, ), ); $this->webfont_registry_mock diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index bfb50f824aae4..032ffd437795e 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -323,6 +323,7 @@ public function data_get_by_provider_integrated() { 'font-style' => 'italic', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), 'roboto.normal.900' => array( 'provider' => 'google', @@ -330,6 +331,7 @@ public function data_get_by_provider_integrated() { 'font-style' => 'normal', 'font-weight' => '900', 'font-display' => 'fallback', + 'is-external' => true, ), ), ), @@ -364,6 +366,7 @@ public function data_get_by_provider_integrated() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-display' => 'fallback', + 'is-external' => true, 'font-stretch' => 'normal', 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index 8b3a100eb22a9..2bf5d6ec873e1 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -197,6 +197,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-family' => 'Open Sans', 'font-style' => 'normal', 'font-weight' => '400', + 'is-external' => true, ), 'expected' => array( 'provider' => 'google', @@ -204,6 +205,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), ), 'basic schema in opposite order' => array( @@ -212,6 +214,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-family' => 'Open Sans', 'provider' => 'google', + 'is-external' => true, ), 'expected' => array( 'provider' => 'google', @@ -219,6 +222,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), ), 'src: with protocol' => array( @@ -235,6 +239,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-display' => 'fallback', + 'is-external' => true, 'src' => 'http://example.org/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2', ), ), @@ -252,6 +257,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-display' => 'fallback', + 'is-external' => true, 'src' => '//example.org/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2', ), ), @@ -269,6 +275,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-display' => 'fallback', + 'is-external' => true, 'src' => 'data:font/opentype; base64, SGVsbG8sIFdvcmxkIQ==', ), ), @@ -287,6 +294,7 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-display' => 'fallback', + 'is-external' => true, 'font-stretch' => 'normal', 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), @@ -321,6 +329,7 @@ public function data_set_valid_properties_with_invalid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), ), 'with invalid @font-face property' => array( @@ -335,6 +344,7 @@ public function data_set_valid_properties_with_invalid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), ), 'font-style: invalid value' => array( @@ -349,6 +359,7 @@ public function data_set_valid_properties_with_invalid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), ), 'font-weight: invalid value' => array( @@ -363,6 +374,7 @@ public function data_set_valid_properties_with_invalid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), ), 'font-display: invalid value' => array( @@ -377,6 +389,7 @@ public function data_set_valid_properties_with_invalid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', + 'is-external' => true, ), ), ); From 4ea1f3444a6f3f4324b5c1ef9e52bd5888f52d3d Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 4 Nov 2021 13:42:04 +0200 Subject: [PATCH 74/94] bugfix --- .../providers/class-wp-webfonts-google-provider.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 8489981af60b7..40552783896f7 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -168,10 +168,12 @@ private function organize_webfonts() { * @return array The font-weights array. */ protected function get_font_weights( $font_weights ) { - if ( false !== strpos( $font_weights, ' ' ) ) { - $font_weights = explode( ' ', $font_weights ); + if ( false === strpos( $font_weights, ' ' ) ) { + return $font_weights; } + $font_weights = explode( ' ', $font_weights ); + // If there are 2 values, treat them as a range. if ( 2 === count( $font_weights ) ) { $font_weights = range( $font_weights[0], $font_weights[1], 100 ); From 864b5d1638252ae27fa9f8ffa411ad2c9d926e05 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 4 Nov 2021 13:42:54 +0200 Subject: [PATCH 75/94] typecast to array --- .../providers/class-wp-webfonts-google-provider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 40552783896f7..3a3c72fee773c 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -169,7 +169,7 @@ private function organize_webfonts() { */ protected function get_font_weights( $font_weights ) { if ( false === strpos( $font_weights, ' ' ) ) { - return $font_weights; + return (array) $font_weights; } $font_weights = explode( ' ', $font_weights ); From 9dd78c014a37ce93894a9149026b67b3fdc3e784 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 4 Nov 2021 12:59:23 -0500 Subject: [PATCH 76/94] Improves the WP_Webfonts_Controller::generate_styles(). * Micro-optimizations: * Guard clause at method opening to bail out if there are no registered providers. * Initialize the $styles variable after the guard clause. * Use `array_filter` instead of `foreach` as it's built for this task of validating each element in the target array of things. * Moves the guard clause instead of the "is_external" block to only run when checking if "okay to fetch". * Readability: Adds inline comments to explain each step. --- .../class-wp-webfonts-controller.php | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 6741455fb378c..f596d2d8b26cd 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -118,26 +118,55 @@ public function generate_and_enqueue_editor_styles() { * @return string $styles Generated styles. */ private function generate_styles() { - $styles = ''; $providers = $this->get_providers()->get_all_registered(); + if ( empty( $providers ) ) { + return ''; + } + + $styles = ''; + + /* + * Loop through each of the providers to get the CSS for their respective webfonts + * to incrementally generate the collective styles for all of them. + */ foreach ( $providers as $provider_id => $provider ) { $registered_webfonts = $this->webfonts_registry->get_by_provider( $provider_id ); - if ( ! empty( $registered_webfonts ) && $provider->is_external() ) { - foreach ( $registered_webfonts as $key => $webfont ) { - if ( empty( $webfont['is-external'] ) || true !== $webfont['is-external'] ) { - unset( $registered_webfonts[ $key ] ); - } - } - } - + // If there are no registered webfonts for this provider, skip it. if ( empty( $registered_webfonts ) ) { continue; } + /* + * If the provider is external (fetches from an external font service), + * remove any registered webfonts that are not marked as "okay to fetch" + * from the external service. + */ + if ( $provider->is_external() ) { + $registered_webfonts = array_filter( + $registered_webfonts, + static function( $webfont ) { + return ( isset( $webfont['is-external'] ) && true === $webfont['is-external'] ); + } + ); + + /* + * After checking if each registered webfont is okay to fetch from external service, + * if there are no remaining webfonts to process, skip this provider. + */ + if ( empty( $registered_webfonts ) ) { + continue; + } + } + + /* + * Process the webfonts by first passing them to the provider via `set_webfonts()` + * and then getting the CSS from the provider. + */ $provider->set_webfonts( $registered_webfonts ); $styles .= $provider->get_css(); } + return $styles; } From 0f8953879a5662c896faf489ca8798f71e96110e Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 4 Nov 2021 14:08:20 -0500 Subject: [PATCH 77/94] Require valid CSS properties for webfont schema. Though `theme.json` will be in `camelCase`, using cameCase keys is inconsistent with other core APIs. This commit changes the schema to use kebab-case keys, which then are valid CSS properties. In doing so, no conversion from camelCase to kebab-case is required during the registration process. --- .../class-wp-webfonts-provider-registry.php | 2 + .../class-wp-webfonts-registry.php | 67 ++++------ .../tests/webfonts-api/wpWebfontsRegistry.php | 121 ++++++------------ 3 files changed, 69 insertions(+), 121 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php index 8727ff004a191..add7bfaa86cf0 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -83,6 +83,8 @@ private function register_core_providers() { * @since 5.9.0 * * @param string $classname The provider's class name. + * The class should be a child of `WP_Webfonts_Provider`. + * See {@see WP_Webfonts_Provider}. * @return bool True when registered. False when provider does not exist. */ public function register( $classname ) { diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index ce0a29740818c..39ce8c58856e7 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -112,36 +112,34 @@ public function get_by_provider( $provider_id ) { * @param array $webfont { * Webfont definition. * - * @type string $provider The provider ID (e.g. 'local', 'google'). - * @type string $fontFamily The @font-face font-family property. - * @type string $fontWeight The @font-face font-weight property. - * The font-weight can be a single value, or a range. - * If a single value, then the font-weight can either be - * a numeric value (400, 700, etc), or a word value (normal, bold, etc). - * If a range, then the font-weight can be a numeric range - * using 2 values, separated by a space ('100 700'). - * @type string $fontStyle The @font-face font-style property. - * The font-style can be a valid CSS value (normal, italic etc). - * @type string $fontDisplay The @font-face font-display property. - * Accepted values: 'auto', 'block', 'fallback', 'swap'. - * @type array|string $src The @font-face src property. - * The src can be a single URL, or an array of URLs. - * @type string $fontStretch The @font-face font-stretch property. - * @type string $fontVariant The @font-face font-variant property. - * @type string $fontFeatureSettings The @font-face font-feature-settings property. - * @type string $fontVariationSettings The @font-face font-variation-settings property. - * @type string $lineHeightOverride The @font-face line-gap-override property. - * @type string $sizeAdjust The @font-face size-adjust property. - * @type string $unicodeRange The @font-face unicode-range property. - * @type string $ascendOverride The @font-face ascend-override property. - * @type string $descendOverride The @font-face descend-override property. + * @type string $provider The provider ID (e.g. 'local', 'google'). + * @type string $font-family The @font-face font-family property. + * @type string $font-weight The @font-face font-weight property. + * The font-weight can be a single value, or a range. + * If a single value, then the font-weight can either be + * a numeric value (400, 700, etc), or a word value + * (normal, bold, etc). + * If a range, then the font-weight can be a numeric range + * using 2 values, separated by a space ('100 700'). + * @type string $font-style The @font-face font-style property. + * The font-style can be a valid CSS value (normal, italic etc). + * @type string $font-display The @font-face font-display property. + * Accepted values: 'auto', 'block', 'fallback', 'swap'. + * @type array|string $src The @font-face src property. + * The src can be a single URL, or an array of URLs. + * @type string $font-stretch The @font-face font-stretch property. + * @type string $font-variant The @font-face font-variant property. + * @type string $font-feature-settings The @font-face font-feature-settings property. + * @type string $font-variation-settings The @font-face font-variation-settings property. + * @type string $line-gap-override The @font-face line-gap-override property. + * @type string $size-adjust The @font-face size-adjust property. + * @type string $unicode-range The @font-face unicode-range property. + * @type string $ascend-override The @font-face ascend-override property. + * @type string $descend-override The @font-face descend-override property. * } - * * @return string Registration key. */ public function register( array $webfont ) { - $webfont = $this->convert_to_kebab_case( $webfont ); - // Validate schema. if ( ! $this->validator->is_valid_schema( $webfont ) ) { return ''; @@ -183,21 +181,6 @@ private function store_for_query_by( array $webfont, $registration_key ) { $this->registry_by_provider[ $provider ][] = $registration_key; } - /** - * Convert camelCase parameters into kebab_case. - * - * @since 5.9.0 - * - * @param array $webfont Webfont definition. - * @return array Webfont with kebab_case parameters (keys). - */ - private function convert_to_kebab_case( array $webfont ) { - $kebab_case = preg_replace( '/(?validator_mock = $this->getMockBuilder( 'WP_Webfonts_Schema_Validator' ) - ->getMock(); + $this->validator_mock = $this->getMockBuilder( 'WP_Webfonts_Schema_Validator' )->getMock(); $this->registry = new WP_Webfonts_Registry( $this->validator_mock ); } @@ -85,9 +84,9 @@ public function data_register_with_invalid_schema() { ), 'provider: not defined' => array( array( - 'fontFamily' => 'Some Font', - 'fontStyle' => 'normal', - 'fontWeight' => '400', + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', ), ), 'provider: empty string' => array( @@ -100,17 +99,17 @@ public function data_register_with_invalid_schema() { ), 'provider: not a string' => array( array( - 'provider' => null, - 'fontFamily' => 'Some Font', - 'fontStyle' => 'normal', - 'fontWeight' => '400', + 'provider' => null, + 'font-family' => 'Some Font', + 'font-style' => 'normal', + 'font-weight' => '400', ), ), 'font family: not defined' => array( array( - 'provider' => 'local', - 'fontStyle' => 'normal', - 'fontWeight' => '400', + 'provider' => 'local', + 'font-style' => 'normal', + 'font-weight' => '400', ), ), 'font-family: not defined' => array( @@ -140,7 +139,7 @@ public function data_register_with_invalid_schema() { } /** - * @covers WP_Webfonts_Registry::register + * @covers WP_Webfonts_Registry::register * * @dataProvider data_register_with_valid_schema * @@ -168,23 +167,7 @@ public function test_register_with_valid_schema( array $webfont, array $validate */ public function data_register_with_valid_schema() { return array( - 'camelCase schema' => array( - 'webfont' => array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - ), - 'validated_webfont' => array( - 'provider' => 'google', - 'font-family' => 'Open Sans', - 'font-style' => 'normal', - 'font-weight' => '400', - 'font-display' => 'fallback', - ), - 'expected' => 'open-sans.normal.400', - ), - 'kebab-case schema' => array( + 'valid schema without font-display' => array( 'webfont' => array( 'provider' => 'google', 'font-family' => 'Roboto', @@ -200,27 +183,7 @@ public function data_register_with_valid_schema() { ), 'expected' => 'roboto.normal.normal', ), - 'camelCase with src' => array( - 'webfont' => array( - 'provider' => 'local', - 'fontFamily' => 'Source Serif Pro', - 'fontStyle' => 'normal', - 'fontWeight' => '200 900', - 'fontStretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - ), - 'validated_webfont' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-display' => 'fallback', - 'font-stretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - ), - 'expected' => 'source-serif-pro.normal.200 900', - ), - 'kebab-case with src' => array( + 'valid schema with src' => array( 'webfont' => array( 'provider' => 'local', 'font-family' => 'Source Serif Pro', @@ -262,8 +225,8 @@ public function test_get_by_provider_when_does_not_exist() { * As there are many moving parts to getting by provider, this test is an integration * test that does not mock. * - * @covers WP_Webfonts_Registry::get_by_provider - * @covers WP_Webfonts_Registry::register + * @covers WP_Webfonts_Registry::get_by_provider + * @covers WP_Webfonts_Registry::register * * @dataProvider data_get_by_provider_integrated * @@ -291,10 +254,10 @@ public function data_get_by_provider_integrated() { 'no webfonts for requested provider' => array( 'webfonts' => array( array( - 'provider' => 'google', - 'fontFamily' => 'Lato', - 'fontStyle' => 'italic', - 'fontWeight' => '400', + 'provider' => 'google', + 'font-family' => 'Lato', + 'font-style' => 'italic', + 'font-weight' => '400', ), ), 'provider_id' => 'local', @@ -303,16 +266,16 @@ public function data_get_by_provider_integrated() { 'with one provider' => array( 'webfonts' => array( array( - 'provider' => 'google', - 'fontFamily' => 'Lato', - 'fontStyle' => 'italic', - 'fontWeight' => '400', + 'provider' => 'google', + 'font-family' => 'Lato', + 'font-style' => 'italic', + 'font-weight' => '400', ), array( - 'provider' => 'google', - 'fontFamily' => 'Roboto', - 'fontStyle' => 'normal', - 'fontWeight' => '900', + 'provider' => 'google', + 'font-family' => 'Roboto', + 'font-style' => 'normal', + 'font-weight' => '900', ), ), 'provider_id' => 'google', @@ -338,24 +301,24 @@ public function data_get_by_provider_integrated() { 'with multiple providers' => array( 'webfonts' => array( array( - 'provider' => 'google', - 'fontFamily' => 'Open Sans', - 'fontStyle' => 'normal', - 'fontWeight' => '400', + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-sstyle' => 'normal', + 'font-weight' => '400', ), array( - 'provider' => 'local', - 'fontFamily' => 'Source Serif Pro', - 'fontStyle' => 'normal', - 'fontWeight' => '200 900', - 'fontStretch' => 'normal', - 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', + 'provider' => 'local', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', + 'font-stretch' => 'normal', + 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), array( - 'provider' => 'google', - 'fontFamily' => 'Roboto', - 'fontStyle' => 'normal', - 'fontWeight' => '900', + 'provider' => 'google', + 'font-family' => 'Roboto', + 'font-style' => 'normal', + 'font-weight' => '900', ), ), 'provider_id' => 'local', From 9a9f91c21286eac43bd4278b88fb4a09a3ea815a Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 4 Nov 2021 14:20:01 -0500 Subject: [PATCH 78/94] Document classname needs for provider registration. --- src/wp-includes/webfonts.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/webfonts.php b/src/wp-includes/webfonts.php index 70b35432f2ee5..fa997925351be 100644 --- a/src/wp-includes/webfonts.php +++ b/src/wp-includes/webfonts.php @@ -58,6 +58,7 @@ function wp_register_webfonts( array $webfonts ) { * * @param array $webfont Webfont to be registered. * See {@see WP_Webfonts_Registry::register()} for a list of supported arguments. + * @return string Registration key. */ function wp_register_webfont( array $webfont ) { wp_webfonts()->get_webfonts_registry()->register( $webfont ); @@ -68,10 +69,13 @@ function wp_register_webfont( array $webfont ) { * * @since 5.9.0 * - * @param string $classname The provider class name. + * @param string $classname The provider's class name. + * The class should be a child of `WP_Webfonts_Provider`. + * See {@see WP_Webfonts_Provider}. + * @return bool True when registered. False when provider does not exist. */ function wp_register_webfont_provider( $classname ) { - wp_webfonts()->get_providers()->register( $classname ); + return wp_webfonts()->get_providers()->register( $classname ); } /** From d035860f8928bcde58b60154b6e92dfdf7861d2f Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 4 Nov 2021 14:45:38 -0500 Subject: [PATCH 79/94] Removes WP_Webfonts_Provider::prepare(). Preparation work is now done when the `@font-face` CSS is requested. Setting the webfonts property no longer has processing. Why? * Logic simplification * Micro-optimization: looping through the webfonts once instead twice --- .../class-wp-webfonts-local-provider.php | 42 +++++++--------- .../providers/class-wp-webfonts-provider.php | 22 ++------- .../providers/wpWebfontsLocalProvider.php | 49 ++----------------- 3 files changed, 25 insertions(+), 88 deletions(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index e755acabc1322..1c0f601ff7fd7 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -33,15 +33,24 @@ class WP_Webfonts_Local_Provider extends WP_Webfonts_Provider { protected $is_external = false; /** - * Prepares the given webfont. + * Get the CSS for a collection of webfonts. * * @since 5.9.0 * - * @param array $webfont Webfont to validate. - * @return array + * @return string The CSS. */ - protected function prepare( array $webfont ) { - return $this->order_src( $webfont ); + public function get_css() { + $css = ''; + + foreach ( $this->webfonts as $webfont ) { + // Order the webfont's `src` items to optimize for browser support. + $webfont = $this->order_src( $webfont ); + + // Build the @font-face CSS for this webfont. + $css .= "@font-face{\n" . $this->build_font_face_css( $webfont ) . "}\n"; + } + + return $css; } /** @@ -49,8 +58,8 @@ protected function prepare( array $webfont ) { * * @since 5.9.0 * - * @param string[] $webfont Webfont to process. - * @return string[] + * @param array $webfont Webfont to process. + * @return array */ private function order_src( array $webfont ) { if ( ! is_array( $webfont['src'] ) ) { @@ -117,23 +126,6 @@ private function order_src( array $webfont ) { return $webfont; } - /** - * Get the CSS for a collection of webfonts. - * - * @since 5.9.0 - * - * @return string The CSS. - */ - public function get_css() { - $css = ''; - - foreach ( $this->webfonts as $webfont ) { - $css .= "@font-face{\n" . $this->build_font_css( $webfont ) . "}\n"; - } - - return $css; - } - /** * Builds the font-family's CSS. * @@ -142,7 +134,7 @@ public function get_css() { * @param array $webfont Webfont to process. * @return string This font-family's CSS. */ - private function build_font_css( array $webfont ) { + private function build_font_face_css( array $webfont ) { $css = ''; // Wrap font-family in quotes if it contains spaces. diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index 9a5ffd12a5640..1bae43a774384 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -88,10 +88,10 @@ public function get_root_url() { } /** - * Sets the webfonts. + * Sets this provider's webfonts property. * - * The webfonts have been validated, are in kebab_case, and - * are arranged by provider. + * The API's Controller passes this provider's webfonts + * for processing here in the provider. * * @since 5.9.0 * @@ -99,22 +99,6 @@ public function get_root_url() { */ public function set_webfonts( array $webfonts ) { $this->webfonts = $webfonts; - - foreach ( $this->webfonts as $registered_key => $webfont ) { - $this->webfonts[ $registered_key ] = $this->prepare( $webfont ); - } - } - - /** - * Prepares the given webfont. - * - * @since 5.9.0 - * - * @param array $webfont Webfont to validate. - * @return array - */ - protected function prepare( array $webfont ) { - return $webfont; } /** diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php index d693faad51350..993a52ac4fd51 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php @@ -75,39 +75,10 @@ public function test_set_webfonts() { ), ); - $expected = array( - 'source-serif-pro.normal.200 900' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => array( - array( - 'url' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', - 'format' => 'woff2', - ), - ), - ), - 'source-serif-pro.italic.200 900' => array( - 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'italic', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', - 'src' => array( - array( - 'url' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', - 'format' => 'woff2', - ), - ), - ), - ); - $this->provider->set_webfonts( $webfonts ); $property = $this->get_webfonts_property(); - $this->assertSame( $expected, $property->getValue( $this->provider ) ); + $this->assertSame( $webfonts, $property->getValue( $this->provider ) ); } /** @@ -140,12 +111,7 @@ public function data_get_css() { 'font-style' => 'italic', 'font-weight' => '400 900', 'font-stretch' => 'normal', - 'src' => array( - array( - 'url' => 'http://example.org/assets/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf', - 'format' => 'ttf', - ), - ), + 'src' => 'http://example.org/assets/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf', ), 'open-sans.normal.400 900' => array( 'provider' => 'local', @@ -153,12 +119,7 @@ public function data_get_css() { 'font-style' => 'normal', 'font-weight' => '400 900', 'font-stretch' => 'normal', - 'src' => array( - array( - 'url' => 'http://example.org/assets/fonts/OpenSans-VariableFont_wdth,wght.ttf', - 'format' => 'ttf', - ), - ), + 'src' => 'http://example.org/assets/fonts/OpenSans-VariableFont_wdth,wght.ttf', ), ), 'expected' => << Date: Thu, 4 Nov 2021 15:14:32 -0500 Subject: [PATCH 80/94] Require CSS properties in snake_case for registration. For consistency with other core APIs, webfont keys (i.e. CSS properties) will be in snake_case format, e.g. font_family, font_weight, font_style, etc. During registration, these keys are converted into kebab-case to become valid CSS properties. --- .../class-wp-webfonts-registry.php | 46 +++++++--- .../tests/webfonts-api/wpWebfontsRegistry.php | 90 +++++++++---------- 2 files changed, 78 insertions(+), 58 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index 39ce8c58856e7..f8519239a922e 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -113,33 +113,35 @@ public function get_by_provider( $provider_id ) { * Webfont definition. * * @type string $provider The provider ID (e.g. 'local', 'google'). - * @type string $font-family The @font-face font-family property. - * @type string $font-weight The @font-face font-weight property. + * @type string $font_family The @font-face font-family property. + * @type string $font_weight The @font-face font-weight property. * The font-weight can be a single value, or a range. * If a single value, then the font-weight can either be * a numeric value (400, 700, etc), or a word value * (normal, bold, etc). * If a range, then the font-weight can be a numeric range * using 2 values, separated by a space ('100 700'). - * @type string $font-style The @font-face font-style property. + * @type string $font_style The @font-face font-style property. * The font-style can be a valid CSS value (normal, italic etc). - * @type string $font-display The @font-face font-display property. + * @type string $font_display The @font-face font-display property. * Accepted values: 'auto', 'block', 'fallback', 'swap'. * @type array|string $src The @font-face src property. * The src can be a single URL, or an array of URLs. - * @type string $font-stretch The @font-face font-stretch property. - * @type string $font-variant The @font-face font-variant property. - * @type string $font-feature-settings The @font-face font-feature-settings property. - * @type string $font-variation-settings The @font-face font-variation-settings property. - * @type string $line-gap-override The @font-face line-gap-override property. - * @type string $size-adjust The @font-face size-adjust property. - * @type string $unicode-range The @font-face unicode-range property. - * @type string $ascend-override The @font-face ascend-override property. - * @type string $descend-override The @font-face descend-override property. + * @type string $font_stretch The @font-face font-stretch property. + * @type string $font_variant The @font-face font-variant property. + * @type string $font_feature_settings The @font-face font-feature-settings property. + * @type string $font_variation_settings The @font-face font-variation-settings property. + * @type string $line_gap_override The @font-face line-gap-override property. + * @type string $size_adjust The @font-face size-adjust property. + * @type string $unicode_range The @font-face unicode-range property. + * @type string $ascend_override The @font-face ascend-override property. + * @type string $descend_override The @font-face descend-override property. * } * @return string Registration key. */ public function register( array $webfont ) { + $webfont = $this->convert_to_kebab_case( $webfont ); + // Validate schema. if ( ! $this->validator->is_valid_schema( $webfont ) ) { return ''; @@ -159,6 +161,24 @@ public function register( array $webfont ) { return $registration_key; } + /** + * Convert snake_case keys into kebab-case. + * + * @since 5.9.0 + * + * @param array $webfont Webfont definition. + * @return array Webfont with kebab-case properties (keys). + */ + private function convert_to_kebab_case( array $webfont ) { + $kebab_case = array(); + foreach ( $webfont as $key => $value ) { + $converted_key = str_replace( '_', '-', $key ); + $kebab_case[ $converted_key ] = $value; + } + + return $kebab_case; + } + /** * Store the webfont for query by request. * diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index 79401d64f5d14..0e300e5ef77dc 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -84,55 +84,55 @@ public function data_register_with_invalid_schema() { ), 'provider: not defined' => array( array( - 'font-family' => 'Some Font', - 'font-style' => 'normal', - 'font-weight' => '400', + 'font_family' => 'Some Font', + 'font_style' => 'normal', + 'font_weight' => '400', ), ), 'provider: empty string' => array( array( 'provider' => '', - 'font-family' => 'Some Font', - 'font-style' => 'normal', - 'font-weight' => '400', + 'font_family' => 'Some Font', + 'font_style' => 'normal', + 'font_weight' => '400', ), ), 'provider: not a string' => array( array( 'provider' => null, - 'font-family' => 'Some Font', - 'font-style' => 'normal', - 'font-weight' => '400', + 'font_family' => 'Some Font', + 'font_style' => 'normal', + 'font_weight' => '400', ), ), 'font family: not defined' => array( array( 'provider' => 'local', - 'font-style' => 'normal', - 'font-weight' => '400', + 'font_style' => 'normal', + 'font_weight' => '400', ), ), 'font-family: not defined' => array( array( 'provider' => 'some-provider', - 'font-style' => 'normal', - 'font-weight' => '400', + 'font_style' => 'normal', + 'font_weight' => '400', ), ), 'font-family: empty string' => array( array( 'provider' => 'some-provider', - 'font-family' => '', - 'font-style' => 'normal', - 'font-weight' => '400', + 'font_family' => '', + 'font_style' => 'normal', + 'font_weight' => '400', ), ), 'font-family: not a string' => array( array( 'provider' => 'some-provider', - 'font-family' => null, - 'font-style' => 'normal', - 'font-weight' => '400', + 'font_family' => null, + 'font_style' => 'normal', + 'font_weight' => '400', ), ), ); @@ -170,9 +170,9 @@ public function data_register_with_valid_schema() { 'valid schema without font-display' => array( 'webfont' => array( 'provider' => 'google', - 'font-family' => 'Roboto', - 'font-style' => 'normal', - 'font-weight' => 'normal', + 'font_family' => 'Roboto', + 'font_style' => 'normal', + 'font_weight' => 'normal', ), 'validated_webfont' => array( 'provider' => 'google', @@ -186,10 +186,10 @@ public function data_register_with_valid_schema() { 'valid schema with src' => array( 'webfont' => array( 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', + 'font_family' => 'Source Serif Pro', + 'font_style' => 'normal', + 'font_weight' => '200 900', + 'font_stretch' => 'normal', 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), 'validated_webfont' => array( @@ -255,9 +255,9 @@ public function data_get_by_provider_integrated() { 'webfonts' => array( array( 'provider' => 'google', - 'font-family' => 'Lato', - 'font-style' => 'italic', - 'font-weight' => '400', + 'font_family' => 'Lato', + 'font_style' => 'italic', + 'font_weight' => '400', ), ), 'provider_id' => 'local', @@ -267,15 +267,15 @@ public function data_get_by_provider_integrated() { 'webfonts' => array( array( 'provider' => 'google', - 'font-family' => 'Lato', - 'font-style' => 'italic', - 'font-weight' => '400', + 'font_family' => 'Lato', + 'font_style' => 'italic', + 'font_weight' => '400', ), array( 'provider' => 'google', - 'font-family' => 'Roboto', - 'font-style' => 'normal', - 'font-weight' => '900', + 'font_family' => 'Roboto', + 'font_style' => 'normal', + 'font_weight' => '900', ), ), 'provider_id' => 'google', @@ -302,23 +302,23 @@ public function data_get_by_provider_integrated() { 'webfonts' => array( array( 'provider' => 'google', - 'font-family' => 'Open Sans', - 'font-sstyle' => 'normal', - 'font-weight' => '400', + 'font_family' => 'Open Sans', + 'font_style' => 'normal', + 'font_weight' => '400', ), array( 'provider' => 'local', - 'font-family' => 'Source Serif Pro', - 'font-style' => 'normal', - 'font-weight' => '200 900', - 'font-stretch' => 'normal', + 'font_family' => 'Source Serif Pro', + 'font_style' => 'normal', + 'font_weight' => '200 900', + 'font_stretch' => 'normal', 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), array( 'provider' => 'google', - 'font-family' => 'Roboto', - 'font-style' => 'normal', - 'font-weight' => '900', + 'font_family' => 'Roboto', + 'font_style' => 'normal', + 'font_weight' => '900', ), ), 'provider_id' => 'local', From 125c29ce9c644a0e35e83a06c303b1fe901ceab7 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Thu, 4 Nov 2021 16:18:12 -0500 Subject: [PATCH 81/94] Shortens the Controller's getter names. For consistency with `WP_Customize_Manager`, the webfonts and provider registry getters are shortened to `webfonts()` and `providers()`. --- .../class-wp-webfonts-controller.php | 73 +++++++++---------- src/wp-includes/webfonts.php | 8 +- .../webfonts-api/wpWebfontsController.php | 14 ++++ 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index f596d2d8b26cd..0c1c1886f1d4e 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -17,7 +17,7 @@ class WP_Webfonts_Controller { /** - * Instance of the webfonts registry. + * Instance of the webfont's registry. * * @since 5.9.0 * @@ -26,13 +26,13 @@ class WP_Webfonts_Controller { private $webfonts_registry; /** - * Instance of the provider's registry. + * Instance of the providers' registry. * * @since 5.9.0 * * @var WP_Webfonts_Provider_Registry */ - private $providers; + private $providers_registry; /** * Stylesheet handle. @@ -48,15 +48,15 @@ class WP_Webfonts_Controller { * * @since 5.9.0 * - * @param WP_Webfonts_Registry $webfonts_registry Instance of the webfonts registry. - * @param WP_Webfonts_Provider_Registry $provider_registry Instance of the providers registry. + * @param WP_Webfonts_Registry $webfonts_registry Instance of the webfonts' registry. + * @param WP_Webfonts_Provider_Registry $providers_registry Instance of the providers' registry. */ public function __construct( WP_Webfonts_Registry $webfonts_registry, - WP_Webfonts_Provider_Registry $provider_registry + WP_Webfonts_Provider_Registry $providers_registry ) { - $this->webfonts_registry = $webfonts_registry; - $this->providers = $provider_registry; + $this->webfonts_registry = $webfonts_registry; + $this->providers_registry = $providers_registry; } /** @@ -65,7 +65,7 @@ public function __construct( * @since 5.9.0 */ public function init() { - $this->providers->init(); + $this->providers_registry->init(); // Register callback to generate and enqueue styles. if ( did_action( 'wp_enqueue_scripts' ) ) { @@ -84,6 +84,28 @@ public function init() { add_filter( 'wp_resource_hints', array( $this, 'get_resource_hints' ), 10, 2 ); } + /** + * Gets the instance of the webfonts' registry. + * + * @since 5.9.0 + * + * @return WP_Webfonts_Registry + */ + public function webfonts() { + return $this->webfonts_registry; + } + + /** + * Gets the instance of the providers' registry. + * + * @since 5.9.0 + * + * @return WP_Webfonts_Provider_Registry + */ + public function providers() { + return $this->providers_registry; + } + /** * Generate and enqueue webfonts styles. * @@ -118,7 +140,7 @@ public function generate_and_enqueue_editor_styles() { * @return string $styles Generated styles. */ private function generate_styles() { - $providers = $this->get_providers()->get_all_registered(); + $providers = $this->providers_registry->get_all_registered(); if ( empty( $providers ) ) { return ''; } @@ -171,29 +193,7 @@ static function( $webfont ) { } /** - * Get the webfonts registry. - * - * @since 5.9.0 - * - * @return WP_Webfonts_Registry - */ - public function get_webfonts_registry() { - return $this->webfonts_registry; - } - - /** - * Get the providers registry. - * - * @since 5.9.0 - * - * @return WP_Webfonts_Provider_Registry - */ - public function get_providers() { - return $this->providers; - } - - /** - * Get the resource hints. + * Gets the resource hints. * * @since 5.9.0 * @@ -213,14 +213,11 @@ public function get_providers() { * } * @param string $relation_type The relation type the URLs are printed for, * e.g. 'preconnect' or 'prerender'. - * * @return array URLs to print for resource hints. */ public function get_resource_hints( $urls, $relation_type ) { - $providers = $this->get_providers()->get_all_registered(); - foreach ( $providers as $provider ) { - $hints = $provider->get_resource_hints(); - foreach ( $hints as $relation => $relation_hints ) { + foreach ( $this->providers_registry->get_all_registered() as $provider ) { + foreach ( $provider->get_resource_hints() as $relation => $relation_hints ) { if ( $relation !== $relation_type ) { continue; } diff --git a/src/wp-includes/webfonts.php b/src/wp-includes/webfonts.php index fa997925351be..03d0d1d96ce33 100644 --- a/src/wp-includes/webfonts.php +++ b/src/wp-includes/webfonts.php @@ -47,7 +47,7 @@ function wp_register_webfonts( array $webfonts ) { } foreach ( $webfonts as $webfont ) { - wp_webfonts()->get_webfonts_registry()->register( $webfont ); + wp_webfonts()->webfonts()->register( $webfont ); } } @@ -61,7 +61,7 @@ function wp_register_webfonts( array $webfonts ) { * @return string Registration key. */ function wp_register_webfont( array $webfont ) { - wp_webfonts()->get_webfonts_registry()->register( $webfont ); + return wp_webfonts()->webfonts()->register( $webfont ); } /** @@ -75,7 +75,7 @@ function wp_register_webfont( array $webfont ) { * @return bool True when registered. False when provider does not exist. */ function wp_register_webfont_provider( $classname ) { - return wp_webfonts()->get_providers()->register( $classname ); + return wp_webfonts()->providers()->register( $classname ); } /** @@ -86,5 +86,5 @@ function wp_register_webfont_provider( $classname ) { * @return WP_Webfonts_Provider[] Array of registered providers. */ function wp_get_webfont_providers() { - return wp_webfonts()->get_providers()->get_all_registered(); + return wp_webfonts()->providers()->get_all_registered(); } diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index 0725e6e938509..153e81115e2da 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -153,4 +153,18 @@ public function data_generate_and_enqueue_editor_styles() { 'for wp_print_footer_scripts' => array( 'webfonts-footer' ), ); } + + /** + * @covers WP_Webfonts_Controller::webfonts + */ + public function test_webfonts() { + $this->assertSame( $this->webfont_registry_mock, $this->controller->webfonts() ); + } + + /** + * @covers WP_Webfonts_Controller::providers + */ + public function test_providers() { + $this->assertSame( $this->provider_registry_mock, $this->controller->providers() ); + } } From 1380de5e0bde59a2f39346fccafb66941cdc425e Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Thu, 4 Nov 2021 17:24:15 -0500 Subject: [PATCH 82/94] Use MINUTE_IN_SECONDS constant instead of hard coded 60. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Crisoforo Gaspar Hernández --- .../webfonts-api/providers/class-wp-webfonts-provider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index 1bae43a774384..e015c1c2e1d85 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -131,7 +131,7 @@ public function get_cached_remote_styles( $id, $url, array $args = array(), arra // Early return if the request failed. // Cache an empty string for 60 seconds to avoid bottlenecks. if ( empty( $css ) ) { - set_site_transient( $id, '', 60 ); + set_site_transient( $id, '', MINUTE_IN_SECONDS ); return ''; } From acde2152804d11e0326164914c3ca411c14e26eb Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Fri, 5 Nov 2021 07:25:43 -0500 Subject: [PATCH 83/94] Type cast font weights to int for range(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Crisoforo Gaspar Hernández --- .../providers/class-wp-webfonts-google-provider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 3a3c72fee773c..87f6c05812acd 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -176,7 +176,7 @@ protected function get_font_weights( $font_weights ) { // If there are 2 values, treat them as a range. if ( 2 === count( $font_weights ) ) { - $font_weights = range( $font_weights[0], $font_weights[1], 100 ); + $font_weights = range( (int) $font_weights[0], (int) $font_weights[1], 100 ); } return $font_weights; From f88500cb2eaec48d33c29e568bfbec9d299c2d14 Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Fri, 5 Nov 2021 07:34:39 -0500 Subject: [PATCH 84/94] Remove unnecessary empty line. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Denis Žoljom --- .../webfonts-api/class-wp-webfonts-schema-validator.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 5d90dd4c74426..045cec181c2a7 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -126,7 +126,6 @@ public function is_valid_schema( array $webfont ) { * @return bool True if valid. False if invalid. */ private function is_valid_provider( array $webfont ) { - if ( empty( $webfont['provider'] ) || ! is_string( $webfont['provider'] ) From 6a0c723c59a9613d6af5ba89dc242f74ec7d47ce Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Fri, 5 Nov 2021 10:14:25 -0500 Subject: [PATCH 85/94] Micro-optimizations, fixes, and comments. * Removes the unused additional_props code as extra args for a font service was previously removed. * Micro-optimization for adding arrays to the end of a collection. Why? To avoid fully iterating the haystack array and creating a new array. * Refactor: Moves the font-weight collection processing to a separate private method. Why? To encapsulate the know-how of that specific task including documenting how it works. * Detail comments added to the WP_Webfonts_Google_Provider per code review including what it does, examples, etc. * Fixes a test. --- .../class-wp-webfonts-controller.php | 5 +- .../class-wp-webfonts-google-provider.php | 208 +++++++++++++++--- .../providers/class-wp-webfonts-provider.php | 53 ++--- .../tests/webfonts-api/wpWebfontsRegistry.php | 6 +- 4 files changed, 200 insertions(+), 72 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 0c1c1886f1d4e..a3a7c6315072f 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -221,9 +221,8 @@ public function get_resource_hints( $urls, $relation_type ) { if ( $relation !== $relation_type ) { continue; } - foreach ( $relation_hints as $hint ) { - $urls[] = $hint; - } + // Append this provider's resource hints to the end of the given `$urls` array. + array_push( $urls, ...$relation_hints ); } } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 87f6c05812acd..87d4ee24932bf 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -8,7 +8,19 @@ */ /** - * Webfonts API provider for Google Fonts. + * A core bundled provider for making remote requests to the + * Google Fonts API service and generating `@font-face` styles. + * + * This provider builds optimized request URL(s) for its webfonts, + * makes a remote request to the Google Fonts API service, and + * then generates the `@font-face` styles. + * + * When enqueued styles are rendered, the Controller passes its + * 'google' webfonts via the `set_webfonts()` method and then triggers + * the processing via the `get_css()` method. + * + * All know-how (business logic) for how to interact with and + * generate styles from Google Fonts API is contained in this provider. * * @since 5.9.0 */ @@ -48,11 +60,17 @@ class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { ); /** - * Get the CSS for a collection of fonts. + * Gets the `@font-face` CSS styles for Google Fonts. * - * @access public - * @since 5.9.0 - * @return string + * This method does the following processing tasks: + * 1. Orchestrates an optimized Google Fonts API URL for each font-family. + * 2. Caches each URL, if not already cached. + * 3. Does a remote request to the Google Fonts API service to fetch the styles. + * 4. Generates the `@font-face` for all its webfonts. + * + * @since 5.9.0 + * + * @return string The `@font-face` CSS. */ public function get_css() { $css = ''; @@ -66,35 +84,49 @@ public function get_css() { } /** - * Build the API URL for a collection of fonts. + * Builds the Google Fonts URL for a collection of webfonts. + * + * For example, if given the following webfonts: + * ``` + * array( + * array( + * 'font-family' => 'Source Serif Pro', + * 'font-style' => 'normal', + * 'font-weight' => '200 400', + * ), + * array( + * 'font-family' => 'Source Serif Pro', + * 'font-style' => 'italic', + * 'font-weight' => '400 600', + * ), + * ) + * ``` + * then the returned collection would be: + * ``` + * array( + * 'https://fonts.googleapis.com/css2?family=Source+Serif+Pro:ital,wght@0,200;0,300;0,400;1,400;1,500;1,600&display=fallback' + * ) + * ``` * * @since 5.9.0 * * @return array Collection of font-family urls. */ - protected function build_collection_api_urls() { + private function build_collection_api_urls() { $font_families_urls = array(); - // Iterate over each font-family group and build the API URL partial for that font-family. + /* + * Iterate over each font-family group to build the Google Fonts API URL + * for that specific family. Each is added to the collection of URLs to be + * returned to the `get_css()` method for making the remote request. + */ foreach ( $this->organize_webfonts() as $font_display => $font_families ) { - $font_display_url_parts = array(); + $url_parts = array(); foreach ( $font_families as $font_family => $webfonts ) { - $normal_weights = array(); - $italic_weights = array(); - $url_part = urlencode( $font_family ); - - // Build an array of font-weights for italics and default styles. - foreach ( $webfonts as $font ) { - if ( 'italic' === $font['font-style'] ) { - $italic_weights = array_merge( $italic_weights, $this->get_font_weights( $font['font-weight'] ) ); - } else { - $normal_weights = array_merge( $normal_weights, $this->get_font_weights( $font['font-weight'] ) ); - } - } - - $normal_weights = array_unique( $normal_weights ); - $italic_weights = array_unique( $italic_weights ); + list( $normal_weights, $italic_weights ) = $this->collect_font_weights( $webfonts ); + // Build the font-style with its font-weights. + $url_part = urlencode( $font_family ); if ( empty( $italic_weights ) && ! empty( $normal_weights ) ) { $url_part .= ':wght@' . implode( ';', $normal_weights ); } elseif ( ! empty( $italic_weights ) && empty( $normal_weights ) ) { @@ -103,21 +135,61 @@ protected function build_collection_api_urls() { $url_part .= ':ital,wght@0,' . implode( ';0,', $normal_weights ) . ';1,' . implode( ';1,', $italic_weights ); } - $font_display_url_parts[] = $url_part; + // Add it to the collection. + $url_parts[] = $url_part; } - $font_families_urls[] = $this->root_url . '?family=' . implode( '&family=', $font_display_url_parts ) . '&display=' . $font_display; + // Build the URL for this font-family and add it to the collection. + $font_families_urls[] = $this->root_url . '?family=' . implode( '&family=', $url_parts ) . '&display=' . $font_display; } return $font_families_urls; } /** - * Organizes the webfonts by font-display. + * Organizes the webfonts by font-display and then font-family. + * + * To optimizing building the URL for the Google Fonts API request, + * this method organizes the webfonts first by font-display and then + * by font-family. + * + * For example, if given the following webfonts: + * ``` + * array( + * array( + * 'font-family' => 'Source Serif Pro', + * 'font-style' => 'normal', + * 'font-weight' => '200 400', + * ), + * array( + * 'font-family' => 'Source Serif Pro', + * 'font-style' => 'italic', + * 'font-weight' => '400 600', + * ), + * ) + * ``` + * then the returned collection would be: + * ``` + * array( + * 'fallback' => array( + * 'Source Serif Pro' => array( + * array( + * 'font-family' => 'Source Serif Pro', + * 'font-style' => 'normal', + * 'font-weight' => '200 400', + * ), + * array( + * 'font-family' => 'Source Serif Pro', + * 'font-style' => 'italic', + * 'font-weight' => '400 600', + * ), + * ), + * ), + * ) * - * @since 5.8.0 + * @since 5.9.0 * - * @return array Sorted by font-display. + * @return array[][] Webfonts organized by font-display and then font-family. */ private function organize_webfonts() { $font_display_groups = array(); @@ -159,17 +231,87 @@ private function organize_webfonts() { } /** - * Get an array of font-weights from the given font-weights if using a space delimited string. + * Collects all font-weights grouped by 'normal' and 'italic' font-style. + * + * For example, if given the following webfonts: + * ``` + * array( + * array( + * 'font-family' => 'Source Serif Pro', + * 'font-style' => 'normal', + * 'font-weight' => '200 400', + * ), + * array( + * 'font-family' => 'Source Serif Pro', + * 'font-style' => 'italic', + * 'font-weight' => '400 600', + * ), + * ) + * ``` + * Then the returned collection would be: + * ``` + * array( + * array( 200, 300, 400 ), + * array( 400, 500, 600 ), + * ) + * ``` * * @since 5.9.0 * - * @param string $font_weights The font-weights string. + * @param array $webfonts Webfonts to process. + * @return array[] { + * The font-weights grouped by font-style. + * + * @type array $normal_weights Individual font-weight values for 'normal' font-style. + * @type array $italic_weights Individual font-weight values for 'italic' font-style. + * } + */ + private function collect_font_weights( array $webfonts ) { + $normal_weights = array(); + $italic_weights = array(); + + foreach ( $webfonts as $webfont ) { + $font_weights = $this->get_font_weights( $webfont['font-weight'] ); + // Skip this webfont if it does not have a font-weight defined. + if ( empty( $font_weights ) ) { + continue; + } + + // Add the individual font-weights to the end of font-style array. + if ( 'italic' === $webfont['font-style'] ) { + array_push( $italic_weights, ...$font_weights ); + } else { + array_push( $normal_weights, ...$font_weights ); + } + } + + // Remove duplicates. + $normal_weights = array_unique( $normal_weights ); + $italic_weights = array_unique( $italic_weights ); + + return array( $normal_weights, $italic_weights ); + } + + /** + * Converts the given string of font-weight into an array of individual weight values. + * + * When given a single font-weight, the value is wrapped into an array. + * + * A range of font-weights is specified as '400 600' with the lightest value first, + * a space, and then the heaviest value last. * + * When given a range of font-weight values, the range is converted into individual + * font-weight values. For example, a range of '400 600' is converted into + * `array( 400, 500, 600 )`. + * + * @since 5.9.0 + * + * @param string $font_weights The font-weights string. * @return array The font-weights array. */ - protected function get_font_weights( $font_weights ) { + private function get_font_weights( $font_weights ) { if ( false === strpos( $font_weights, ' ' ) ) { - return (array) $font_weights; + return array( $font_weights ); } $font_weights = explode( ' ', $font_weights ); diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index e015c1c2e1d85..e3a1cda595961 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -76,17 +76,6 @@ public function get_id() { return $this->id; } - /** - * Get the root URL for the provider. - * - * @since 5.9.0 - * - * @return string - */ - public function get_root_url() { - return $this->root_url; - } - /** * Sets this provider's webfonts property. * @@ -102,34 +91,40 @@ public function set_webfonts( array $webfonts ) { } /** - * Get the CSS for the font. + * Gets the `@font-face` CSS for the provider's webfonts. + * + * This method is where the provider does it processing to build the + * needed `@font-face` CSS for all of its webfonts. Specifics of how + * this processing is done is contained in each provider. * * @since 5.9.0 * - * @return string Webfonts CSS. + * @return string The `@font-face` CSS. */ abstract public function get_css(); /** - * Get cached styles from a remote URL. + * Gets cached styles from a remote URL. * * @since 5.9.0 * - * @param string $id An ID used to cache the styles. - * @param string $url The URL to fetch. - * @param array $args The arguments to pass to wp_remote_get(). - * @param array $additional_props Additional properties to add to the @font-face styles. + * @param string $id An ID used to cache the styles. + * @param string $url The URL to fetch. + * @param array $args Optional. The arguments to pass to `wp_remote_get()`. + * Default empty array. * @return string The styles. */ - public function get_cached_remote_styles( $id, $url, array $args = array(), array $additional_props = array() ) { + protected function get_cached_remote_styles( $id, $url, array $args = array() ) { $css = get_site_transient( $id ); // Get remote response and cache the CSS if it hasn't been cached already. if ( false === $css ) { $css = $this->get_remote_styles( $url, $args ); - // Early return if the request failed. - // Cache an empty string for 60 seconds to avoid bottlenecks. + /* + * Early return if the request failed. + * Cache an empty string for 60 seconds to avoid bottlenecks. + */ if ( empty( $css ) ) { set_site_transient( $id, '', MINUTE_IN_SECONDS ); return ''; @@ -139,28 +134,20 @@ public function get_cached_remote_styles( $id, $url, array $args = array(), arra set_site_transient( $id, $css, MONTH_IN_SECONDS ); } - // If there are additional props not included in the CSS provided by the API, add them to the final CSS. - foreach ( $additional_props as $prop ) { - $css = str_replace( - '@font-face {', - '@font-face {' . $prop . ':' . $this->params[ $prop ] . ';', - $css - ); - } - return $css; } /** - * Get styles from a remote URL. + * Gets styles from the remote font service via the given URL. * * @since 5.9.0 * * @param string $url The URL to fetch. - * @param array $args The arguments to pass to wp_remote_get(). + * @param array $args Optional. The arguments to pass to `wp_remote_get()`. + * Default empty array. * @return string The styles on success. Empty string on failure. */ - public function get_remote_styles( $url, array $args = array() ) { + protected function get_remote_styles( $url, array $args = array() ) { // Use a modern user-agent, to get woff2 files. $args['user-agent'] = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0'; diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index 0e300e5ef77dc..aa32c6af1a18f 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -24,7 +24,7 @@ public function set_up() { /** * @covers WP_Webfonts_Registry::get_all_registered */ - public function get_all_registered() { + public function test_get_all_registered() { $expected = array( 'open-sans.normal.400' => array( 'provider' => 'google', @@ -46,7 +46,7 @@ public function get_all_registered() { * Set the registry property. * This is set in WP_Webfonts_Registry::register(), which not part of this test. */ - $property = new ReflectionProperty( $this->registry, 'registry' ); + $property = new ReflectionProperty( $this->registry, 'registered' ); $property->setAccessible( true ); $property->setValue( $this->registry, $expected ); @@ -54,7 +54,7 @@ public function get_all_registered() { } /** - * @covers WP_Webfonts_Registry::register + * @covers WP_Webfonts_Registry::register * * @dataProvider data_register_with_invalid_schema * From 14efc74db40fc86dc5cb76fe530a6eeea83b966d Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Fri, 5 Nov 2021 10:43:03 -0500 Subject: [PATCH 86/94] Removes unnecessary guard clauses. The Validator checks the value and its data type before any processing happens. As the registry data is held in internally without the means to modify its data after validation, additional checks are not needed. The API can then trust the data in each registry. --- .../webfonts-api/class-wp-webfonts-registry.php | 4 ---- .../providers/class-wp-webfonts-google-provider.php | 8 ++++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index f8519239a922e..46798a0717e37 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -232,10 +232,6 @@ private function generate_registration_key( array $webfont ) { * @return string Font-family as a key. */ private function convert_font_family_into_key( $font_family ) { - if ( ! is_string( $font_family ) || '' === $font_family ) { - return ''; - } - return sanitize_title( $font_family ); } } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 87d4ee24932bf..964bef16829c1 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -199,10 +199,6 @@ private function organize_webfonts() { * Each font-display will need to be a separate request. */ foreach ( $this->webfonts as $webfont ) { - if ( ! isset( $webfont['font-display'] ) ) { - $webfont['font-display'] = 'fallback'; - } - if ( ! isset( $font_display_groups[ $webfont['font-display'] ] ) ) { $font_display_groups[ $webfont['font-display'] ] = array(); } @@ -310,10 +306,14 @@ private function collect_font_weights( array $webfonts ) { * @return array The font-weights array. */ private function get_font_weights( $font_weights ) { + $font_weights = trim( $font_weights ); + + // A single font-weight. if ( false === strpos( $font_weights, ' ' ) ) { return array( $font_weights ); } + // Process a range of font-weight values that are delimited by ' '. $font_weights = explode( ' ', $font_weights ); // If there are 2 values, treat them as a range. From c8ea4083b7c1994f81fac40ccc5764d4b6b351a6 Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Fri, 5 Nov 2021 10:52:23 -0500 Subject: [PATCH 87/94] Makes reflection get_webfonts_property() consistent in tests. --- .../webfonts-api/providers/wpWebfontsGoogleProvider.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php index 23b82f9a6f6c1..243f968811496 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php @@ -48,7 +48,8 @@ public function test_set_webfonts() { $this->provider->set_webfonts( $webfonts ); - $this->assertSame( $webfonts, $this->get_webfonts_property() ); + $property = $this->get_webfonts_property(); + $this->assertSame( $webfonts, $property->getValue( $this->provider ) ); } /** @@ -156,6 +157,6 @@ private function get_webfonts_property() { $property = new ReflectionProperty( $this->provider, 'webfonts' ); $property->setAccessible( true ); - return $property->getValue( $this->provider ); + return $property; } } From d8984622c6b90b578b256eb7d8c950e3aaa822cd Mon Sep 17 00:00:00 2001 From: Tonya Mork Date: Fri, 5 Nov 2021 10:57:45 -0500 Subject: [PATCH 88/94] Simplify wp_register_webfonts by removing the unnecessary guard clause. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `foreach` will not run if the given array is empty. Therefore, the empty() guard clause is unnecessary. Co-authored-by: Crisoforo Gaspar Hernández --- src/wp-includes/webfonts.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/wp-includes/webfonts.php b/src/wp-includes/webfonts.php index 03d0d1d96ce33..706c4b8ad78ee 100644 --- a/src/wp-includes/webfonts.php +++ b/src/wp-includes/webfonts.php @@ -40,12 +40,7 @@ function wp_webfonts() { * This contains ar array of webfonts to be registered. Each webfont is an array. * See {@see WP_Webfonts_Registry::register()} for a list of supported arguments for each webfont. */ -function wp_register_webfonts( array $webfonts ) { - // Bail out if the webfonts collection is empty. - if ( empty( $webfonts ) ) { - return; - } - +function wp_register_webfonts( array $webfonts = array() ) { foreach ( $webfonts as $webfont ) { wp_webfonts()->webfonts()->register( $webfont ); } From cb0c9bc3c15e794fc8d5e170cc6481b339b7168f Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Fri, 5 Nov 2021 13:34:17 -0500 Subject: [PATCH 89/94] Adds tests for Google Fonts provider `get_css()`. --- .../providers/wpWebfontsGoogleProvider.php | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php index 243f968811496..4f172b7391c0e 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php @@ -150,6 +150,202 @@ public function data_build_collection_api_urls() { 'https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,400;1,700&family=Roboto:wght@900&display=fallback', ), ), + 'range of font-weight values' => array( + 'webfonts' => array( + 'open-sans.normal.400 900' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400 900', + 'font-display' => 'fallback', + 'is-external' => true, + ), + 'open-sans.normal.100 400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '100 400', + 'font-display' => 'fallback', + 'is-external' => true, + ), + ), + 'expected' => array( + 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700;800;900;100;200;300&display=fallback', + ), + ), + 'duplicate font-weight values' => array( + 'webfonts' => array( + 'open-sans.normal.400 900' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400 600', + 'font-display' => 'fallback', + 'is-external' => true, + ), + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + 'is-external' => true, + ), + ), + 'expected' => array( + 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600&display=fallback', + ), + ), + ); + } + + /** + * @covers WP_Webfonts_Google_Provider::get_css + * + * @dataProvider data_get_css + * + * @param array $webfonts Prepared webfonts (to store in WP_Webfonts_Local_Provider::$webfonts property) + * @param int $response_code Remote request response code. + * @param string $expected Expected CSS. + */ + public function test_get_css( array $webfonts, $response_code, $expected ) { + $property = $this->get_webfonts_property(); + $property->setValue( $this->provider, $webfonts ); + + add_filter( + 'pre_http_request', + static function() use ( $response_code, $expected ) { + return array( + 'headers' => array(), + 'body' => $expected, + 'response' => array( + 'code' => $response_code, + ), + 'cookies' => array(), + 'filename' => null, + ); + } + ); + + $this->assertSame( $expected, $this->provider->get_css() ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_get_css() { + return array( + 'failure: invalid font-family' => array( + 'webfonts' => array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'font-family' => 'Invalid', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => '', + ), + ), + 'response_code' => 400, // The requested font families are not available. + 'expected' => '', + ), + 'success: single font-family' => array( + 'webfonts' => array( + 'open-sans.normal.400' => array( + 'provider' => 'google', + 'font-family' => 'Open Sans', + 'font-style' => 'normal', + 'font-weight' => '400', + 'font-display' => 'fallback', + ), + ), + 'response_code' => 200, + 'expected' => << Date: Fri, 5 Nov 2021 14:30:46 -0500 Subject: [PATCH 90/94] Changes the remote privacy permission logic. A filter is provided to grant permission to do remote requests to external service providers. By default, this filter is set to `false`, meaning the API will not do remote requests or generate `@font-face` styles for external service providers. Removes the `is_external()` logic as all providers are external except for the local provider bundled in core. Removes the `is-external` parameter from the webfonts schema and validation code. --- .../class-wp-webfonts-controller.php | 57 +++++++++++++------ .../class-wp-webfonts-schema-validator.php | 3 +- .../class-wp-webfonts-local-provider.php | 11 +--- .../providers/class-wp-webfonts-provider.php | 20 ------- ...class-my-custom-webfonts-provider-mock.php | 4 -- .../providers/wpWebfontsGoogleProvider.php | 10 ---- .../webfonts-api/wpWebfontsController.php | 46 ++++++++++++++- .../tests/webfonts-api/wpWebfontsRegistry.php | 3 - .../wpWebfontsSchemaValidator.php | 13 ----- 9 files changed, 84 insertions(+), 83 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index a3a7c6315072f..93916ca2c7589 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -115,6 +115,11 @@ public function generate_and_enqueue_styles() { // Generate the styles. $styles = $this->generate_styles(); + // Bail out if there are no styles to enqueue. + if ( '' === $styles ) { + return; + } + // Enqueue the stylesheet. wp_register_style( $this->stylesheet_handle, '' ); wp_enqueue_style( $this->stylesheet_handle ); @@ -129,12 +134,25 @@ public function generate_and_enqueue_styles() { * @since 5.9.0 */ public function generate_and_enqueue_editor_styles() { - wp_add_inline_style( 'wp-block-library', $this->generate_styles() ); + // Generate the styles. + $styles = $this->generate_styles(); + + // Bail out if there are no styles to enqueue. + if ( '' === $styles ) { + return; + } + + wp_add_inline_style( 'wp-block-library', $styles ); } /** * Generate styles for webfonts. * + * By default (due to privacy concerns), this API will not do remote requests to + * external webfont services nor generate `@font-face` styles for these remote + * providers. The filter `'has_remote_webfonts_request_permission'` is provided + * to grant permission to do the remote request. + * * @since 5.9.0 * * @return string $styles Generated styles. @@ -160,25 +178,28 @@ private function generate_styles() { } /* - * If the provider is external (fetches from an external font service), - * remove any registered webfonts that are not marked as "okay to fetch" - * from the external service. + * Skip fetching from a remote fonts service if the user has not + * consented to the remote request. */ - if ( $provider->is_external() ) { - $registered_webfonts = array_filter( - $registered_webfonts, - static function( $webfont ) { - return ( isset( $webfont['is-external'] ) && true === $webfont['is-external'] ); - } - ); - - /* - * After checking if each registered webfont is okay to fetch from external service, - * if there are no remaining webfonts to process, skip this provider. + if ( + 'local' !== $provider_id && + /** + * Allows permission to be set for doing remote requests + * to a webfont service provider. + * + * By default, the Webfonts API will not make remote requests + * due to privacy concerns. + * + * @since 5.9.0 + * + * @param bool $has_permission Permission to do the remote request. + * Default false. + * @param string $provider_id Provider's ID, e.g. 'google', to identify + * the remote webfonts service provider. */ - if ( empty( $registered_webfonts ) ) { - continue; - } + true !== apply_filters( 'has_remote_webfonts_request_permission', false, $provider_id ) + ) { + continue; } /* diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 045cec181c2a7..9e61cbd767c74 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -80,7 +80,6 @@ class WP_Webfonts_Schema_Validator { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ); /** @@ -252,7 +251,7 @@ private function set_valid_font_face_property() { * Skip valid configuration parameters * (these are configuring the webfont but are not @font-face properties). */ - if ( 'provider' === $property || 'provider-params' === $property || 'is-external' === $property ) { + if ( 'provider' === $property || 'provider-params' === $property ) { continue; } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index 1c0f601ff7fd7..b80757ef596eb 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -23,15 +23,6 @@ class WP_Webfonts_Local_Provider extends WP_Webfonts_Provider { */ protected $id = 'local'; - /** - * Whether the provider fetches external resources or not. - * - * @since 5.9.0 - * - * @var bool - */ - protected $is_external = false; - /** * Get the CSS for a collection of webfonts. * @@ -149,7 +140,7 @@ private function build_font_face_css( array $webfont ) { foreach ( $webfont as $key => $value ) { // Skip "provider". - if ( 'provider' === $key || 'is-external' === $key ) { + if ( 'provider' === $key ) { continue; } diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index e3a1cda595961..03af6ef8c5d0d 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -56,15 +56,6 @@ abstract class WP_Webfonts_Provider { */ protected $resource_hints = array(); - /** - * Whether the provider fetches external resources or not. - * - * @since 5.9.0 - * - * @var bool - */ - protected $is_external = true; - /** * Get the provider's unique ID. * @@ -173,15 +164,4 @@ protected function get_remote_styles( $url, array $args = array() ) { public function get_resource_hints() { return $this->resource_hints; } - - /** - * Whether the provider fetches external resources or not. - * - * @since 5.9.0 - * - * @return bool - */ - public function is_external() { - return $this->is_external; - } } diff --git a/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php b/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php index e12051a033333..d62a8789b88a7 100644 --- a/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php +++ b/tests/phpunit/tests/webfonts-api/mocks/class-my-custom-webfonts-provider-mock.php @@ -14,10 +14,6 @@ class My_Custom_Webfonts_Provider_Mock extends WP_Webfonts_Provider { ), ); - public function set_webfonts( array $webfonts ) { - // do nothing. - } - public function get_css() { return " @font-face{ diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php index 4f172b7391c0e..a3d763d105c47 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsGoogleProvider.php @@ -89,7 +89,6 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'expected' => array( @@ -104,7 +103,6 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), 'open-sans.italic.700' => array( 'provider' => 'google', @@ -112,7 +110,6 @@ public function data_build_collection_api_urls() { 'font-style' => 'italic', 'font-weight' => '700', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'expected' => array( @@ -127,7 +124,6 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), 'open-sans.italic.700' => array( 'provider' => 'google', @@ -135,7 +131,6 @@ public function data_build_collection_api_urls() { 'font-style' => 'italic', 'font-weight' => '700', 'font-display' => 'fallback', - 'is-external' => true, ), 'roboto.normal.900' => array( 'provider' => 'google', @@ -143,7 +138,6 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '900', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'expected' => array( @@ -158,7 +152,6 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '400 900', 'font-display' => 'fallback', - 'is-external' => true, ), 'open-sans.normal.100 400' => array( 'provider' => 'google', @@ -166,7 +159,6 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '100 400', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'expected' => array( @@ -181,7 +173,6 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '400 600', 'font-display' => 'fallback', - 'is-external' => true, ), 'open-sans.normal.400' => array( 'provider' => 'google', @@ -189,7 +180,6 @@ public function data_build_collection_api_urls() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'expected' => array( diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index 153e81115e2da..53ca2a3e2efb6 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -77,6 +77,46 @@ public function data_init() { ); } + /** + * By default, the Webfonts API should not request webfonts from + * a remote provider. Test the permissions logic works as expected. + * + * @covers WP_Webfonts_Controller::generate_and_enqueue_styles + * + * @dataProvider data_generate_and_enqueue_editor_styles + * + * @param string $stylestyle_handle Handle for the registered stylesheet. + */ + public function test_generate_and_enqueue_styles_default( $stylesheet_handle ) { + /* + * Set the stylesheet_handle property. + * This is set in WP_Webfonts_Controller::init(); however, init is not part + * of this test (as it has its own test). + */ + $property = new ReflectionProperty( $this->controller, 'stylesheet_handle' ); + $property->setAccessible( true ); + $property->setValue( $this->controller, $stylesheet_handle ); + + // Set up the provider mock. + $provider = $this->getMockBuilder( 'WP_Webfonts_Google_Provider' )->getMock(); + $providers = array( + 'google' => $provider, + ); + $this->provider_registry_mock + ->expects( $this->once() ) + ->method( 'get_all_registered' ) + ->willReturn( $providers ); + // The Google Fonts provider should never be called. + $provider + ->expects( $this->never() ) + ->method( 'set_webfonts' ); + + // Fire the method being tested. + $this->controller->generate_and_enqueue_styles(); + $this->expectOutputString( '' ); + wp_print_styles( $stylesheet_handle ); + } + /** * @covers WP_Webfonts_Controller::generate_and_enqueue_styles * @covers WP_Webfonts_Controller::generate_and_enqueue_editor_styles @@ -85,7 +125,9 @@ public function data_init() { * * @param string $stylestyle_handle Handle for the registered stylesheet. */ - public function test_generate_and_enqueue_editor_styles( $stylesheet_handle ) { + public function test_generate_and_enqueue_styles_with_permission( $stylesheet_handle ) { + add_filter( 'has_remote_webfonts_request_permission', '__return_true' ); + /* * Set the stylesheet_handle property. * This is set in WP_Webfonts_Controller::init(); however, init is not part @@ -112,14 +154,12 @@ public function test_generate_and_enqueue_editor_styles( $stylesheet_handle ) { 'font-family' => 'Source Serif Pro', 'font-style' => 'normal', 'font-weight' => '200 900', - 'is-external' => true, ), 'source-serif-pro.italic.200 900' => array( 'provider' => 'my-custom-provider', 'font-family' => 'Source Serif Pro', 'font-style' => 'italic', 'font-weight' => '200 900', - 'is-external' => true, ), ); $this->webfont_registry_mock diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index aa32c6af1a18f..67bf8bca9514f 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -286,7 +286,6 @@ public function data_get_by_provider_integrated() { 'font-style' => 'italic', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), 'roboto.normal.900' => array( 'provider' => 'google', @@ -294,7 +293,6 @@ public function data_get_by_provider_integrated() { 'font-style' => 'normal', 'font-weight' => '900', 'font-display' => 'fallback', - 'is-external' => true, ), ), ), @@ -329,7 +327,6 @@ public function data_get_by_provider_integrated() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-display' => 'fallback', - 'is-external' => true, 'font-stretch' => 'normal', 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php index 2bf5d6ec873e1..8b3a100eb22a9 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsSchemaValidator.php @@ -197,7 +197,6 @@ public function data_set_valid_properties_with_valid_input() { 'font-family' => 'Open Sans', 'font-style' => 'normal', 'font-weight' => '400', - 'is-external' => true, ), 'expected' => array( 'provider' => 'google', @@ -205,7 +204,6 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'basic schema in opposite order' => array( @@ -214,7 +212,6 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-family' => 'Open Sans', 'provider' => 'google', - 'is-external' => true, ), 'expected' => array( 'provider' => 'google', @@ -222,7 +219,6 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'src: with protocol' => array( @@ -239,7 +235,6 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-display' => 'fallback', - 'is-external' => true, 'src' => 'http://example.org/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2', ), ), @@ -257,7 +252,6 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-display' => 'fallback', - 'is-external' => true, 'src' => '//example.org/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2', ), ), @@ -275,7 +269,6 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-display' => 'fallback', - 'is-external' => true, 'src' => 'data:font/opentype; base64, SGVsbG8sIFdvcmxkIQ==', ), ), @@ -294,7 +287,6 @@ public function data_set_valid_properties_with_valid_input() { 'font-style' => 'normal', 'font-weight' => '200 900', 'font-display' => 'fallback', - 'is-external' => true, 'font-stretch' => 'normal', 'src' => 'https://example.com/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), @@ -329,7 +321,6 @@ public function data_set_valid_properties_with_invalid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'with invalid @font-face property' => array( @@ -344,7 +335,6 @@ public function data_set_valid_properties_with_invalid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'font-style: invalid value' => array( @@ -359,7 +349,6 @@ public function data_set_valid_properties_with_invalid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'font-weight: invalid value' => array( @@ -374,7 +363,6 @@ public function data_set_valid_properties_with_invalid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), ), 'font-display: invalid value' => array( @@ -389,7 +377,6 @@ public function data_set_valid_properties_with_invalid_input() { 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', - 'is-external' => true, ), ), ); From 81326fdd0b0cc1db5a7d70f6b3b7316f50648a5c Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Sat, 6 Nov 2021 09:22:20 -0500 Subject: [PATCH 91/94] Code review: comments, typos, micro-optimizations. Further documents the API with examples. Includes code review fixes. --- .../class-wp-webfonts-controller.php | 38 ++++-- .../class-wp-webfonts-provider-registry.php | 48 ++++++-- .../class-wp-webfonts-schema-validator.php | 2 +- .../class-wp-webfonts-google-provider.php | 5 +- .../providers/class-wp-webfonts-provider.php | 28 +++-- src/wp-includes/webfonts.php | 112 +++++++++++++++++- .../providers/wpWebfontsLocalProvider.php | 2 +- .../webfonts-api/wpWebfontsController.php | 4 +- .../tests/webfonts-api/wpWebfontsRegistry.php | 2 +- 9 files changed, 203 insertions(+), 38 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php index 93916ca2c7589..c14980c4fcfed 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-controller.php @@ -8,9 +8,26 @@ */ /** - * Webfonts Controller. + * Webfonts Controller exposes the public entry point into this API + * and coordinates the interactions between the webfonts registry, + * providers registry, and the Dependencies API. * - * Receives the incoming requests and handles the processing. + * event + * ↕ + * Controller + * ⤢ ⤡ + * Webfonts Registry Providers Registry + * ↕ ⤢ ⤡ .. [custom providers] + * Validator Local Google Fonts + * Provider Provider + * ↕ ↕ + * Filesystem Remote Font API Service + * + * The Controller receives an event such as a request to register + * a webfont or provider, print `@font-face` styles in the `` + * (e.g. `'wp_enqueue_scripts'`), or print the resource `` + * (`'wp_resource_hints'` ). Then it interacts with the components + * in this API to process the event. * * @since 5.9.0 */ @@ -87,6 +104,9 @@ public function init() { /** * Gets the instance of the webfonts' registry. * + * The Webfonts Registry handles the registration + * and in-memory storage of webfonts. + * * @since 5.9.0 * * @return WP_Webfonts_Registry @@ -98,6 +118,9 @@ public function webfonts() { /** * Gets the instance of the providers' registry. * + * @see WP_Webfonts_Provider_Registry for more information + * on the available methods for use. + * * @since 5.9.0 * * @return WP_Webfonts_Provider_Registry @@ -158,12 +181,8 @@ public function generate_and_enqueue_editor_styles() { * @return string $styles Generated styles. */ private function generate_styles() { + $styles = ''; $providers = $this->providers_registry->get_all_registered(); - if ( empty( $providers ) ) { - return ''; - } - - $styles = ''; /* * Loop through each of the providers to get the CSS for their respective webfonts @@ -216,6 +235,11 @@ private function generate_styles() { /** * Gets the resource hints. * + * Callback hooked to the filter `'wp_resource_hints'`. Generation + * and rendering of the resource `` is handled where that filter + * fires. This method adds the resource link attributes to pass back + * to that handler. + * * @since 5.9.0 * * @param array $urls { diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php index add7bfaa86cf0..89366e68077a4 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-provider-registry.php @@ -8,15 +8,19 @@ */ /** - * Provider Registry. + * The Providers Registry handles the registration + * of core providers, registration of custom providers, + * instantiation of each provider, and in-memory storage + * of the registered providers. Each provider is stored + * in the registry by its unique ID, such as 'local', along + * with an instance of the provider (object). * - * This registry exists to handle all providers. + * Each provider contains the business logic for how to + * process its specific font service (i.e. local or remote) + * and how to generate the `@font-face` styles for its service. * - * It handles the following within the API: - * - loads the bundled provider files into memory; - * - registers each provider with the API by: - * 1. creating an instance (object); - * 2. storing it in-memory (by its unique provider ID) for use with the API; + * This registry is for collecting those providers for + * use within the API. * * @since 5.9.0 */ @@ -39,9 +43,15 @@ class WP_Webfonts_Provider_Registry { /** * Gets all registered providers. * + * Return an array of providers, each keyed by their unique + * ID (i.e. the `$id` property in the provider's object) with + * an instance of the provider (object): + * ID => provider instance + * * @since 5.9.0 * - * @return WP_Webfonts_Provider[] All registered providers each keyed by their unique provider ID. + * @return WP_Webfonts_Provider[] All registered providers, + * each keyed by their unique ID. */ public function get_all_registered() { return $this->registered; @@ -78,18 +88,34 @@ private function register_core_providers() { } /** - * Registers the given provider. + * Registers a webfont provider. + * + * The provider will be registered by its unique ID + * (via `WP_Webfonts_Provider::get_id()`) and instance of + * the provider (object): + * ID => provider instance + * + * Once registered, provider is ready for use within the API. * * @since 5.9.0 * * @param string $classname The provider's class name. * The class should be a child of `WP_Webfonts_Provider`. * See {@see WP_Webfonts_Provider}. + * * @return bool True when registered. False when provider does not exist. */ public function register( $classname ) { - // If the class does not exist in memory, or is not a subclass of WP_Webfonts_Provider, bail out. - if ( ! class_exists( $classname ) || ! is_subclass_of( $classname, 'WP_Webfonts_Provider' ) ) { + /* + * Bail out if the class does not exist in memory (its file + * has to be loaded into memory before registration) or the + * `class` itself is not a child that extends `WP_Webfonts_Provider` + * (the parent class of a provider). + */ + if ( + ! class_exists( $classname ) || + ! is_subclass_of( $classname, 'WP_Webfonts_Provider' ) + ) { return false; } diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 9e61cbd767c74..14fe139e28b2c 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -166,7 +166,7 @@ private function is_valid_font_family( array $webfont ) { * @param array $webfont Webfont to validate. * @return bool True if valid. False if invalid. */ - private function is_src_valid( $webfont ) { + private function is_src_valid( array $webfont ) { if ( empty( $webfont['src'] ) || ( diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index 964bef16829c1..a4b163430f17a 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -44,11 +44,12 @@ class WP_Webfonts_Google_Provider extends WP_Webfonts_Provider { protected $root_url = 'https://fonts.googleapis.com/css2'; /** - * Array of resources hints. + * Array of resources hints, used to render the resource `` in the ``. * * @since 5.9.0 * - * @var array + * @var string[] See {@see WP_Webfonts_Provider::$resource_hints} for + * the list of resource hints. */ protected $resource_hints = array( 'preconnect' => array( diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index 03af6ef8c5d0d..e37b515b6dc96 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -44,15 +44,24 @@ abstract class WP_Webfonts_Provider { protected $webfonts = array(); /** - * Array of resources hints. + * Array of resources hints, used to render the resource `` in the ``. * - * Keyed by relation-type: + * @since 5.9.0 * - * @type string $key => @type array resource hint. + * @var string[] { + * Resource attributes for each relation type (e.g. 'preconnect' or 'prerender'). * - * @since 5.9.0 + * @type string $relation_type => array { + * Array of resource attributes. * - * @var array + * @type string $href URL to include in resource hints. Required. + * @type string $as Optional. How the browser should treat the resource + * (`script`, `style`, `image`, `document`, etc). + * @type string $crossorigin Optional. Indicates the CORS policy of the specified resource. + * @type float $pr Optional. Expected probability that the resource hint will be used. + * @type string $type Optional. Type of the resource (`text/html`, `text/css`, etc). + * } + * } */ protected $resource_hints = array(); @@ -155,11 +164,16 @@ protected function get_remote_styles( $url, array $args = array() ) { } /** - * Get the provider's resource hints. + * Gets the provider's resource hints. + * + * The Controller calls this method {@see WP_Webfonts_Controller::get_resource_hints()} + * when the `'wp_resource_hints'` filter fires. * * @since 5.9.0 * - * @return array + * @return string[] Array of resource attributes. + * See {@see WP_Webfonts_Provider::$resource_hints} for + * the list of resource hints. */ public function get_resource_hints() { return $this->resource_hints; diff --git a/src/wp-includes/webfonts.php b/src/wp-includes/webfonts.php index 706c4b8ad78ee..279cf572a327e 100644 --- a/src/wp-includes/webfonts.php +++ b/src/wp-includes/webfonts.php @@ -32,13 +32,60 @@ function wp_webfonts() { } /** - * Registers a webfont collection. + * Registers a collection of webfonts. + * + * Example of how to register Source Serif Pro font with font-weight range of 200-900 + * and font-style of normal and italic: + * + * If the font files are contained within the theme: + * ``` + * wp_register_webfonts( + * array( + * array( + * 'provider' => 'local', + * 'font_family' => 'Source Serif Pro', + * 'font_weight' => '200 900', + * 'font_style' => 'normal', + * 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + * ), + * array( + * 'provider' => 'local', + * 'font_family' => 'Source Serif Pro', + * 'font_weight' => '200 900', + * 'font_style' => 'italic', + * 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ), + * ), + * ) + * ); + * ``` + * + * When requesting from the remote Google Fonts API service provider: + * ``` + * wp_register_webfonts( + * array( + * array( + * 'provider' => 'google', + * 'font_family' => 'Source Serif Pro', + * 'font_weight' => '200 900', + * 'font_style' => 'normal', + * ), + * array( + * 'provider' => 'google', + * 'font_family' => 'Source Serif Pro', + * 'font_weight' => '200 900', + * 'font_style' => 'italic', + * ), + * ) + * ); + * ``` * * @since 5.9.0 * * @param array $webfonts Webfonts to be registered. - * This contains ar array of webfonts to be registered. Each webfont is an array. - * See {@see WP_Webfonts_Registry::register()} for a list of supported arguments for each webfont. + * This contains an array of webfonts to be registered. + * Each webfont is an array. + * See {@see WP_Webfonts_Registry::register()} for a list of + * supported arguments for each webfont. */ function wp_register_webfonts( array $webfonts = array() ) { foreach ( $webfonts as $webfont ) { @@ -49,6 +96,33 @@ function wp_register_webfonts( array $webfonts = array() ) { /** * Registers a single webfont. * + * Example of how to register Source Serif Pro font with font-weight range of 200-900: + * + * If the font file is contained within the theme: + * ``` + * wp_register_webfont( + * array( + * 'provider' => 'local', + * 'font_family' => 'Source Serif Pro', + * 'font_weight' => '200 900', + * 'font_style' => 'normal', + * 'src' => get_theme_file_uri( 'assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + * ) + * ); + * ``` + * + * When requesting from the remote Google Fonts API service provider: + * ``` + * wp_register_webfonts( + * array( + * 'provider' => 'google', + * 'font_family' => 'Source Serif Pro', + * 'font_weight' => '200 900', + * 'font_style' => 'normal', + * ) + * ); + * ``` + * * @since 5.9.0 * * @param array $webfont Webfont to be registered. @@ -60,13 +134,29 @@ function wp_register_webfont( array $webfont ) { } /** - * Register a webfont provider. + * Registers a custom font service provider. + * + * A webfont provider contains the business logic for how to + * interact with a remote font service and how to generate + * the `@font-face` styles for that remote service. + * + * See the `WP_Webfonts_Google_Provider` for inspiration. + * + * How to register a custom font service provider: + * 1. Load its class file into memory before registration. + * 2. Pass the class' name to this function. + * + * For example, for a class named `My_Custom_Font_Service_Provider`: + * ``` + * wp_register_webfont_provider( My_Custom_Font_Service_Provider::class ); + * ``` * * @since 5.9.0 * * @param string $classname The provider's class name. * The class should be a child of `WP_Webfonts_Provider`. * See {@see WP_Webfonts_Provider}. + * * @return bool True when registered. False when provider does not exist. */ function wp_register_webfont_provider( $classname ) { @@ -74,11 +164,21 @@ function wp_register_webfont_provider( $classname ) { } /** - * Get webfonts providers. + * Gets all registered providers. + * + * Return an array of providers, each keyed by their unique + * ID (i.e. the `$id` property in the provider's object) with + * an instance of the provider (object): + * ID => provider instance + * + * Each provider contains the business logic for how to + * process its specific font service (i.e. local or remote) + * and how to generate the `@font-face` styles for its service. * * @since 5.9.0 * - * @return WP_Webfonts_Provider[] Array of registered providers. + * @return WP_Webfonts_Provider[] All registered providers, + * each keyed by their unique ID. */ function wp_get_webfont_providers() { return wp_webfonts()->providers()->get_all_registered(); diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php index 993a52ac4fd51..10f059d1fc160 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php @@ -2,7 +2,7 @@ /** * @group webfonts - * @covers WP_Webfonts_Google_Provider + * @covers WP_WebfontsLocal_Provider */ class Tests_Webfonts_API_wpWebfontsLocalProvider extends WP_UnitTestCase { private $provider; diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php index 53ca2a3e2efb6..5ed1a1765d136 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsController.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsController.php @@ -85,7 +85,7 @@ public function data_init() { * * @dataProvider data_generate_and_enqueue_editor_styles * - * @param string $stylestyle_handle Handle for the registered stylesheet. + * @param string $stylesheet_handle Handle for the registered stylesheet. */ public function test_generate_and_enqueue_styles_default( $stylesheet_handle ) { /* @@ -123,7 +123,7 @@ public function test_generate_and_enqueue_styles_default( $stylesheet_handle ) { * * @dataProvider data_generate_and_enqueue_editor_styles * - * @param string $stylestyle_handle Handle for the registered stylesheet. + * @param string $stylesheet_handle Handle for the registered stylesheet. */ public function test_generate_and_enqueue_styles_with_permission( $stylesheet_handle ) { add_filter( 'has_remote_webfonts_request_permission', '__return_true' ); diff --git a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php index 67bf8bca9514f..33490659cc7c7 100644 --- a/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php +++ b/tests/phpunit/tests/webfonts-api/wpWebfontsRegistry.php @@ -234,7 +234,7 @@ public function test_get_by_provider_when_does_not_exist() { * @param string $provider_id Provider ID to query. * @param array $expected Expected return value. */ - public function test_get_by_provider_integrated( array $webfonts, $provider_id, $expected ) { + public function test_get_by_provider_integrated( array $webfonts, $provider_id, array $expected ) { $registry = new WP_Webfonts_Registry( new WP_Webfonts_Schema_Validator() ); foreach ( $webfonts as $webfont ) { From 6024894d1e2679b722568f30e7df23cba596f35e Mon Sep 17 00:00:00 2001 From: hellofromtonya Date: Sun, 7 Nov 2021 08:04:36 -0600 Subject: [PATCH 92/94] Adds remaining class DocBlock comments. --- .../class-wp-webfonts-registry.php | 22 ++++--- .../class-wp-webfonts-schema-validator.php | 11 +++- .../class-wp-webfonts-google-provider.php | 5 +- .../class-wp-webfonts-local-provider.php | 61 ++++++++++++++++++- .../providers/class-wp-webfonts-provider.php | 14 +++++ src/wp-includes/webfonts.php | 8 +-- .../providers/wpWebfontsLocalProvider.php | 59 ++++++++++++------ 7 files changed, 143 insertions(+), 37 deletions(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php index 46798a0717e37..1c8a3867414f8 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-registry.php @@ -8,15 +8,23 @@ */ /** - * Webfonts Registry. + * The Webfonts Registry handles the registration of webfonts and in-memory storage + * of the validated registered webfonts. * - * This registry exists to handle all webfonts. + * Each webfont is stored in the registry by a unique key composed of its + * font-family.font-style.font-weight. * - * It handles the following within the API: - * - loads the bundled provider files into memory; - * - registers each provider with the API by: - * 1. creating an instance (object); - * 2. storing it in-memory (by its unique provider ID) for use with the API; + * To optimize querying by provider ID {@see WP_Webfonts_Registry::get_by_provider()}, + * each webfont is also associated to its provider in an in-memory lookup map. + * + * During registration {@see WP_Webfonts_Registry::register()}, the following tasks + * occur: + * * snake_case properties are converted into + * kebab-case (i.e. valid CSS properties). + * * require properties are validated + * {@see WP_Webfonts_Schema_Validator::is_valid_schema()}. + * * optional properties are set if missing, else checked and, if invalid, set to a + * default value {@see WP_Webfonts_Schema_Validator::set_valid_properties()}. * * @since 5.9.0 */ diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 14fe139e28b2c..08e8769f647ca 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -8,9 +8,16 @@ */ /** - * Webfonts Schema Validator. + * The validator checks ensures a given webfont is ready for processing + * in the API. After processing, the API can trust each webfont schema, + * meaning no additional validation is needed within the API. * - * Validates the webfont schema. + * Required webfont properties are validated {@see WP_Webfonts_Schema_Validator::is_valid_schema()} + * and optional webfont properties are set if missing, else checked and, if invalid, set to a + * default value {@see WP_Webfonts_Schema_Validator::set_valid_properties()}. + * + * The validator is a dependency to the `WP_Webfonts_Registry` which + * interacts with this validator before registering a webfont. * * @since 5.9.0 */ diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php index a4b163430f17a..4379ca835e373 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-google-provider.php @@ -16,8 +16,9 @@ * then generates the `@font-face` styles. * * When enqueued styles are rendered, the Controller passes its - * 'google' webfonts via the `set_webfonts()` method and then triggers - * the processing via the `get_css()` method. + * 'google' webfonts {@see WP_Webfonts_Provider::set_setfonts()} + * and then triggers {@see WP_Webfonts_Google_Provider::get_css()} + * the processing to transform them into `@font-face` styles. * * All know-how (business logic) for how to interact with and * generate styles from Google Fonts API is contained in this provider. diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php index b80757ef596eb..4d8b02bd06c6e 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-local-provider.php @@ -8,7 +8,20 @@ */ /** - * Webfonts API provider for locally-hosted fonts. + * A core bundled provider for generating `@font-face` styles + * from locally-hosted font files. + * + * This provider builds an optimized `src` (for browser support) + * and then generates the `@font-face` styles. + * + * When enqueued styles are rendered, the Controller passes its + * 'local' webfonts {@see WP_Webfonts_Provider::set_setfonts()} + * and then triggers {@see WP_Webfonts_Local_Provider::get_css()} + * the processing to transform them into `@font-face` styles. + * + * All know-how (business logic) for how to interact with and + * generate styles from locally-hosted font files is contained + * in this provider. * * @since 5.9.0 */ @@ -24,11 +37,53 @@ class WP_Webfonts_Local_Provider extends WP_Webfonts_Provider { protected $id = 'local'; /** - * Get the CSS for a collection of webfonts. + * Gets the `@font-face` CSS styles for locally-hosted font files. + * + * This method does the following processing tasks: + * 1. Orchestrates an optimized `src` (with format) for browser support. + * 2. Generates the `@font-face` for all its webfonts. + * + * For example, when given these webfonts: + * + * array( + * 'source-serif-pro.normal.200 900' => array( + * 'provider' => 'local', + * 'font_family' => 'Source Serif Pro', + * 'font_weight' => '200 900', + * 'font_style' => 'normal', + * 'src' => 'https://example.com/wp-content/themes/twentytwentytwo/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2' ), + * ), + * 'source-serif-pro.italic.400 900' => array( + * 'provider' => 'local', + * 'font_family' => 'Source Serif Pro', + * 'font_weight' => '200 900', + * 'font_style' => 'italic', + * 'src' => 'https://example.com/wp-content/themes/twentytwentytwo/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2' ), + * ), + * ) + * + * + * the following `@font-face` styles are generated and returned: + * + * @font-face{ + * font-family:"Source Serif Pro"; + * font-style:normal; + * font-weight:200 900; + * font-stretch:normal; + * src:local("Source Serif Pro"), url('/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2') format('woff2'); + * } + * @font-face{ + * font-family:"Source Serif Pro"; + * font-style:italic; + * font-weight:200 900; + * font-stretch:normal; + * src:local("Source Serif Pro"), url('/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2') format('woff2'); + * } + * * * @since 5.9.0 * - * @return string The CSS. + * @return string The `@font-face` CSS. */ public function get_css() { $css = ''; diff --git a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php index e37b515b6dc96..73b756c5d37d2 100644 --- a/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php +++ b/src/wp-includes/webfonts-api/providers/class-wp-webfonts-provider.php @@ -12,6 +12,20 @@ /** * Abstract class for Webfonts API providers. * + * The starting point to building a webfont service provider. + * + * What is a Provider? + * + * A provider contains the know-how (business logic) for how to + * process its specific font service (i.e. local or remote) + * and how to generate the `@font-face` styles for its service. + * + * It receives a collection of webfonts from the Controller + * {@see WP_Webfonts_Provider::set_setfonts()}, and when requested + * {@see WP_Webfonts_Provider::get_css()}, it transforms them + * into styles (in a performant way for the provider service + * it manages). + * * @since 5.9.0 */ abstract class WP_Webfonts_Provider { diff --git a/src/wp-includes/webfonts.php b/src/wp-includes/webfonts.php index 279cf572a327e..1b120aad5839e 100644 --- a/src/wp-includes/webfonts.php +++ b/src/wp-includes/webfonts.php @@ -38,7 +38,7 @@ function wp_webfonts() { * and font-style of normal and italic: * * If the font files are contained within the theme: - * ``` + * * wp_register_webfonts( * array( * array( @@ -57,10 +57,10 @@ function wp_webfonts() { * ), * ) * ); - * ``` + * * * When requesting from the remote Google Fonts API service provider: - * ``` + * * wp_register_webfonts( * array( * array( @@ -77,7 +77,7 @@ function wp_webfonts() { * ), * ) * ); - * ``` + * * * @since 5.9.0 * diff --git a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php index 10f059d1fc160..c0b4dbcb54434 100644 --- a/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php +++ b/tests/phpunit/tests/webfonts-api/providers/wpWebfontsLocalProvider.php @@ -103,39 +103,60 @@ public function test_get_css( array $webfonts, $expected ) { */ public function data_get_css() { return array( - 'URL to root assets dir' => array( + 'truetype format' => array( 'webfonts' => array( - 'open-sans.italic.400 900' => array( + 'open-sans.italic.bold' => array( + 'provider' => 'local', + 'font-family' => 'Open Sans', + 'font-style' => 'italic', + 'font-weight' => 'bold', + 'src' => 'http://example.org/assets/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf', + ), + ), + 'expected' => << array( + 'webfonts' => array( + 'source-serif-pro.normal.200 900' => array( 'provider' => 'local', - 'font-family' => '"Open Sans"', - 'font-style' => 'italic', - 'font-weight' => '400 900', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'normal', + 'font-weight' => '200 900', 'font-stretch' => 'normal', - 'src' => 'http://example.org/assets/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf', + 'src' => 'http://example.org/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2', ), - 'open-sans.normal.400 900' => array( + 'source-serif-pro.italic.400 900' => array( 'provider' => 'local', - 'font-family' => '"Open Sans"', - 'font-style' => 'normal', - 'font-weight' => '400 900', + 'font-family' => 'Source Serif Pro', + 'font-style' => 'italic', + 'font-weight' => '200 900', 'font-stretch' => 'normal', - 'src' => 'http://example.org/assets/fonts/OpenSans-VariableFont_wdth,wght.ttf', + 'src' => 'http://example.org/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2', ), ), 'expected' => << Date: Mon, 8 Nov 2021 09:31:06 +0200 Subject: [PATCH 93/94] typo fix --- .../webfonts-api/class-wp-webfonts-schema-validator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php index 08e8769f647ca..263f5595b9b02 100644 --- a/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php +++ b/src/wp-includes/webfonts-api/class-wp-webfonts-schema-validator.php @@ -281,7 +281,7 @@ private function set_valid_font_style() { ) { trigger_error( __( 'Webfont font style must be a non-empty string.' ) ); - } elseif ( // Bail out if the font-weight is a valid value. + } elseif ( // Bail out if the font-style is a valid value. in_array( $this->webfont['font-style'], self::VALID_FONT_STYLE, true ) || preg_match( '/^oblique\s+(\d+)%/', $this->webfont['font-style'] ) ) { From 0ce34f62c54ccfe04cfc1e469fe2ffb07b90a468 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Thu, 18 Nov 2021 15:52:11 +0200 Subject: [PATCH 94/94] should be an instance of WP_Webfonts_Controller, not WP_Webfonts --- src/wp-includes/webfonts.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/webfonts.php b/src/wp-includes/webfonts.php index 1b120aad5839e..2ad7fcee36eb9 100644 --- a/src/wp-includes/webfonts.php +++ b/src/wp-includes/webfonts.php @@ -18,7 +18,7 @@ function wp_webfonts() { static $instance; - if ( ! $instance instanceof WP_Webfonts ) { + if ( ! $instance instanceof WP_Webfonts_Controller ) { $instance = new WP_Webfonts_Controller( new WP_Webfonts_Registry( new WP_Webfonts_Schema_Validator()