summaryrefslogtreecommitdiffstats
path: root/wp-includes/fonts/class-wp-font-collection.php
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--wp-includes/fonts/class-wp-font-collection.php295
1 files changed, 295 insertions, 0 deletions
diff --git a/wp-includes/fonts/class-wp-font-collection.php b/wp-includes/fonts/class-wp-font-collection.php
new file mode 100644
index 0000000..b915e3e
--- /dev/null
+++ b/wp-includes/fonts/class-wp-font-collection.php
@@ -0,0 +1,295 @@
+<?php
+/**
+ * Font Collection class.
+ *
+ * This file contains the Font Collection class definition.
+ *
+ * @package WordPress
+ * @subpackage Fonts
+ * @since 6.5.0
+ */
+
+/**
+ * Font Collection class.
+ *
+ * @since 6.5.0
+ *
+ * @see wp_register_font_collection()
+ */
+final class WP_Font_Collection {
+ /**
+ * The unique slug for the font collection.
+ *
+ * @since 6.5.0
+ * @var string
+ */
+ public $slug;
+
+ /**
+ * Font collection data.
+ *
+ * @since 6.5.0
+ * @var array|WP_Error|null
+ */
+ private $data;
+
+ /**
+ * Font collection JSON file path or URL.
+ *
+ * @since 6.5.0
+ * @var string|null
+ */
+ private $src;
+
+ /**
+ * WP_Font_Collection constructor.
+ *
+ * @since 6.5.0
+ *
+ * @param string $slug Font collection slug. May only contain alphanumeric characters, dashes,
+ * and underscores. See sanitize_title().
+ * @param array $args Font collection data. See wp_register_font_collection() for information on accepted arguments.
+ */
+ public function __construct( string $slug, array $args ) {
+ $this->slug = sanitize_title( $slug );
+ if ( $this->slug !== $slug ) {
+ _doing_it_wrong(
+ __METHOD__,
+ /* translators: %s: Font collection slug. */
+ sprintf( __( 'Font collection slug "%s" is not valid. Slugs must use only alphanumeric characters, dashes, and underscores.' ), $slug ),
+ '6.5.0'
+ );
+ }
+
+ $required_properties = array( 'name', 'font_families' );
+
+ if ( isset( $args['font_families'] ) && is_string( $args['font_families'] ) ) {
+ // JSON data is lazy loaded by ::get_data().
+ $this->src = $args['font_families'];
+ unset( $args['font_families'] );
+
+ $required_properties = array( 'name' );
+ }
+
+ $this->data = $this->sanitize_and_validate_data( $args, $required_properties );
+ }
+
+ /**
+ * Retrieves the font collection data.
+ *
+ * @since 6.5.0
+ *
+ * @return array|WP_Error An array containing the font collection data, or a WP_Error on failure.
+ */
+ public function get_data() {
+ if ( is_wp_error( $this->data ) ) {
+ return $this->data;
+ }
+
+ // If the collection uses JSON data, load it and cache the data/error.
+ if ( isset( $this->src ) ) {
+ $this->data = $this->load_from_json( $this->src );
+ }
+
+ if ( is_wp_error( $this->data ) ) {
+ return $this->data;
+ }
+
+ // Set defaults for optional properties.
+ $defaults = array(
+ 'description' => '',
+ 'categories' => array(),
+ );
+
+ return wp_parse_args( $this->data, $defaults );
+ }
+
+ /**
+ * Loads font collection data from a JSON file or URL.
+ *
+ * @since 6.5.0
+ *
+ * @param string $file_or_url File path or URL to a JSON file containing the font collection data.
+ * @return array|WP_Error An array containing the font collection data on success,
+ * else an instance of WP_Error on failure.
+ */
+ private function load_from_json( $file_or_url ) {
+ $url = wp_http_validate_url( $file_or_url );
+ $file = file_exists( $file_or_url ) ? wp_normalize_path( realpath( $file_or_url ) ) : false;
+
+ if ( ! $url && ! $file ) {
+ // translators: %s: File path or URL to font collection JSON file.
+ $message = __( 'Font collection JSON file is invalid or does not exist.' );
+ _doing_it_wrong( __METHOD__, $message, '6.5.0' );
+ return new WP_Error( 'font_collection_json_missing', $message );
+ }
+
+ $data = $url ? $this->load_from_url( $url ) : $this->load_from_file( $file );
+
+ if ( is_wp_error( $data ) ) {
+ return $data;
+ }
+
+ $data = array(
+ 'name' => $this->data['name'],
+ 'font_families' => $data['font_families'],
+ );
+
+ if ( isset( $this->data['description'] ) ) {
+ $data['description'] = $this->data['description'];
+ }
+
+ if ( isset( $this->data['categories'] ) ) {
+ $data['categories'] = $this->data['categories'];
+ }
+
+ return $data;
+ }
+
+ /**
+ * Loads the font collection data from a JSON file path.
+ *
+ * @since 6.5.0
+ *
+ * @param string $file File path to a JSON file containing the font collection data.
+ * @return array|WP_Error An array containing the font collection data on success,
+ * else an instance of WP_Error on failure.
+ */
+ private function load_from_file( $file ) {
+ $data = wp_json_file_decode( $file, array( 'associative' => true ) );
+ if ( empty( $data ) ) {
+ return new WP_Error( 'font_collection_decode_error', __( 'Error decoding the font collection JSON file contents.' ) );
+ }
+
+ return $this->sanitize_and_validate_data( $data, array( 'font_families' ) );
+ }
+
+ /**
+ * Loads the font collection data from a JSON file URL.
+ *
+ * @since 6.5.0
+ *
+ * @param string $url URL to a JSON file containing the font collection data.
+ * @return array|WP_Error An array containing the font collection data on success,
+ * else an instance of WP_Error on failure.
+ */
+ private function load_from_url( $url ) {
+ // Limit key to 167 characters to avoid failure in the case of a long URL.
+ $transient_key = substr( 'wp_font_collection_url_' . $url, 0, 167 );
+ $data = get_site_transient( $transient_key );
+
+ if ( false === $data ) {
+ $response = wp_safe_remote_get( $url );
+ if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
+ return new WP_Error(
+ 'font_collection_request_error',
+ sprintf(
+ // translators: %s: Font collection URL.
+ __( 'Error fetching the font collection data from "%s".' ),
+ $url
+ )
+ );
+ }
+
+ $data = json_decode( wp_remote_retrieve_body( $response ), true );
+ if ( empty( $data ) ) {
+ return new WP_Error( 'font_collection_decode_error', __( 'Error decoding the font collection data from the HTTP response JSON.' ) );
+ }
+
+ // Make sure the data is valid before storing it in a transient.
+ $data = $this->sanitize_and_validate_data( $data, array( 'font_families' ) );
+ if ( is_wp_error( $data ) ) {
+ return $data;
+ }
+
+ set_site_transient( $transient_key, $data, DAY_IN_SECONDS );
+ }
+
+ return $data;
+ }
+
+ /**
+ * Sanitizes and validates the font collection data.
+ *
+ * @since 6.5.0
+ *
+ * @param array $data Font collection data to sanitize and validate.
+ * @param array $required_properties Required properties that must exist in the passed data.
+ * @return array|WP_Error Sanitized data if valid, otherwise a WP_Error instance.
+ */
+ private function sanitize_and_validate_data( $data, $required_properties = array() ) {
+ $schema = self::get_sanitization_schema();
+ $data = WP_Font_Utils::sanitize_from_schema( $data, $schema );
+
+ foreach ( $required_properties as $property ) {
+ if ( empty( $data[ $property ] ) ) {
+ $message = sprintf(
+ // translators: 1: Font collection slug, 2: Missing property name, e.g. "font_families".
+ __( 'Font collection "%1$s" has missing or empty property: "%2$s".' ),
+ $this->slug,
+ $property
+ );
+ _doing_it_wrong( __METHOD__, $message, '6.5.0' );
+ return new WP_Error( 'font_collection_missing_property', $message );
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Retrieves the font collection sanitization schema.
+ *
+ * @since 6.5.0
+ *
+ * @return array Font collection sanitization schema.
+ */
+ private static function get_sanitization_schema() {
+ return array(
+ 'name' => 'sanitize_text_field',
+ 'description' => 'sanitize_text_field',
+ 'font_families' => array(
+ array(
+ 'font_family_settings' => array(
+ 'name' => 'sanitize_text_field',
+ 'slug' => static function ( $value ) {
+ return _wp_to_kebab_case( sanitize_title( $value ) );
+ },
+ 'fontFamily' => 'WP_Font_Utils::sanitize_font_family',
+ 'preview' => 'sanitize_url',
+ 'fontFace' => array(
+ array(
+ 'fontFamily' => 'sanitize_text_field',
+ 'fontStyle' => 'sanitize_text_field',
+ 'fontWeight' => 'sanitize_text_field',
+ 'src' => static function ( $value ) {
+ return is_array( $value )
+ ? array_map( 'sanitize_text_field', $value )
+ : sanitize_text_field( $value );
+ },
+ 'preview' => 'sanitize_url',
+ 'fontDisplay' => 'sanitize_text_field',
+ 'fontStretch' => 'sanitize_text_field',
+ 'ascentOverride' => 'sanitize_text_field',
+ 'descentOverride' => 'sanitize_text_field',
+ 'fontVariant' => 'sanitize_text_field',
+ 'fontFeatureSettings' => 'sanitize_text_field',
+ 'fontVariationSettings' => 'sanitize_text_field',
+ 'lineGapOverride' => 'sanitize_text_field',
+ 'sizeAdjust' => 'sanitize_text_field',
+ 'unicodeRange' => 'sanitize_text_field',
+ ),
+ ),
+ ),
+ 'categories' => array( 'sanitize_title' ),
+ ),
+ ),
+ 'categories' => array(
+ array(
+ 'name' => 'sanitize_text_field',
+ 'slug' => 'sanitize_title',
+ ),
+ ),
+ );
+ }
+}