[wp-trac] [WordPress Trac] #23035: do_action() can't be nested because of global variable

WordPress Trac noreply at wordpress.org
Fri Dec 21 09:31:13 UTC 2012


#23035: do_action() can't be nested because of global variable
-----------------------------+--------------------------
 Reporter:  cheeserolls      |       Type:  defect (bug)
   Status:  new              |   Priority:  normal
Milestone:  Awaiting Review  |  Component:  Plugins
  Version:  3.5              |   Severity:  normal
 Keywords:  has-patch        |
-----------------------------+--------------------------
 wp-includes/plugin.php lines 401-408:


 {{{
 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 );
 }}}



 The loop relies on the internal pointer of a global variable:
 $wp_filter[$tag]

 This means that an action cannot be nested inside the same action, because
 nested calls to do_action will try to use the same pointer for the loop.

 For example:
 The WPML plugin (wordpress multilingual) maintains duplicates of posts
 which haven't yet been translated.  Whenever you update the main post,
 WPML will also update the duplicates for you.

 It does this using the 'save_post' action.  When the 'save_post' action
 occurs, WPML loops through the duplicated posts and saves them as well.
 However, saving the duplicates, causes the 'save_post' action to be
 triggered again.

 $wp_filter['save_post'] is reset and looped for each of the duplicates.
 When all the duplicates are done, the loop exits and we return to the
 outer 'save_post' action.  But the inner loop and outer loop share the
 same pointer.  So the outer loop thinks it is finished, and exits.  Any
 other 'save_post' actions that were queued up for the main post don't get
 executed.

 Suggest instead the following patch, to ensure that each call to do_action
 gets it's own private loop:


 {{{
 $this_filter = $wp_filter[$tag]; // get a copy of the array to loop over
 reset( $this_filter );

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

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

-- 
Ticket URL: <http://core.trac.wordpress.org/ticket/23035>
WordPress Trac <http://core.trac.wordpress.org/>
WordPress blogging software


More information about the wp-trac mailing list