/**
  * Fetch descendants for located comments.
  *
  * Instead of calling `get_children()` separately on each child comment, we do a single set of queries to fetch
  * the descendant trees for all matched top-level comments.
  *
  * @since 4.4.0
  *
  * @param array $comments Array of top-level comments whose descendants should be filled in.
  * @return array
  */
 protected function fill_descendants($comments)
 {
     global $wpdb;
     $levels = array(0 => wp_list_pluck($comments, 'comment_ID'));
     /*
      * The WHERE clause for the descendant query is the same as for the top-level
      * query, minus the `parent`, `parent__in`, and `parent__not_in` sub-clauses.
      */
     $_where = $this->filtered_where_clause;
     $exclude_keys = array('parent', 'parent__in', 'parent__not_in');
     foreach ($exclude_keys as $exclude_key) {
         if (isset($this->sql_clauses['where'][$exclude_key])) {
             $clause = $this->sql_clauses['where'][$exclude_key];
             // Strip the clause as well as any adjacent ANDs.
             $pattern = '|(?:AND)?\\s*' . $clause . '\\s*(?:AND)?|';
             $_where_parts = preg_split($pattern, $_where);
             // Remove empties.
             $_where_parts = array_filter(array_map('trim', $_where_parts));
             // Reassemble with an AND.
             $_where = implode(' AND ', $_where_parts);
         }
     }
     $key = md5(serialize(wp_array_slice_assoc($this->query_vars, array_keys($this->query_var_defaults))));
     $last_changed = wp_cache_get_last_changed('comment');
     // Fetch an entire level of the descendant tree at a time.
     $level = 0;
     do {
         // Parent-child relationships may be cached. Only query for those that are not.
         $child_ids = $uncached_parent_ids = array();
         $_parent_ids = $levels[$level];
         foreach ($_parent_ids as $parent_id) {
             $cache_key = "get_comment_child_ids:{$parent_id}:{$key}:{$last_changed}";
             $parent_child_ids = wp_cache_get($cache_key, 'comment');
             if (false !== $parent_child_ids) {
                 $child_ids = array_merge($child_ids, $parent_child_ids);
             } else {
                 $uncached_parent_ids[] = $parent_id;
             }
         }
         if ($uncached_parent_ids) {
             // Fetch this level of comments.
             $parent_query_args = $this->query_vars;
             foreach ($exclude_keys as $exclude_key) {
                 $parent_query_args[$exclude_key] = '';
             }
             $parent_query_args['parent__in'] = $uncached_parent_ids;
             $parent_query_args['no_found_rows'] = true;
             $parent_query_args['hierarchical'] = false;
             $parent_query_args['offset'] = 0;
             $parent_query_args['number'] = 0;
             $level_comments = get_comments($parent_query_args);
             // Cache parent-child relationships.
             $parent_map = array_fill_keys($uncached_parent_ids, array());
             foreach ($level_comments as $level_comment) {
                 $parent_map[$level_comment->comment_parent][] = $level_comment->comment_ID;
                 $child_ids[] = $level_comment->comment_ID;
             }
             foreach ($parent_map as $parent_id => $children) {
                 $cache_key = "get_comment_child_ids:{$parent_id}:{$key}:{$last_changed}";
                 wp_cache_set($cache_key, $children, 'comment');
             }
         }
         $level++;
         $levels[$level] = $child_ids;
     } while ($child_ids);
     // Prime comment caches for non-top-level comments.
     $descendant_ids = array();
     for ($i = 1, $c = count($levels); $i < $c; $i++) {
         $descendant_ids = array_merge($descendant_ids, $levels[$i]);
     }
     _prime_comment_caches($descendant_ids, $this->query_vars['update_comment_meta_cache']);
     // Assemble a flat array of all comments + descendants.
     $all_comments = $comments;
     foreach ($descendant_ids as $descendant_id) {
         $all_comments[] = get_comment($descendant_id);
     }
     // If a threaded representation was requested, build the tree.
     if ('threaded' === $this->query_vars['hierarchical']) {
         $threaded_comments = $ref = array();
         foreach ($all_comments as $k => $c) {
             $_c = get_comment($c->comment_ID);
             // If the comment isn't in the reference array, it goes in the top level of the thread.
             if (!isset($ref[$c->comment_parent])) {
                 $threaded_comments[$_c->comment_ID] = $_c;
                 $ref[$_c->comment_ID] = $threaded_comments[$_c->comment_ID];
                 // Otherwise, set it as a child of its parent.
             } else {
                 $ref[$_c->comment_parent]->add_child($_c);
                 $ref[$_c->comment_ID] = $ref[$_c->comment_parent]->get_child($_c->comment_ID);
             }
         }
         // Set the 'populated_children' flag, to ensure additional database queries aren't run.
         foreach ($ref as $_ref) {
             $_ref->populated_children(true);
         }
         $comments = $threaded_comments;
     } else {
         $comments = $all_comments;
     }
     return $comments;
 }
 /**
  * Gets a list of networks matching the query vars.
  *
  * @since 4.6.0
  * @access public
  *
  * @return int|array The list of networks.
  */
 public function get_networks()
 {
     $this->parse_query();
     /**
      * Fires before networks are retrieved.
      *
      * @since 4.6.0
      *
      * @param WP_Network_Query &$this Current instance of WP_Network_Query, passed by reference.
      */
     do_action_ref_array('pre_get_networks', array(&$this));
     // $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
     $key = md5(serialize(wp_array_slice_assoc($this->query_vars, array_keys($this->query_var_defaults))));
     $last_changed = wp_cache_get_last_changed('networks');
     $cache_key = "get_network_ids:{$key}:{$last_changed}";
     $cache_value = wp_cache_get($cache_key, 'networks');
     if (false === $cache_value) {
         $network_ids = $this->get_network_ids();
         if ($network_ids) {
             $this->set_found_networks();
         }
         $cache_value = array('network_ids' => $network_ids, 'found_networks' => $this->found_networks);
         wp_cache_add($cache_key, $cache_value, 'networks');
     } else {
         $network_ids = $cache_value['network_ids'];
         $this->found_networks = $cache_value['found_networks'];
     }
     if ($this->found_networks && $this->query_vars['number']) {
         $this->max_num_pages = ceil($this->found_networks / $this->query_vars['number']);
     }
     // If querying for a count only, there's nothing more to do.
     if ($this->query_vars['count']) {
         // $network_ids is actually a count in this case.
         return intval($network_ids);
     }
     $network_ids = array_map('intval', $network_ids);
     if ('ids' == $this->query_vars['fields']) {
         $this->networks = $network_ids;
         return $this->networks;
     }
     if ($this->query_vars['update_network_cache']) {
         _prime_network_caches($network_ids);
     }
     // Fetch full network objects from the primed cache.
     $_networks = array();
     foreach ($network_ids as $network_id) {
         if ($_network = get_network($network_id)) {
             $_networks[] = $_network;
         }
     }
     /**
      * Filters the network query results.
      *
      * @since 4.6.0
      *
      * @param array            $results  An array of networks.
      * @param WP_Network_Query &$this    Current instance of WP_Network_Query, passed by reference.
      */
     $_networks = apply_filters_ref_array('the_networks', array($_networks, &$this));
     // Convert to WP_Network instances
     $this->networks = array_map('get_network', $_networks);
     return $this->networks;
 }
 /**
  * Get terms, based on query_vars.
  *
  * @param 4.6.0
  * @access public
  *
  * @global wpdb $wpdb WordPress database abstraction object.
  *
  * @return array
  */
 public function get_terms()
 {
     global $wpdb;
     $this->parse_query($this->query_vars);
     $args = $this->query_vars;
     // Set up meta_query so it's available to 'pre_get_terms'.
     $this->meta_query = new WP_Meta_Query();
     $this->meta_query->parse_query_vars($args);
     /**
      * Fires before terms are retrieved.
      *
      * @since 4.6.0
      *
      * @param WP_Term_Query $this Current instance of WP_Term_Query.
      */
     do_action('pre_get_terms', $this);
     $taxonomies = $args['taxonomy'];
     // Save queries by not crawling the tree in the case of multiple taxes or a flat tax.
     $has_hierarchical_tax = false;
     if ($taxonomies) {
         foreach ($taxonomies as $_tax) {
             if (is_taxonomy_hierarchical($_tax)) {
                 $has_hierarchical_tax = true;
             }
         }
     }
     if (!$has_hierarchical_tax) {
         $args['hierarchical'] = false;
         $args['pad_counts'] = false;
     }
     // 'parent' overrides 'child_of'.
     if (0 < intval($args['parent'])) {
         $args['child_of'] = false;
     }
     if ('all' == $args['get']) {
         $args['childless'] = false;
         $args['child_of'] = 0;
         $args['hide_empty'] = 0;
         $args['hierarchical'] = false;
         $args['pad_counts'] = false;
     }
     /**
      * Filters the terms query arguments.
      *
      * @since 3.1.0
      *
      * @param array $args       An array of get_terms() arguments.
      * @param array $taxonomies An array of taxonomies.
      */
     $args = apply_filters('get_terms_args', $args, $taxonomies);
     // Avoid the query if the queried parent/child_of term has no descendants.
     $child_of = $args['child_of'];
     $parent = $args['parent'];
     if ($child_of) {
         $_parent = $child_of;
     } elseif ($parent) {
         $_parent = $parent;
     } else {
         $_parent = false;
     }
     if ($_parent) {
         $in_hierarchy = false;
         foreach ($taxonomies as $_tax) {
             $hierarchy = _get_term_hierarchy($_tax);
             if (isset($hierarchy[$_parent])) {
                 $in_hierarchy = true;
             }
         }
         if (!$in_hierarchy) {
             return array();
         }
     }
     // 'term_order' is a legal sort order only when joining the relationship table.
     $_orderby = $this->query_vars['orderby'];
     if ('term_order' === $_orderby && empty($this->query_vars['object_ids'])) {
         $_orderby = 'term_id';
     }
     $orderby = $this->parse_orderby($_orderby);
     if ($orderby) {
         $orderby = "ORDER BY {$orderby}";
     }
     $order = $this->parse_order($this->query_vars['order']);
     if ($taxonomies) {
         $this->sql_clauses['where']['taxonomy'] = "tt.taxonomy IN ('" . implode("', '", array_map('esc_sql', $taxonomies)) . "')";
     }
     $exclude = $args['exclude'];
     $exclude_tree = $args['exclude_tree'];
     $include = $args['include'];
     $inclusions = '';
     if (!empty($include)) {
         $exclude = '';
         $exclude_tree = '';
         $inclusions = implode(',', wp_parse_id_list($include));
     }
     if (!empty($inclusions)) {
         $this->sql_clauses['where']['inclusions'] = 't.term_id IN ( ' . $inclusions . ' )';
     }
     $exclusions = array();
     if (!empty($exclude_tree)) {
         $exclude_tree = wp_parse_id_list($exclude_tree);
         $excluded_children = $exclude_tree;
         foreach ($exclude_tree as $extrunk) {
             $excluded_children = array_merge($excluded_children, (array) get_terms($taxonomies[0], array('child_of' => intval($extrunk), 'fields' => 'ids', 'hide_empty' => 0)));
         }
         $exclusions = array_merge($excluded_children, $exclusions);
     }
     if (!empty($exclude)) {
         $exclusions = array_merge(wp_parse_id_list($exclude), $exclusions);
     }
     // 'childless' terms are those without an entry in the flattened term hierarchy.
     $childless = (bool) $args['childless'];
     if ($childless) {
         foreach ($taxonomies as $_tax) {
             $term_hierarchy = _get_term_hierarchy($_tax);
             $exclusions = array_merge(array_keys($term_hierarchy), $exclusions);
         }
     }
     if (!empty($exclusions)) {
         $exclusions = 't.term_id NOT IN (' . implode(',', array_map('intval', $exclusions)) . ')';
     } else {
         $exclusions = '';
     }
     /**
      * Filters the terms to exclude from the terms query.
      *
      * @since 2.3.0
      *
      * @param string $exclusions `NOT IN` clause of the terms query.
      * @param array  $args       An array of terms query arguments.
      * @param array  $taxonomies An array of taxonomies.
      */
     $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies);
     if (!empty($exclusions)) {
         // Must do string manipulation here for backward compatibility with filter.
         $this->sql_clauses['where']['exclusions'] = preg_replace('/^\\s*AND\\s*/', '', $exclusions);
     }
     if (!empty($args['name'])) {
         $names = (array) $args['name'];
         foreach ($names as &$_name) {
             // `sanitize_term_field()` returns slashed data.
             $_name = stripslashes(sanitize_term_field('name', $_name, 0, reset($taxonomies), 'db'));
         }
         $this->sql_clauses['where']['name'] = "t.name IN ('" . implode("', '", array_map('esc_sql', $names)) . "')";
     }
     if (!empty($args['slug'])) {
         if (is_array($args['slug'])) {
             $slug = array_map('sanitize_title', $args['slug']);
             $this->sql_clauses['where']['slug'] = "t.slug IN ('" . implode("', '", $slug) . "')";
         } else {
             $slug = sanitize_title($args['slug']);
             $this->sql_clauses['where']['slug'] = "t.slug = '{$slug}'";
         }
     }
     if (!empty($args['term_taxonomy_id'])) {
         if (is_array($args['term_taxonomy_id'])) {
             $tt_ids = implode(',', array_map('intval', $args['term_taxonomy_id']));
             $this->sql_clauses['where']['term_taxonomy_id'] = "tt.term_taxonomy_id IN ({$tt_ids})";
         } else {
             $this->sql_clauses['where']['term_taxonomy_id'] = $wpdb->prepare("tt.term_taxonomy_id = %d", $args['term_taxonomy_id']);
         }
     }
     if (!empty($args['name__like'])) {
         $this->sql_clauses['where']['name__like'] = $wpdb->prepare("t.name LIKE %s", '%' . $wpdb->esc_like($args['name__like']) . '%');
     }
     if (!empty($args['description__like'])) {
         $this->sql_clauses['where']['description__like'] = $wpdb->prepare("tt.description LIKE %s", '%' . $wpdb->esc_like($args['description__like']) . '%');
     }
     if (!empty($args['object_ids'])) {
         $object_ids = $args['object_ids'];
         if (!is_array($object_ids)) {
             $object_ids = array($object_ids);
         }
         $object_ids = implode(', ', array_map('intval', $object_ids));
         $this->sql_clauses['where']['object_ids'] = "tr.object_id IN ({$object_ids})";
     }
     /*
      * When querying for object relationships, the 'count > 0' check
      * added by 'hide_empty' is superfluous.
      */
     if (!empty($args['object_ids'])) {
         $args['hide_empty'] = false;
     }
     if ('' !== $parent) {
         $parent = (int) $parent;
         $this->sql_clauses['where']['parent'] = "tt.parent = '{$parent}'";
     }
     $hierarchical = $args['hierarchical'];
     if ('count' == $args['fields']) {
         $hierarchical = false;
     }
     if ($args['hide_empty'] && !$hierarchical) {
         $this->sql_clauses['where']['count'] = 'tt.count > 0';
     }
     $number = $args['number'];
     $offset = $args['offset'];
     // Don't limit the query results when we have to descend the family tree.
     if ($number && !$hierarchical && !$child_of && '' === $parent) {
         if ($offset) {
             $limits = 'LIMIT ' . $offset . ',' . $number;
         } else {
             $limits = 'LIMIT ' . $number;
         }
     } else {
         $limits = '';
     }
     if (!empty($args['search'])) {
         $this->sql_clauses['where']['search'] = $this->get_search_sql($args['search']);
     }
     // Meta query support.
     $join = '';
     $distinct = '';
     // Reparse meta_query query_vars, in case they were modified in a 'pre_get_terms' callback.
     $this->meta_query->parse_query_vars($this->query_vars);
     $mq_sql = $this->meta_query->get_sql('term', 't', 'term_id');
     $meta_clauses = $this->meta_query->get_clauses();
     if (!empty($meta_clauses)) {
         $join .= $mq_sql['join'];
         $this->sql_clauses['where']['meta_query'] = preg_replace('/^\\s*AND\\s*/', '', $mq_sql['where']);
         $distinct .= "DISTINCT";
     }
     $selects = array();
     switch ($args['fields']) {
         case 'all':
         case 'all_with_object_id':
         case 'tt_ids':
         case 'slugs':
             $selects = array('t.*', 'tt.*');
             if ('all_with_object_id' === $args['fields'] && !empty($args['object_ids'])) {
                 $selects[] = 'tr.object_id';
             }
             break;
         case 'ids':
         case 'id=>parent':
             $selects = array('t.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy');
             break;
         case 'names':
             $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy');
             break;
         case 'count':
             $orderby = '';
             $order = '';
             $selects = array('COUNT(*)');
             break;
         case 'id=>name':
             $selects = array('t.term_id', 't.name', 'tt.count', 'tt.taxonomy');
             break;
         case 'id=>slug':
             $selects = array('t.term_id', 't.slug', 'tt.count', 'tt.taxonomy');
             break;
     }
     $_fields = $args['fields'];
     /**
      * Filters the fields to select in the terms query.
      *
      * Field lists modified using this filter will only modify the term fields returned
      * by the function when the `$fields` parameter set to 'count' or 'all'. In all other
      * cases, the term fields in the results array will be determined by the `$fields`
      * parameter alone.
      *
      * Use of this filter can result in unpredictable behavior, and is not recommended.
      *
      * @since 2.8.0
      *
      * @param array $selects    An array of fields to select for the terms query.
      * @param array $args       An array of term query arguments.
      * @param array $taxonomies An array of taxonomies.
      */
     $fields = implode(', ', apply_filters('get_terms_fields', $selects, $args, $taxonomies));
     $join .= " INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
     if (!empty($this->query_vars['object_ids'])) {
         $join .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id";
     }
     $where = implode(' AND ', $this->sql_clauses['where']);
     /**
      * Filters the terms query SQL clauses.
      *
      * @since 3.1.0
      *
      * @param array $pieces     Terms query SQL clauses.
      * @param array $taxonomies An array of taxonomies.
      * @param array $args       An array of terms query arguments.
      */
     $clauses = apply_filters('terms_clauses', compact('fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits'), $taxonomies, $args);
     $fields = isset($clauses['fields']) ? $clauses['fields'] : '';
     $join = isset($clauses['join']) ? $clauses['join'] : '';
     $where = isset($clauses['where']) ? $clauses['where'] : '';
     $distinct = isset($clauses['distinct']) ? $clauses['distinct'] : '';
     $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : '';
     $order = isset($clauses['order']) ? $clauses['order'] : '';
     $limits = isset($clauses['limits']) ? $clauses['limits'] : '';
     if ($where) {
         $where = "WHERE {$where}";
     }
     $this->sql_clauses['select'] = "SELECT {$distinct} {$fields}";
     $this->sql_clauses['from'] = "FROM {$wpdb->terms} AS t {$join}";
     $this->sql_clauses['orderby'] = $orderby ? "{$orderby} {$order}" : '';
     $this->sql_clauses['limits'] = $limits;
     $this->request = "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['orderby']} {$this->sql_clauses['limits']}";
     // $args can be anything. Only use the args defined in defaults to compute the key.
     $key = md5(serialize(wp_array_slice_assoc($args, array_keys($this->query_var_defaults))) . serialize($taxonomies) . $this->request);
     $last_changed = wp_cache_get_last_changed('terms');
     $cache_key = "get_terms:{$key}:{$last_changed}";
     $cache = wp_cache_get($cache_key, 'terms');
     if (false !== $cache) {
         if ('all' === $_fields) {
             $cache = array_map('get_term', $cache);
         }
         $this->terms = $cache;
         return $this->terms;
     }
     if ('count' == $_fields) {
         $count = $wpdb->get_var($this->request);
         wp_cache_set($cache_key, $count, 'terms');
         return $count;
     }
     $terms = $wpdb->get_results($this->request);
     if ('all' == $_fields || 'all_with_object_id' === $_fields) {
         update_term_cache($terms);
     }
     // Prime termmeta cache.
     if ($args['update_term_meta_cache']) {
         $term_ids = wp_list_pluck($terms, 'term_id');
         update_termmeta_cache($term_ids);
     }
     if (empty($terms)) {
         wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS);
         return array();
     }
     if ($child_of) {
         foreach ($taxonomies as $_tax) {
             $children = _get_term_hierarchy($_tax);
             if (!empty($children)) {
                 $terms = _get_term_children($child_of, $terms, $_tax);
             }
         }
     }
     // Update term counts to include children.
     if ($args['pad_counts'] && 'all' == $_fields) {
         foreach ($taxonomies as $_tax) {
             _pad_term_counts($terms, $_tax);
         }
     }
     // Make sure we show empty categories that have children.
     if ($hierarchical && $args['hide_empty'] && is_array($terms)) {
         foreach ($terms as $k => $term) {
             if (!$term->count) {
                 $children = get_term_children($term->term_id, $term->taxonomy);
                 if (is_array($children)) {
                     foreach ($children as $child_id) {
                         $child = get_term($child_id, $term->taxonomy);
                         if ($child->count) {
                             continue 2;
                         }
                     }
                 }
                 // It really is empty.
                 unset($terms[$k]);
             }
         }
     }
     /*
      * When querying for terms connected to objects, we may get
      * duplicate results. The duplicates should be preserved if
      * `$fields` is 'all_with_object_id', but should otherwise be
      * removed.
      */
     if (!empty($args['object_ids']) && 'all_with_object_id' != $_fields) {
         $_tt_ids = $_terms = array();
         foreach ($terms as $term) {
             if (isset($_tt_ids[$term->term_id])) {
                 continue;
             }
             $_tt_ids[$term->term_id] = 1;
             $_terms[] = $term;
         }
         $terms = $_terms;
     }
     $_terms = array();
     if ('id=>parent' == $_fields) {
         foreach ($terms as $term) {
             $_terms[$term->term_id] = $term->parent;
         }
     } elseif ('ids' == $_fields) {
         foreach ($terms as $term) {
             $_terms[] = (int) $term->term_id;
         }
     } elseif ('tt_ids' == $_fields) {
         foreach ($terms as $term) {
             $_terms[] = (int) $term->term_taxonomy_id;
         }
     } elseif ('names' == $_fields) {
         foreach ($terms as $term) {
             $_terms[] = $term->name;
         }
     } elseif ('slugs' == $_fields) {
         foreach ($terms as $term) {
             $_terms[] = $term->slug;
         }
     } elseif ('id=>name' == $_fields) {
         foreach ($terms as $term) {
             $_terms[$term->term_id] = $term->name;
         }
     } elseif ('id=>slug' == $_fields) {
         foreach ($terms as $term) {
             $_terms[$term->term_id] = $term->slug;
         }
     }
     if (!empty($_terms)) {
         $terms = $_terms;
     }
     // Hierarchical queries are not limited, so 'offset' and 'number' must be handled now.
     if ($hierarchical && $number && is_array($terms)) {
         if ($offset >= count($terms)) {
             $terms = array();
         } else {
             $terms = array_slice($terms, $offset, $number, true);
         }
     }
     wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS);
     if ('all' === $_fields || 'all_with_object_id' === $_fields) {
         $terms = array_map('get_term', $terms);
     }
     $this->terms = $terms;
     return $this->terms;
 }
