Make WordPress Core

Changes between Initial Version and Version 7 of Ticket #51525


Ignore:
Timestamp:
10/19/2020 05:13:45 PM (5 years ago)
Author:
jrf
Comment:

Hi @giuseppe.mazzapica, thank you for sharing your thoughts on this. You bring up some good points, though unfortunately I disagree with nearly all.

As the title of the issue seems to leave the impression that these functions should support full type declarations as supported by PHP, I'm going to change the _typesafe in the suggested function names to _single_type.

Let's step through the points you bring up one by one:

Regarding null

Regarding null there are two cases: the first is that null is the value to be filtered, the second is that null is the value returned by the filters. In the first case, there's basically nothing to do, passing null to apply_filters_typesafe would equal to apply_filters. In the second case, null could be acceptable by the caller.

null should never be acceptable. As stated before, passing null as the original value, would mean that running the filters is useless as null would be the only acceptable outcome. Regarding filter functions returning null, that is also not acceptable as it breaks the principle of this function being typesafe. Filters should always return a value and in the case of filters called via these functions, a value of the same type as originally received, so null would never be an acceptable return.

You are basically suggesting to support "nullable types" in this function. This goes against the principle of one type and one type only and would significantly reduce the advantages of having these functions in the first place.

Regarding callable

I can't think of a single reason for a callable being filterable. To be honest, that just sounds like a security issue waiting to be exploited, which I don't think is a good case to support.

If people still want to do so, let them use apply_filters(), but I'd really wouldn't want to support it in a function with safe in the function name.

Regarding iterable and countable

These I'd need to have a think about, but I'm leaning against a "no", if for no other reason than that it would still require the code in the callback functions hooked into the "typesafe" filter to allow for several different types and several different way to add or remove something from the iterable/countable.

Again, that situation can be handled by using apply_filters().

Regarding number and numeric

First of all, this violates the single type principle again. Second of all, too few people actually understand that is_numeric() will also accept NAN, INF, '1e1' (floats in exponent annotation), ' 1' and more.

Also see: https://phpcheatsheets.com/test/numeric_tests/

Supporting number/numeric would also negate some of the advantages regarding PHP 8, which introduces "saner numeric comparisons" and "saner numeric strings". In other words, the behaviour of whatever is passed through would become unpredicatable PHP cross-version, which goes against the whole point of these new functions and would make identifying the callbacks doing it wrong again more difficult.

Regarding mixed

No. Just plain no. Use apply_filters(), that's the behaviour you expect there. Just like union types, mixed should not be supported by this function.

Regarding adding an extra argument, let's call it $type_spec.

I tried to avoid adding an extra argument to make switching existing calls from apply_filters() to the new functions as easy as possible, but I can see your point about objects.

If we'd go that way, we would basically need to have a "type" key and an "instanceof" key. If "instanceof" is set, "type" would not need to be set, though setting it to object would be acceptable.

But then, why have two array keys ? We know what the supported basic types are and if whatever is passed (as a string) is not one of the basic types, doing an instanceof could be done automatically.

Also, and again if we'd go the way of adding a "type" argument, then I'd advocate for the original value being passed to apply_filters_single_type() to also be evaluated for validity before any callback functions are called, a doing it wrong being thrown if it is not of the expected type and the function short circuiting (not calling any callbacks) if the wrong type was passed.

This function should never fallback to calling apply_filters() as in that case, it negates the whole point of having this function in place, as callbacks would still not be able to trust the input received from via "typesafe" hooks.

plugin.php can't have external dependencies, so before doing any _doing_it_wrong it is necessary to check that function is defined.

Good point :+1:

// Objects are passed by ref, clone to return original unchanged in case of errors.

Good point, this should definitely be taken into account.

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #51525

    • Property Summary changed from Add new functions apply_filters_typesafe() and apply_filters_ref_array_typesafe() to Add new functions apply_filters_single_type() and apply_filters_ref_array_single_type()
  • Ticket #51525 – Description

    initial v7  
    1 This is a proposal to add two new functions `apply_filters_typesafe()` and `apply_filters_ref_array_typesafe()`.
     1This is a proposal to add two new functions ~~`apply_filters_typesafe()`~~ `apply_filters_single_type()` and ~~`apply_filters_ref_array_typesafe()`~~ `apply_filters_ref_array_single_type()`.
    22
    33== Underlying reasons
     
    2525== Proposed solution
    2626
    27 To combat this situation I propose to add two new functions `apply_filters_typesafe()` and `apply_filters_ref_array_typesafe()`.
     27To combat this situation I propose to add two new functions `apply_filters_single_type()` and `apply_filters_ref_array_single_type()`.
    2828
    2929These functions would basically have the same underlying logic as the existing `apply_filters()` and `apply_filters_ref_array()` functions, with one big difference:
    3030* At the start of the function, the variable type of the second parameter (the value being filtered) received is checked and remembered.
    31 * In the loop which calls the hooked in functions, a check is done on the variable type received back from each function and if it doesn't match the expected variable type, the previous value is used for the call to the next hooked-in function and a `__doing_it_wrong()` error is thrown identifying the function which returned the incorrectly typed variable. This includes doing this check after the last hooked-in function is called, before returning the value to the original caller of `apply_filters_typesafe()`.
     31* In the loop which calls the hooked in functions, a check is done on the variable type received back from each function and if it doesn't match the expected variable type, the previous value is used for the call to the next hooked-in function and a `__doing_it_wrong()` error is thrown identifying the function which returned the incorrectly typed variable. This includes doing this check after the last hooked-in function is called, before returning the value to the original caller of `apply_filters_single_type()`.
    3232
    3333For `apply_filters_ref_array()`, this would apply to the first array item in `$args`.