Make WordPress Core

Changeset 55569


Ignore:
Timestamp:
03/21/2023 12:47:20 PM (3 years ago)
Author:
spacedmonkey
Message:

Posts, Post Types: Use WP_Query internally in get_pages.

Convert get_pages to use WP_Query internally. Using WP_Query means that a lot of code has been removed however existing parameters supported by get_pages are transformed in to query arguments. The custom caching solution found in the old version of this function is replaced with the caching found in WP_Query (added in [53941]). This change adds consistency to the codebase, as improvements and changes to WP_Query will filter down to the get_pages function.

Props mikeschinkel, spacedmonkey, nacin, scribu, filosofo, jane, garyc40, markoheijnen, grandslambert, kevinB, wlindley, dbernar1, atimmer, mdawaffe, helen, benjibee, johnbillion, peterwilsoncc, costdev, flixos90, joemcgill.
Fixes #12821.

Location:
trunk
Files:
2 edited

Legend:

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

    r55526 r55569  
    58875887 * Retrieves an array of pages (or hierarchical post type items).
    58885888 *
    5889  * @global wpdb $wpdb WordPress database abstraction object.
    5890  *
    58915889 * @since 1.5.0
     5890 * @since 6.3.0 Use WP_Query internally.
    58925891 *
    58935892 * @param array|string $args {
     
    59295928 */
    59305929function get_pages( $args = array() ) {
    5931         global $wpdb;
    5932 
    59335930        $defaults = array(
    59345931                'child_of'     => 0,
     
    59795976        }
    59805977
    5981         // $args can be whatever, only use the args defined in defaults to compute the key.
    5982         $key          = md5( serialize( wp_array_slice_assoc( $parsed_args, array_keys( $defaults ) ) ) );
    5983         $last_changed = wp_cache_get_last_changed( 'posts' );
    5984 
    5985         $cache_key = "get_pages:$key:$last_changed";
    5986         $cache     = wp_cache_get( $cache_key, 'post-queries' );
    5987         if ( false !== $cache ) {
    5988                 _prime_post_caches( $cache, false, false );
    5989 
    5990                 // Convert to WP_Post instances.
    5991                 $pages = array_map( 'get_post', $cache );
    5992                 /** This filter is documented in wp-includes/post.php */
    5993                 $pages = apply_filters( 'get_pages', $pages, $parsed_args );
    5994 
    5995                 return $pages;
    5996         }
    5997 
    5998         $inclusions = '';
     5978        $query_args = array(
     5979                'orderby'                => 'post_title',
     5980                'order'                  => 'ASC',
     5981                'post__not_in'           => wp_parse_id_list( $exclude ),
     5982                'meta_key'               => $meta_key,
     5983                'meta_value'             => $meta_value,
     5984                'posts_per_page'         => -1,
     5985                'offset'                 => $offset,
     5986                'post_type'              => $parsed_args['post_type'],
     5987                'post_status'            => $post_status,
     5988                'update_post_term_cache' => false,
     5989                'update_post_meta_cache' => false,
     5990                'ignore_sticky_posts'    => true,
     5991                'no_found_rows'          => true,
     5992        );
     5993
    59995994        if ( ! empty( $parsed_args['include'] ) ) {
    6000                 $child_of     = 0; // Ignore child_of, parent, exclude, meta_key, and meta_value params if using include.
    6001                 $parent       = -1;
    6002                 $exclude      = '';
    6003                 $meta_key     = '';
    6004                 $meta_value   = '';
    6005                 $hierarchical = false;
    6006                 $incpages     = wp_parse_id_list( $parsed_args['include'] );
    6007                 if ( ! empty( $incpages ) ) {
    6008                         $inclusions = ' AND ID IN (' . implode( ',', $incpages ) . ')';
    6009                 }
    6010         }
    6011 
    6012         $exclusions = '';
    6013         if ( ! empty( $exclude ) ) {
    6014                 $expages = wp_parse_id_list( $exclude );
    6015                 if ( ! empty( $expages ) ) {
    6016                         $exclusions = ' AND ID NOT IN (' . implode( ',', $expages ) . ')';
    6017                 }
    6018         }
    6019 
    6020         $author_query = '';
     5995                $child_of = 0; // Ignore child_of, parent, exclude, meta_key, and meta_value params if using include.
     5996                $parent   = -1;
     5997                unset( $query_args['post__not_in'], $query_args['meta_key'], $query_args['meta_value'] );
     5998                $hierarchical           = false;
     5999                $query_args['post__in'] = wp_parse_id_list( $parsed_args['include'] );
     6000        }
     6001
    60216002        if ( ! empty( $parsed_args['authors'] ) ) {
    60226003                $post_authors = wp_parse_list( $parsed_args['authors'] );
    60236004
    60246005                if ( ! empty( $post_authors ) ) {
     6006                        $query_args['author__in'] = array();
    60256007                        foreach ( $post_authors as $post_author ) {
    60266008                                // Do we have an author id or an author login?
     
    60356017                                        $post_author = $post_author->ID;
    60366018                                }
    6037 
    6038                                 if ( '' === $author_query ) {
    6039                                         $author_query = $wpdb->prepare( ' post_author = %d ', $post_author );
    6040                                 } else {
    6041                                         $author_query .= $wpdb->prepare( ' OR post_author = %d ', $post_author );
    6042                                 }
     6019                                $query_args['author__in'][] = $post_author;
    60436020                        }
    6044                         if ( '' !== $author_query ) {
    6045                                 $author_query = " AND ($author_query)";
    6046                         }
    6047                 }
    6048         }
    6049 
    6050         $join  = '';
    6051         $where = "$exclusions $inclusions ";
    6052         if ( '' !== $meta_key || '' !== $meta_value ) {
    6053                 $join = " LEFT JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )";
    6054 
    6055                 // meta_key and meta_value might be slashed.
    6056                 $meta_key   = wp_unslash( $meta_key );
    6057                 $meta_value = wp_unslash( $meta_value );
    6058                 if ( '' !== $meta_key ) {
    6059                         $where .= $wpdb->prepare( " AND $wpdb->postmeta.meta_key = %s", $meta_key );
    6060                 }
    6061                 if ( '' !== $meta_value ) {
    6062                         $where .= $wpdb->prepare( " AND $wpdb->postmeta.meta_value = %s", $meta_value );
    60636021                }
    60646022        }
    60656023
    60666024        if ( is_array( $parent ) ) {
    6067                 $post_parent__in = implode( ',', array_map( 'absint', (array) $parent ) );
     6025                $post_parent__in = array_map( 'absint', (array) $parent );
    60686026                if ( ! empty( $post_parent__in ) ) {
    6069                         $where .= " AND post_parent IN ($post_parent__in)";
     6027                        $query_args['post_parent__in'] = $post_parent__in;
    60706028                }
    60716029        } elseif ( $parent >= 0 ) {
    6072                 $where .= $wpdb->prepare( ' AND post_parent = %d ', $parent );
    6073         }
    6074 
    6075         if ( 1 === count( $post_status ) ) {
    6076                 $where_post_type = $wpdb->prepare( 'post_type = %s AND post_status = %s', $parsed_args['post_type'], reset( $post_status ) );
    6077         } else {
    6078                 $post_status     = implode( "', '", $post_status );
    6079                 $where_post_type = $wpdb->prepare( "post_type = %s AND post_status IN ('$post_status')", $parsed_args['post_type'] );
    6080         }
    6081 
    6082         $orderby_array = array();
    6083         $allowed_keys  = array(
    6084                 'author',
    6085                 'post_author',
    6086                 'date',
    6087                 'post_date',
    6088                 'title',
    6089                 'post_title',
    6090                 'name',
    6091                 'post_name',
    6092                 'modified',
    6093                 'post_modified',
    6094                 'modified_gmt',
    6095                 'post_modified_gmt',
    6096                 'menu_order',
    6097                 'parent',
    6098                 'post_parent',
    6099                 'ID',
    6100                 'rand',
    6101                 'comment_count',
    6102         );
    6103 
    6104         foreach ( explode( ',', $parsed_args['sort_column'] ) as $orderby ) {
    6105                 $orderby = trim( $orderby );
    6106                 if ( ! in_array( $orderby, $allowed_keys, true ) ) {
    6107                         continue;
    6108                 }
    6109 
    6110                 switch ( $orderby ) {
    6111                         case 'menu_order':
    6112                                 break;
    6113                         case 'ID':
    6114                                 $orderby = "$wpdb->posts.ID";
    6115                                 break;
    6116                         case 'rand':
    6117                                 $orderby = 'RAND()';
    6118                                 break;
    6119                         case 'comment_count':
    6120                                 $orderby = "$wpdb->posts.comment_count";
    6121                                 break;
    6122                         default:
    6123                                 if ( 0 === strpos( $orderby, 'post_' ) ) {
    6124                                         $orderby = "$wpdb->posts." . $orderby;
    6125                                 } else {
    6126                                         $orderby = "$wpdb->posts.post_" . $orderby;
    6127                                 }
    6128                 }
    6129 
    6130                 $orderby_array[] = $orderby;
    6131 
    6132         }
    6133         $sort_column = ! empty( $orderby_array ) ? implode( ',', $orderby_array ) : "$wpdb->posts.post_title";
    6134 
    6135         $sort_order = strtoupper( $parsed_args['sort_order'] );
    6136         if ( '' !== $sort_order && ! in_array( $sort_order, array( 'ASC', 'DESC' ), true ) ) {
    6137                 $sort_order = 'ASC';
    6138         }
    6139 
    6140         $query  = "SELECT * FROM $wpdb->posts $join WHERE ($where_post_type) $where ";
    6141         $query .= $author_query;
    6142         $query .= ' ORDER BY ' . $sort_column . ' ' . $sort_order;
     6030                $query_args['post_parent'] = $parent;
     6031        }
     6032
     6033        $orderby = wp_parse_list( $parsed_args['sort_column'] );
     6034        $orderby = array_map( 'trim', $orderby );
     6035        if ( $orderby ) {
     6036                $query_args['orderby'] = array_fill_keys( $orderby, $parsed_args['sort_order'] );
     6037        }
     6038
     6039        $order = $parsed_args['sort_order'];
     6040        if ( $order ) {
     6041                $query_args['order'] = $order;
     6042        }
    61436043
    61446044        if ( ! empty( $number ) ) {
    6145                 $query .= ' LIMIT ' . $offset . ',' . $number;
    6146         }
    6147 
    6148         $pages = $wpdb->get_results( $query );
    6149 
    6150         if ( empty( $pages ) ) {
    6151                 wp_cache_set( $cache_key, array(), 'post-queries' );
    6152 
    6153                 /** This filter is documented in wp-includes/post.php */
    6154                 $pages = apply_filters( 'get_pages', array(), $parsed_args );
    6155 
    6156                 return $pages;
    6157         }
    6158 
    6159         // Sanitize before caching so it'll only get done once.
    6160         $num_pages = count( $pages );
    6161         for ( $i = 0; $i < $num_pages; $i++ ) {
    6162                 $pages[ $i ] = sanitize_post( $pages[ $i ], 'raw' );
    6163         }
    6164 
    6165         // Update cache.
    6166         update_post_cache( $pages );
     6045                $query_args['posts_per_page'] = $number;
     6046        }
     6047
     6048        $query = new WP_Query( $query_args );
     6049        $pages = $query->get_posts();
    61676050
    61686051        if ( $child_of || $hierarchical ) {
     
    61866069                }
    61876070        }
    6188 
    6189         $page_structure = array();
    6190         foreach ( $pages as $page ) {
    6191                 $page_structure[] = $page->ID;
    6192         }
    6193 
    6194         wp_cache_set( $cache_key, $page_structure, 'post-queries' );
    6195 
    6196         // Convert to WP_Post instances.
    6197         $pages = array_map( 'get_post', $pages );
    61986071
    61996072        /**
  • trunk/tests/phpunit/tests/post/getPages.php

    r54402 r55569  
    6767                clean_post_cache( $pages[0]->ID );
    6868                $this->assertNotEquals( $time1, $time2 = wp_cache_get( 'last_changed', 'posts' ) );
    69 
     69                get_post( $pages[0]->ID );
    7070                $num_queries = $wpdb->num_queries;
    7171
     
    171171                $found_ids = wp_list_pluck( $found, 'ID' );
    172172                $this->assertSameSets( array( $posts[0] ), $found_ids );
     173        }
     174
     175        /**
     176         * @ticket 12821
     177         * @covers ::get_pages
     178         */
     179        public function test_include_ignore_meta_key() {
     180                $posts = self::factory()->post->create_many(
     181                        2,
     182                        array(
     183                                'post_type' => 'page',
     184                        )
     185                );
     186
     187                $pages = get_pages(
     188                        array(
     189                                'include'    => $posts,
     190                                'meta_key'   => 'foo',
     191                                'meta_value' => 'bar',
     192                        )
     193                );
     194
     195                $page_ids = wp_list_pluck( $pages, 'ID' );
     196                $this->assertSameSets( $posts, $page_ids );
     197        }
     198
     199        /**
     200         * @ticket 12821
     201         * @covers ::get_pages
     202         */
     203        public function test_include_ignore_exclude() {
     204                $includes = self::factory()->post->create_many(
     205                        2,
     206                        array(
     207                                'post_type' => 'page',
     208                        )
     209                );
     210
     211                $excludes = self::factory()->post->create_many(
     212                        2,
     213                        array(
     214                                'post_type' => 'page',
     215                        )
     216                );
     217
     218                $pages = get_pages(
     219                        array(
     220                                'include' => $includes,
     221                                'exclude' => $excludes,
     222                        )
     223                );
     224
     225                $page_ids = wp_list_pluck( $pages, 'ID' );
     226                $this->assertSameSets( $includes, $page_ids );
    173227        }
    174228
     
    720774                $this->assertSame( $num_queries, $wpdb->num_queries );
    721775        }
     776
     777        /**
     778         * @ticket 12821
     779         * @covers ::get_pages
     780         */
     781        public function test_get_pages_post_type() {
     782                register_post_type( 'wptests_pt', array( 'hierarchical' => true ) );
     783                $posts = self::factory()->post->create_many( 2, array( 'post_type' => 'wptests_pt' ) );
     784                $pages = get_pages(
     785                        array(
     786                                'post_type' => 'wptests_pt',
     787                        )
     788                );
     789                $this->assertSameSets( $posts, wp_list_pluck( $pages, 'ID' ) );
     790        }
     791
     792
     793        /**
     794         * @ticket 12821
     795         * @covers ::get_pages
     796         */
     797        public function test_get_pages_author() {
     798                $author_1 = self::factory()->user->create(
     799                        array(
     800                                'user_login' => 'author1',
     801                                'role'       => 'author',
     802                        )
     803                );
     804                $posts    = self::factory()->post->create_many(
     805                        2,
     806                        array(
     807                                'post_type'   => 'page',
     808                                'post_author' => $author_1,
     809                        )
     810                );
     811                $pages    = get_pages(
     812                        array(
     813                                'authors' => $author_1,
     814                        )
     815                );
     816
     817                $this->assertSameSets( $posts, wp_list_pluck( $pages, 'ID' ) );
     818        }
     819
     820
     821        /**
     822         * @ticket 12821
     823         * @covers ::get_pages
     824         */
     825        public function test_get_pages_post_status() {
     826                register_post_status(
     827                        'foo',
     828                        array(
     829                                'public' => true,
     830                        )
     831                );
     832
     833                $posts = self::factory()->post->create_many(
     834                        2,
     835                        array(
     836                                'post_type'   => 'page',
     837                                'post_status' => 'foo',
     838                        )
     839                );
     840                $pages = get_pages(
     841                        array(
     842                                'post_status' => 'foo',
     843                        )
     844                );
     845
     846                $this->assertSameSets( $posts, wp_list_pluck( $pages, 'ID' ) );
     847        }
     848
     849        /**
     850         * @ticket 12821
     851         * @covers ::get_pages
     852         */
     853        public function test_get_pages_offset() {
     854                $posts = self::factory()->post->create_many( 4, array( 'post_type' => 'page' ) );
     855                $pages = get_pages(
     856                        array(
     857                                'offset' => 2,
     858                                'number' => 2,
     859                        )
     860                );
     861
     862                $this->assertSameSets( array( $posts[2], $posts[3] ), wp_list_pluck( $pages, 'ID' ) );
     863        }
     864
     865
     866        /**
     867         * @ticket 12821
     868         * @covers ::get_pages
     869         */
     870        public function test_get_pages_authors() {
     871                $author_1 = self::factory()->user->create(
     872                        array(
     873                                'user_login' => 'author1',
     874                                'role'       => 'author',
     875                        )
     876                );
     877                $post_1   = self::factory()->post->create(
     878                        array(
     879                                'post_title'  => 'Page 1',
     880                                'post_type'   => 'page',
     881                                'post_author' => $author_1,
     882                                'post_date'   => '2007-01-01 00:00:00',
     883                        )
     884                );
     885
     886                $author_2 = self::factory()->user->create(
     887                        array(
     888                                'user_login' => 'author2',
     889                                'role'       => 'author',
     890                        )
     891                );
     892                $post_2   = self::factory()->post->create(
     893                        array(
     894                                'post_title'  => 'Page 2',
     895                                'post_type'   => 'page',
     896                                'post_author' => $author_2,
     897                                'post_date'   => '2007-01-01 00:00:00',
     898                        )
     899                );
     900                $pages    = get_pages(
     901                        array(
     902                                'authors' => "{$author_1}, {$author_2}",
     903                        )
     904                );
     905
     906                $this->assertSameSets( array( $post_1, $post_2 ), wp_list_pluck( $pages, 'ID' ) );
     907        }
     908
     909        /**
     910         * @ticket 12821
     911         * @covers ::get_pages
     912         */
     913        public function test_get_pages_authors_names() {
     914                $author_1 = self::factory()->user->create(
     915                        array(
     916                                'user_login' => 'author1',
     917                                'role'       => 'author',
     918                        )
     919                );
     920                $post_1   = self::factory()->post->create(
     921                        array(
     922                                'post_title'  => 'Page 1',
     923                                'post_type'   => 'page',
     924                                'post_author' => $author_1,
     925                                'post_date'   => '2007-01-01 00:00:00',
     926                        )
     927                );
     928
     929                $author_2 = self::factory()->user->create(
     930                        array(
     931                                'user_login' => 'author2',
     932                                'role'       => 'author',
     933                        )
     934                );
     935                $post_2   = self::factory()->post->create(
     936                        array(
     937                                'post_title'  => 'Page 2',
     938                                'post_type'   => 'page',
     939                                'post_author' => $author_2,
     940                                'post_date'   => '2007-01-01 00:00:00',
     941                        )
     942                );
     943                $pages    = get_pages(
     944                        array(
     945                                'authors' => 'author1, author2',
     946                        )
     947                );
     948
     949                $this->assertSameSets( array( $post_1, $post_2 ), wp_list_pluck( $pages, 'ID' ) );
     950        }
     951
     952        /**
     953         * @ticket 12821
     954         * @covers ::get_pages
     955         */
     956        public function test_orderby() {
     957                global $wpdb;
     958                // 'rand' is a valid value.
     959                get_pages( array( 'sort_column' => 'rand' ) );
     960                $this->assertStringContainsString( 'ORDER BY RAND()', $wpdb->last_query, 'Check order is random' );
     961
     962                // This isn't allowed.
     963                get_pages( array( 'sort_order' => 'rand' ) );
     964                $this->assertStringContainsString( 'ORDER BY', $wpdb->last_query, 'Check orderby is present' );
     965                $this->assertStringNotContainsString( 'RAND()', $wpdb->last_query, 'Check order is random is not present' );
     966                $this->assertStringContainsString( 'DESC', $wpdb->last_query, 'Check DESC is random is not present' );
     967
     968                // 'none' is a valid value.
     969                get_pages( array( 'sort_column' => 'none' ) );
     970                $this->assertStringNotContainsString( 'ORDER BY', $wpdb->last_query, 'Check orderby is not present' );
     971                $this->assertStringNotContainsString( 'DESC', $wpdb->last_query, 'Check DESC is not present' );
     972                $this->assertStringNotContainsString( 'ASC', $wpdb->last_query, 'Check ASC is not present' );
     973
     974                // False is a valid value.
     975                get_pages( array( 'sort_column' => false ) );
     976                $this->assertStringContainsString( 'ORDER BY', $wpdb->last_query, 'Check orderby is present if sort_column equal false is passed.' );
     977
     978                // Empty array() is a valid value.
     979                get_pages( array( 'sort_column' => array() ) );
     980                $this->assertStringContainsString( 'ORDER BY', $wpdb->last_query, 'Check orderby is present  if sort_column equals an empty array is passed.' );
     981        }
     982
     983        /**
     984         * @ticket 12821
     985         * @covers ::get_pages
     986         */
     987        public function test_order() {
     988                global $wpdb;
     989
     990                get_pages(
     991                        array(
     992                                'sort_column' => 'post_type',
     993                        )
     994                );
     995                $this->assertStringContainsString(
     996                        "ORDER BY $wpdb->posts.post_type ASC",
     997                        $wpdb->last_query,
     998                        'Check order is post type'
     999                );
     1000
     1001                get_pages(
     1002                        array(
     1003                                'sort_column' => 'title',
     1004                                'sort_order'  => 'foo',
     1005                        )
     1006                );
     1007                $this->assertStringContainsString(
     1008                        "ORDER BY $wpdb->posts.post_title DESC",
     1009                        $wpdb->last_query,
     1010                        'Check order is default'
     1011                );
     1012
     1013                get_pages(
     1014                        array(
     1015                                'sort_order'  => 'asc',
     1016                                'sort_column' => 'date',
     1017                        )
     1018                );
     1019                $this->assertStringContainsString(
     1020                        "ORDER BY $wpdb->posts.post_date ASC",
     1021                        $wpdb->last_query,
     1022                        'Check order is post date'
     1023                );
     1024        }
    7221025}
Note: See TracChangeset for help on using the changeset viewer.