[wp-trac] [WordPress Trac] #59126: remove_filter - missing support for "First class callable"

WordPress Trac noreply at wordpress.org
Wed Aug 16 22:57:53 UTC 2023


#59126: remove_filter - missing support for "First class callable"
-------------------------------+-----------------------------
 Reporter:  jave.web           |      Owner:  (none)
     Type:  defect (bug)       |     Status:  new
 Priority:  normal             |  Milestone:  Awaiting Review
Component:  General            |    Version:  6.3
 Severity:  normal             |   Keywords:
  Focuses:  php-compatibility  |
-------------------------------+-----------------------------
 PHP 8.1 introduced something called "First class callable" or First-class
 Callable Syntax -
 [https://www.php.net/releases/8.1/en.php#first_class_callable_syntax]

 Which basically means much easier way to reference any function,
 especially class methods - both dynamic and static like so:

 {{{
 $this->myDynamicCallback(...);
 self::myStaticCallback(...);
 }}}
 ''**The 3 dots "..." are not omission - it is the syntax**''

 Now this syntax actually works for `add_filter` however, it can fail (and
 I've encountered cases where it fails) for `remove_filter` which leads to
 very unexpected results.

 Temporary bypass can be done using `remove_all_filters` or the old-school
 syntax `[__CLASS__, 'myStaticCallback']`.

 The core issue is in the function `_wp_filter_build_unique_id` which
 generates the key under which the callback is stored, more specifically,
 in the way how the PHP's `spl_object_hash` work - this sentence from docs
 "This id can be used as a hash key for storing objects, or for identifying
 an object, **as long as the object is not destroyed**."

 Now this is an educated wild guess, but I would guess that the callable
 objects created by the First-class callable syntax may not live that long
 and garbage collector can destroy them if it sees fit.

 Anyways... the core is - `spl_object_hash` is NOT the right tool to cache
 ALL callables, but can only be used where the callable is passed around
 (e.g. stored in a variable) and therefore it is ensured it's still the
 same object (I would hope).

 **Solution**
 ...for such cases is to create a `\ReflectionFunction` instance and check
 for `getClosureScopeClass()` and `getName()` :-)

 https://www.php.net/manual/en/class.reflectionfunction.php
 https://www.php.net/manual/en/reflectionfunctionabstract.getclosurescopeclass.php
 https://www.php.net/manual/en/reflectionfunctionabstract.getname.php

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/59126>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list