WordPress.org

Make WordPress Core

Ticket #17817: 17817.4.patch

File 17817.4.patch, 22.4 KB (added by jbrinley, 5 years ago)
  • src/wp-includes/class-wp-hook.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
     1<?php
     2
     3/**
     4 * Class WP_Hook
     5 *
     6 * TODO: This implements SplSubject. Make that explicit
     7 *       when we can depend on SPL's presence.
     8 */
     9class WP_Hook {
     10        // TODO: When PHP 5.3 is the minimum version, use SplObjectStorage
     11        /** @var SplObserver[] */
     12        private $observers = array();
     13        public $callbacks = array();
     14
     15        public function add_filter( $function_to_add, $priority, $accepted_args, $tag ) {
     16                $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
     17                $this->callbacks[$priority][$idx] = array( 'function' => $function_to_add, 'accepted_args' => $accepted_args );
     18
     19                if ( $this->observers ) {
     20                        $this->notify();
     21                }
     22        }
     23
     24        /**
     25         * @param string $function_key
     26         * @param int $priority
     27         *
     28         * @return bool Whether the callback existed before it was removed
     29         */
     30        public function remove_filter( $function_key, $priority ) {
     31                $exists = isset($this->callbacks[$priority][$function_key]);
     32                if ( $exists ) {
     33                        unset($this->callbacks[$priority][$function_key]);
     34                        if ( empty($this->callbacks[$priority]) ) {
     35                                unset($this->callbacks[$priority]);
     36                        }
     37                        if ( $this->observers ) {
     38                                $this->notify();
     39                        }
     40                }
     41                return $exists;
     42        }
     43
     44        /**
     45         * Check if any action has been registered for a hook.
     46         *
     47         * @param string $function_key The hashed index of the filter
     48         * @return mixed The priority of that hook is returned, or false if the function is not attached.
     49         */
     50        public function has_filter( $function_key ) {
     51                foreach ( $this->callbacks as $priority => &$callbacks ) {
     52                        if ( isset($callbacks[$function_key]) ) {
     53                                return $priority;
     54                        }
     55                }
     56                return false;
     57        }
     58
     59        /**
     60         * Remove all of the callbacks from the filter.
     61         *
     62         * @param int|bool $priority The priority number to remove.
     63         * @return void
     64         */
     65        public function remove_all_filters( $priority = false ) {
     66                if ( empty($this->callbacks) ) {
     67                        return;
     68                }
     69                if( false !== $priority && isset($this->callbacks[$priority]) ) {
     70                        unset($this->callbacks[$priority]);
     71                } else {
     72                        $this->callbacks = array();
     73                }
     74                $this->notify();
     75        }
     76
     77        public function get_iterator() {
     78                $iterator = new WP_Hook_Iterator( $this );
     79                return $iterator;
     80        }
     81
     82        public function attach( $observer ) {
     83                $this->observers[] = $observer;
     84        }
     85
     86        public function detach( $observer ) {
     87                foreach ( $this->observers as $key => $o ) {
     88                        if ( $observer === $o ) {
     89                                unset($this->observers[$key]);
     90                        }
     91                }
     92        }
     93
     94        public function notify() {
     95                foreach ( $this->observers as &$o ) {
     96                        $o->update($this);
     97                }
     98        }
     99}
     100 
  • tests/phpunit/includes/functions.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    11<?php
    22
    3 // For adding hooks before loading WP
    4 function tests_add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
    5         global $wp_filter, $merged_filters;
    6 
    7         $idx = _test_filter_build_unique_id($tag, $function_to_add, $priority);
    8         $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
    9         unset( $merged_filters[ $tag ] );
    10         return true;
    11 }
    12 
    13 function _test_filter_build_unique_id($tag, $function, $priority) {
    14         global $wp_filter;
    15         static $filter_id_count = 0;
    16 
    17         if ( is_string($function) )
    18                 return $function;
    19 
    20         if ( is_object($function) ) {
    21                 // Closures are currently implemented as objects
    22                 $function = array( $function, '' );
    23         } else {
    24                 $function = (array) $function;
    25         }
    26 
    27         if (is_object($function[0]) ) {
    28                 return spl_object_hash($function[0]) . $function[1];
    29         } else if ( is_string($function[0]) ) {
    30                 // Static Calling
    31                 return $function[0].$function[1];
    32         }
    33 }
    34 
    353function _delete_all_posts() {
    364        global $wpdb;
    375
  • src/wp-includes/plugin.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    2020 */
    2121
    2222// Initialize the filter globals.
    23 global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
     23require( ABSPATH . '/wp-includes/class-wp-hook.php' );
     24require( ABSPATH . '/wp-includes/class-wp-hook-iterator.php' );
     25/** @var WP_Hook[] $wp_filter */
     26global $wp_filter, $wp_actions, $wp_current_filter;
    2427
    2528if ( ! isset( $wp_filter ) )
    2629        $wp_filter = array();
     
    2831if ( ! isset( $wp_actions ) )
    2932        $wp_actions = array();
    3033
    31 if ( ! isset( $merged_filters ) )
    32         $merged_filters = array();
    33 
    3434if ( ! isset( $wp_current_filter ) )
    3535        $wp_current_filter = array();
    3636
     
    6464 * so everything is as quick as possible.
    6565 *
    6666 * @global array $wp_filter      A multidimensional array of all hooks and the callbacks hooked to them.
    67  * @global array $merged_filters Tracks the tags that need to be merged for later. If the hook is added, it doesn't need to run through that process.
    6867 *
    6968 * @since 0.71
    7069 *
     
    7776 * @return boolean true
    7877 */
    7978function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
    80         global $wp_filter, $merged_filters;
     79        global $wp_filter;
     80        if ( !isset($wp_filter[$tag]) ) {
     81                $wp_filter[$tag] = new WP_Hook();
     82        }
     83        $wp_filter[$tag]->add_filter( $function_to_add, $priority, $accepted_args, $tag );
    8184
    82         $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
    83         $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
    84         unset( $merged_filters[ $tag ] );
    8585        return true;
    8686}
    8787
     
    102102function has_filter($tag, $function_to_check = false) {
    103103        global $wp_filter;
    104104
    105         $has = !empty($wp_filter[$tag]);
     105        $has = isset($wp_filter[$tag]) && !empty($wp_filter[$tag]->callbacks);
    106106        if ( false === $function_to_check || false == $has )
    107107                return $has;
    108108
    109109        if ( !$idx = _wp_filter_build_unique_id($tag, $function_to_check, false) )
    110110                return false;
    111111
    112         foreach ( (array) array_keys($wp_filter[$tag]) as $priority ) {
    113                 if ( isset($wp_filter[$tag][$priority][$idx]) )
    114                         return $priority;
    115         }
     112        return $wp_filter[$tag]->has_filter($idx);
     113}
    116114
    117         return false;
    118 }
    119 
    120115/**
    121116 * Call the functions added to a filter hook.
    122117 *
     
    143138 * </code>
    144139 *
    145140 * @global array $wp_filter         Stores all of the filters
    146  * @global array $merged_filters    Merges the filter hooks using this function.
    147141 * @global array $wp_current_filter stores the list of current filters with the current one last
    148142 *
    149143 * @since 0.71
     
    154148 * @return mixed The filtered value after all hooked functions are applied to it.
    155149 */
    156150function apply_filters( $tag, $value ) {
    157         global $wp_filter, $merged_filters, $wp_current_filter;
     151        global $wp_filter, $wp_current_filter;
    158152
    159153        $args = array();
    160154
     
    174168        if ( !isset($wp_filter['all']) )
    175169                $wp_current_filter[] = $tag;
    176170
    177         // Sort
    178         if ( !isset( $merged_filters[ $tag ] ) ) {
    179                 ksort($wp_filter[$tag]);
    180                 $merged_filters[ $tag ] = true;
    181         }
    182 
    183         reset( $wp_filter[ $tag ] );
    184 
    185171        if ( empty($args) )
    186172                $args = func_get_args();
    187173
    188         do {
    189                 foreach( (array) current($wp_filter[$tag]) as $the_ )
    190                         if ( !is_null($the_['function']) ){
    191                                 $args[1] = $value;
    192                                 $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
    193                         }
     174        $iterator = $wp_filter[$tag]->get_iterator();
     175        foreach ( $iterator as $the_ ) {
     176                $args[1] = $value;
     177                $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
     178        }
     179        $iterator->cleanup();
    194180
    195         } while ( next($wp_filter[$tag]) !== false );
    196 
    197181        array_pop( $wp_current_filter );
    198182
    199183        return $value;
     
    207191 *
    208192 * @since 3.0.0
    209193 * @global array $wp_filter Stores all of the filters
    210  * @global array $merged_filters Merges the filter hooks using this function.
    211194 * @global array $wp_current_filter stores the list of current filters with the current one last
    212195 *
    213196 * @param string $tag The name of the filter hook.
     
    215198 * @return mixed The filtered value after all hooked functions are applied to it.
    216199 */
    217200function apply_filters_ref_array($tag, $args) {
    218         global $wp_filter, $merged_filters, $wp_current_filter;
     201        global $wp_filter, $wp_current_filter;
    219202
    220203        // Do 'all' actions first
    221204        if ( isset($wp_filter['all']) ) {
     
    233216        if ( !isset($wp_filter['all']) )
    234217                $wp_current_filter[] = $tag;
    235218
    236         // Sort
    237         if ( !isset( $merged_filters[ $tag ] ) ) {
    238                 ksort($wp_filter[$tag]);
    239                 $merged_filters[ $tag ] = true;
     219        $iterator = $wp_filter[$tag]->get_iterator();
     220        foreach ( $iterator as $the_ ) {
     221                $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    240222        }
     223        $iterator->cleanup();
    241224
    242         reset( $wp_filter[ $tag ] );
    243 
    244         do {
    245                 foreach( (array) current($wp_filter[$tag]) as $the_ )
    246                         if ( !is_null($the_['function']) )
    247                                 $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    248 
    249         } while ( next($wp_filter[$tag]) !== false );
    250 
    251225        array_pop( $wp_current_filter );
    252226
    253227        return $args[0];
     
    273247 * @return boolean Whether the function existed before it was removed.
    274248 */
    275249function remove_filter( $tag, $function_to_remove, $priority = 10 ) {
    276         $function_to_remove = _wp_filter_build_unique_id($tag, $function_to_remove, $priority);
     250        global $wp_filter;
    277251
    278         $r = isset($GLOBALS['wp_filter'][$tag][$priority][$function_to_remove]);
    279 
    280         if ( true === $r) {
    281                 unset($GLOBALS['wp_filter'][$tag][$priority][$function_to_remove]);
    282                 if ( empty($GLOBALS['wp_filter'][$tag][$priority]) )
    283                         unset($GLOBALS['wp_filter'][$tag][$priority]);
    284                 unset($GLOBALS['merged_filters'][$tag]);
     252        $r = false;
     253        if ( isset($wp_filter[$tag]) ) {
     254                $function_to_remove = _wp_filter_build_unique_id($tag, $function_to_remove, $priority);
     255                $r = $wp_filter[$tag]->remove_filter($function_to_remove, $priority);
    285256        }
    286257
    287258        return $r;
     
    297268 * @return bool True when finished.
    298269 */
    299270function remove_all_filters($tag, $priority = false) {
    300         global $wp_filter, $merged_filters;
     271        global $wp_filter;
    301272
    302273        if( isset($wp_filter[$tag]) ) {
    303                 if( false !== $priority && isset($wp_filter[$tag][$priority]) )
    304                         unset($wp_filter[$tag][$priority]);
    305                 else
    306                         unset($wp_filter[$tag]);
     274                $wp_filter[$tag]->remove_all_filters($priority);
    307275        }
    308276
    309         if( isset($merged_filters[$tag]) )
    310                 unset($merged_filters[$tag]);
    311 
    312277        return true;
    313278}
    314279
     
    423388 * @return null Will return null if $tag does not exist in $wp_filter array
    424389 */
    425390function do_action($tag, $arg = '') {
    426         global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
     391        global $wp_filter, $wp_actions, $wp_current_filter;
    427392
    428393        if ( ! isset($wp_actions[$tag]) )
    429394                $wp_actions[$tag] = 1;
     
    454419        for ( $a = 2; $a < func_num_args(); $a++ )
    455420                $args[] = func_get_arg($a);
    456421
    457         // Sort
    458         if ( !isset( $merged_filters[ $tag ] ) ) {
    459                 ksort($wp_filter[$tag]);
    460                 $merged_filters[ $tag ] = true;
     422        $iterator = $wp_filter[$tag]->get_iterator();
     423        foreach ( $iterator as $the_ ) {
     424                call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    461425        }
     426        $iterator->cleanup();
    462427
    463         reset( $wp_filter[ $tag ] );
    464 
    465         do {
    466                 foreach ( (array) current($wp_filter[$tag]) as $the_ )
    467                         if ( !is_null($the_['function']) )
    468                                 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    469 
    470         } while ( next($wp_filter[$tag]) !== false );
    471 
    472428        array_pop($wp_current_filter);
    473429}
    474430
     
    507463 * @return null Will return null if $tag does not exist in $wp_filter array
    508464 */
    509465function do_action_ref_array($tag, $args) {
    510         global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
     466        global $wp_filter, $wp_actions, $wp_current_filter;
    511467
    512468        if ( ! isset($wp_actions[$tag]) )
    513469                $wp_actions[$tag] = 1;
     
    530486        if ( !isset($wp_filter['all']) )
    531487                $wp_current_filter[] = $tag;
    532488
    533         // Sort
    534         if ( !isset( $merged_filters[ $tag ] ) ) {
    535                 ksort($wp_filter[$tag]);
    536                 $merged_filters[ $tag ] = true;
     489        $iterator = $wp_filter[$tag]->get_iterator();
     490        foreach ( $iterator as $the_ ) {
     491                call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    537492        }
     493        $iterator->cleanup();
    538494
    539         reset( $wp_filter[ $tag ] );
    540 
    541         do {
    542                 foreach( (array) current($wp_filter[$tag]) as $the_ )
    543                         if ( !is_null($the_['function']) )
    544                                 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    545 
    546         } while ( next($wp_filter[$tag]) !== false );
    547 
    548495        array_pop($wp_current_filter);
    549496}
    550497
     
    779726 */
    780727function _wp_call_all_hook($args) {
    781728        global $wp_filter;
    782 
    783         reset( $wp_filter['all'] );
    784         do {
    785                 foreach( (array) current($wp_filter['all']) as $the_ )
    786                         if ( !is_null($the_['function']) )
    787                                 call_user_func_array($the_['function'], $args);
    788 
    789         } while ( next($wp_filter['all']) !== false );
     729        $iterator = $wp_filter['all']->get_iterator();
     730        foreach ( $iterator as $the_ ) {
     731                call_user_func_array($the_['function'], $args);
     732        }
     733        $iterator->cleanup();
    790734}
    791735
    792736/**
  • tests/phpunit/tests/actions.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    356356                $this->assertTrue( doing_filter( 'testing_nested' ) );
    357357                $this->assertFalse( doing_filter( 'something_else' ) );
    358358        }
     359
     360        /**
     361         * @ticket 17817
     362         */
     363        function test_action_recursion() {
     364                $tag = rand_str();
     365                $a = new MockAction();
     366                $b = new MockAction();
     367
     368                add_action( $tag, array($a, 'action'), 11, 1 );
     369                add_action( $tag, array($b, 'action'), 13, 1 );
     370                add_action( $tag, array($this, 'action_that_causes_recursion'), 12, 1 );
     371                do_action( $tag, $tag );
     372
     373                $this->assertEquals( 2, $a->get_call_count(), 'recursive actions should call all callbacks with earlier priority' );
     374                $this->assertEquals( 2, $b->get_call_count(), 'recursive actions should call callbacks with later priority' );
     375        }
     376
     377        function action_that_causes_recursion( $tag ) {
     378                static $recursing = FALSE;
     379                if ( !$recursing ) {
     380                        $recursing = TRUE;
     381                        do_action( $tag, $tag );
     382                }
     383                $recursing = FALSE;
     384        }
     385
     386        /**
     387         * @ticket 9968
     388         */
     389        function test_action_callback_manipulation_while_running() {
     390                $tag = rand_str();
     391                $a = new MockAction();
     392                $b = new MockAction();
     393                $c = new MockAction();
     394                $d = new MockAction();
     395                $e = new MockAction();
     396
     397                add_action( $tag, array($a, 'action'), 11, 2 );
     398                add_action( $tag, array($this, 'action_that_manipulates_a_running_hook'), 12, 2 );
     399                add_action( $tag, array($b, 'action'), 12, 2 );
     400
     401                do_action( $tag, $tag, array($a,$b,$c,$d,$e) );
     402                do_action( $tag, $tag, array($a,$b,$c,$d,$e) );
     403
     404                $this->assertEquals( 2, $a->get_call_count(), 'callbacks should run unless otherwise instructed' );
     405                $this->assertEquals( 1, $b->get_call_count(), 'callback removed by same priority callback should still get called' );
     406                $this->assertEquals( 1, $c->get_call_count(), 'callback added by same priority callback should not get called' );
     407                $this->assertEquals( 2, $d->get_call_count(), 'callback added by earlier priority callback should get called' );
     408                $this->assertEquals( 1, $e->get_call_count(), 'callback added by later priority callback should not get called' );
     409        }
     410
     411        function action_that_manipulates_a_running_hook( $tag, $mocks ) {
     412                remove_action( $tag, array($mocks[1], 'action'), 12, 2 );
     413                add_action( $tag, array($mocks[2], 'action' ), 12, 2 );
     414                add_action( $tag, array($mocks[3], 'action' ), 13, 2 );
     415                add_action( $tag, array($mocks[4], 'action' ), 10, 2 );
     416        }
    359417}
  • src/wp-settings.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    7272require( ABSPATH . WPINC . '/functions.php' );
    7373require( ABSPATH . WPINC . '/class-wp.php' );
    7474require( ABSPATH . WPINC . '/class-wp-error.php' );
    75 require( ABSPATH . WPINC . '/plugin.php' );
     75require_once( ABSPATH . WPINC . '/plugin.php' );
    7676require( ABSPATH . WPINC . '/pomo/mo.php' );
    7777
    7878// Include the wpdb class and, if present, a db.php database drop-in.
  • tests/phpunit/includes/bootstrap.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    6767
    6868require_once dirname( __FILE__ ) . '/functions.php';
    6969
     70// load plugin.php out of order so that we can set up
     71// some filters in advance
     72require_once ABSPATH . '/wp-includes/plugin.php';
     73
    7074// Preset WordPress options defined in bootstrap file.
    7175// Used to activate themes, plugins, as well as  other settings.
    7276if(isset($GLOBALS['wp_tests_options'])) {
     
    7680        }
    7781
    7882        foreach ( array_keys( $GLOBALS['wp_tests_options'] ) as $key ) {
    79                 tests_add_filter( 'pre_option_'.$key, 'wp_tests_options' );
     83                add_filter( 'pre_option_'.$key, 'wp_tests_options' );
    8084        }
    8185}
    8286
  • src/wp-includes/class-wp-hook-iterator.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
     1<?php
     2
     3/**
     4 * Class WP_Hook_Iterator
     5 *
     6 * TODO: This implements SplObserver. Make that explicit
     7 *       when we can depend on SPL's presence.
     8 */
     9class WP_Hook_Iterator implements Iterator {
     10        /** @var WP_Hook  */
     11        private $hook = NULL;
     12        private $current_callback = NULL;
     13        private $current_callback_index = NULL;
     14        private $current_priority = NULL;
     15        private $current_priority_index = NULL;
     16        private $all_priorities = NULL;
     17        private $callbacks_for_current_priority = array();
     18
     19        public function __construct( WP_Hook $hook ) {
     20                $this->hook = $hook;
     21                $this->hook->attach($this);
     22        }
     23
     24        public function cleanup() {
     25                $this->hook->detach($this);
     26        }
     27
     28        /**
     29         * Return the current element
     30         *
     31         * @link http://php.net/manual/en/iterator.current.php
     32         * @return mixed Can return any type.
     33         */
     34        public function current() {
     35                return $this->current_callback;
     36        }
     37
     38        /**
     39         * Move forward to next element
     40         * @link http://php.net/manual/en/iterator.next.php
     41         * @return void Any returned value is ignored.
     42         */
     43        public function next() {
     44                $next = next( $this->callbacks_for_current_priority );
     45
     46                if ( $next === FALSE ) {
     47                        do {
     48                                $this->increment_priority();
     49                        } while ( empty($this->callbacks_for_current_priority) && isset( $this->current_priority) );
     50
     51                        if ( !$this->callbacks_for_current_priority ) {
     52                                $this->current_callback = NULL;
     53                                $this->current_callback_index = NULL;
     54                                return;
     55                        }
     56
     57                        $next = reset( $this->callbacks_for_current_priority );
     58                }
     59
     60                $this->current_callback = $next;
     61                $this->current_callback_index = key($this->callbacks_for_current_priority);
     62        }
     63
     64        /**
     65         * Setup the callbacks array for the next available priority
     66         * @return void
     67         */
     68        private function increment_priority() {
     69                $this->set_current_priority();
     70                if ( isset($this->current_priority) ) {
     71                        $this->callbacks_for_current_priority = $this->get_callbacks($this->current_priority);
     72                } else {
     73                        $this->callbacks_for_current_priority = array();
     74                }
     75        }
     76
     77        /**
     78         * Move to the next available priority
     79         * @return void
     80         */
     81        private function set_current_priority() {
     82                if ( empty($this->hook->callbacks) ) { // no callbacks on this hook
     83                        $this->all_priorities = array();
     84                        $this->current_priority = NULL;
     85                        return;
     86                }
     87
     88                if ( !isset($this->all_priorities) ) {
     89                        $this->all_priorities = array_keys($this->hook->callbacks);
     90                        sort($this->all_priorities);
     91                        if ( isset($this->current_priority) ) { // callbacks were manipulated while we were running
     92                                foreach ( $this->all_priorities as $index => &$p ) { // go to the next larger priority
     93                                        if ( $p > $this->current_priority ) {
     94                                                $this->current_priority_index = $index;
     95                                                $this->current_priority = $p;
     96                                                break;
     97                                        }
     98                                }
     99                        } else { // starting fresh
     100                                $this->current_priority_index = 0;
     101                                $this->current_priority = $this->all_priorities[0];
     102                        }
     103                        return;
     104                }
     105
     106                if ( !isset($this->current_priority_index) ) {
     107                        $this->current_priority_index = 0;
     108                } else {
     109                        $this->current_priority_index++;
     110                }
     111                if ( isset($this->all_priorities[$this->current_priority_index]) ) {
     112                        $this->current_priority = $this->all_priorities[$this->current_priority_index];
     113                        return;
     114                }
     115
     116                $this->current_priority = NULL;
     117                return;
     118        }
     119
     120        private function get_callbacks( $priority ) {
     121                if ( isset($this->hook->callbacks[$priority]) ) {
     122                        return $this->hook->callbacks[$priority];
     123                }
     124                return array();
     125        }
     126
     127        /**
     128         * Return the key of the current element
     129         * @link http://php.net/manual/en/iterator.key.php
     130         * @return mixed scalar on success, or null on failure.
     131         */
     132        public function key() {
     133                return $this->current_callback_index;
     134        }
     135
     136        /**
     137         * Checks if current position is valid
     138         * @link http://php.net/manual/en/iterator.valid.php
     139         * @return boolean The return value will be casted to boolean and then evaluated.
     140         * Returns true on success or false on failure.
     141         */
     142        public function valid() {
     143                return isset($this->current_callback);
     144        }
     145
     146        /**
     147         * Rewind the Iterator to the first element
     148         * @link http://php.net/manual/en/iterator.rewind.php
     149         * @return void Any returned value is ignored.
     150         */
     151        public function rewind() {
     152                $this->current_priority = NULL;
     153                $this->current_priority_index = NULL;
     154                $this->current_callback = NULL;
     155                $this->current_callback_index = NULL;
     156                $this->callbacks_for_current_priority = array();
     157                $this->next();
     158        }
     159
     160        /**
     161         * Receive update from subject
     162         *
     163         * @link http://php.net/manual/en/splobserver.update.php
     164         *
     165         * @param SplSubject $subject The SplSubject notifying the observer of an update.
     166         *
     167         * @return void
     168         */
     169        public function update( $subject ) {
     170                unset($this->all_priorities);
     171                unset($this->current_priority_index);
     172        }
     173}