Make WordPress Core

Changes between Version 1 and Version 2 of Ticket #51525, comment 4


Ignore:
Timestamp:
10/19/2020 02:35:40 PM (3 years ago)
Author:
giuseppe.mazzapica
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #51525, comment 4

    v1 v2  
    2121function apply_filters_typesafe( $tag, $arguments = array(), $value = null, ...$values ) {
    2222
    23     if ( null === $value ) {
    24         return apply_filters( $tag, $value, ...$values );
     23    if ( ! has_filter( $tag ) ) {
     24        return $value;
     25    }
     26
     27    static $types_map;
     28    if (!$types_map) {
     29        $types_map = [
     30            'boolean' => 'boolean',
     31            'integer' => 'numeric',
     32            'double' => 'numeric',
     33            'string' => 'string',
     34            'array' => 'array',
     35            'resource' => 'resource',
     36            'resource (closed)' => 'resource',
     37            'NULL' => 'mixed',
     38        ];
    2539    }
    2640
    2741    $type = gettype( $value );
    28     $is_object = false;
    29     switch ( $type ) {
    30         case 'boolean':
    31             $accepted_types = array( 'boolean' );
    32             break;
    33         case 'integer':
    34         case 'double':
    35             $accepted_types = array( 'numeric' );
    36             break;
    37         case 'string':
    38             $accepted_types = array( 'string' );
    39             break;
    40         case 'array':
    41             $accepted_types = array( 'array' );
    42             break;
    43         case 'resource':
    44         case 'resource (closed)':
    45             $accepted_types = array( 'resource' );
    46             break;
    47         case 'object':
    48             $is_object = true;
    49         default:
    50             $accepted_types = array();
    51     }
     42    $is_object = is_object( $value );
     43    $accepted_types = isset( $types_map[ $type ] ) ? array( $types_map[ $type ] ) : array();
    5244
     45    // Do not calculate multiple times for same class.
     46    static $classes_types = [];
    5347    // Skip calculation of accepted types if they are are explicitly passed.
    5448    if ( $is_object && empty ( $arguments['accepted_types'] ) ) {
    5549        $class = get_class( $value );
    56         $accepted_types = array( $class );
    57         $parent = get_parent_class( $class );
    58         while ( $parent ) {
    59             $accepted_types[] = $parent;
    60             $parent = get_parent_class( $parent );
     50        if ( isset( $classes_types[ $class ] ) ) {
     51            $accepted_types = $classes_types[ $class ];
     52        } else {
     53            $accepted_types = array( $class );
     54            $parent = get_parent_class( $class );
     55            while ( $parent ) {
     56                $accepted_types[] = $parent;
     57                $parent = get_parent_class( $parent );
     58            }
     59
     60            $accepted_types = array_merge( $accepted_types, class_implements( $class ) );
    6161        }
    6262
    63         $accepted_types = array_merge( $accepted_types, class_implements( $class ) );
     63        $classes_types[ $class ] = $accepted_types;
    6464    }
    6565
    66     // Can't use wp_parse_args because no external dependencies.
    6766    $arguments = array_replace(
    6867        array(
     
    7776    $to_filter = $is_object ? clone $value : $value;
    7877
    79     $filtered = apply_filters( $tag, $to_filter, ...$values );
     78    // `next_filter` function doesn't exist, but you get the point
     79    $filter = next_filter( $tag );
     80    $filtered = $filter( $to_filter, ...$values );
    8081
    8182    // 'mixed' is a valid PHP 8 pseudo-type so we support for consistency.
    8283    // That said, if mixed is fine then just use apply_filters.
    8384    if ( in_array( 'mixed', (array)$arguments['accepted_types'] ) ) {
    84         return $filtered;
     85        // `has_next_filter` function doesn't exist, but you get the point
     86        return has_next_filter( $tag )
     87            ? apply_filters_typesafe( $tag, $arguments, $filtered, ...$values )
     88            : $filtered;
    8589    }
    8690
     
    9094    }
    9195
    92     if ( ( null === $filtered && !$arguments['nullable'] ) ) {
    93         if ( $can_do_it_wrong ) {
    94             _doing_it_wrong(
    95                 __FUNCTION__,
    96                 "Filters for $tag where not expected to return null.",
    97                 '5.6'
    98             );
     96    if ( null === $filtered ) {
     97        if ( !$arguments['nullable'] ) {
     98            $filtered = $original;
     99
     100            if ( $can_do_it_wrong ) {
     101                _doing_it_wrong(
     102                    __FUNCTION__,
     103                    "Filters for '$tag' where not expected to return null.",
     104                    '5.6'
     105                );
     106            }
    99107        }
    100108
    101         return $original;
     109        // `has_next_filter` function doesn't exist, but you get the point
     110        return has_next_filter( $tag )
     111            ? apply_filters_typesafe( $tag, $arguments, $filtered, ...$values )
     112            : $filtered;
    102113    }
    103114
     
    105116    if ( ! $functions ) {
    106117        $functions = array(
     118            'int' => 'is_int',
    107119            'integer' => 'is_int',
    108120            'double' => 'is_float',
     
    122134    }
    123135
    124     foreach ( $arguments['accepted_types'] as $type ) {
     136    foreach ( (array)$arguments['accepted_types'] as $type ) {
    125137        if ( isset( $functions[ $type ] ) && call_user_func( $functions[ $type ], $filtered ) ) {
    126             return $filtered;
     138            // `has_next_filter` function doesn't exist, but you get the point
     139            return has_next_filter( $tag )
     140                ? apply_filters_typesafe( $tag, $arguments, $filtered, ...$values )
     141                : $filtered;
    127142        }
    128143
    129144        if ( $is_object && is_string ( $type ) && is_a( $filtered, $type ) ) {
    130             return $filtered;
     145            // `has_next_filter` function doesn't exist, but you get the point
     146            return has_next_filter( $tag )
     147                ? apply_filters_typesafe( $tag, $arguments, $filtered, ...$values )
     148                : $filtered;
    131149        }
    132150    }
    133151
    134152    if ( $can_do_it_wrong ) {
    135         $expected = implode( ', ', $arguments['accepted_types'] );
     153        $expected = implode( "', '", $arguments['accepted_types'] );
    136154        $actual = is_object( $filtered ) ? 'instance of ' . get_class($filtered) : gettype( $filtered );
    137155        _doing_it_wrong(
    138156            __FUNCTION__,
    139             "Filters for $tag where expected to return a value of one of types $expected. Got $actual instead.",
     157            "Filters for '$tag' where expected to return a value of one of types: '$expected'. Got '$actual' instead.",
    140158            '5.6'
    141159        );
    142160    }
    143 
    144     return $original;
     161   
     162    // `has_next_filter` function doesn't exist, but you get the point
     163    return has_next_filter( $tag )
     164        ? apply_filters_typesafe( $tag, $arguments, $original, ...$values )
     165        : $original;
    145166}
    146 
    147167}}}
    148168