WordPress.org

Make WordPress Core

Opened 3 years ago

Last modified 23 months ago

#40397 new defect (bug)

PHP 7.1.x problem in WP_Query

Reported by: dglingren Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 4.7.3
Component: Query Keywords:
Focuses: Cc:
PR Number:

Description

I believe I have found another instance of the defect reported in Trac #37772. This was reported in my plugin's support forum:

https://wordpress.org/support/topic/warning-parameter-2-to-mlaquery

The first post there includes the PHP message:

Warning: Parameter 2 to MLAQuery::mla_query_posts_search_filter() expected to be a reference, value given in /home/prodport/staging/1/wp-includes/class-wp-hook.php on line 298

A later post from @adrianb includes a call trace showing the call to apply_filters() from line 1995 in WP_Query.

Change History (7)

#1 @SergeyBiryukov
3 years ago

  • Component changed from Database to Query

#2 @SergeyBiryukov
3 years ago

Reproduced with this snippet:

class MLAQuery {

	function __construct() {
		add_filter( 'posts_search', 'MLAQuery::mla_query_posts_search_filter', 10, 2 );
	}

	public static function mla_query_posts_search_filter( $search_string, &$query_object ) {
		return $search_string;
	}

}

new MLAQuery;

Removing the ampersand from &$query_object fixes it, so it appears to be a plugin issue rather than WordPress core.

#3 @dglingren
3 years ago

Thanks, @SergeyBiryukov, for your response. Removing the ampersand avoids the PHP message but I do not believe it solves the problem. The original call on line 1995 of class-wp-query.php reads:

$search = apply_filters_ref_array( 'posts_search', array( $search, &$this ) );

It looks like the WP_Query object is being passed to the filter by reference, not by value, so the filter can modify elements such as the query_vars array. The Codex documentation for apply_filters_ref_array includes:

"As of PHP 5.4, the array is no longer passed by reference despite the function's name. You cannot even use the reference sign '&' because call time pass by reference now throws an error. What you can do is pass the reference pointer as an array element. Doing so does require all callbacks added to the filter to expect a reference pointer. This is not something you will see in WordPress actions. This technique is provided for informational purposes only.

apply_filters_ref_array( 'my_filter', array( &$args ));

add_action('my_filter', 'my_callback');
function my_callback( &$args ) {
    //access values with $args[0], $args[1] etc.
}

Because the original array was passed by reference, any changes to the array elements are applied to the original array outside of the function's scope."

You can see the ampersand in my_callback; removing it defeats one purpose of the filter. Am I just confused?


#4 @dd32
3 years ago

It looks like the WP_Query object is being passed to the filter by reference, not by value, so the filter can modify elements such as the query_vars array. The Codex documentation for apply_filters_ref_array includes:

That's correct, however is a hold-over from PHP4 - in PHP5+ all object are passed by reference, regardless of if & is used. apply_filters_ref_array() and do_action_ref_array() are vestiges for that time.

The & in the plugin was never needed - WordPress was passing a reference to the function already, there was no need for the plugin to specify that the incoming value should be treated a as a reference (effectively, it was saying, treat this reference as a reference to the reference).

I don't think there's anything that core needs to do here - the plugin simply needs to update to remove the byref specifier.

#5 follow-up: @dglingren
3 years ago

Thanks, @dd32, for your update. I just ran a quick experiment that doesn't agree with your explanation. Using PHP Version 7.0.14 I tried the filter with and without the ampersand. In both cases I modified one of the elements in the query_vars array and added an error_log() statement in class-wp-query.php to see if the modification was reflected in the object after the call.

With the ampersand added to the filter's second parameter declaration the modified array element was present after the filter call; without the ampersand the modification was lost.

The Codex documentation says "As of PHP 5.4, ..." which indicates that this isn't a holdover from PHP 4.x. I remain confused.

#6 in reply to: ↑ 5 @apearlman
23 months ago

@dglingren - did you ever find a solution to this? I'm having a similar issue.

Replying to dglingren:

Thanks, @dd32, for your update. I just ran a quick experiment that doesn't agree with your explanation. Using PHP Version 7.0.14 I tried the filter with and without the ampersand. In both cases I modified one of the elements in the query_vars array and added an error_log() statement in class-wp-query.php to see if the modification was reflected in the object after the call.

With the ampersand added to the filter's second parameter declaration the modified array element was present after the filter call; without the ampersand the modification was lost.

The Codex documentation says "As of PHP 5.4, ..." which indicates that this isn't a holdover from PHP 4.x. I remain confused.

Version 0, edited 23 months ago by apearlman (next)

#7 @dglingren
23 months ago

@apearlman - I have not done any more on this issue since my last post. I have not found any way to modify the values in the $query_vars array without generating the PHP Warning.

In my plugin I do not actually use the $query_vars argument so I simply eliminated it from my filter.

If you need that functionality I suggest you work with the people who responded above to see what can be done in Core. Good luck with your application.

Note: See TracTickets for help on using tickets.