PATH:
var
/
www
/
clients
/
client1
/
web1
/
web
/
wp-content
/
plugins
/
contextual-related-posts
/
includes
<?php /** * Contextual Related Posts: Main CRP Class * * @package Contextual_Related_Posts * @since 3.5.0 */ namespace WebberZone\Contextual_Related_Posts; use WebberZone\Contextual_Related_Posts\Frontend\Display; use WebberZone\Contextual_Related_Posts\Util\Cache; use WebberZone\Contextual_Related_Posts\Util\Helpers; // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { die; } /** * Contextual Related Posts: Main CRP Class. * * @since 3.0.0 */ class CRP_Core_Query { /** * Source Post to find related posts for. * * @var \WP_Post A WP_Post instance. */ public $source_post; /** * Query vars, before parsing * * @since 3.0.2 * @var array */ public $input_query_args = array(); /** * Query vars, after parsing * * @since 3.0.0 * @var array */ public $query_args = array(); /** * Flag to check if it is a CRP query. * * @since 3.5.0 * @var bool */ public $is_crp_query = false; /** * Flag to turn relevance matching ON or OFF. * * @since 3.0.0 * @var bool */ public $enable_relevance = true; /** * Random order flag. * * @since 3.0.0 * @var bool */ public $random_order = false; /** * CRP Post Meta. * * @since 3.0.0 * @var mixed */ public $crp_post_meta; /** * Array of manual related post IDs. * * @since 3.3.0 * @var array */ public $manual_related = array(); /** * Number of manual related posts. * * @since 3.3.0 * @var int */ public $no_of_manual_related = 0; /** * Fields to be matched. * * @since 3.0.0 * @var string */ public $match_fields = ''; /** * Match SQL. * * @since 4.0.0 * @var string */ public $match_sql = ''; /** * Holds the text to be matched. * * @since 3.0.0 * @var string */ public $stuff = ''; /** * Cache set flag. * * @since 3.0.0 * @var bool */ public $in_cache = false; /** * Array of SQL LIKE statements when using the Include Words functionality. * * @since 3.4.2 * @var array */ public $include_orderby_title = array(); /** * Main constructor. * * @since 3.0.0 * * @param array|string $args The Query variables. Accepts an array or a query string. */ public function __construct( $args = array() ) { $this->prepare_query_args( $args ); if ( isset( $args['crp_query'] ) && true === $args['crp_query'] && ! $this->is_crp_query ) { $this->hooks(); } } /** * Initialise search hooks. * * @since 3.5.0 */ public function hooks() { add_filter( 'pre_get_posts', array( $this, 'pre_get_posts' ), 10 ); add_filter( 'posts_fields', array( $this, 'posts_fields' ), 10, 2 ); add_filter( 'posts_join', array( $this, 'posts_join' ), 10, 2 ); add_filter( 'posts_where', array( $this, 'posts_where' ), 10, 2 ); add_filter( 'posts_orderby', array( $this, 'posts_orderby' ), 10, 2 ); add_filter( 'posts_groupby', array( $this, 'posts_groupby' ), 10, 2 ); add_filter( 'posts_request', array( $this, 'posts_request' ), 10, 2 ); add_filter( 'posts_pre_query', array( $this, 'posts_pre_query' ), 10, 2 ); add_filter( 'the_posts', array( $this, 'the_posts' ), 10, 2 ); } /** * Prepare the query variables. * * @since 3.0.0 * @see WP_Query::parse_query() * @see crp_get_registered_settings() * * @param string|array $args { * Optional. Array or string of Query parameters. * * @type array|string $include_cat_ids An array or comma-separated string of category or custom taxonomy term_taxonoy_id. * @type array|string $include_post_ids An array or comma-separated string of post IDs. * @type bool $offset Offset the related posts returned by this number. * @type int $postid Get related posts for a specific post ID. * @type bool $strict_limit If this is set to false, then it will fetch 3x posts. * } */ public function prepare_query_args( $args = array() ) { global $post; $crp_settings = crp_get_settings(); $defaults = array( 'include_cat_ids' => 0, 'include_post_ids' => null, 'offset' => 0, 'postid' => false, 'strict_limit' => true, ); $defaults = array_merge( $defaults, $crp_settings ); $args = wp_parse_args( $args, $defaults ); $args = Helpers::parse_wp_query_arguments( $args ); // Set the postid if it's different from the queried object. $queried_object_post_id = get_queried_object_id(); if ( is_main_query() && $queried_object_post_id && empty( $args['postid'] ) && ( $queried_object_post_id !== $args['postid'] ) ) { $args['postid'] = $queried_object_post_id; } // Set necessary variables. $args['crp_query'] = true; $args['suppress_filters'] = false; $args['ignore_sticky_posts'] = true; $args['no_found_rows'] = true; // Store query args before we manipulate them. $this->input_query_args = $args; $this->is_crp_query = isset( $this->input_query_args['is_crp_query'] ) ? $this->input_query_args['is_crp_query'] : false; // Set the source post. $source_post = empty( $args['postid'] ) && empty( $args['post_id'] ) ? $post : ( isset( $args['postid'] ) ? get_post( $args['postid'] ) : get_post( $args['post_id'] ) ); if ( ! $source_post ) { $source_post = get_post(); } $this->source_post = $source_post; /** * Applies filters to the query arguments before executing the query. * * This function allows developers to modify the query arguments before the query is executed. * * @since 3.5.0 * * @param array $args The query arguments. * @param \WP_Post $source_post The source post. */ $args = apply_filters( 'crp_query_args_before', $args, $source_post ); // Save post meta into a class-wide variable. $this->crp_post_meta = get_post_meta( $source_post->ID, 'crp_post_meta', true ); // Handle manual_related argument. if ( isset( $args['manual_related'] ) ) { $this->manual_related = ( 0 === (int) $args['manual_related'] ) ? array() : wp_parse_id_list( $args['manual_related'] ); } else { $args['manual_related'] = ! empty( $this->crp_post_meta['manual_related'] ) ? $this->crp_post_meta['manual_related'] : ''; $this->manual_related = wp_parse_id_list( $args['manual_related'] ); } // Handle include_post_ids argument. if ( isset( $args['include_post_ids'] ) ) { $include_post_ids = ( 0 === (int) $args['include_post_ids'] ) ? array() : wp_parse_id_list( $args['include_post_ids'] ); $this->manual_related = array_merge( $this->manual_related, $include_post_ids ); } $this->no_of_manual_related = count( $this->manual_related ); if ( empty( $args['keyword'] ) ) { $args['keyword'] = ! empty( $this->crp_post_meta['keyword'] ) ? $this->crp_post_meta['keyword'] : ''; } // Set the random order and save it in a class-wide variable. $random_order = ( $args['random_order'] || ( isset( $args['ordering'] ) && 'random' === $args['ordering'] ) ) ? true : false; // If we need to order randomly then set strict_limit to false. if ( $random_order ) { $args['strict_limit'] = false; } $this->random_order = $random_order; // Set the number of posts to be retrieved. Use posts_per_page if set else use limit. if ( empty( $args['posts_per_page'] ) ) { $args['posts_per_page'] = ( $args['strict_limit'] ) ? absint( $args['limit'] ) : ( absint( $args['limit'] ) * 3 ); } if ( empty( $args['post_type'] ) ) { // If post_types is empty or contains a query string then use parse_str else consider it comma-separated. if ( ! empty( $args['post_types'] ) && is_array( $args['post_types'] ) ) { $post_types = $args['post_types']; } elseif ( ! empty( $args['post_types'] ) && false === strpos( $args['post_types'], '=' ) ) { $post_types = explode( ',', $args['post_types'] ); } else { parse_str( $args['post_types'], $post_types ); // Save post types in $post_types variable. } // If post_types is empty or if we want all the post types. if ( empty( $post_types ) || 'all' === $args['post_types'] ) { $post_types = get_post_types( array( 'public' => true, ) ); } // If we only want posts from the same post type. if ( $args['same_post_type'] ) { $post_types = (array) $source_post->post_type; } /** * Filter the post_types passed to the query. * * @since 2.2.0 * @since 3.0.0 Changed second argument from post ID to WP_Post object. * * @param array $post_types Array of post types to filter by. * @param \WP_Post $source_post Source Post instance. * @param array $args Arguments array. */ $args['post_type'] = apply_filters( 'crp_posts_post_types', $post_types, $source_post, $args ); } // Tax Query. if ( ! empty( $args['tax_query'] ) && is_array( $args['tax_query'] ) ) { $tax_query = $args['tax_query']; } else { $tax_query = array(); } if ( ! empty( $args['include_cat_ids'] ) ) { $tax_query[] = array( 'field' => 'term_taxonomy_id', 'terms' => wp_parse_id_list( $args['include_cat_ids'] ), 'include_children' => false, ); } if ( ! empty( $args['exclude_categories'] ) ) { $tax_query[] = array( 'field' => 'term_taxonomy_id', 'terms' => wp_parse_id_list( $args['exclude_categories'] ), 'operator' => 'NOT IN', 'include_children' => false, ); } if ( ! empty( $args['primary_term'] ) ) { // Get the taxonomies used by the post type. $post_taxonomies = get_object_taxonomies( $source_post ); foreach ( (array) $post_taxonomies as $term ) { if ( empty( $primary_term['primary'] ) ) { $primary_term = Helpers::get_primary_term( $source_post, $term ); } } if ( ! empty( $primary_term['primary'] ) ) { $tax_query[] = array( 'field' => 'term_taxonomy_id', 'terms' => wp_parse_id_list( $primary_term['primary']->term_taxonomy_id ), 'include_children' => false, ); } } // Process same taxonomies option. if ( isset( $args['same_taxes'] ) && $args['same_taxes'] ) { $taxonomies = explode( ',', $args['same_taxes'] ); // Get the taxonomies used by the post type. if ( empty( $post_taxonomies ) ) { $post_taxonomies = get_object_taxonomies( $source_post ); } // Only limit the taxonomies to what is selected for the current post. $current_taxonomies = array_values( array_intersect( $taxonomies, $post_taxonomies ) ); // Store the number of common taxonomies. $args['taxonomy_count'] = count( $current_taxonomies ); // Get the terms for the current post. $terms = wp_get_object_terms( $source_post->ID, (array) $current_taxonomies ); if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) { $term_taxonomy_ids = array_unique( wp_list_pluck( $terms, 'term_taxonomy_id' ) ); $tax_query[] = array( 'field' => 'term_taxonomy_id', 'terms' => wp_parse_id_list( $term_taxonomy_ids ), 'operator' => 'IN', 'include_children' => false, ); } } /** * Filter the tax_query passed to the query. * * @since 3.0.0 * * @param array $tax_query Array of tax_query parameters. * @param \WP_Post $source_post Source Post instance. * @param array $args Arguments array. */ $tax_query = apply_filters( 'crp_query_tax_query', $tax_query, $source_post, $args ); // Add a relation key if more than one $tax_query. if ( count( $tax_query ) > 1 && ! isset( $tax_query['relation'] ) ) { /** * Filter the tax_query relation parameter. * * @since 3.5.3 * * @param string $relation The logical relationship between each inner taxonomy array when there is more than one. Default is 'AND'. * @param array $args Arguments array. */ $tax_query['relation'] = apply_filters( 'crp_query_tax_query_relation', 'AND', $args ); } $args['tax_query'] = $tax_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query // Set date_query. $date_query = array( array( 'after' => ( 0 === absint( $args['daily_range'] ) ) ? '' : gmdate( 'Y-m-d', strtotime( current_time( 'mysql' ) ) - ( absint( $args['daily_range'] ) * DAY_IN_SECONDS ) ), 'before' => current_time( 'mysql' ), 'inclusive' => true, ), ); /** * Filter the date_query passed to WP_Query. * * @since 3.3.0 * * @param array $date_query Array of date parameters to be passed to WP_Query. * @param array $args Arguments array. */ $args['date_query'] = apply_filters( 'crp_query_date_query', $date_query, $args ); // Meta Query. if ( ! empty( $args['meta_query'] ) && is_array( $args['meta_query'] ) ) { $meta_query = $args['meta_query']; } else { $meta_query = array(); } /** * Filter the meta_query passed to WP_Query. * * @since 3.3.0 * * @param array $meta_query Array of meta_query parameters. * @param array $args Arguments array. */ $meta_query = apply_filters( 'crp_query_meta_query', $meta_query, $args ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query // Validate meta_query structure. if ( ! is_array( $meta_query ) ) { $meta_query = array(); } // Add a relation key if more than one $meta_query and if 'relation' is not already set. if ( count( $meta_query ) > 1 && ! isset( $meta_query['relation'] ) ) { /** * Filter the meta_query relation parameter. * * @since 3.3.0 * * @param string $relation The logical relationship between each inner meta_query array when there is more than one. Default is 'AND'. * @param array $args Arguments array. */ $meta_query['relation'] = apply_filters( 'crp_query_meta_query_relation', 'AND', $args ); } $args['meta_query'] = $meta_query; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query // Set post_status. $args['post_status'] = empty( $args['post_status'] ) ? array( 'publish', 'inherit' ) : $args['post_status']; // Set post__not_in for WP_Query using exclude_post_ids. $args['post__not_in'] = $this->exclude_post_ids( $args ); // Same author. if ( isset( $args['same_author'] ) && $args['same_author'] ) { $args['author'] = $source_post->post_author; } // Disable contextual matching. if ( ! empty( $args['disable_contextual'] ) ) { /* If post or page and we're not disabling custom post types */ if ( ( 'post' === $source_post->post_type || 'page' === $source_post->post_type ) && ( $args['disable_contextual_cpt'] ) ) { $this->enable_relevance = true; } else { $this->enable_relevance = false; } } // Unset what we don't need. unset( $args['title'] ); unset( $args['blank_output'] ); unset( $args['blank_output_text'] ); unset( $args['show_excerpt'] ); unset( $args['excerpt_length'] ); unset( $args['show_date'] ); unset( $args['show_author'] ); unset( $args['title_length'] ); unset( $args['link_new_window'] ); unset( $args['link_nofollow'] ); unset( $args['before_list'] ); unset( $args['after_list'] ); unset( $args['before_list_item'] ); unset( $args['after_list_item'] ); unset( $args['more_link_text'] ); unset( $args['extra_class'] ); unset( $args['className'] ); unset( $args['other_attributes'] ); /** * Filters the arguments of the query. * * @since 3.0.0 * * @param array $args The arguments of the query. * @param \CRP_Query $query The CRP_Query instance (passed by reference). */ $this->query_args = apply_filters_ref_array( 'crp_query_args', array( $args, &$this ) ); } /** * Get the MATCH sql. * * @since 3.0.0 * * @return string Updated Fields */ public function get_match_sql() { global $wpdb; /** * Filter the source post before getting the match SQL. * * @since 3.5.0 * * @param string $match_sql The match SQL. * @param \WP_Post $source_post Source Post instance. * @param array $query_args Arguments array. */ $match = apply_filters( 'crp_query_pre_get_match_sql', '', $this->source_post, $this->query_args ); if ( ! empty( $match ) ) { return $match; } // Are we matching only the title or the post content as well? $match_fields = array( "$wpdb->posts.post_title", ); $match_fields_content = array( Helpers::strip_stopwords( $this->source_post->post_title ), ); if ( isset( $this->query_args['match_content'] ) && $this->query_args['match_content'] ) { $match_fields[] = "$wpdb->posts.post_content"; $match_fields_content[] = Helpers::strip_stopwords( Display::get_the_excerpt( $this->source_post, min( $this->query_args['match_content_words'], CRP_MAX_WORDS ), false ) ); } if ( ! empty( $this->query_args['keyword'] ) ) { $match_fields_content = array( $this->query_args['keyword'], ); } /** * Filter the fields that are to be matched. * * @since 4.0.0 * * @param array $match_fields Array of fields to be matched. * @param \WP_Post $source_post Source Post instance. * @param array $query_args Arguments array. */ $match_fields = apply_filters( 'crp_query_posts_match_fields', $match_fields, $this->source_post, $this->query_args ); /** * Filter the content of the fields that are to be matched. * * @since 4.0.0 * * @param array $match_fields_content Array of content of fields to be matched * @param \WP_Post $source_post Source Post instance. * @param array $query_args Arguments array. */ $match_fields_content = apply_filters( 'crp_query_posts_match_fields_content', $match_fields_content, $this->source_post, $this->query_args ); // Convert our arrays into their corresponding strings after they have been filtered. $this->match_fields = implode( ',', $match_fields ); $this->stuff = implode( ' ', $match_fields_content ); // Create the base MATCH clause. $match = $wpdb->prepare( ' MATCH (' . $this->match_fields . ') AGAINST (%s) ', $this->stuff ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared /** * Filter the match SQL. * * @since 4.0.0 * * @param string $match The match SQL. * @param \WP_Post $source_post Source Post instance. * @param array $query_args Arguments array. * @param string $match_fields The fields to be matched. * @param string $stuff The content of the fields to be matched. */ return apply_filters( 'crp_query_match_sql', $match, $this->source_post, $this->query_args, $this->match_fields, $this->stuff ); } /** * Modify the pre_get_posts clause. * * @since 3.5.0 * * @param \WP_Query $query The WP_Query instance. */ public function pre_get_posts( $query ) { if ( true === $query->get( 'crp_query' ) ) { if ( ! empty( $this->query_args['date_query'] ) ) { $query->set( 'date_query', $this->query_args['date_query'] ); } if ( ! empty( $this->query_args['tax_query'] ) ) { $query->set( 'tax_query', $this->query_args['tax_query'] ); } if ( ! empty( $this->query_args['meta_query'] ) ) { $query->set( 'meta_query', $this->query_args['meta_query'] ); } if ( ! empty( $this->query_args['post_type'] ) ) { $query->set( 'post_type', $this->query_args['post_type'] ); } if ( ! empty( $this->query_args['post__not_in'] ) ) { $query->set( 'post__not_in', $this->query_args['post__not_in'] ); } if ( ! empty( $this->query_args['post_status'] ) ) { $query->set( 'post_status', $this->query_args['post_status'] ); } if ( ! empty( $this->query_args['posts_per_page'] ) ) { $query->set( 'posts_per_page', $this->query_args['posts_per_page'] ); } if ( ! empty( $this->query_args['author'] ) ) { $query->set( 'author', $this->query_args['author'] ); } $query->set( 'suppress_filters', false ); $query->set( 'no_found_rows', true ); $query->set( 'ignore_sticky_posts', true ); remove_filter( 'pre_get_posts', array( $this, 'pre_get_posts' ) ); } } /** * Modify the SELECT clause - posts_fields. * * @since 3.0.0 * * @param string $fields The SELECT clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @return string Updated Fields */ public function posts_fields( $fields, $query ) { // Return if it is not a CRP_Query. if ( true !== $query->get( 'crp_query' ) ) { return $fields; } if ( $this->enable_relevance ) { if ( empty( $this->match_sql ) ) { $this->match_sql = $this->get_match_sql(); } $match = ', ' . $this->match_sql . ' as score '; $fields .= $match; } /** * Filters the fields returned by the SELECT clause. * * @since 3.2.0 * @since 4.0.0 Added $instance * * @param string $fields The fields returned by the SELECT clause. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @param CRP_Core_Query $instance The CRP_Core_Query instance. */ $fields = apply_filters_ref_array( 'crp_query_posts_fields', array( $fields, $query, &$this ) ); remove_filter( 'posts_fields', array( $this, 'posts_fields' ) ); return $fields; } /** * Modify the posts_join clause. * * @since 3.0.0 * * @param string $join The JOIN clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @return string Updated JOIN */ public function posts_join( $join, $query ) { global $wpdb; // Return if it is not a CRP_Query. if ( true !== $query->get( 'crp_query' ) ) { return $join; } if ( ! empty( $this->query_args['match_all'] ) || ( isset( $this->query_args['no_of_common_terms'] ) && absint( $this->query_args['no_of_common_terms'] ) > 1 ) ) { if ( strpos( $join, 'crp_tr' ) === false ) { $join .= " INNER JOIN $wpdb->term_relationships AS crp_tr ON ($wpdb->posts.ID = crp_tr.object_id) "; } if ( strpos( $join, 'crp_tt' ) === false ) { $join .= " INNER JOIN $wpdb->term_taxonomy AS crp_tt ON (crp_tr.term_taxonomy_id = crp_tt.term_taxonomy_id) "; } } /** * Filters the posts_join of CRP_Query after processing and before returning. * * @since 3.2.0 * @since 4.0.0 Added $instance * * @param string $join The JOIN clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @param CRP_Core_Query $instance The CRP_Core_Query instance. */ $join = apply_filters_ref_array( 'crp_query_posts_join', array( $join, $query, &$this ) ); remove_filter( 'posts_join', array( $this, 'posts_join' ) ); return $join; } /** * Modify the posts_where clause. * * @since 3.0.0 * * @param string $where The WHERE clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @return string Updated WHERE */ public function posts_where( $where, $query ) { global $wpdb; // Return if it is not a CRP_Query. if ( true !== $query->get( 'crp_query' ) ) { return $where; } $match_clause = ''; $include = ''; $exclude = ''; $search_columns = array( 'post_title', 'post_excerpt', 'post_content' ); if ( $this->enable_relevance ) { $match_clause = ! empty( $this->match_sql ) ? $this->match_sql : $this->get_match_sql(); /** * Filter the MATCH clause of the WHERE clause of the query. * * @since 4.0.0 * * @param string $match_clause The MATCH section of the WHERE clause of the query. * @param string $where The WHERE clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @param CRP_Core_Query $instance The CRP_Core_Query instance. */ $match_clause = apply_filters_ref_array( 'crp_query_posts_where_match', array( $match_clause, $where, $query, &$this ) ); } if ( isset( $this->query_args['include_words'] ) ) { $n = '%'; $includeand = ''; $include_words = preg_split( '/[,\s]+/', $this->query_args['include_words'] ); $include_words = array_filter( $include_words ); foreach ( (array) $include_words as $word ) { $like_op = 'LIKE'; $andor_op = 'OR'; $like = $n . $wpdb->esc_like( strtolower( $word ) ) . $n; $this->include_orderby_title[] = $wpdb->prepare( "{$wpdb->posts}.post_title LIKE %s", $like ); $search_columns_parts = array(); foreach ( $search_columns as $search_column ) { $search_columns_parts[ $search_column ] = $wpdb->prepare( "({$wpdb->posts}.$search_column $like_op %s)", $like ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared } $include .= "$includeand(" . implode( " $andor_op ", $search_columns_parts ) . ')'; $includeand = ' OR '; } } // if both $match and $include are not empty, then join them using OR. Else, use the one that is not empty. if ( ! empty( $match_clause ) && ! empty( $include ) ) { $where .= " AND ( $match_clause OR $include )"; } elseif ( ! empty( $match_clause ) ) { $where .= " AND ( $match_clause )"; } elseif ( ! empty( $include ) ) { $where .= " AND ( 1=1 OR $include )"; } if ( isset( $this->crp_post_meta['exclude_words'] ) ) { $n = '%'; $excludeand = ''; $exclude_words = preg_split( '/[,\s]+/', $this->crp_post_meta['exclude_words'] ); $exclude_words = array_filter( $exclude_words ); foreach ( (array) $exclude_words as $word ) { $like_op = 'NOT LIKE'; $andor_op = 'AND'; $like = $n . $wpdb->esc_like( strtolower( $word ) ) . $n; $search_columns_parts = array(); foreach ( $search_columns as $search_column ) { $search_columns_parts[ $search_column ] = $wpdb->prepare( "({$wpdb->posts}.$search_column $like_op %s)", $like ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared } $exclude .= "$excludeand(" . implode( " $andor_op ", $search_columns_parts ) . ')'; $excludeand = ' AND '; } if ( ! empty( $exclude ) ) { $where .= " AND ({$exclude}) "; } } /** * Filters the posts_where of CRP_Query after processing and before returning. * * @since 3.2.0 * @since 4.0.0 Added $instance * * @param string $where The WHERE clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @param CRP_Core_Query $instance The CRP_Core_Query instance. */ $where = apply_filters_ref_array( 'crp_query_posts_where', array( $where, $query, &$this ) ); remove_filter( 'posts_where', array( $this, 'posts_where' ) ); return $where; } /** * Modify the posts_orderby clause. * * @since 3.0.0 * * @param string $orderby The ORDER BY clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @return string Updated ORDER BY */ public function posts_orderby( $orderby, $query ) { global $wpdb; // Return if it is not a CRP_Query. if ( true !== $query->get( 'crp_query' ) ) { return $orderby; } // If orderby is set, then this was done intentionally and we don't make any modifications. if ( ! empty( $query->get( 'orderby' ) ) ) { // if orderby is set to relevance, then we need to set the orderby to the match clause. if ( 'relevance' === $query->get( 'orderby' ) || 'relatedness' === $query->get( 'orderby' ) ) { if ( empty( $this->match_sql ) ) { $this->match_sql = $this->get_match_sql(); } $orderby = ' ' . $this->match_sql . ' DESC '; } return apply_filters( 'crp_query_posts_orderby', $orderby, $query ); } // Initialize an array to build the orderby clauses. $orderby_clauses = array(); // Add include_words at the beginning. if ( ! empty( $this->query_args['include_words'] ) ) { $include_orderby = $this->parse_include_words_order(); if ( ! empty( $include_orderby ) ) { $orderby_clauses[] = $include_orderby; } } if ( $this->enable_relevance && isset( $this->query_args['ordering'] ) && 'date' !== $this->query_args['ordering'] ) { if ( empty( $this->match_sql ) ) { $this->match_sql = $this->get_match_sql(); } $orderby_clauses[] = ' ' . $this->match_sql . ' DESC '; } // Set order by in case of date. if ( isset( $this->query_args['ordering'] ) && 'date' === $this->query_args['ordering'] ) { $orderby_clauses[] = " $wpdb->posts.post_date DESC "; } /** * Filters the posts_orderby of CRP_Query after processing and before returning. * * @since 3.5.2 * @since 4.0.0 Added $instance * * @param string[] $orderby_clauses The SELECT clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @param CRP_Core_Query $instance The CRP_Core_Query instance. */ $orderby_clauses = apply_filters_ref_array( 'crp_query_posts_orderby_clauses', array( $orderby_clauses, $query, &$this ) ); // Combine all the orderby clauses. if ( ! empty( $orderby_clauses ) ) { $orderby = implode( ', ', $orderby_clauses ); } /** * Filters the posts_orderby of CRP_Query after processing and before returning. * * @since 3.2.0 * @since 4.0.0 Added $instance * * @param string $orderby The SELECT clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @param CRP_Core_Query $instance The CRP_Core_Query instance. */ $orderby = apply_filters_ref_array( 'crp_query_posts_orderby', array( $orderby, $query, &$this ) ); remove_filter( 'posts_orderby', array( $this, 'posts_orderby' ) ); return $orderby; } /** * Modify the posts_groupby clause. * * @since 3.0.0 * * @param string $groupby The GROUP BY clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @return string Updated GROUP BY */ public function posts_groupby( $groupby, $query ) { if ( true !== $query->get( 'crp_query' ) ) { return $groupby; } /** * Filters the GROUP BY clause of the CRP_Query. * * @since 3.0.0 * @since 4.0.0 Added $instance * * @param string $groupby The GROUP BY clause of the query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @param CRP_Core_Query $instance The CRP_Core_Query instance. */ $groupby = apply_filters_ref_array( 'crp_query_posts_groupby', array( $groupby, $query, &$this ) ); remove_filter( 'posts_groupby', array( $this, 'posts_groupby' ) ); return $groupby; } /** * Modify the completed SQL query before sending. * * @since 3.0.0 * * @param string $sql The complete SQL query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @return string Updated SQL query. */ public function posts_request( $sql, $query ) { global $wpdb; $conditions = array(); // Return if it is not a CRP_Query. if ( true !== $query->get( 'crp_query' ) ) { return $sql; } if ( ! empty( $this->query_args['match_all'] ) && ! empty( $this->query_args['taxonomy_count'] ) ) { $conditions[] = $wpdb->prepare( 'COUNT(DISTINCT crp_tt.taxonomy) = %d', $this->query_args['taxonomy_count'] ); } if ( isset( $this->query_args['no_of_common_terms'] ) && absint( $this->query_args['no_of_common_terms'] ) > 1 ) { $conditions[] = $wpdb->prepare( 'COUNT(DISTINCT crp_tt.term_id) >= %d', absint( $this->query_args['no_of_common_terms'] ) ); } if ( ! empty( $conditions ) ) { $conditions = implode( ' AND ', $conditions ); $having = "HAVING ( {$conditions} ) ORDER BY"; $sql = str_replace( 'ORDER BY', $having, $sql ); } /** * Filters the posts_request of CRP_Query after processing and before returning. * * @since 3.2.0 * @since 4.0.0 Added $instance * * @param string $sql The SQL Query. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @param CRP_Core_Query $instance The CRP_Core_Query instance. */ $sql = apply_filters_ref_array( 'crp_query_posts_request', array( $sql, $query, &$this ) ); remove_filter( 'posts_request', array( $this, 'posts_request' ) ); return $sql; } /** * Filter posts_pre_query to allow caching to work. * * @since 3.0.0 * * @param \WP_Post[] $posts Array of post data. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @return \WP_Post[] Updated Array of post objects. */ public function posts_pre_query( $posts, $query ) { // Return if it is not a CRP_Query. if ( true !== $query->get( 'crp_query' ) ) { return $posts; } $post_ids = array(); // Check the cache if there are any posts saved. if ( ! empty( $this->query_args['cache_posts'] ) && ! ( is_preview() || is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) ) { $meta_key = Cache::get_key( $this->input_query_args ); $cached_data = Cache::get_cache( $this->source_post->ID, $meta_key ); if ( ! empty( $cached_data ) ) { $post_ids = $cached_data; $this->in_cache = true; } } if ( ! empty( $this->manual_related ) && ( $this->no_of_manual_related >= $this->query_args['limit'] ) ) { $post_ids = array_merge( $post_ids, $this->manual_related ); } if ( ! empty( $post_ids ) ) { $posts = get_posts( array( 'post__in' => array_unique( $post_ids ), 'fields' => $query->get( 'fields' ), 'orderby' => 'post__in', 'numberposts' => $query->get( 'posts_per_page' ), 'post_type' => $query->get( 'post_type' ), 'post_status' => $query->get( 'post_status' ), ) ); $query->found_posts = count( $posts ); $query->max_num_pages = (int) ceil( $query->found_posts / $query->get( 'posts_per_page' ) ); } /** * Filters the posts_pre_query of CRP_Query after processing and before returning. * * @since 3.2.0 * @since 4.0.0 Added $instance * * @param \WP_Post[] $posts Array of post data. * @param \WP_Query $query The WP_Query or CRP_Query instance. * @param CRP_Core_Query $instance The CRP_Core_Query instance. */ $posts = apply_filters_ref_array( 'crp_query_posts_pre_query', array( $posts, $query, &$this ) ); remove_filter( 'posts_pre_query', array( $this, 'posts_pre_query' ) ); return $posts; } /** * Modify the array of retrieved posts. * * @since 3.0.0 * * @param \WP_Post[] $posts Array of post objects. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance (passed by reference). * @return \WP_Post[] Updated Array of post objects. */ public function the_posts( $posts, $query ) { // Return if it is not a CRP_Query. if ( true !== $query->get( 'crp_query' ) ) { return $posts; } // Support caching to speed up retrieval. if ( ! empty( $this->query_args['cache_posts'] ) && ! $this->in_cache && ! ( is_preview() || is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) ) { $meta_key = Cache::get_key( $this->input_query_args ); $post_ids = wp_list_pluck( $query->posts, 'ID' ); Cache::set_cache( $this->source_post->ID, $meta_key, $post_ids ); } // Shuffle posts if random order is set. if ( $this->random_order ) { shuffle( $posts ); } // Related posts by meta_key. if ( ! empty( $this->query_args['related_meta_keys'] ) ) { $related_meta_query = array(); $related_meta_keys = wp_parse_list( $this->query_args['related_meta_keys'] ); foreach ( $related_meta_keys as $related_meta_key ) { $related_meta_value = (string) get_post_meta( $this->source_post->ID, $related_meta_key, true ); if ( ! empty( $related_meta_value ) ) { $related_meta_query[] = array( 'key' => $related_meta_key, 'value' => $related_meta_value, ); } } if ( count( $related_meta_query ) > 1 ) { /** * Filter the meta_query relation parameter for related posts by meta_key. * * @since 3.3.0 * * @param string $relation The logical relationship between each inner meta_query array when there is more than one. Default is 'OR'. */ $related_meta_query['relation'] = apply_filters( 'crp_query_related_meta_query_relation', 'OR' ); } if ( ! empty( $related_meta_query ) ) { $meta_posts = get_posts( array( 'post__not_in' => $this->exclude_post_ids( $this->query_args ), 'fields' => $query->get( 'fields' ), 'numberposts' => $query->get( 'posts_per_page' ), 'post_type' => $query->get( 'post_type' ), 'meta_query' => $related_meta_query, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query ) ); $posts = array_merge( $meta_posts, $posts ); } } // Manual Posts (manual_related - set via the Post Meta) or Include Posts (can be set as a parameter). $post_ids = array(); if ( ! empty( $this->manual_related ) && ( $this->no_of_manual_related < $this->query_args['limit'] ) ) { $post_ids = array_merge( $post_ids, $this->manual_related ); } if ( ! empty( $post_ids ) ) { $extra_posts = get_posts( array( 'post__in' => array_unique( $post_ids ), 'fields' => $query->get( 'fields' ), 'orderby' => 'post__in', 'numberposts' => -1, 'post_type' => 'any', 'post_status' => $query->get( 'post_status' ), ) ); $posts = array_merge( $extra_posts, $posts ); } /** * Set the flag if CRP should fill random posts if there is a shortage of related posts. * * @since 3.2.0 * * @param bool $fill_random_posts Fill random posts flag. Default false. * @param \WP_Post[] $posts Array of post objects. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. */ $fill_random_posts = apply_filters( 'crp_fill_random_posts', false, $posts, $query ); if ( $fill_random_posts ) { $no_of_random_posts = $this->query_args['limit'] - count( $posts ); if ( $no_of_random_posts > 0 ) { $random_posts = get_posts( array( 'fields' => $query->get( 'fields' ), 'orderby' => 'rand', 'numberposts' => $no_of_random_posts, 'post_type' => $query->get( 'post_type' ), 'post_status' => $query->get( 'post_status' ), ) ); $posts = array_merge( $posts, $random_posts ); } } /** * Filter array of WP_Post objects before it is returned to the CRP_Query instance. * * @since 1.9 * @since 2.9.3 Added $args * @since 4.0.0 Added $instance * * @param \WP_Post[] $posts Array of post objects. * @param array $args Arguments array. * @param \WP_Query|\CRP_Query $query The WP_Query or CRP_Query instance. * @param CRP_Core_Query $instance The CRP_Core_Query instance. */ $posts = apply_filters_ref_array( 'crp_query_the_posts', array( $posts, $this->query_args, $query, &$this ) ); remove_filter( 'the_posts', array( $this, 'the_posts' ) ); return $posts; } /** * Exclude Post IDs. Allows other plugins/functions to hook onto this and extend the list. * * @param array $args Array of arguments for CRP_Query. * @return array Array of post IDs to exclude. */ public function exclude_post_ids( $args ) { static $exclude_post_ids_cache = array(); $post_id = absint( $this->source_post->ID ); if ( isset( $exclude_post_ids_cache[ $post_id ] ) ) { return $exclude_post_ids_cache[ $post_id ]; } global $wpdb; $exclude_post_ids = empty( $args['exclude_post_ids'] ) ? array() : wp_parse_id_list( $args['exclude_post_ids'] ); // Exclude posts with exclude_this_post set to true or exclude_post_ids set. $crp_post_metas = $wpdb->get_results( "SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE `meta_key` = 'crp_post_meta'", ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching foreach ( $crp_post_metas as $crp_post_meta ) { $meta_value = maybe_unserialize( $crp_post_meta['meta_value'] ); if ( isset( $meta_value['exclude_this_post'] ) && $meta_value['exclude_this_post'] ) { $exclude_post_ids[] = $crp_post_meta['post_id']; } if ( isset( $meta_value['exclude_post_ids'] ) && $meta_value['exclude_post_ids'] ) { $exclude_post_ids = array_merge( $exclude_post_ids, explode( ',', $meta_value['exclude_post_ids'] ) ); } } /** * Filter exclude post IDs array. * * @since 2.3.0 * @since 2.9.3 Added $args * @since 3.2.0 Added $source_post * * @param array $exclude_post_ids Array of post IDs. * @param array $args Arguments array. * @param \WP_Post $source_post Source post. */ $exclude_post_ids = apply_filters( 'crp_exclude_post_ids', $exclude_post_ids, $args, $this->source_post ); $exclude_post_ids[] = $this->source_post->ID; $exclude_post_ids_cache[ $post_id ] = array_unique( $exclude_post_ids ); return $exclude_post_ids_cache[ $post_id ]; } /** * Generates SQL for the ORDER BY condition based on passed include words. * * @since 3.4.2 * * @global \wpdb $wpdb WordPress database abstraction object. * * @return string ORDER BY clause. */ protected function parse_include_words_order() { global $wpdb; if ( ! isset( $this->query_args['include_words'] ) ) { return ''; } $num_terms = count( $this->include_orderby_title ); if ( $num_terms > 1 ) { // Sentence match in 'post_title'. $like = '%' . $wpdb->esc_like( $this->query_args['include_words'] ) . '%'; $search_orderby = $wpdb->prepare( "WHEN {$wpdb->posts}.post_title LIKE %s THEN 1 ", $like ); /* * Sanity limit, sort as sentence when more than 6 terms * (few searches are longer than 6 terms and most titles are not). */ if ( $num_terms < 7 ) { // All words in title. $search_orderby .= 'WHEN ' . implode( ' AND ', $this->include_orderby_title ) . ' THEN 2 '; $search_orderby .= 'WHEN ' . implode( ' OR ', $this->include_orderby_title ) . ' THEN 3 '; } // Sentence match in 'post_content' and 'post_excerpt'. $search_orderby .= $wpdb->prepare( "WHEN {$wpdb->posts}.post_excerpt LIKE %s THEN 4 ", $like ); $search_orderby .= $wpdb->prepare( "WHEN {$wpdb->posts}.post_content LIKE %s THEN 5 ", $like ); $search_orderby = '(CASE ' . $search_orderby . 'ELSE 6 END)'; } else { // Single word or sentence search. $search_orderby = reset( $this->include_orderby_title ) . ' DESC'; } return $search_orderby; } }
[+]
util
[-] class-hook-loader.php
[edit]
[-] autoloader.php
[edit]
[-] index.php
[edit]
[+]
admin
[+]
frontend
[-] class-main.php
[edit]
[-] functions.php
[edit]
[-] class-crp-core-query.php
[edit]
[-] options-api.php
[edit]
[-] class-crp-query.php
[edit]
[+]
..