[wp-trac] [WordPress Trac] #27488: Problem with action hook recursion causing later callbacks not to be called

WordPress Trac noreply at wordpress.org
Sat Mar 22 14:19:12 UTC 2014


#27488: Problem with action hook recursion causing later callbacks not to be called
--------------------------+-----------------------------
 Reporter:  PolyMe        |      Owner:
     Type:  defect (bug)  |     Status:  new
 Priority:  normal        |  Milestone:  Awaiting Review
Component:  General       |    Version:  3.8.1
 Severity:  normal        |   Keywords:
  Focuses:                |
--------------------------+-----------------------------
 Calling an action hook from within the first action hook callback for an
 action hook that contains 2+ callbacks will cause the additional callbacks
 not to be called for the initial action calls.

 Bug was discovered using the following example code...

 {{{#!php
 function add_more_meta( $meta_id, $post_id, $meta_key, $_meta_value ) {

         if ( 'initial_meta' !== $meta_key ) {
                 return;
         }

         echo "Updating 'some_key'<br>\n";

         update_post_meta( $post_id, 'some_key', 'some_value' . rand( 0,
 9999 ) );
         echo "'some_key' updated<br>\n";
 }

 function echo_meta( $meta_id, $post_id, $meta_key, $_meta_value ) {

         echo "echoing...$meta_key<br>\n";

         echo get_post_meta( $post_id, $meta_key, true ) . "<br>\n";
 }

 add_action( 'updated_post_meta', 'add_more_meta', 5, 4 );
 add_action( 'updated_post_meta', 'echo_meta', 10, 4 );

 $post_id = 1;

 update_post_meta( $post_id, 'initial_meta', 'initial_meta_value' . rand(
 0, 9999 ) );
 }}}

 In the above example, the updated_post_meta action hook is called within
 another updated_post_meta action hook callback.

 Because the code that calls the action hooks, uses an array pointed on the
 global variable, the secondary action hook call, moves the pointer, so
 that when execution returns to the original callback, it thinks that it
 has completed all the callbacks.

 The fix...
 Instead of using the $wp_filter global array, by copying the array to a
 variable with function scope, this pointer is not reset by subsequent
 calls to the action hook.

 Update the code at the bottom of the do_action() function as follows...

 Before...

 {{{#!php
         reset( $wp_filter[ $tag ] );

         do {
                 foreach( (array) current($wp_filter[$tag]) as $the_ )
                         if ( !is_null($the_['function']) )
                                 call_user_func_array($the_['function'],
 array_slice($args, 0, (int) $the_['accepted_args']));

         } while ( next($wp_filter[$tag]) !== false );
 }}}

 After...

 {{{#!php
         $callbacks = $wp_filter[$tag];

         reset( $wp_filter[ $tag ] );

         do {
                 foreach( (array) current($callbacks) as $the_ )
                         if ( !is_null($the_['function']) )
                                 call_user_func_array($the_['function'],
 array_slice($args, 0, (int) $the_['accepted_args']));

         } while ( next($callbacks) !== false );
 }}}


 This will also need updating in the following functions...

 * apply_filters()
 * apply_filters_ref_array()
 * do_action_ref_array()

 I'll now look at preparing a patch to fix this.

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


More information about the wp-trac mailing list