• File: class-wp-font-collection.php
  • Full Path: /home/u505525515/domains/medineeestates.com/public_html/wp-includes/fonts/class-wp-font-collection.php
  • Date Modified: 05/25/2026 6:29 AM
  • File size: 8.68 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?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',
                ),
            ),
        );
    }
}