+ /**
+ * If the passed orderby value is allowed, convert the alias to a
+ * properly-prefixed orderby value.
+ *
+ * @since 4.0.0
+ * @access protected
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param string $orderby Alias for the field to order by.
+ * @return string|false Table-prefixed value to used in the ORDER clause. False otherwise.
+ */
+ protected function parse_orderby( $orderby ) {
+ global $wpdb;
+
+ // Used to filter values.
+ $allowed_keys = array(
+ 'post_name', 'post_author', 'post_date', 'post_title', 'post_modified',
+ 'post_parent', 'post_type', 'name', 'author', 'date', 'title', 'modified',
+ 'parent', 'type', 'ID', 'menu_order', 'comment_count', 'rand',
+ );
+
+ $primary_meta_key = '';
+ $primary_meta_query = false;
+ $meta_clauses = $this->meta_query->get_clauses();
+ if ( ! empty( $meta_clauses ) ) {
+ $primary_meta_query = reset( $meta_clauses );
+
+ if ( ! empty( $primary_meta_query['key'] ) ) {
+ $primary_meta_key = $primary_meta_query['key'];
+ $allowed_keys[] = $primary_meta_key;
+ }
+
+ $allowed_keys[] = 'meta_value';
+ $allowed_keys[] = 'meta_value_num';
+ $allowed_keys = array_merge( $allowed_keys, array_keys( $meta_clauses ) );
+ }
+
+ // If RAND() contains a seed value, sanitize and add to allowed keys.
+ $rand_with_seed = false;
+ if ( preg_match( '/RAND\(([0-9]+)\)/i', $orderby, $matches ) ) {
+ $orderby = sprintf( 'RAND(%s)', intval( $matches[1] ) );
+ $allowed_keys[] = $orderby;
+ $rand_with_seed = true;
+ }
+
+ if ( ! in_array( $orderby, $allowed_keys, true ) ) {
+ return false;
+ }
+
+ switch ( $orderby ) {
+ case 'post_name':
+ case 'post_author':
+ case 'post_date':
+ case 'post_title':
+ case 'post_modified':
+ case 'post_parent':
+ case 'post_type':
+ case 'ID':
+ case 'menu_order':
+ case 'comment_count':
+ $orderby_clause = "$wpdb->posts.{$orderby}";
+ break;
+ case 'rand':
+ $orderby_clause = 'RAND()';
+ break;
+ case $primary_meta_key:
+ case 'meta_value':
+ if ( ! empty( $primary_meta_query['type'] ) ) {
+ $orderby_clause = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
+ } else {
+ $orderby_clause = "{$primary_meta_query['alias']}.meta_value";
+ }
+ break;
+ case 'meta_value_num':
+ $orderby_clause = "{$primary_meta_query['alias']}.meta_value+0";
+ break;
+ default:
+ if ( array_key_exists( $orderby, $meta_clauses ) ) {
+ // $orderby corresponds to a meta_query clause.
+ $meta_clause = $meta_clauses[ $orderby ];
+ $orderby_clause = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
+ } elseif ( $rand_with_seed ) {
+ $orderby_clause = $orderby;
+ } else {
+ // Default: order by post field.
+ $orderby_clause = "$wpdb->posts.post_" . sanitize_key( $orderby );
+ }
+
+ break;
+ }
+
+ return $orderby_clause;
+ }
+
+ /**
+ * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
+ *
+ * @since 4.0.0
+ * @access protected
+ *
+ * @param string $order The 'order' query variable.
+ * @return string The sanitized 'order' query variable.
+ */
+ protected function parse_order( $order ) {
+ if ( ! is_string( $order ) || empty( $order ) ) {
+ return 'DESC';
+ }
+
+ if ( 'ASC' === strtoupper( $order ) ) {
+ return 'ASC';
+ } else {
+ return 'DESC';
+ }
+ }
+
+ /**
+ * Sets the 404 property and saves whether query is feed.
+ *
+ * @since 2.0.0
+ * @access public
+ */
+ public function set_404() {
+ $is_feed = $this->is_feed;
+
+ $this->init_query_flags();
+ $this->is_404 = true;
+
+ $this->is_feed = $is_feed;
+ }
+
+ /**
+ * Retrieve query variable.
+ *
+ * @since 1.5.0
+ * @since 3.9.0 The `$default` argument was introduced.
+ *
+ * @access public
+ *
+ * @param string $query_var Query variable key.
+ * @param mixed $default Optional. Value to return if the query variable is not set. Default empty.
+ * @return mixed Contents of the query variable.
+ */
+ public function get( $query_var, $default = '' ) {
+ if ( isset( $this->query_vars[ $query_var ] ) ) {
+ return $this->query_vars[ $query_var ];
+ }
+
+ return $default;
+ }
+
+ /**
+ * Set query variable.
+ *
+ * @since 1.5.0
+ * @access public
+ *
+ * @param string $query_var Query variable key.
+ * @param mixed $value Query variable value.
+ */
+ public function set($query_var, $value) {
+ $this->query_vars[$query_var] = $value;
+ }
+
+ /**
+ * Retrieve the posts based on query variables.
+ *
+ * There are a few filters and actions that can be used to modify the post
+ * database query.
+ *
+ * @since 1.5.0
+ * @access public
+ *
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @return array List of posts.
+ */
+ public function get_posts() {
+ global $wpdb;
+
+ $this->parse_query();
+
+ /**
+ * Fires after the query variable object is created, but before the actual query is run.
+ *
+ * Note: If using conditional tags, use the method versions within the passed instance
+ * (e.g. $this->is_main_query() instead of is_main_query()). This is because the functions
+ * like is_main_query() test against the global $wp_query instance, not the passed one.
+ *
+ * @since 2.0.0
+ *
+ * @param WP_Query &$this The WP_Query instance (passed by reference).
+ */
+ do_action_ref_array( 'pre_get_posts', array( &$this ) );
+
+ // Shorthand.
+ $q = &$this->query_vars;
+
+ // Fill again in case pre_get_posts unset some vars.
+ $q = $this->fill_query_vars($q);
+
+ // Parse meta query
+ $this->meta_query = new WP_Meta_Query();
+ $this->meta_query->parse_query_vars( $q );
+
+ // Set a flag if a pre_get_posts hook changed the query vars.
+ $hash = md5( serialize( $this->query_vars ) );
+ if ( $hash != $this->query_vars_hash ) {
+ $this->query_vars_changed = true;
+ $this->query_vars_hash = $hash;
+ }
+ unset($hash);
+
+ // First let's clear some variables
+ $distinct = '';
+ $whichauthor = '';
+ $whichmimetype = '';
+ $where = '';
+ $limits = '';
+ $join = '';
+ $search = '';
+ $groupby = '';
+ $post_status_join = false;
+ $page = 1;
+
+ if ( isset( $q['caller_get_posts'] ) ) {
+ _deprecated_argument( 'WP_Query', '3.1', __( '"caller_get_posts" is deprecated. Use "ignore_sticky_posts" instead.' ) );
+ if ( !isset( $q['ignore_sticky_posts'] ) )
+ $q['ignore_sticky_posts'] = $q['caller_get_posts'];
+ }
+
+ if ( !isset( $q['ignore_sticky_posts'] ) )
+ $q['ignore_sticky_posts'] = false;
+
+ if ( !isset($q['suppress_filters']) )
+ $q['suppress_filters'] = false;
+
+ if ( !isset($q['cache_results']) ) {
+ if ( wp_using_ext_object_cache() )
+ $q['cache_results'] = false;