/**
 * Display archive links based on type and format.
 *
 * @since 1.2.0
 * @since 4.4.0 $post_type arg was added.
 *
 * @see get_archives_link()
 *
 * @global wpdb      $wpdb
 * @global WP_Locale $wp_locale
 *
 * @param string|array $args {
 *     Default archive links arguments. Optional.
 *
 *     @type string     $type            Type of archive to retrieve. Accepts 'daily', 'weekly', 'monthly',
 *                                       'yearly', 'postbypost', or 'alpha'. Both 'postbypost' and 'alpha'
 *                                       display the same archive link list as well as post titles instead
 *                                       of displaying dates. The difference between the two is that 'alpha'
 *                                       will order by post title and 'postbypost' will order by post date.
 *                                       Default 'monthly'.
 *     @type string|int $limit           Number of links to limit the query to. Default empty (no limit).
 *     @type string     $format          Format each link should take using the $before and $after args.
 *                                       Accepts 'link' (`<link>` tag), 'option' (`<option>` tag), 'html'
 *                                       (`<li>` tag), or a custom format, which generates a link anchor
 *                                       with $before preceding and $after succeeding. Default 'html'.
 *     @type string     $before          Markup to prepend to the beginning of each link. Default empty.
 *     @type string     $after           Markup to append to the end of each link. Default empty.
 *     @type bool       $show_post_count Whether to display the post count alongside the link. Default false.
 *     @type bool|int   $echo            Whether to echo or return the links list. Default 1|true to echo.
 *     @type string     $order           Whether to use ascending or descending order. Accepts 'ASC', or 'DESC'.
 *                                       Default 'DESC'.
 *     @type string     $post_type       Post type. Default 'post'.
 * }
 * @return string|void String when retrieving.
 */
