WordPress.org

Make WordPress Core

Ticket #17817: 17817.3.patch

File 17817.3.patch, 27.5 KB (added by jbrinley, 6 years ago)
  • 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/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                global $wp_filter;
     17                $idx = $wp_filter->build_unique_id($function_to_add, $tag, $priority);
     18                $this->callbacks[$priority][$idx] = array( 'function' => $function_to_add, 'accepted_args' => $accepted_args );
     19                $this->notify();
     20        }
     21
     22        /**
     23         * @param string $function_key
     24         * @param int $priority
     25         *
     26         * @return bool Whether the callback existed before it was removed
     27         */
     28        public function remove_filter( $function_key, $priority ) {
     29                $exists = isset($this->callbacks[$priority][$function_key]);
     30                if ( $exists ) {
     31                        unset($this->callbacks[$priority][$function_key]);
     32                        if ( empty($this->callbacks[$priority]) ) {
     33                                unset($this->callbacks[$priority]);
     34                        }
     35                        $this->notify();
     36                }
     37                return $exists;
     38        }
     39
     40        /**
     41         * Check if any action has been registered for a hook.
     42         *
     43         * @param string $function_key The hashed index of the filter
     44         * @return mixed The priority of that hook is returned, or false if the function is not attached.
     45         */
     46        public function has_filter( $function_key ) {
     47                foreach ( $this->callbacks as $priority => &$callbacks ) {
     48                        if ( isset($callbacks[$function_key]) ) {
     49                                return $priority;
     50                        }
     51                }
     52                return false;
     53        }
     54
     55        /**
     56         * Remove all of the callbacks from the filter.
     57         *
     58         * @param int|bool $priority The priority number to remove.
     59         * @return void
     60         */
     61        public function remove_all_filters( $priority = false ) {
     62                if ( empty($this->callbacks) ) {
     63                        return;
     64                }
     65                if( false !== $priority && isset($this->callbacks[$priority]) ) {
     66                        unset($this->callbacks[$priority]);
     67                } else {
     68                        $this->callbacks = array();
     69                }
     70                $this->notify();
     71        }
     72
     73        public function get_iterator() {
     74                $iterator = new WP_Hook_Iterator( $this );
     75                return $iterator;
     76        }
     77
     78        public function attach( $observer ) {
     79                $this->observers[] = $observer;
     80        }
     81
     82        public function detach( $observer ) {
     83                foreach ( $this->observers as $key => $o ) {
     84                        if ( $observer === $o ) {
     85                                unset($this->observers[$key]);
     86                        }
     87                }
     88        }
     89
     90        public function notify() {
     91                foreach ( $this->observers as $o ) {
     92                        $o->update($this);
     93                }
     94        }
     95}
     96 
     97 No newline at end of file
  • src/wp-includes/class-wp-filters.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
     1<?php
     2
     3/**
     4 * Class WP_Filters
     5 */
     6class WP_Filters implements ArrayAccess {
     7        /** @var WP_Hook[] */
     8        private $hooks = array();
     9        /** @var callable */
     10        private $hasher;
     11
     12        public function __construct() {
     13                $this->setup_hasher();
     14        }
     15
     16        /**
     17         * Decide how we're going to hash object methods
     18         * @return void
     19         */
     20        private function setup_hasher() {
     21                if ( function_exists('spl_object_hash') ) {
     22                        $this->hasher = array( $this, 'spl_hasher' );
     23                } else {
     24                        $this->hasher = array( $this, 'nospl_hasher' );
     25                }
     26        }
     27
     28        /**
     29         * Build a unique ID for storage and retrieval of a callback.
     30         *
     31         * @param callback $function Used for creating unique id
     32         * @param string $tag Used when SPL is unavailable for counting how many hooks were applied
     33         * @param int|bool $priority Used when SPL is unavailable for counting how many hooks were applied. If === false and $function is an object reference, we return the unique id only if it already has one, false otherwise.
     34         * @return string|bool Unique ID for usage as array key or false if $priority === false and $function is an object reference, and it does not already have a unique id.
     35         */
     36        public function build_unique_id( $function, $tag, $priority ) {
     37                if ( is_string($function) )
     38                        return $function;
     39
     40                if ( is_object($function) ) {
     41                        // Closures are currently implemented as objects
     42                        $function = array( $function, '' );
     43                } else {
     44                        $function = (array) $function;
     45                }
     46
     47                if (is_object($function[0]) ) {
     48                        return call_user_func( $this->hasher, $function, $tag, $priority );
     49                } else if ( is_string($function[0]) ) {
     50                        // Static Calling
     51                        return $function[0] . '::' . $function[1];
     52                }
     53        }
     54
     55        private function spl_hasher( $function ) {
     56                return spl_object_hash($function[0]) . $function[1];
     57        }
     58
     59        private function nospl_hasher( $function, $tag, $priority ) {
     60                static $filter_id_count = 0;
     61
     62                $obj_idx = get_class($function[0]).$function[1];
     63                if ( !isset($function[0]->wp_filter_id) ) {
     64                        if ( false === $priority )
     65                                return false;
     66                        $obj_idx .= isset($this->hooks[$tag]->callbacks[$priority]) ? count((array)$this->hooks[$tag]->callbacks[$priority]) : $filter_id_count;
     67                        $function[0]->wp_filter_id = $filter_id_count;
     68                        ++$filter_id_count;
     69                } else {
     70                        $obj_idx .= $function[0]->wp_filter_id;
     71                }
     72
     73                return $obj_idx;
     74        }
     75
     76        /**
     77         * Whether a offset exists
     78         * @link http://php.net/manual/en/arrayaccess.offsetexists.php
     79         *
     80         * @param mixed $offset An offset to check for.
     81         *
     82         * @return boolean true on success or false on failure.
     83         */
     84        public function offsetExists( $offset ) {
     85                return isset($this->hooks[$offset]);
     86        }
     87
     88        /**
     89         * Offset to retrieve
     90         *
     91         * @link http://php.net/manual/en/arrayaccess.offsetget.php
     92         *
     93         * @param mixed $offset The offset to retrieve.
     94         *
     95         * @return WP_Hook Can return all value types.
     96         */
     97        public function offsetGet( $offset ) {
     98                return isset($this->hooks[$offset]) ? $this->hooks[$offset] : null;
     99        }
     100
     101        /**
     102         * Offset to set
     103         *
     104         * @link http://php.net/manual/en/arrayaccess.offsetset.php
     105         *
     106         * @param mixed $offset The offset to assign the value to.
     107         * @param WP_Hook $value The value to set.
     108         *
     109         * @return void
     110         */
     111        public function offsetSet( $offset, $value ) {
     112                if ( is_null($offset) ) {
     113                        $this->hooks[] = $value;
     114                } else {
     115                        $this->hooks[$offset] = $value;
     116                }
     117        }
     118
     119        /**
     120         * Offset to unset
     121         *
     122         * @link http://php.net/manual/en/arrayaccess.offsetunset.php
     123         *
     124         * @param mixed $offset The offset to unset.
     125         *
     126         * @return void
     127         */
     128        public function offsetUnset( $offset ) {
     129                unset($this->hooks[$offset]);
     130        }
     131}
     132 
     133 No newline at end of file
  • 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-filters.php' );
     24require( ABSPATH . '/wp-includes/class-wp-hook.php' );
     25require( ABSPATH . '/wp-includes/class-wp-hook-iterator.php' );
     26global $wp_filter, $wp_actions, $wp_current_filter;
    2427
    2528if ( ! isset( $wp_filter ) )
    26         $wp_filter = array();
     29        $wp_filter = new WP_Filters();
    2730
    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
     
    6767 * @subpackage Plugin
    6868 *
    6969 * @global array $wp_filter      A multidimensional array of all hooks and the callbacks hooked to them.
    70  * @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.
    7170 *
    7271 * @since 0.71
    7372 *
     
    8079 * @return boolean true
    8180 */
    8281function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
    83         global $wp_filter, $merged_filters;
     82        global $wp_filter;
     83        if ( !isset($wp_filter[$tag]) ) {
     84                $wp_filter[$tag] = new WP_Hook();
     85        }
     86        $wp_filter[$tag]->add_filter( $function_to_add, $priority, $accepted_args, $tag );
    8487
    85         $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
    86         $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
    87         unset( $merged_filters[ $tag ] );
    8888        return true;
    8989}
    9090
     
    106106function has_filter($tag, $function_to_check = false) {
    107107        global $wp_filter;
    108108
    109         $has = !empty($wp_filter[$tag]);
     109        $has = isset($wp_filter[$tag]) && !empty($wp_filter[$tag]->callbacks);
    110110        if ( false === $function_to_check || false == $has )
    111111                return $has;
    112112
    113113        if ( !$idx = _wp_filter_build_unique_id($tag, $function_to_check, false) )
    114114                return false;
    115115
    116         foreach ( (array) array_keys($wp_filter[$tag]) as $priority ) {
    117                 if ( isset($wp_filter[$tag][$priority][$idx]) )
    118                         return $priority;
    119         }
     116        return $wp_filter[$tag]->has_filter($idx);
     117}
    120118
    121         return false;
    122 }
    123 
    124119/**
    125120 * Call the functions added to a filter hook.
    126121 *
     
    150145 * @subpackage Plugin
    151146 *
    152147 * @global array $wp_filter         Stores all of the filters
    153  * @global array $merged_filters    Merges the filter hooks using this function.
    154148 * @global array $wp_current_filter stores the list of current filters with the current one last
    155149 *
    156150 * @since 0.71
     
    161155 * @return mixed The filtered value after all hooked functions are applied to it.
    162156 */
    163157function apply_filters( $tag, $value ) {
    164         global $wp_filter, $merged_filters, $wp_current_filter;
     158        global $wp_filter, $wp_current_filter;
    165159
    166160        $args = array();
    167161
     
    181175        if ( !isset($wp_filter['all']) )
    182176                $wp_current_filter[] = $tag;
    183177
    184         // Sort
    185         if ( !isset( $merged_filters[ $tag ] ) ) {
    186                 ksort($wp_filter[$tag]);
    187                 $merged_filters[ $tag ] = true;
    188         }
    189 
    190         reset( $wp_filter[ $tag ] );
    191 
    192178        if ( empty($args) )
    193179                $args = func_get_args();
    194180
    195         do {
    196                 foreach( (array) current($wp_filter[$tag]) as $the_ )
    197                         if ( !is_null($the_['function']) ){
    198                                 $args[1] = $value;
    199                                 $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
    200                         }
     181        $iterator = $wp_filter[$tag]->get_iterator();
     182        foreach ( $iterator as $the_ ) {
     183                $args[1] = $value;
     184                $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
     185        }
     186        $iterator->cleanup();
    201187
    202         } while ( next($wp_filter[$tag]) !== false );
    203 
    204188        array_pop( $wp_current_filter );
    205189
    206190        return $value;
     
    216200 * @subpackage Plugin
    217201 * @since 3.0.0
    218202 * @global array $wp_filter Stores all of the filters
    219  * @global array $merged_filters Merges the filter hooks using this function.
    220203 * @global array $wp_current_filter stores the list of current filters with the current one last
    221204 *
    222205 * @param string $tag The name of the filter hook.
     
    224207 * @return mixed The filtered value after all hooked functions are applied to it.
    225208 */
    226209function apply_filters_ref_array($tag, $args) {
    227         global $wp_filter, $merged_filters, $wp_current_filter;
     210        global $wp_filter, $wp_current_filter;
    228211
    229212        // Do 'all' actions first
    230213        if ( isset($wp_filter['all']) ) {
     
    242225        if ( !isset($wp_filter['all']) )
    243226                $wp_current_filter[] = $tag;
    244227
    245         // Sort
    246         if ( !isset( $merged_filters[ $tag ] ) ) {
    247                 ksort($wp_filter[$tag]);
    248                 $merged_filters[ $tag ] = true;
     228        $iterator = $wp_filter[$tag]->get_iterator();
     229        foreach ( $iterator as $the_ ) {
     230                $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    249231        }
     232        $iterator->cleanup();
    250233
    251         reset( $wp_filter[ $tag ] );
    252 
    253         do {
    254                 foreach( (array) current($wp_filter[$tag]) as $the_ )
    255                         if ( !is_null($the_['function']) )
    256                                 $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    257 
    258         } while ( next($wp_filter[$tag]) !== false );
    259 
    260234        array_pop( $wp_current_filter );
    261235
    262236        return $args[0];
     
    284258 * @return boolean Whether the function existed before it was removed.
    285259 */
    286260function remove_filter( $tag, $function_to_remove, $priority = 10 ) {
    287         $function_to_remove = _wp_filter_build_unique_id($tag, $function_to_remove, $priority);
     261        global $wp_filter;
    288262
    289         $r = isset($GLOBALS['wp_filter'][$tag][$priority][$function_to_remove]);
    290 
    291         if ( true === $r) {
    292                 unset($GLOBALS['wp_filter'][$tag][$priority][$function_to_remove]);
    293                 if ( empty($GLOBALS['wp_filter'][$tag][$priority]) )
    294                         unset($GLOBALS['wp_filter'][$tag][$priority]);
    295                 unset($GLOBALS['merged_filters'][$tag]);
     263        $r = false;
     264        if ( isset($wp_filter[$tag]) ) {
     265                $function_to_remove = $wp_filter->build_unique_id($function_to_remove, $tag, $priority);
     266                $r = $wp_filter[$tag]->remove_filter($function_to_remove, $priority);
    296267        }
    297268
    298269        return $r;
     
    308279 * @return bool True when finished.
    309280 */
    310281function remove_all_filters($tag, $priority = false) {
    311         global $wp_filter, $merged_filters;
     282        global $wp_filter;
    312283
    313284        if( isset($wp_filter[$tag]) ) {
    314                 if( false !== $priority && isset($wp_filter[$tag][$priority]) )
    315                         unset($wp_filter[$tag][$priority]);
    316                 else
    317                         unset($wp_filter[$tag]);
     285                $wp_filter[$tag]->remove_all_filters($priority);
    318286        }
    319287
    320         if( isset($merged_filters[$tag]) )
    321                 unset($merged_filters[$tag]);
    322 
    323288        return true;
    324289}
    325290
     
    384349 * @return null Will return null if $tag does not exist in $wp_filter array
    385350 */
    386351function do_action($tag, $arg = '') {
    387         global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
     352        global $wp_filter, $wp_actions, $wp_current_filter;
    388353
    389354        if ( ! isset($wp_actions[$tag]) )
    390355                $wp_actions[$tag] = 1;
     
    415380        for ( $a = 2; $a < func_num_args(); $a++ )
    416381                $args[] = func_get_arg($a);
    417382
    418         // Sort
    419         if ( !isset( $merged_filters[ $tag ] ) ) {
    420                 ksort($wp_filter[$tag]);
    421                 $merged_filters[ $tag ] = true;
     383        $iterator = $wp_filter[$tag]->get_iterator();
     384        foreach ( $iterator as $the_ ) {
     385                call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    422386        }
     387        $iterator->cleanup();
    423388
    424         reset( $wp_filter[ $tag ] );
    425 
    426         do {
    427                 foreach ( (array) current($wp_filter[$tag]) as $the_ )
    428                         if ( !is_null($the_['function']) )
    429                                 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    430 
    431         } while ( next($wp_filter[$tag]) !== false );
    432 
    433389        array_pop($wp_current_filter);
    434390}
    435391
     
    470426 * @return null Will return null if $tag does not exist in $wp_filter array
    471427 */
    472428function do_action_ref_array($tag, $args) {
    473         global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
     429        global $wp_filter, $wp_actions, $wp_current_filter;
    474430
    475431        if ( ! isset($wp_actions[$tag]) )
    476432                $wp_actions[$tag] = 1;
     
    493449        if ( !isset($wp_filter['all']) )
    494450                $wp_current_filter[] = $tag;
    495451
    496         // Sort
    497         if ( !isset( $merged_filters[ $tag ] ) ) {
    498                 ksort($wp_filter[$tag]);
    499                 $merged_filters[ $tag ] = true;
     452        $iterator = $wp_filter[$tag]->get_iterator();
     453        foreach ( $iterator as $the_ ) {
     454                call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    500455        }
     456        $iterator->cleanup();
    501457
    502         reset( $wp_filter[ $tag ] );
    503 
    504         do {
    505                 foreach( (array) current($wp_filter[$tag]) as $the_ )
    506                         if ( !is_null($the_['function']) )
    507                                 call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
    508 
    509         } while ( next($wp_filter[$tag]) !== false );
    510 
    511458        array_pop($wp_current_filter);
    512459}
    513460
     
    731678 */
    732679function _wp_call_all_hook($args) {
    733680        global $wp_filter;
    734 
    735         reset( $wp_filter['all'] );
    736         do {
    737                 foreach( (array) current($wp_filter['all']) as $the_ )
    738                         if ( !is_null($the_['function']) )
    739                                 call_user_func_array($the_['function'], $args);
    740 
    741         } while ( next($wp_filter['all']) !== false );
    742 }
     681        $iterator = $wp_filter['all']->get_iterator();
     682        foreach ( $iterator as $the_ ) {
     683                if ( !is_null($the_['function']) ) {
     684                        call_user_func_array($the_['function'], $args);
     685                }
     686        }
     687        $iterator->cleanup();
     688}
    743689
    744690/**
    745691 * Build Unique ID for storage and retrieval.
     
    772718 */
    773719function _wp_filter_build_unique_id($tag, $function, $priority) {
    774720        global $wp_filter;
    775         static $filter_id_count = 0;
    776 
    777         if ( is_string($function) )
    778                 return $function;
    779 
    780         if ( is_object($function) ) {
    781                 // Closures are currently implemented as objects
    782                 $function = array( $function, '' );
    783         } else {
    784                 $function = (array) $function;
    785         }
    786 
    787         if (is_object($function[0]) ) {
    788                 // Object Class Calling
    789                 if ( function_exists('spl_object_hash') ) {
    790                         return spl_object_hash($function[0]) . $function[1];
    791                 } else {
    792                         $obj_idx = get_class($function[0]).$function[1];
    793                         if ( !isset($function[0]->wp_filter_id) ) {
    794                                 if ( false === $priority )
    795                                         return false;
    796                                 $obj_idx .= isset($wp_filter[$tag][$priority]) ? count((array)$wp_filter[$tag][$priority]) : $filter_id_count;
    797                                 $function[0]->wp_filter_id = $filter_id_count;
    798                                 ++$filter_id_count;
    799                         } else {
    800                                 $obj_idx .= $function[0]->wp_filter_id;
    801                         }
    802 
    803                         return $obj_idx;
    804                 }
    805         } else if ( is_string($function[0]) ) {
    806                 // Static Calling
    807                 return $function[0] . '::' . $function[1];
    808         }
     721        return $wp_filter->build_unique_id($function, $tag, $priority);
    809722}
  • tests/phpunit/tests/actions.php

    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
     
    256256        function action_self_removal() {
    257257                remove_action( 'test_action_self_removal', array( $this, 'action_self_removal' ) );
    258258        }
     259
     260        /**
     261         * @ticket 17817
     262         */
     263        function test_action_recursion() {
     264                $tag = rand_str();
     265                $a = new MockAction();
     266                $b = new MockAction();
     267
     268                add_action( $tag, array($a, 'action'), 11, 1 );
     269                add_action( $tag, array($b, 'action'), 13, 1 );
     270                add_action( $tag, array($this, 'action_that_causes_recursion'), 12, 1 );
     271                do_action( $tag, $tag );
     272
     273                $this->assertEquals( 2, $a->get_call_count(), 'recursive actions should call all callbacks with earlier priority' );
     274                $this->assertEquals( 2, $b->get_call_count(), 'recursive actions should call callbacks with later priority' );
     275        }
     276
     277        function action_that_causes_recursion( $tag ) {
     278                static $recursing = FALSE;
     279                if ( !$recursing ) {
     280                        $recursing = TRUE;
     281                        do_action( $tag, $tag );
     282                }
     283                $recursing = FALSE;
     284        }
     285
     286        /**
     287         * @ticket 9968
     288         */
     289        function test_action_callback_manipulation_while_running() {
     290                $tag = rand_str();
     291                $a = new MockAction();
     292                $b = new MockAction();
     293                $c = new MockAction();
     294                $d = new MockAction();
     295                $e = new MockAction();
     296
     297                add_action( $tag, array($a, 'action'), 11, 2 );
     298                add_action( $tag, array($this, 'action_that_manipulates_a_running_hook'), 12, 2 );
     299                add_action( $tag, array($b, 'action'), 12, 2 );
     300
     301                do_action( $tag, $tag, array($a,$b,$c,$d,$e) );
     302                do_action( $tag, $tag, array($a,$b,$c,$d,$e) );
     303
     304                $this->assertEquals( 2, $a->get_call_count(), 'callbacks should run unless otherwise instructed' );
     305                $this->assertEquals( 1, $b->get_call_count(), 'callback removed by same priority callback should still get called' );
     306                $this->assertEquals( 1, $c->get_call_count(), 'callback added by same priority callback should not get called' );
     307                $this->assertEquals( 2, $d->get_call_count(), 'callback added by earlier priority callback should get called' );
     308                $this->assertEquals( 1, $e->get_call_count(), 'callback added by later priority callback should not get called' );
     309        }
     310
     311        function action_that_manipulates_a_running_hook( $tag, $mocks ) {
     312                remove_action( $tag, array($mocks[1], 'action'), 12, 2 );
     313                add_action( $tag, array($mocks[2], 'action' ), 12, 2 );
     314                add_action( $tag, array($mocks[3], 'action' ), 13, 2 );
     315                add_action( $tag, array($mocks[4], 'action' ), 10, 2 );
     316        }
    259317}
  • 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-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.
  • 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                $this->rewind();
     23        }
     24
     25        public function cleanup() {
     26                $this->hook->detach($this);
     27        }
     28
     29        /**
     30         * Return the current element
     31         *
     32         * @link http://php.net/manual/en/iterator.current.php
     33         * @return mixed Can return any type.
     34         */
     35        public function current() {
     36                return $this->current_callback;
     37        }
     38
     39        /**
     40         * Move forward to next element
     41         * @link http://php.net/manual/en/iterator.next.php
     42         * @return void Any returned value is ignored.
     43         */
     44        public function next() {
     45                $this->current_callback = NULL;
     46                $this->current_callback_index = NULL;
     47                $next = next( $this->callbacks_for_current_priority );
     48
     49                if ( $next === FALSE ) {
     50                        do {
     51                                $this->increment_priority();
     52                        } while ( empty($this->callbacks_for_current_priority) && isset( $this->current_priority) );
     53
     54                        $next = reset( $this->callbacks_for_current_priority );
     55                }
     56
     57                if ( !empty($next) ) {
     58                        $this->current_callback = $next;
     59                        $this->current_callback_index = key($this->callbacks_for_current_priority);
     60                }
     61        }
     62
     63        /**
     64         * Setup the callbacks array for the next available priority
     65         * @return void
     66         */
     67        private function increment_priority() {
     68                $this->set_current_priority();
     69                if ( isset($this->current_priority) ) {
     70                        $this->callbacks_for_current_priority = $this->get_callbacks($this->current_priority);
     71                } else {
     72                        $this->callbacks_for_current_priority = array();
     73                }
     74        }
     75
     76        /**
     77         * Move to the next available priority
     78         * @return void
     79         */
     80        private function set_current_priority() {
     81                if ( empty($this->hook->callbacks) ) {
     82                        $this->all_priorities = array();
     83                        $this->current_priority = NULL;
     84                        return;
     85                }
     86
     87                if ( !isset($this->all_priorities) ) {
     88                        $this->all_priorities = array_keys($this->hook->callbacks);
     89                        sort($this->all_priorities);
     90                        if ( isset($this->current_priority) ) { // callbacks were manipulated while we were running
     91                                foreach ( $this->all_priorities as $index => &$p ) { // go to the next larger priority
     92                                        if ( $p > $this->current_priority ) {
     93                                                $this->current_priority_index = $index;
     94                                                $this->current_priority = $p;
     95                                                break;
     96                                        }
     97                                }
     98                        } else { // starting fresh
     99                                $this->current_priority_index = 0;
     100                                $this->current_priority = $this->all_priorities[0];
     101                        }
     102                        return;
     103                }
     104
     105                if ( !isset($this->current_priority_index) ) {
     106                        $this->current_priority_index = 0;
     107                } else {
     108                        $this->current_priority_index++;
     109                }
     110                if ( isset($this->all_priorities[$this->current_priority_index]) ) {
     111                        $this->current_priority = $this->all_priorities[$this->current_priority_index];
     112                        return;
     113                }
     114
     115                $this->current_priority = NULL;
     116                return;
     117        }
     118
     119        private function get_callbacks( $priority ) {
     120                if ( isset($this->hook->callbacks[$priority]) ) {
     121                        return $this->hook->callbacks[$priority];
     122                }
     123                return array();
     124        }
     125
     126        /**
     127         * Return the key of the current element
     128         * @link http://php.net/manual/en/iterator.key.php
     129         * @return mixed scalar on success, or null on failure.
     130         */
     131        public function key() {
     132                return $this->current_callback_index;
     133        }
     134
     135        /**
     136         * Checks if current position is valid
     137         * @link http://php.net/manual/en/iterator.valid.php
     138         * @return boolean The return value will be casted to boolean and then evaluated.
     139         * Returns true on success or false on failure.
     140         */
     141        public function valid() {
     142                return isset($this->current_callback);
     143        }
     144
     145        /**
     146         * Rewind the Iterator to the first element
     147         * @link http://php.net/manual/en/iterator.rewind.php
     148         * @return void Any returned value is ignored.
     149         */
     150        public function rewind() {
     151                $this->current_priority = NULL;
     152                $this->current_priority_index = NULL;
     153                $this->current_callback = NULL;
     154                $this->current_callback_index = NULL;
     155                $this->callbacks_for_current_priority = array();
     156                $this->next();
     157        }
     158
     159        /**
     160         * Receive update from subject
     161         *
     162         * @link http://php.net/manual/en/splobserver.update.php
     163         *
     164         * @param SplSubject $subject The SplSubject notifying the observer of an update.
     165         *
     166         * @return void
     167         */
     168        public function update( $subject ) {
     169                unset($this->all_priorities);
     170                unset($this->current_priority_index);
     171        }
     172}