<?php
// All of the code needed for this test.
function __( $text ) {
return $text;
}
/**
* Webfonts Schema Validator.
*
* Validates the webfont schema.
*/
class WP_Webfonts_Schema_Validator {
/**
* Valid font styles.
*
* @since 5.9.0
*
* @var string[]
*/
const VALID_FONT_STYLE = array( 'normal', 'italic', 'oblique', 'inherit', 'initial', 'revert', 'unset' );
/**
* Valid font display values.
*
* @since 5.9.0
*
* @var string[]
*/
const VALID_FONT_DISPLAY = array( 'auto', 'block', 'fallback', 'swap' );
/**
* Valid font weight values.
*
* @since 5.9.0
*
* @var string[]
*/
const VALID_FONT_WEIGHT = array( 'normal', 'bold', 'bolder', 'lighter', 'inherit' );
/**
* 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.
*
* Set as a property for performance.
*
* @var array
*/
private $webfont = array();
/**
* Checks if the given webfont schema is valid.
*
* @since 5.9.0
*
* @param array $webfont Webfont to validate.
* @return bool True when valid. False when invalid.
*/
public function is_valid_schema( array $webfont ) {
$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;
}
/**
* Checks if the provider is validate.
*
* @since 5.9.0
*
* @param array $webfont Webfont to validate.
* @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'] ) ||
! is_string( $webfont['provider'] )
) {
trigger_error( __( 'Webfont provider must be a non-empty string.' ) );
return false;
}
return true;
}
/**
* Checks if the font family is validate.
*
* @since 5.9.0
*
* @param array $webfont Webfont to validate.
* @return bool True when valid. False when invalid.
*/
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;
}
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.
*
* @since 5.9.0
*
* @param array $webfont Webfont definition.
* @return array Updated webfont.
*/
public function set_valid_properties( array $webfont ) {
$this->webfont = array_merge( $this->basic_schema, $webfont );
$this->set_valid_font_face_property();
$this->set_valid_font_style();
$this->set_valid_font_weight();
$this->set_valid_font_display();
$webfont = $this->webfont;
$this->webfont = array(); // Reset property.
return $webfont;
}
/**
* Checks if the CSS property is valid for @font-face.
*
* @since 5.9.0
*/
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 ] );
}
}
}
/**
* 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'], self::VALID_FONT_STYLE, true ) ||
preg_match( '/^oblique\s+(\d+)%/', $this->webfont['font-style'] )
) {
return;
}
$this->webfont['font-style'] = 'normal';
}
/**
* Sets a default font weight if invalid.
*
* @since 5.9.0
*/
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.' ) );
} elseif ( // Bail out if the font-weight is a valid value.
// 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;
}
// 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'], self::VALID_FONT_DISPLAY, true )
) {
return;
}
$this->webfont['font-display'] = 'fallback';
}
}
class Profiler {
private $validator;
private $total_in_microseconds = 0.0;
private $cycles = 0;
public function __construct( $validator ) {
$this->validator = $validator;
}
public function register( array $webfont ) {
$webfont = $this->convert_to_kabeb_case( $webfont );
$this->cycles++;
$start_time = microtime( true );
// Validate schema.
if ( ! $this->validator->is_valid_schema( $webfont ) ) {
// return ''; // Skip for this test.
}
$webfont = $this->validator->set_valid_properties( $webfont );
// Do the registration.
// Get stats.
$end_time = microtime( true );
$this->total_in_microseconds += ( $end_time - $start_time ) * 1000000.0; // store in microseconds.
}
private function convert_to_kabeb_case( array $webfont ) {
$kebab_case = preg_replace( '/(?<!^)[A-Z]/', '-$0', array_keys( $webfont ) );
$kebab_case = array_map( 'strtolower', $kebab_case );
return array_combine( $kebab_case, array_values( $webfont ) );
}
public function get_avg_microseconds() {
return $this->total_in_microseconds / $this->cycles;
}
}
// Let's Go!
$profiler = new Profiler( new WP_Webfonts_Schema_Validator() );
$webfont = array(
'fontFamily' => 'Source Serif Pro',
'fontWeight' => '200 900',
'fontStyle' => 'normal',
'fontStretch' => 'normal',
'src' => 'file:./assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2',
'provider' => 'local',
);
/**
* Benchmark the average time the Validator takes to validate a single webfont.
* The profiler is set to 10,000 cycles to get a fair sampling and distribution.
*/
$num_time_to_run_validator = 10000;
// do work.
for ( $i = 1; $i <= $num_time_to_run_validator; $i++ ) {
$profiler->register( $webfont );
}
printf( "Ran %d in %s microseconds",
$num_time_to_run_validator,
number_format( $profiler->get_avg_microseconds(), 4 )
);
- Output for git.master
- Ran 10000 in 2.6662 microseconds
- Output for git.master_jit
- Ran 10000 in 2.7155 microseconds
- Output for rfc.property-hooks
- Ran 10000 in 1.6104 microseconds
This tab shows result from various feature-branches currently under review by the php developers. Contact me to have additional branches featured.
Active branches
Archived branches
Once feature-branches are merged or declined, they are no longer available. Their functionality (when merged) can be viewed from the main output page
preferences:
29.83 ms | 407 KiB | 5 Q