@ 2021-10-18T22:33:50Z <?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 )
);
Enable javascript to submit You have javascript disabled. You will not be able to edit any code.
Output for 8.4.2 Ran 10000 in 1.6888 microseconds Output for 8.4.1 Ran 10000 in 1.6847 microseconds Output for 8.3.15 Ran 10000 in 2.6340 microseconds Output for 8.3.14 Ran 10000 in 1.6368 microseconds Output for 8.3.13 Ran 10000 in 1.6343 microseconds Output for 8.3.12 Ran 10000 in 1.6864 microseconds Output for 8.3.11 Ran 10000 in 2.0219 microseconds Output for 8.3.10 Ran 10000 in 1.6677 microseconds Output for 8.3.9 Ran 10000 in 1.6211 microseconds Output for 8.3.8 Ran 10000 in 2.0905 microseconds Output for 8.3.7 Ran 10000 in 3.0412 microseconds Output for 8.3.6 Ran 10000 in 4.1990 microseconds Output for 8.3.5 Warning: PHP Startup: Unable to load dynamic library 'sodium.so' (tried: /usr/lib/php/8.3.5/modules/sodium.so (libsodium.so.23: cannot open shared object file: No such file or directory), /usr/lib/php/8.3.5/modules/sodium.so.so (/usr/lib/php/8.3.5/modules/sodium.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
Ran 10000 in 3.0553 microseconds Output for 8.3.4 Ran 10000 in 2.9318 microseconds Output for 8.3.3 Ran 10000 in 3.3287 microseconds Output for 8.3.2 Ran 10000 in 1.6985 microseconds Output for 8.3.1 Ran 10000 in 3.1882 microseconds Output for 8.3.0 Ran 10000 in 1.7071 microseconds Output for 8.2.27 Ran 10000 in 1.6796 microseconds Output for 8.2.26 Ran 10000 in 1.8962 microseconds Output for 8.2.25 Ran 10000 in 1.6220 microseconds Output for 8.2.24 Ran 10000 in 2.4544 microseconds Output for 8.2.23 Ran 10000 in 1.6439 microseconds Output for 8.2.22 Ran 10000 in 1.6470 microseconds Output for 8.2.21 Ran 10000 in 1.7876 microseconds Output for 8.2.20 Ran 10000 in 2.0853 microseconds Output for 8.2.19 Ran 10000 in 3.4909 microseconds Output for 8.2.18 Ran 10000 in 3.9805 microseconds Output for 8.2.17 Ran 10000 in 3.0434 microseconds Output for 8.2.16 Ran 10000 in 2.9504 microseconds Output for 8.2.15 Ran 10000 in 3.4366 microseconds Output for 8.2.14 Ran 10000 in 1.6483 microseconds Output for 8.2.13 Ran 10000 in 2.7213 microseconds Output for 8.2.12 Ran 10000 in 1.6361 microseconds Output for 8.2.11 Ran 10000 in 2.8729 microseconds Output for 8.2.10 Ran 10000 in 3.1891 microseconds Output for 8.1.31 Ran 10000 in 1.7001 microseconds Output for 8.1.30 Ran 10000 in 1.6673 microseconds Output for 8.1.29 Ran 10000 in 2.0177 microseconds Output for 8.1.28 Ran 10000 in 3.1056 microseconds Output for 8.1.27 Ran 10000 in 1.5858 microseconds Output for 8.1.26 Ran 10000 in 1.6865 microseconds Output for 8.1.25 Ran 10000 in 1.8417 microseconds Output for 8.1.24 Ran 10000 in 2.7932 microseconds Output for 8.1.23 Ran 10000 in 2.7701 microseconds Output for 8.0.11 Ran 10000 in 2.9277 microseconds Output for 7.4.24 Ran 10000 in 2.9567 microseconds preferences:dark mode live preview
77.54 ms | 469 KiB | 5 Q