@ 2020-10-19T13:44:30Z <?php
// Mocking WordPress
function add_filter(string $tag, callable $value) {
$GLOBALS[$tag][] = $value;
}
function _doing_it_wrong($fn, string $message) {
echo "\n--------------\nDOING IT WRONG: $message\n--------------\n";
}
// ------------------------------------------------------------------
function apply_filters_typesafe( $tag, $arguments = array(), $value = null, ...$values ) {
if ( null === $value || empty( $GLOBALS[$tag] ) ) {
return $value;
}
$type = gettype( $value );
$is_object = false;
switch ( $type ) {
case 'boolean':
$accepted_types = array( 'boolean' );
break;
case 'integer':
case 'double':
$accepted_types = array( 'numeric' );
break;
case 'string':
$accepted_types = array( 'string' );
break;
case 'array':
$accepted_types = array( 'array' );
break;
case 'resource':
case 'resource (closed)':
$accepted_types = array( 'resource' );
break;
case 'object':
$is_object = true;
default:
$accepted_types = array();
}
// Skip calculation of accepted types if they are are explicitly passed.
if ( $is_object && empty ( $arguments['accepted_types'] ) ) {
$class = get_class( $value );
$accepted_types = array( $class );
$parent = get_parent_class( $class );
while ( $parent ) {
$accepted_types[] = $parent;
$parent = get_parent_class( $parent );
}
$accepted_types = array_merge( $accepted_types, class_implements( $class ) );
}
$arguments = array_replace(
array(
'nullable' => false,
'accepted_types' => $accepted_types
),
$arguments
);
$original = $value;
// Objects are passed by ref, clone to return original unchanged in case of errors.
$to_filter = $is_object ? clone $value : $value;
$filter = array_shift($GLOBALS[$tag]);
$filtered = $filter( $tag, $to_filter, ...$values );
// 'mixed' is a valid PHP 8 pseudo-type so we support for consistency.
// That said, if mixed is fine then just use apply_filters.
if ( in_array( 'mixed', (array)$arguments['accepted_types'] ) ) {
return $GLOBALS[$tag]
? apply_filters_typesafe( $tag, $arguments, $filtered, ...$values )
: $filtered;
}
static $can_do_it_wrong = false;
if ( ! $can_do_it_wrong && function_exists( '_doing_it_wrong' ) ) {
$can_do_it_wrong = true;
}
if ( ( null === $filtered && !$arguments['nullable'] ) ) {
if ( $can_do_it_wrong ) {
_doing_it_wrong(
__FUNCTION__,
"Filters for '$tag' where not expected to return null.",
'5.6'
);
}
return $GLOBALS[$tag]
? apply_filters_typesafe( $tag, $arguments, $original, ...$values )
: $original;
}
static $functions;
if ( ! $functions ) {
$functions = array(
'integer' => 'is_int',
'double' => 'is_float',
'float' => 'is_float',
'numeric' => 'is_numeric',
'number' => 'is_numeric',
'bool' => 'is_bool',
'boolean' => 'is_boolean',
'string' => 'is_string',
'array' => 'is_array',
'callable' => 'is_callable',
'function' => 'is_callable',
'resource' => 'is_resource',
'iterable' => 'is_iterable',
'countable' => 'is_countable',
);
}
foreach ( $arguments['accepted_types'] as $type ) {
if ( isset( $functions[ $type ] ) && call_user_func( $functions[ $type ], $filtered ) ) {
return $GLOBALS[$tag]
? apply_filters_typesafe( $tag, $arguments, $filtered, ...$values )
: $filtered;
}
if ( $is_object && is_string ( $type ) && is_a( $filtered, $type ) ) {
return $GLOBALS[$tag]
? apply_filters_typesafe( $tag, $arguments, $filtered, ...$values )
: $filtered;
}
}
if ( $can_do_it_wrong ) {
$expected = implode( "', '", $arguments['accepted_types'] );
$actual = is_object( $filtered ) ? 'instance of ' . get_class($filtered) : gettype( $filtered );
_doing_it_wrong(
__FUNCTION__,
"Filters for '$tag' where expected to return a value of one of types: '$expected'. Got '$actual' instead.",
'5.6'
);
}
return $GLOBALS[$tag]
? apply_filters_typesafe( $tag, $arguments, $original, ...$values )
: $original;
}
// Let's try now to add some hooks, the first is bad, the second is good.
add_filter('hello', function () {
return 'This is not a callable' ;
});
add_filter('hello', function () {
return function () {
return 'It works!';
};
});
// And lets' see if it works:
$callable = apply_filters_typesafe('hello', ['accepted_types' => ['callable']], '__return_empty_string');
echo $callable();
Enable javascript to submit You have javascript disabled. You will not be able to edit any code.
Output for 7.2.0 - 7.2.34 , 7.3.0 - 7.3.33 , 7.4.0 - 7.4.33 , 8.0.0 - 8.0.30 , 8.1.0 - 8.1.32 , 8.2.0 - 8.2.28 , 8.3.0 - 8.3.22 , 8.4.1 - 8.4.8 --------------
DOING IT WRONG: Filters for 'hello' where expected to return a value of one of types: 'callable'. Got 'string' instead.
--------------
It works! preferences:dark mode live preview ace vim emacs key bindings
147.32 ms | 407 KiB | 5 Q