File "Post.php"
Full Path: /home/romayxjt/public_html/wp-content/plugins/the-events-calendar/common/src/Tribe/Duplicate/Post.php
File size: 11.27 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* Class Tribe__Duplicate__Post
*
* Provides the functionality to find an existing post starting from the post data.
*
* @since 4.6
*/
class Tribe__Duplicate__Post {
const AND_OPERATOR = 'AND';
const OR_OPERATOR = 'OR';
/**
* @var array The columns of the post table.
*/
public static $post_table_columns = [
'ID',
'post_author',
'post_date',
'post_date_gmt',
'post_content',
'post_title',
'post_excerpt',
'post_status',
'comment_status',
'ping_status',
'post_password',
'post_name',
'to_ping',
'pinged',
'post_modified',
'post_modified_gmt',
'post_content_filtered',
'post_parent',
'guid',
'menu_order',
'post_type',
'post_mime_type',
'comment_count',
];
/**
* @var array The post fields that should be used to find a duplicate.
*/
protected $post_fields = [];
/**
* @var array The custom fields that should be used to find a duplicate.
*/
protected $custom_fields = [];
/**
* @var Tribe__Duplicate__Strategy_Factory
*/
protected $factory;
/**
* @var string The SQL logic operator that should be used to join the WHERE queries frags.
*/
protected $where_operator = self::AND_OPERATOR;
/**
* @var string The post type that should be used to find duplicates.
*/
protected $post_type = 'post';
/**
* @var int The limit that should be applied to the number of JOIN in a single query.
*/
protected $join_limit = 2;
/**
* Tribe__Duplicate__Post constructor.
*
* @param Tribe__Duplicate__Strategy_Factory|null $factory
*
* @since 4.6
*/
public function __construct( Tribe__Duplicate__Strategy_Factory $factory = null ) {
$this->factory = null !== $factory ? $factory : tribe( 'post-duplicate.strategy-factory' );
}
/**
* Sets the post fields that should be used to find a duplicate in the database.
*
* Each entry should be in the [ <post field> => [ 'match' => <strategy> ]] format.
* If not the strategy will be set to the default one.
*
* @param array $post_fields
*
* @since 4.6
*/
public function use_post_fields( array $post_fields ) {
if ( empty( $post_fields ) ) {
$this->post_fields = [];
return;
}
$cast = $this->cast_to_strategy( $post_fields );
$this->post_fields = array_intersect_key( $cast, array_combine( self::$post_table_columns, self::$post_table_columns ) );
}
/**
* Converts an array of fields to the format required by the class.
*
* @param array $fields
*
* @return array
*
* @since 4.6
*/
protected function cast_to_strategy( array $fields ) {
$cast = [];
foreach ( $fields as $key => $value ) {
if ( is_numeric( $key ) ) {
$cast[ $value ] = [ 'match' => 'same' ];
} elseif ( is_array( $value ) ) {
if ( ! empty( $value['match'] ) ) {
$cast[ $key ] = $value;
} else {
$cast[ $key ] = array_merge( $value, [ 'match' => 'same' ] );
}
}
}
return $cast;
}
/**
* Finds a duplicate with the data provided.
*
* The more post and custom fields are used to find a match the less likely it is to find one and the more
* likely it is for a duplicate to be a good match.
*
* @param array $postarr An array of post data, post fields and custom fields, that should be used to find the
* duplicate.
*
* @return bool|int `false` if a duplicate was not found, the post ID of the duplicate if found.
*
* @since 4.6
*/
public function find_for( array $postarr ) {
if ( empty( $this->post_fields ) && empty( $this->custom_fields ) ) {
return false;
}
$prepared = $this->prepare_queries( $postarr );
if ( false === $prepared ) {
return false;
}
$id = false;
/** @var wpdb $wpdb */
global $wpdb;
foreach ( $prepared as $query ) {
$this_id = $wpdb->get_var( $query );
if ( self::AND_OPERATOR === $this->where_operator ) {
if ( empty( $this_id ) ) {
return false;
}
$id = empty( $id )
? $this_id
: $this_id == $id;
if ( empty( $id ) ) {
return false;
}
} else {
if ( ! empty( $this_id ) ) {
return $this_id;
}
}
}
return $id;
}
/**
* Finds all the duplicates with the data provided.
*
* The more post and custom fields are used to find a match the less likely it is to find one and the more
* likely it is for a duplicate to be a good match.
*
* @param array $postarr An array of post data, post fields and custom fields, that should be used to find the
* duplicate.
*
* @return bool|array `false` if a duplicate was not found, an array of the duplicate post IDs if any were found.
*
* @since 4.6
*/
public function find_all_for( array $postarr ) {
if ( empty( $this->post_fields ) && empty( $this->custom_fields ) ) {
return false;
}
$prepared = $this->prepare_queries( $postarr );
if ( false === $prepared ) {
return false;
}
$ids = false;
/** @var wpdb $wpdb */
global $wpdb;
foreach ( $prepared as $query ) {
$this_ids = $wpdb->get_results( $query );
$this_ids = ! empty( $this_ids )
? array_map( 'intval', wp_list_pluck( $this_ids, 'ID' ) )
: false;
if ( self::AND_OPERATOR === $this->where_operator ) {
if ( empty( $this_ids ) ) {
return false;
}
$ids = empty( $ids )
? $this_ids
: array_intersect( (array) $ids, (array) $this_ids );
if ( empty( $ids ) ) {
return false;
}
} else {
$ids = empty( $ids )
? $this_ids
: array_unique( array_merge( (array) $ids, array_filter( (array) $this_ids ) ) );
}
}
return $ids;
}
/**
* Sets the custom fields that should be used to find a duplicate in the database.
*
* Each entry should be in the [ <custom field> => [ 'match' => <strategy> ]] format.
* If not the strategy will be set to the default one.
*
* @param array $custom_fields
*
* @since 4.6
*/
public function use_custom_fields( array $custom_fields ) {
$cast = $this->cast_to_strategy( $custom_fields );
$this->custom_fields = $cast;
}
/**
* Gets the SQL logic operator that will be used to join the WHERE queries frags.
*
* @return string
*
* @since 4.6
*/
public function get_where_operator() {
return $this->where_operator;
}
/**
* Sets the SQL logic operator that should be used to join the WHERE queries frags.
*
* @param string $where_operator
*
* @since 4.6
*/
public function set_where_operator( $where_operator ) {
$this->where_operator = self::AND_OPERATOR === strtoupper( $where_operator )
? self::AND_OPERATOR
: self::OR_OPERATOR;
}
/**
* Prepares the query that should be used to query for duplicates according
* to the current post and custom fields.
*
* @param array $postarr
*
* @return bool|array An array of prepared queries or `false` on failure.
*
* @since 4.6
*/
protected function prepare_queries( array $postarr ) {
/** @var wpdb $wpdb */
global $wpdb;
$where_frags = [];
$custom_fields_where_frags = [];
$join = [];
if ( ! empty( $this->post_fields ) ) {
$queryable_post_fields = array_intersect_key( $postarr, $this->post_fields );
if ( empty( $queryable_post_fields ) ) {
return false;
}
foreach ( $queryable_post_fields as $key => $value ) {
$match_strategy = $this->factory->make( $this->post_fields[ $key ]['match'] );
$where_frags[] = $match_strategy->where( $key, $postarr[ $key ] );
}
}
if ( ! empty( $this->custom_fields ) ) {
// we had post fields and found a match
$queryable_custom_fields = array_intersect_key( $postarr, $this->custom_fields );
$i = 0;
foreach ( $queryable_custom_fields as $key => $value ) {
$match_strategy = $this->factory->make( $this->custom_fields[ $key ]['match'] );
$meta_value = is_array( $value ) ? reset( $value ) : $value;
$custom_fields_where_frags[] = $match_strategy->where_custom_field( $key, $meta_value, "pm{$i}" );
$i ++;
}
$count = count( $custom_fields_where_frags );
for ( $i = 0; $i < $count; $i ++ ) {
$join[] = " \nLEFT JOIN {$wpdb->postmeta} pm{$i} ON pm{$i}.post_id = {$wpdb->posts}.ID ";
}
}
/**
* Filters the JOIN limit.
*
* @param int $join_limit How many joins will be made per query at most.
* @param array $where_frags The WHERE components for this duplicate search query
* @param string $post_type The post type that's being used for this duplicate search query.
*
* @since 4.6
*/
$join_limit = apply_filters( 'tribe_duplicate_post_join_limit', $this->join_limit, $where_frags, $this->post_type );
$excluded_status = [
'trash',
'autodraft',
];
/**
* Filters the excluded status.
*
* @param array $excluded_status The list of post_status to exclude from the query.
* @param string $post_type The post type that's being used for this duplicate search query.
*
* @since 4.6
*/
$excluded_status = apply_filters( 'tribe_duplicate_post_excluded_status', $excluded_status, $where_frags, $this->post_type );
$post_type_conditional = $wpdb->prepare( "{$wpdb->posts}.post_type = %s", $this->post_type );
$post_status_conditional = '';
if ( $excluded_status ) {
$in_string = array_fill( 0, count( $excluded_status ), '%s' );
$in_string = implode( ', ', $in_string );
// @codingStandardsIgnoreLine
$post_status_conditional = $wpdb->prepare( "{$wpdb->posts}.post_status NOT IN ( {$in_string} )", $excluded_status );
}
$queries = [];
if ( ! empty( $join_limit ) && ! empty( $join ) ) {
while ( count( $join ) ) {
$current_wheres = array_splice( $custom_fields_where_frags, 0, $join_limit );
$current_joins = array_splice( $join, 0, $join_limit );
$this_join = implode( "\n", $current_joins );
$this_where = "\n" . implode( " \n{$this->where_operator} ", array_merge( $where_frags, $current_wheres ) );
$this_where = sprintf( '%s AND (%s)', $post_type_conditional, $this_where );
if ( '' !== $post_status_conditional ) {
$this_where = sprintf( '%s AND %s', $post_status_conditional, $this_where );
}
$queries[] = "SELECT DISTINCT {$wpdb->posts}.ID from {$wpdb->posts} {$this_join} \nWHERE {$this_where}";
}
} else {
$where = implode( " \n{$this->where_operator} ", $where_frags );
$where = sprintf( '%s AND (%s)', $post_type_conditional, $where );
if ( '' !== $post_status_conditional ) {
$where = sprintf( '%s AND %s', $post_status_conditional, $where );
}
$queries[] = "SELECT DISTINCT {$wpdb->posts}.ID from {$wpdb->posts} \nWHERE {$where}";
}
return $queries;
}
/**
* Gets the post type that will be used to find duplicates.
*
* @return string
*
* @since 4.6
*/
public function get_post_type() {
return $this->post_type;
}
/**
* Sets the post type that should be used to find duplicates.
*
* @param string $post_type
*
* @since 4.6
*/
public function set_post_type( $post_type ) {
$this->post_type = $post_type;
}
/**
* Sets the limit that should be applied to the number of JOIN in a single query.
*
* Setting the limit to an empty value will remove the limit (very bad idea).
*
* @param int $join_limit
*
* @since 4.6
*/
public function set_join_limit( $join_limit ) {
$this->join_limit = empty( $join_limit )
? 999
: intval( $join_limit );
}
/**
* Returns the limit that will be applied to the number of JOIN in a single query.
*
* @return int
*
* @since 4.6
*/
public function get_join_limit() {
return $this->join_limit;
}
}