Make WordPress Core

Changeset 38928


Ignore:
Timestamp:
10/25/2016 09:25:25 PM (10 years ago)
Author:
ocean90
Message:

General: Introduce a wp_list_sort() helper function, v2.

In addition to wp_list_filter() for filtering a list of objects, and wp_list_pluck() for plucking a certain field out of each object in a list, this new function can be used for sorting a list of objects by specific fields. These functions are now all contained within the new WP_List_Util() class and wp_list_sort() is used in various parts of core for sorting lists.

This was previously committed in [38859] but got reverted in [38862] and [38863]. To fix the previous issues, wp_list_sort() supports now an additional argument to preserve array keys via uasort().

Props flixos90, DrewAPicture, jorbin.
Fixes #37128.

Location:
trunk
Files:
2 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/category-template.php

    r38863 r38928  
    9898         */
    9999        return apply_filters( 'get_the_categories', $categories, $id );
    100 }
    101 
    102 /**
    103  * Sort categories by name.
    104  *
    105  * Used by usort() as a callback, should not be used directly. Can actually be
    106  * used to sort any term object.
    107  *
    108  * @since 2.3.0
    109  * @access private
    110  *
    111  * @param object $a
    112  * @param object $b
    113  * @return int
    114  */
    115 function _usort_terms_by_name( $a, $b ) {
    116         return strcmp( $a->name, $b->name );
    117 }
    118 
    119 /**
    120  * Sort categories by ID.
    121  *
    122  * Used by usort() as a callback, should not be used directly. Can actually be
    123  * used to sort any term object.
    124  *
    125  * @since 2.3.0
    126  * @access private
    127  *
    128  * @param object $a
    129  * @param object $b
    130  * @return int
    131  */
    132 function _usort_terms_by_ID( $a, $b ) {
    133         if ( $a->term_id > $b->term_id )
    134                 return 1;
    135         elseif ( $a->term_id < $b->term_id )
    136                 return -1;
    137         else
    138                 return 0;
    139100}
    140101
  • trunk/src/wp-includes/class-wp-customize-manager.php

    r38906 r38928  
    25062506         *
    25072507         * @since 3.4.0
     2508         * @deprecated 4.7.0 Use wp_list_sort()
    25082509         *
    25092510         * @param WP_Customize_Panel|WP_Customize_Section|WP_Customize_Control $a Object A.
     
    25122513         */
    25132514        protected function _cmp_priority( $a, $b ) {
     2515                _deprecated_function( __METHOD__, '4.7.0', 'wp_list_sort' );
     2516
    25142517                if ( $a->priority === $b->priority ) {
    25152518                        return $a->instance_number - $b->instance_number;
     
    25312534
    25322535                $controls = array();
    2533                 uasort( $this->controls, array( $this, '_cmp_priority' ) );
     2536                $this->controls = wp_list_sort( $this->controls, array(
     2537                        'priority'        => 'ASC',
     2538                        'instance_number' => 'ASC',
     2539                ), 'ASC', true );
    25342540
    25352541                foreach ( $this->controls as $id => $control ) {
     
    25442550
    25452551                // Prepare sections.
    2546                 uasort( $this->sections, array( $this, '_cmp_priority' ) );
     2552                $this->sections = wp_list_sort( $this->sections, array(
     2553                        'priority'        => 'ASC',
     2554                        'instance_number' => 'ASC',
     2555                ), 'ASC', true );
    25472556                $sections = array();
    25482557
     
    25522561                        }
    25532562
    2554                         usort( $section->controls, array( $this, '_cmp_priority' ) );
     2563
     2564                        $section->controls = wp_list_sort( $section->controls, array(
     2565                                'priority'        => 'ASC',
     2566                                'instance_number' => 'ASC',
     2567                        ) );
    25552568
    25562569                        if ( ! $section->panel ) {
     
    25672580
    25682581                // Prepare panels.
    2569                 uasort( $this->panels, array( $this, '_cmp_priority' ) );
     2582                $this->panels = wp_list_sort( $this->panels, array(
     2583                        'priority'        => 'ASC',
     2584                        'instance_number' => 'ASC',
     2585                ), 'ASC', true );
    25702586                $panels = array();
    25712587
     
    25752591                        }
    25762592
    2577                         uasort( $panel->sections, array( $this, '_cmp_priority' ) );
     2593                        $panel->sections = wp_list_sort( $panel->sections, array(
     2594                                'priority'        => 'ASC',
     2595                                'instance_number' => 'ASC',
     2596                        ), 'ASC', true );
    25782597                        $panels[ $panel->id ] = $panel;
    25792598                }
     
    25822601                // Sort panels and top-level sections together.
    25832602                $this->containers = array_merge( $this->panels, $this->sections );
    2584                 uasort( $this->containers, array( $this, '_cmp_priority' ) );
     2603                $this->containers = wp_list_sort( $this->containers, array(
     2604                        'priority'        => 'ASC',
     2605                        'instance_number' => 'ASC',
     2606                ), 'ASC', true );
    25852607        }
    25862608
  • trunk/src/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php

    r38863 r38928  
    533533
    534534                if ( ARRAY_A === $args['output'] ) {
    535                         $GLOBALS['_menu_item_sort_prop'] = $args['output_key'];
    536                         usort( $items, '_sort_nav_menu_items' );
     535                        $items = wp_list_sort( $items, array(
     536                                $args['output_key'] => 'ASC',
     537                        ) );
    537538                        $i = 1;
    538539
  • trunk/src/wp-includes/customize/class-wp-customize-nav-menu-setting.php

    r38863 r38928  
    288288                // Make sure the menu objects get re-sorted after an update/insert.
    289289                if ( ! $is_delete && ! empty( $args['orderby'] ) ) {
    290                         $this->_current_menus_sort_orderby = $args['orderby'];
    291                         usort( $menus, array( $this, '_sort_menus_by_orderby' ) );
     290                        $menus = wp_list_sort( $menus, array(
     291                                $args['orderby'] => 'ASC',
     292                        ) );
    292293                }
    293294                // @todo add support for $args['hide_empty'] === true
     
    314315         *
    315316         * @since 4.3.0
     317         * @deprecated 4.7.0 Use wp_list_sort()
    316318         * @access protected
     319         *
    317320         * @param object $menu1
    318321         * @param object $menu2
     
    322325         */
    323326        protected function _sort_menus_by_orderby( $menu1, $menu2 ) {
     327                _deprecated_function( __METHOD__, '4.7.0', 'wp_list_sort' );
     328
    324329                $key = $this->_current_menus_sort_orderby;
    325330                return strcmp( $menu1->$key, $menu2->$key );
  • trunk/src/wp-includes/deprecated.php

    r38863 r38928  
    37993799        return preg_replace( '%&\s*\{[^}]*(\}\s*;?|$)%', '', $string );
    38003800}
     3801
     3802/**
     3803 * Sort categories by ID.
     3804 *
     3805 * Used by usort() as a callback, should not be used directly. Can actually be
     3806 * used to sort any term object.
     3807 *
     3808 * @since 2.3.0
     3809 * @deprecated 4.7.0 Use wp_list_sort()
     3810 * @access private
     3811 *
     3812 * @param object $a
     3813 * @param object $b
     3814 * @return int
     3815 */
     3816function _usort_terms_by_ID( $a, $b ) {
     3817        _deprecated_function( __FUNCTION__, '4.7.0', 'wp_list_sort' );
     3818
     3819        if ( $a->term_id > $b->term_id )
     3820                return 1;
     3821        elseif ( $a->term_id < $b->term_id )
     3822                return -1;
     3823        else
     3824                return 0;
     3825}
     3826
     3827/**
     3828 * Sort categories by name.
     3829 *
     3830 * Used by usort() as a callback, should not be used directly. Can actually be
     3831 * used to sort any term object.
     3832 *
     3833 * @since 2.3.0
     3834 * @deprecated 4.7.0 Use wp_list_sort()
     3835 * @access private
     3836 *
     3837 * @param object $a
     3838 * @param object $b
     3839 * @return int
     3840 */
     3841function _usort_terms_by_name( $a, $b ) {
     3842        _deprecated_function( __FUNCTION__, '4.7.0', 'wp_list_sort' );
     3843
     3844        return strcmp( $a->name, $b->name );
     3845}
     3846
     3847/**
     3848 * Sort menu items by the desired key.
     3849 *
     3850 * @since 3.0.0
     3851 * @deprecated 4.7.0 Use wp_list_sort()
     3852 * @access private
     3853 *
     3854 * @global string $_menu_item_sort_prop
     3855 *
     3856 * @param object $a The first object to compare
     3857 * @param object $b The second object to compare
     3858 * @return int -1, 0, or 1 if $a is considered to be respectively less than, equal to, or greater than $b.
     3859 */
     3860function _sort_nav_menu_items( $a, $b ) {
     3861        global $_menu_item_sort_prop;
     3862
     3863        _deprecated_function( __FUNCTION__, '4.7.0', 'wp_list_sort' );
     3864
     3865        if ( empty( $_menu_item_sort_prop ) )
     3866                return 0;
     3867
     3868        if ( ! isset( $a->$_menu_item_sort_prop ) || ! isset( $b->$_menu_item_sort_prop ) )
     3869                return 0;
     3870
     3871        $_a = (int) $a->$_menu_item_sort_prop;
     3872        $_b = (int) $b->$_menu_item_sort_prop;
     3873
     3874        if ( $a->$_menu_item_sort_prop == $b->$_menu_item_sort_prop )
     3875                return 0;
     3876        elseif ( $_a == $a->$_menu_item_sort_prop && $_b == $b->$_menu_item_sort_prop )
     3877                return $_a < $_b ? -1 : 1;
     3878        else
     3879                return strcmp( $a->$_menu_item_sort_prop, $b->$_menu_item_sort_prop );
     3880}
  • trunk/src/wp-includes/functions.php

    r38884 r38928  
    34903490 *
    34913491 * @since 3.0.0
     3492 * @since 4.7.0 Uses WP_List_Util class.
    34923493 *
    34933494 * @param array       $list     An array of objects to filter
     
    35033504 */
    35043505function wp_filter_object_list( $list, $args = array(), $operator = 'and', $field = false ) {
    3505         if ( ! is_array( $list ) )
     3506        if ( ! is_array( $list ) ) {
    35063507                return array();
    3507 
    3508         $list = wp_list_filter( $list, $args, $operator );
    3509 
    3510         if ( $field )
    3511                 $list = wp_list_pluck( $list, $field );
    3512 
    3513         return $list;
     3508        }
     3509
     3510        $util = new WP_List_Util( $list );
     3511
     3512        $util->filter( $args, $operator );
     3513
     3514        if ( $field ) {
     3515                $util->pluck( $field );
     3516        }
     3517
     3518        return $util->get_output();
    35143519}
    35153520
     
    35183523 *
    35193524 * @since 3.1.0
     3525 * @since 4.7.0 Uses WP_List_Util class.
    35203526 *
    35213527 * @param array  $list     An array of objects to filter.
     
    35293535 */
    35303536function wp_list_filter( $list, $args = array(), $operator = 'AND' ) {
    3531         if ( ! is_array( $list ) )
     3537        if ( ! is_array( $list ) ) {
    35323538                return array();
    3533 
    3534         if ( empty( $args ) )
    3535                 return $list;
    3536 
    3537         $operator = strtoupper( $operator );
    3538         $count = count( $args );
    3539         $filtered = array();
    3540 
    3541         foreach ( $list as $key => $obj ) {
    3542                 $to_match = (array) $obj;
    3543 
    3544                 $matched = 0;
    3545                 foreach ( $args as $m_key => $m_value ) {
    3546                         if ( array_key_exists( $m_key, $to_match ) && $m_value == $to_match[ $m_key ] )
    3547                                 $matched++;
    3548                 }
    3549 
    3550                 if ( ( 'AND' == $operator && $matched == $count )
    3551                   || ( 'OR' == $operator && $matched > 0 )
    3552                   || ( 'NOT' == $operator && 0 == $matched ) ) {
    3553                         $filtered[$key] = $obj;
    3554                 }
    3555         }
    3556 
    3557         return $filtered;
     3539        }
     3540
     3541        $util = new WP_List_Util( $list );
     3542        return $util->filter( $args, $operator );
    35583543}
    35593544
     
    35663551 * @since 3.1.0
    35673552 * @since 4.0.0 $index_key parameter added.
     3553 * @since 4.7.0 Uses WP_List_Util class.
    35683554 *
    35693555 * @param array      $list      List of objects or arrays
     
    35763562 */
    35773563function wp_list_pluck( $list, $field, $index_key = null ) {
    3578         if ( ! $index_key ) {
    3579                 /*
    3580                  * This is simple. Could at some point wrap array_column()
    3581                  * if we knew we had an array of arrays.
    3582                  */
    3583                 foreach ( $list as $key => $value ) {
    3584                         if ( is_object( $value ) ) {
    3585                                 $list[ $key ] = $value->$field;
    3586                         } else {
    3587                                 $list[ $key ] = $value[ $field ];
    3588                         }
    3589                 }
    3590                 return $list;
    3591         }
    3592 
    3593         /*
    3594          * When index_key is not set for a particular item, push the value
    3595          * to the end of the stack. This is how array_column() behaves.
    3596          */
    3597         $newlist = array();
    3598         foreach ( $list as $value ) {
    3599                 if ( is_object( $value ) ) {
    3600                         if ( isset( $value->$index_key ) ) {
    3601                                 $newlist[ $value->$index_key ] = $value->$field;
    3602                         } else {
    3603                                 $newlist[] = $value->$field;
    3604                         }
    3605                 } else {
    3606                         if ( isset( $value[ $index_key ] ) ) {
    3607                                 $newlist[ $value[ $index_key ] ] = $value[ $field ];
    3608                         } else {
    3609                                 $newlist[] = $value[ $field ];
    3610                         }
    3611                 }
    3612         }
    3613 
    3614         return $newlist;
     3564        $util = new WP_List_Util( $list );
     3565        return $util->pluck( $field, $index_key );
     3566}
     3567
     3568/**
     3569 * Sorts a list of objects, based on one or more orderby arguments.
     3570 *
     3571 * @since 4.7.0
     3572 *
     3573 * @param array        $list          An array of objects to filter.
     3574 * @param string|array $orderby       Optional. Either the field name to order by or an array
     3575 *                                    of multiple orderby fields as $orderby => $order.
     3576 * @param string       $order         Optional. Either 'ASC' or 'DESC'. Only used if $orderby
     3577 *                                    is a string.
     3578 * @param bool         $preserve_keys Optional. Whether to preserve keys. Default false.
     3579 * @return array The sorted array.
     3580 */
     3581function wp_list_sort( $list, $orderby = array(), $order = 'ASC', $preserve_keys = false ) {
     3582        if ( ! is_array( $list ) ) {
     3583                return array();
     3584        }
     3585
     3586        $util = new WP_List_Util( $list );
     3587        return $util->sort( $orderby, $order, $preserve_keys );
    36153588}
    36163589
  • trunk/src/wp-includes/link-template.php

    r38863 r38928  
    170170                        $cats = get_the_category($post->ID);
    171171                        if ( $cats ) {
    172                                 usort($cats, '_usort_terms_by_ID'); // order by ID
     172                                $cats = wp_list_sort( $cats, array(
     173                                        'term_id' => 'ASC',
     174                                ) );
    173175
    174176                                /**
  • trunk/src/wp-includes/nav-menu.php

    r38863 r38928  
    559559
    560560/**
    561  * Sort menu items by the desired key.
    562  *
    563  * @since 3.0.0
    564  * @access private
    565  *
    566  * @global string $_menu_item_sort_prop
    567  *
    568  * @param object $a The first object to compare
    569  * @param object $b The second object to compare
    570  * @return int -1, 0, or 1 if $a is considered to be respectively less than, equal to, or greater than $b.
    571  */
    572 function _sort_nav_menu_items( $a, $b ) {
    573         global $_menu_item_sort_prop;
    574 
    575         if ( empty( $_menu_item_sort_prop ) )
    576                 return 0;
    577 
    578         if ( ! isset( $a->$_menu_item_sort_prop ) || ! isset( $b->$_menu_item_sort_prop ) )
    579                 return 0;
    580 
    581         $_a = (int) $a->$_menu_item_sort_prop;
    582         $_b = (int) $b->$_menu_item_sort_prop;
    583 
    584         if ( $a->$_menu_item_sort_prop == $b->$_menu_item_sort_prop )
    585                 return 0;
    586         elseif ( $_a == $a->$_menu_item_sort_prop && $_b == $b->$_menu_item_sort_prop )
    587                 return $_a < $_b ? -1 : 1;
    588         else
    589                 return strcmp( $a->$_menu_item_sort_prop, $b->$_menu_item_sort_prop );
    590 }
    591 
    592 /**
    593561 * Return if a menu item is valid.
    594562 *
     
    683651
    684652        if ( ARRAY_A == $args['output'] ) {
    685                 $GLOBALS['_menu_item_sort_prop'] = $args['output_key'];
    686                 usort($items, '_sort_nav_menu_items');
     653                $items = wp_list_sort( $items, array(
     654                        $args['output_key'] => 'ASC',
     655                ) );
    687656                $i = 1;
    688657                foreach ( $items as $k => $item ) {
     
    777746                                $menu_item->type_label = __( 'Post Type Archive' );
    778747                                $post_content = wp_trim_words( $menu_item->post_content, 200 );
    779                                 $post_type_description = '' == $post_content ? $post_type_description : $post_content; 
     748                                $post_type_description = '' == $post_content ? $post_type_description : $post_content;
    780749                                $menu_item->url = get_post_type_archive_link( $menu_item->object );
    781750                        } elseif ( 'taxonomy' == $menu_item->type ) {
  • trunk/src/wp-settings.php

    r38899 r38928  
    9090// Load early WordPress files.
    9191require( ABSPATH . WPINC . '/compat.php' );
     92require( ABSPATH . WPINC . '/class-wp-list-util.php' );
    9293require( ABSPATH . WPINC . '/functions.php' );
    9394require( ABSPATH . WPINC . '/class-wp-matchesmapregex.php' );
  • trunk/tests/phpunit/tests/customize/manager.php

    r38858 r38928  
    18631863                return $this->filtered_device_list();
    18641864        }
     1865
     1866        /**
     1867         * @ticket 37128
     1868         */
     1869        function test_prepare_controls_wp_list_sort_controls() {
     1870                wp_set_current_user( self::$admin_user_id );
     1871
     1872                $controls = array( 'foo' => 2, 'bar' => 4, 'foobar' => 3, 'key' => 1 );
     1873                $controls_sorted = array( 'key', 'foo', 'foobar', 'bar' );
     1874
     1875                $this->manager->add_section( 'foosection', array() );
     1876
     1877                foreach ( $controls as $control_id => $priority ) {
     1878                        $this->manager->add_setting( $control_id );
     1879                        $this->manager->add_control( $control_id, array(
     1880                                'priority' => $priority,
     1881                                'section'  => 'foosection',
     1882                        ) );
     1883                }
     1884
     1885                $this->manager->prepare_controls();
     1886
     1887                $result = $this->manager->controls();
     1888                $this->assertEquals( $controls_sorted, array_keys( $result ) );
     1889        }
     1890
     1891        /**
     1892         * @ticket 37128
     1893         */
     1894        function test_prepare_controls_wp_list_sort_sections() {
     1895                wp_set_current_user( self::$admin_user_id );
     1896
     1897                $sections = array( 'foo' => 2, 'bar' => 4, 'foobar' => 3, 'key' => 1 );
     1898                $sections_sorted = array( 'key', 'foo', 'foobar', 'bar' );
     1899
     1900                foreach ( $sections as $section_id => $priority ) {
     1901                        $this->manager->add_section( $section_id, array(
     1902                                'priority' => $priority,
     1903                        ) );
     1904                }
     1905
     1906                $this->manager->prepare_controls();
     1907
     1908                $result = $this->manager->sections();
     1909                $this->assertEquals( $sections_sorted, array_keys( $result ) );
     1910        }
     1911
     1912        /**
     1913         * @ticket 37128
     1914         */
     1915        function test_prepare_controls_wp_list_sort_panels() {
     1916                wp_set_current_user( self::$admin_user_id );
     1917
     1918                $panels = array( 'foo' => 2, 'bar' => 4, 'foobar' => 3, 'key' => 1 );
     1919                $panels_sorted = array( 'key', 'foo', 'foobar', 'bar' );
     1920
     1921                foreach ( $panels as $panel_id => $priority ) {
     1922                        $this->manager->add_panel( $panel_id, array(
     1923                                'priority' => $priority,
     1924                        ) );
     1925                }
     1926
     1927                $this->manager->prepare_controls();
     1928
     1929                $result = $this->manager->panels();
     1930                $this->assertEquals( $panels_sorted, array_keys( $result ) );
     1931        }
    18651932}
    18661933
Note: See TracChangeset for help on using the changeset viewer.