Opened 6 years ago
Last modified 6 years ago
#45374 new defect (bug)
apply_filters_ref_array() no longer passes arguments by reference
Reported by: | johnbillion | Owned by: | |
---|---|---|---|
Milestone: | Awaiting Review | Priority: | normal |
Severity: | normal | Version: | 4.7 |
Component: | Plugins | Keywords: | needs-patch needs-unit-tests |
Focuses: | Cc: |
Description
It appears that since the introduction of the WP_Hook
class in WordPress 4.7 (#17817), apply_filters_ref_array()
no longer passes its arguments by reference.
Example:
add_filter( 'posts_clauses', function( $clauses, &$query ) { return $clauses; }, 10, 2 );
The above code will trigger the following PHP warning:
Parameter 2 to {closure}() expected to be a reference, value given
cc @jbrinley FYI
Change History (8)
#3
@
6 years ago
The example given doesn't attempt to pass a reference at call time. It is trying to pass values. They can be references according to modern PHP. See https://3v4l.org/AWr5p
#4
@
6 years ago
Taken from the manual entry for call_user_func_array()
Before PHP 5.4, referenced variables in param_arr are passed to the function by reference, regardless of whether the function expects the respective parameter to be passed by reference. This form of call-time pass by reference does not emit a deprecation notice, but it is nonetheless deprecated, and has been removed in PHP 5.4
Ref http://php.net/manual/en/function.call-user-func-array.php
#5
@
6 years ago
Since its almost end of the work day, I leave what I found so far.
This is the code I've tested with
<?php function testFilter( $text, &$object ) { return $text; } add_filter( 'testFilter', 'testFilter', 10, 2 ); function testAction( $text, &$object ) { return; } add_action( 'testAction', 'testAction', 10, 2 ); class Test { public function run() { // Somehow the &$this does not work. apply_filters_ref_array('testFilter', ['test', &$this]); apply_filters_ref_array('testFilter', array('Test', $this)); // It seems to be limited to filter. do_action_ref_array('testAction', array('test', &$this)); } } (new Test())->run(); $object = new Test(); apply_filters_ref_array('testFilter', ['test', $object]); /** * This does not throw. */ apply_filters_ref_array('testFilter', ['test', &$object]);
It seems to be related to calls where the referenced object is $this
. The PHP version I've tested is 7.2.12-1.
It looks to me, it has something to do with the reassignment of the first value of the $args
here https://core.trac.wordpress.org/browser/tags/4.9.8/src/wp-includes/class-wp-hook.php#L279
When I just do not run this assignment, the problem disappears.
But, at least for PHP 7.1.25 the problem seems to be a bit more: https://3v4l.org/DCCeh
As you can see, here $this
is not referenced, although I do not change the $args
. Also noteworthy, in this example I can change $args[0]
without having another problem (except for the PHP 7.1.25).
Edit: I edited my code example for clarification, but it didn't change my outcomes.
#6
@
6 years ago
@websupporter
I've noticed an issue with references and call_user_func_array()
.
Example 1
This uses call_user_func_array()
to pass by reference: https://3v4l.org/JDcqk
Example 2
This uses a function call to pass by reference: https://3v4l.org/sQFsU
#7
@
6 years ago
To add to my last comment
Related to example 1, if we prefix the array elements with &
, we see call_user_func_array()
doesn't cause problems: https://3v4l.org/ZfMrf
@websupporter I think you are right to think the $args[ 0 ] = $value;
assignment is the issue. After this point $args[0]
is not a reference. Hence we get the warning shown in example 1.
#8
@
6 years ago
Related is this PHP bug https://bugs.php.net/bug.php?id=50394
This didn't change with 4.7. From the pre-4.7
do_action_ref_array()
, which hasn't changed much since 2.2 (see #3875):Here's a simplified example to show that we get the same warning: https://3v4l.org/qeUZt
Before 2.2, you could use call-time pass-by-reference to pass your reference through to the callbacks, but that's not supported in modern PHP.