function wp_get_archives($args = '')
{
    global $wpdb, $wp_locale;
    $defaults = array('type' => 'monthly', 'limit' => '', 'format' => 'html', 'before' => '', 'after' => '', 'show_post_count' => false, 'echo' => 1, 'order' => 'DESC', 'post_type' => 'post');
    $r = wp_parse_args($args, $defaults);
    $post_type_object = get_post_type_object($r['post_type']);
    if (!is_post_type_viewable($post_type_object)) {
        return;
    }
    $r['post_type'] = $post_type_object->name;
    if ('' == $r['type']) {
        $r['type'] = 'monthly';
    }
    if (!empty($r['limit'])) {
        $r['limit'] = absint($r['limit']);
        $r['limit'] = ' LIMIT ' . $r['limit'];
    }
    $order = strtoupper($r['order']);
    if ($order !== 'ASC') {
        $order = 'DESC';
    }
    // this is what will separate dates on weekly archive links
    $archive_week_separator = '&#8211;';
    $sql_where = $wpdb->prepare("WHERE post_type = %s AND post_status = 'publish'", $r['post_type']);
    /**
     * Filters the SQL WHERE clause for retrieving archives.
     *
     * @since 2.2.0
     *
     * @param string $sql_where Portion of SQL query containing the WHERE clause.
     * @param array  $r         An array of default arguments.
     */
    $where = apply_filters('getarchives_where', $sql_where, $r);
    /**
     * Filters the SQL JOIN clause for retrieving archives.
     *
     * @since 2.2.0
     *
     * @param string $sql_join Portion of SQL query containing JOIN clause.
     * @param array  $r        An array of default arguments.
     */
    $join = apply_filters('getarchives_join', '', $r);
    $output = '';
    $last_changed = wp_cache_get_last_changed('posts');
    $limit = $r['limit'];
    if ('monthly' == $r['type']) {
        $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM {$wpdb->posts} {$join} {$where} GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date {$order} {$limit}";
        $key = md5($query);
        $key = "wp_get_archives:{$key}:{$last_changed}";
        if (!($results = wp_cache_get($key, 'posts'))) {
            $results = $wpdb->get_results($query);
            wp_cache_set($key, $results, 'posts');
        }
        if ($results) {
            $after = $r['after'];
            foreach ((array) $results as $result) {
                $url = get_month_link($result->year, $result->month);
                if ('post' !== $r['post_type']) {
                    $url = add_query_arg('post_type', $r['post_type'], $url);
                }
                /* translators: 1: month name, 2: 4-digit year */
                $text = sprintf(__('%1$s %2$d'), $wp_locale->get_month($result->month), $result->year);
                if ($r['show_post_count']) {
                    $r['after'] = '&nbsp;(' . $result->posts . ')' . $after;
                }
                $output .= get_archives_link($url, $text, $r['format'], $r['before'], $r['after']);
            }
        }
    } elseif ('yearly' == $r['type']) {
        $query = "SELECT YEAR(post_date) AS `year`, count(ID) as posts FROM {$wpdb->posts} {$join} {$where} GROUP BY YEAR(post_date) ORDER BY post_date {$order} {$limit}";
        $key = md5($query);
        $key = "wp_get_archives:{$key}:{$last_changed}";
        if (!($results = wp_cache_get($key, 'posts'))) {
            $results = $wpdb->get_results($query);
            wp_cache_set($key, $results, 'posts');
        }
        if ($results) {
            $after = $r['after'];
            foreach ((array) $results as $result) {
                $url = get_year_link($result->year);
                if ('post' !== $r['post_type']) {
                    $url = add_query_arg('post_type', $r['post_type'], $url);
                }
                $text = sprintf('%d', $result->year);
                if ($r['show_post_count']) {
                    $r['after'] = '&nbsp;(' . $result->posts . ')' . $after;
                }
                $output .= get_archives_link($url, $text, $r['format'], $r['before'], $r['after']);
            }
        }
    } elseif ('daily' == $r['type']) {
        $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, DAYOFMONTH(post_date) AS `dayofmonth`, count(ID) as posts FROM {$wpdb->posts} {$join} {$where} GROUP BY YEAR(post_date), MONTH(post_date), DAYOFMONTH(post_date) ORDER BY post_date {$order} {$limit}";
        $key = md5($query);
        $key = "wp_get_archives:{$key}:{$last_changed}";
        if (!($results = wp_cache_get($key, 'posts'))) {
            $results = $wpdb->get_results($query);
            wp_cache_set($key, $results, 'posts');
        }
        if ($results) {
            $after = $r['after'];
            foreach ((array) $results as $result) {
                $url = get_day_link($result->year, $result->month, $result->dayofmonth);
                if ('post' !== $r['post_type']) {
                    $url = add_query_arg('post_type', $r['post_type'], $url);
                }
                $date = sprintf('%1$d-%2$02d-%3$02d 00:00:00', $result->year, $result->month, $result->dayofmonth);
                $text = mysql2date(get_option('date_format'), $date);
                if ($r['show_post_count']) {
                    $r['after'] = '&nbsp;(' . $result->posts . ')' . $after;
                }
                $output .= get_archives_link($url, $text, $r['format'], $r['before'], $r['after']);
            }
        }
    } elseif ('weekly' == $r['type']) {
        $week = _wp_mysql_week('`post_date`');
        $query = "SELECT DISTINCT {$week} AS `week`, YEAR( `post_date` ) AS `yr`, DATE_FORMAT( `post_date`, '%Y-%m-%d' ) AS `yyyymmdd`, count( `ID` ) AS `posts` FROM `{$wpdb->posts}` {$join} {$where} GROUP BY {$week}, YEAR( `post_date` ) ORDER BY `post_date` {$order} {$limit}";
        $key = md5($query);
        $key = "wp_get_archives:{$key}:{$last_changed}";
        if (!($results = wp_cache_get($key, 'posts'))) {
            $results = $wpdb->get_results($query);
            wp_cache_set($key, $results, 'posts');
        }
        $arc_w_last = '';
        if ($results) {
            $after = $r['after'];
            foreach ((array) $results as $result) {
                if ($result->week != $arc_w_last) {
                    $arc_year = $result->yr;
                    $arc_w_last = $result->week;
                    $arc_week = get_weekstartend($result->yyyymmdd, get_option('start_of_week'));
                    $arc_week_start = date_i18n(get_option('date_format'), $arc_week['start']);
                    $arc_week_end = date_i18n(get_option('date_format'), $arc_week['end']);
                    $url = add_query_arg(array('m' => $arc_year, 'w' => $result->week), home_url('/'));
                    if ('post' !== $r['post_type']) {
                        $url = add_query_arg('post_type', $r['post_type'], $url);
                    }
                    $text = $arc_week_start . $archive_week_separator . $arc_week_end;
                    if ($r['show_post_count']) {
                        $r['after'] = '&nbsp;(' . $result->posts . ')' . $after;
                    }
                    $output .= get_archives_link($url, $text, $r['format'], $r['before'], $r['after']);
                }
            }
        }
    } elseif ('postbypost' == $r['type'] || 'alpha' == $r['type']) {
        $orderby = 'alpha' == $r['type'] ? 'post_title ASC ' : 'post_date DESC, ID DESC ';
        $query = "SELECT * FROM {$wpdb->posts} {$join} {$where} ORDER BY {$orderby} {$limit}";
        $key = md5($query);
        $key = "wp_get_archives:{$key}:{$last_changed}";
        if (!($results = wp_cache_get($key, 'posts'))) {
            $results = $wpdb->get_results($query);
            wp_cache_set($key, $results, 'posts');
        }
        if ($results) {
            foreach ((array) $results as $result) {
                if ($result->post_date != '0000-00-00 00:00:00') {
                    $url = get_permalink($result);
                    if ($result->post_title) {
                        /** This filter is documented in wp-includes/post-template.php */
                        $text = strip_tags(apply_filters('the_title', $result->post_title, $result->ID));
                    } else {
                        $text = $result->ID;
                    }
                    $output .= get_archives_link($url, $text, $r['format'], $r['before'], $r['after']);
                }
            }
        }
    }
    if ($r['echo']) {
        echo $output;
    } else {
        return $output;
    }
}
Example #5
0
/**
 * Retrieve a list of pages.
 *
 * @global wpdb $wpdb WordPress database abstraction object.
 *
 * @since 1.5.0
 *
 * @param array|string $args {
 *     Optional. Array or string of arguments to retrieve pages.
 *
 *     @type int          $child_of     Page ID to return child and grandchild pages of. Note: The value
 *                                      of `$hierarchical` has no bearing on whether `$child_of` returns
 *                                      hierarchical results. Default 0, or no restriction.
 *     @type string       $sort_order   How to sort retrieved pages. Accepts 'ASC', 'DESC'. Default 'ASC'.
 *     @type string       $sort_column  What columns to sort pages by, comma-separated. Accepts 'post_author',
 *                                      'post_date', 'post_title', 'post_name', 'post_modified', 'menu_order',
 *                                      'post_modified_gmt', 'post_parent', 'ID', 'rand', 'comment_count'.
 *                                      'post_' can be omitted for any values that start with it.
 *                                      Default 'post_title'.
 *     @type bool         $hierarchical Whether to return pages hierarchically. If false in conjunction with
 *                                      `$child_of` also being false, both arguments will be disregarded.
 *                                      Default true.
 *     @type array        $exclude      Array of page IDs to exclude. Default empty array.
 *     @type array        $include      Array of page IDs to include. Cannot be used with `$child_of`,
 *                                      `$parent`, `$exclude`, `$meta_key`, `$meta_value`, or `$hierarchical`.
 *                                      Default empty array.
 *     @type string       $meta_key     Only include pages with this meta key. Default empty.
 *     @type string       $meta_value   Only include pages with this meta value. Requires `$meta_key`.
 *                                      Default empty.
 *     @type string       $authors      A comma-separated list of author IDs. Default empty.
 *     @type int          $parent       Page ID to return direct children of. Default -1, or no restriction.
 *     @type string|array $exclude_tree Comma-separated string or array of page IDs to exclude.
 *                                      Default empty array.
 *     @type int          $number       The number of pages to return. Default 0, or all pages.
 *     @type int          $offset       The number of pages to skip before returning. Requires `$number`.
 *                                      Default 0.
 *     @type string       $post_type    The post type to query. Default 'page'.
 *     @type string|array $post_status  A comma-separated list or array of post statuses to include.
 *                                      Default 'publish'.
 * }
 * @return array|false List of pages matching defaults or `$args`.
 */
function get_pages($args = array())
{
    global $wpdb;
    $defaults = array('child_of' => 0, 'sort_order' => 'ASC', 'sort_column' => 'post_title', 'hierarchical' => 1, 'exclude' => array(), 'include' => array(), 'meta_key' => '', 'meta_value' => '', 'authors' => '', 'parent' => -1, 'exclude_tree' => array(), 'number' => '', 'offset' => 0, 'post_type' => 'page', 'post_status' => 'publish');
    $r = wp_parse_args($args, $defaults);
    $number = (int) $r['number'];
    $offset = (int) $r['offset'];
    $child_of = (int) $r['child_of'];
    $hierarchical = $r['hierarchical'];
    $exclude = $r['exclude'];
    $meta_key = $r['meta_key'];
    $meta_value = $r['meta_value'];
    $parent = $r['parent'];
    $post_status = $r['post_status'];
    // Make sure the post type is hierarchical.
    $hierarchical_post_types = get_post_types(array('hierarchical' => true));
    if (!in_array($r['post_type'], $hierarchical_post_types)) {
        return false;
    }
    if ($parent > 0 && !$child_of) {
        $hierarchical = false;
    }
    // Make sure we have a valid post status.
    if (!is_array($post_status)) {
        $post_status = explode(',', $post_status);
    }
    if (array_diff($post_status, get_post_stati())) {
        return false;
    }
    // $args can be whatever, only use the args defined in defaults to compute the key.
    $key = md5(serialize(wp_array_slice_assoc($r, array_keys($defaults))));
    $last_changed = wp_cache_get_last_changed('posts');
    $cache_key = "get_pages:{$key}:{$last_changed}";
    if ($cache = wp_cache_get($cache_key, 'posts')) {
        // Convert to WP_Post instances.
        $pages = array_map('get_post', $cache);
        /** This filter is documented in wp-includes/post.php */
        $pages = apply_filters('get_pages', $pages, $r);
        return $pages;
    }
    $inclusions = '';
    if (!empty($r['include'])) {
        $child_of = 0;
        //ignore child_of, parent, exclude, meta_key, and meta_value params if using include
        $parent = -1;
        $exclude = '';
        $meta_key = '';
        $meta_value = '';
        $hierarchical = false;
        $incpages = wp_parse_id_list($r['include']);
        if (!empty($incpages)) {
            $inclusions = ' AND ID IN (' . implode(',', $incpages) . ')';
        }
    }
    $exclusions = '';
    if (!empty($exclude)) {
        $expages = wp_parse_id_list($exclude);
        if (!empty($expages)) {
            $exclusions = ' AND ID NOT IN (' . implode(',', $expages) . ')';
        }
    }
    $author_query = '';
    if (!empty($r['authors'])) {
        $post_authors = preg_split('/[\\s,]+/', $r['authors']);
        if (!empty($post_authors)) {
            foreach ($post_authors as $post_author) {
                //Do we have an author id or an author login?
                if (0 == intval($post_author)) {
                    $post_author = get_user_by('login', $post_author);
                    if (empty($post_author)) {
                        continue;
                    }
                    if (empty($post_author->ID)) {
                        continue;
                    }
                    $post_author = $post_author->ID;
                }
                if ('' == $author_query) {
                    $author_query = $wpdb->prepare(' post_author = %d ', $post_author);
                } else {
                    $author_query .= $wpdb->prepare(' OR post_author = %d ', $post_author);
                }
            }
            if ('' != $author_query) {
                $author_query = " AND ({$author_query})";
            }
        }
    }
    $join = '';
    $where = "{$exclusions} {$inclusions} ";
    if ('' !== $meta_key || '' !== $meta_value) {
        $join = " LEFT JOIN {$wpdb->postmeta} ON ( {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id )";
        // meta_key and meta_value might be slashed
        $meta_key = wp_unslash($meta_key);
        $meta_value = wp_unslash($meta_value);
        if ('' !== $meta_key) {
            $where .= $wpdb->prepare(" AND {$wpdb->postmeta}.meta_key = %s", $meta_key);
        }
        if ('' !== $meta_value) {
            $where .= $wpdb->prepare(" AND {$wpdb->postmeta}.meta_value = %s", $meta_value);
        }
    }
    if (is_array($parent)) {
        $post_parent__in = implode(',', array_map('absint', (array) $parent));
        if (!empty($post_parent__in)) {
            $where .= " AND post_parent IN ({$post_parent__in})";
        }
    } elseif ($parent >= 0) {
        $where .= $wpdb->prepare(' AND post_parent = %d ', $parent);
    }
    if (1 == count($post_status)) {
        $where_post_type = $wpdb->prepare("post_type = %s AND post_status = %s", $r['post_type'], reset($post_status));
    } else {
        $post_status = implode("', '", $post_status);
        $where_post_type = $wpdb->prepare("post_type = %s AND post_status IN ('{$post_status}')", $r['post_type']);
    }
    $orderby_array = array();
    $allowed_keys = array('author', 'post_author', 'date', 'post_date', 'title', 'post_title', 'name', 'post_name', 'modified', 'post_modified', 'modified_gmt', 'post_modified_gmt', 'menu_order', 'parent', 'post_parent', 'ID', 'rand', 'comment_count');
    foreach (explode(',', $r['sort_column']) as $orderby) {
        $orderby = trim($orderby);
        if (!in_array($orderby, $allowed_keys)) {
            continue;
        }
        switch ($orderby) {
            case 'menu_order':
                break;
            case 'ID':
                $orderby = "{$wpdb->posts}.ID";
                break;
            case 'rand':
                $orderby = 'RAND()';
                break;
            case 'comment_count':
                $orderby = "{$wpdb->posts}.comment_count";
                break;
            default:
                if (0 === strpos($orderby, 'post_')) {
                    $orderby = "{$wpdb->posts}." . $orderby;
                } else {
                    $orderby = "{$wpdb->posts}.post_" . $orderby;
                }
        }
        $orderby_array[] = $orderby;
    }
    $sort_column = !empty($orderby_array) ? implode(',', $orderby_array) : "{$wpdb->posts}.post_title";
    $sort_order = strtoupper($r['sort_order']);
    if ('' !== $sort_order && !in_array($sort_order, array('ASC', 'DESC'))) {
        $sort_order = 'ASC';
    }
    $query = "SELECT * FROM {$wpdb->posts} {$join} WHERE ({$where_post_type}) {$where} ";
    $query .= $author_query;
    $query .= " ORDER BY " . $sort_column . " " . $sort_order;
    if (!empty($number)) {
        $query .= ' LIMIT ' . $offset . ',' . $number;
    }
    $pages = $wpdb->get_results($query);
    if (empty($pages)) {
        /** This filter is documented in wp-includes/post.php */
        $pages = apply_filters('get_pages', array(), $r);
        return $pages;
    }
    // Sanitize before caching so it'll only get done once.
    $num_pages = count($pages);
    for ($i = 0; $i < $num_pages; $i++) {
        $pages[$i] = sanitize_post($pages[$i], 'raw');
    }
    // Update cache.
    update_post_cache($pages);
    if ($child_of || $hierarchical) {
        $pages = get_page_children($child_of, $pages);
    }
    if (!empty($r['exclude_tree'])) {
        $exclude = wp_parse_id_list($r['exclude_tree']);
        foreach ($exclude as $id) {
            $children = get_page_children($id, $pages);
            foreach ($children as $child) {
                $exclude[] = $child->ID;
            }
        }
        $num_pages = count($pages);
        for ($i = 0; $i < $num_pages; $i++) {
            if (in_array($pages[$i]->ID, $exclude)) {
                unset($pages[$i]);
            }
        }
    }
    $page_structure = array();
    foreach ($pages as $page) {
        $page_structure[] = $page->ID;
    }
    wp_cache_set($cache_key, $page_structure, 'posts');
    // Convert to WP_Post instances
    $pages = array_map('get_post', $pages);
    /**
     * Filters the retrieved list of pages.
     *
     * @since 2.1.0
     *
     * @param array $pages List of pages to retrieve.
     * @param array $r     Array of get_pages() arguments.
     */
    return apply_filters('get_pages', $pages, $r);
}