wp/wp-includes/class-wp-date-query.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
    12  *
    12  *
    13  * @link https://developer.wordpress.org/reference/classes/wp_query/
    13  * @link https://developer.wordpress.org/reference/classes/wp_query/
    14  *
    14  *
    15  * @since 3.7.0
    15  * @since 3.7.0
    16  */
    16  */
       
    17 #[AllowDynamicProperties]
    17 class WP_Date_Query {
    18 class WP_Date_Query {
    18 	/**
    19 	/**
    19 	 * Array of date queries.
    20 	 * Array of date queries.
    20 	 *
    21 	 *
    21 	 * See WP_Date_Query::__construct() for information on date query arguments.
    22 	 * See WP_Date_Query::__construct() for information on date query arguments.
   147 	public function __construct( $date_query, $default_column = 'post_date' ) {
   148 	public function __construct( $date_query, $default_column = 'post_date' ) {
   148 		if ( empty( $date_query ) || ! is_array( $date_query ) ) {
   149 		if ( empty( $date_query ) || ! is_array( $date_query ) ) {
   149 			return;
   150 			return;
   150 		}
   151 		}
   151 
   152 
   152 		if ( isset( $date_query['relation'] ) && 'OR' === strtoupper( $date_query['relation'] ) ) {
   153 		if ( isset( $date_query['relation'] ) ) {
   153 			$this->relation = 'OR';
   154 			$this->relation = $this->sanitize_relation( $date_query['relation'] );
   154 		} else {
   155 		} else {
   155 			$this->relation = 'AND';
   156 			$this->relation = 'AND';
   156 		}
   157 		}
   157 
   158 
   158 		// Support for passing time-based keys in the top level of the $date_query array.
   159 		// Support for passing time-based keys in the top level of the $date_query array.
   216 
   217 
   217 		// Validate the dates passed in the query.
   218 		// Validate the dates passed in the query.
   218 		if ( $this->is_first_order_clause( $queries ) ) {
   219 		if ( $this->is_first_order_clause( $queries ) ) {
   219 			$this->validate_date_values( $queries );
   220 			$this->validate_date_values( $queries );
   220 		}
   221 		}
       
   222 
       
   223 		// Sanitize the relation parameter.
       
   224 		$queries['relation'] = $this->sanitize_relation( $queries['relation'] );
   221 
   225 
   222 		foreach ( $queries as $key => $q ) {
   226 		foreach ( $queries as $key => $q ) {
   223 			if ( ! is_array( $q ) || in_array( $key, $this->time_keys, true ) ) {
   227 			if ( ! is_array( $q ) || in_array( $key, $this->time_keys, true ) ) {
   224 				// This is a first-order query. Trust the values and sanitize when building SQL.
   228 				// This is a first-order query. Trust the values and sanitize when building SQL.
   225 				$cleaned_query[ $key ] = $q;
   229 				$cleaned_query[ $key ] = $q;
   231 
   235 
   232 		return $cleaned_query;
   236 		return $cleaned_query;
   233 	}
   237 	}
   234 
   238 
   235 	/**
   239 	/**
   236 	 * Determine whether this is a first-order clause.
   240 	 * Determines whether this is a first-order clause.
   237 	 *
   241 	 *
   238 	 * Checks to see if the current clause has any time-related keys.
   242 	 * Checks to see if the current clause has any time-related keys.
   239 	 * If so, it's first-order.
   243 	 * If so, it's first-order.
   240 	 *
   244 	 *
   241 	 * @since 4.1.0
   245 	 * @since 4.1.0
   274 	 * This method only generates debug notices for these cases.
   278 	 * This method only generates debug notices for these cases.
   275 	 *
   279 	 *
   276 	 * @since 4.1.0
   280 	 * @since 4.1.0
   277 	 *
   281 	 *
   278 	 * @param array $date_query The date_query array.
   282 	 * @param array $date_query The date_query array.
   279 	 * @return bool  True if all values in the query are valid, false if one or more fail.
   283 	 * @return bool True if all values in the query are valid, false if one or more fail.
   280 	 */
   284 	 */
   281 	public function validate_date_values( $date_query = array() ) {
   285 	public function validate_date_values( $date_query = array() ) {
   282 		if ( empty( $date_query ) ) {
   286 		if ( empty( $date_query ) ) {
   283 			return false;
   287 			return false;
   284 		}
   288 		}
   467 	 * prepended. Prefixed column names (such as 'wp_posts.post_date') bypass this allowed
   471 	 * prepended. Prefixed column names (such as 'wp_posts.post_date') bypass this allowed
   468 	 * check, and are only sanitized to remove illegal characters.
   472 	 * check, and are only sanitized to remove illegal characters.
   469 	 *
   473 	 *
   470 	 * @since 3.7.0
   474 	 * @since 3.7.0
   471 	 *
   475 	 *
       
   476 	 * @global wpdb $wpdb WordPress database abstraction object.
       
   477 	 *
   472 	 * @param string $column The user-supplied column name.
   478 	 * @param string $column The user-supplied column name.
   473 	 * @return string A validated column name value.
   479 	 * @return string A validated column name value.
   474 	 */
   480 	 */
   475 	public function validate_column( $column ) {
   481 	public function validate_column( $column ) {
   476 		global $wpdb;
   482 		global $wpdb;
   486 			'registered',
   492 			'registered',
   487 			'last_updated',
   493 			'last_updated',
   488 		);
   494 		);
   489 
   495 
   490 		// Attempt to detect a table prefix.
   496 		// Attempt to detect a table prefix.
   491 		if ( false === strpos( $column, '.' ) ) {
   497 		if ( ! str_contains( $column, '.' ) ) {
   492 			/**
   498 			/**
   493 			 * Filters the list of valid date query columns.
   499 			 * Filters the list of valid date query columns.
   494 			 *
   500 			 *
   495 			 * @since 3.7.0
   501 			 * @since 3.7.0
   496 			 * @since 4.1.0 Added 'user_registered' to the default recognized columns.
   502 			 * @since 4.1.0 Added 'user_registered' to the default recognized columns.
   537 		// Remove unsafe characters.
   543 		// Remove unsafe characters.
   538 		return preg_replace( '/[^a-zA-Z0-9_$\.]/', '', $column );
   544 		return preg_replace( '/[^a-zA-Z0-9_$\.]/', '', $column );
   539 	}
   545 	}
   540 
   546 
   541 	/**
   547 	/**
   542 	 * Generate WHERE clause to be appended to a main query.
   548 	 * Generates WHERE clause to be appended to a main query.
   543 	 *
   549 	 *
   544 	 * @since 3.7.0
   550 	 * @since 3.7.0
   545 	 *
   551 	 *
   546 	 * @return string MySQL WHERE clause.
   552 	 * @return string MySQL WHERE clause.
   547 	 */
   553 	 */
   560 		 */
   566 		 */
   561 		return apply_filters( 'get_date_sql', $where, $this );
   567 		return apply_filters( 'get_date_sql', $where, $this );
   562 	}
   568 	}
   563 
   569 
   564 	/**
   570 	/**
   565 	 * Generate SQL clauses to be appended to a main query.
   571 	 * Generates SQL clauses to be appended to a main query.
   566 	 *
   572 	 *
   567 	 * Called by the public WP_Date_Query::get_sql(), this method is abstracted
   573 	 * Called by the public WP_Date_Query::get_sql(), this method is abstracted
   568 	 * out to maintain parity with the other Query classes.
   574 	 * out to maintain parity with the other Query classes.
   569 	 *
   575 	 *
   570 	 * @since 4.1.0
   576 	 * @since 4.1.0
   585 
   591 
   586 		return $sql;
   592 		return $sql;
   587 	}
   593 	}
   588 
   594 
   589 	/**
   595 	/**
   590 	 * Generate SQL clauses for a single query array.
   596 	 * Generates SQL clauses for a single query array.
   591 	 *
   597 	 *
   592 	 * If nested subqueries are found, this method recurses the tree to
   598 	 * If nested subqueries are found, this method recurses the tree to
   593 	 * produce the properly nested SQL.
   599 	 * produce the properly nested SQL.
   594 	 *
   600 	 *
   595 	 * @since 4.1.0
   601 	 * @since 4.1.0
   677 	 * compatibility while retaining the naming convention across Query classes.
   683 	 * compatibility while retaining the naming convention across Query classes.
   678 	 *
   684 	 *
   679 	 * @since 3.7.0
   685 	 * @since 3.7.0
   680 	 *
   686 	 *
   681 	 * @param array $query Date query arguments.
   687 	 * @param array $query Date query arguments.
   682 	 * @return string[] {
   688 	 * @return array {
   683 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   689 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   684 	 *
   690 	 *
   685 	 *     @type string $join  SQL fragment to append to the main JOIN clause.
   691 	 *     @type string[] $join  Array of SQL fragments to append to the main JOIN clause.
   686 	 *     @type string $where SQL fragment to append to the main WHERE clause.
   692 	 *     @type string[] $where Array of SQL fragments to append to the main WHERE clause.
   687 	 * }
   693 	 * }
   688 	 */
   694 	 */
   689 	protected function get_sql_for_subquery( $query ) {
   695 	protected function get_sql_for_subquery( $query ) {
   690 		return $this->get_sql_for_clause( $query, '' );
   696 		return $this->get_sql_for_clause( $query, '' );
   691 	}
   697 	}
   693 	/**
   699 	/**
   694 	 * Turns a first-order date query into SQL for a WHERE clause.
   700 	 * Turns a first-order date query into SQL for a WHERE clause.
   695 	 *
   701 	 *
   696 	 * @since 4.1.0
   702 	 * @since 4.1.0
   697 	 *
   703 	 *
       
   704 	 * @global wpdb $wpdb WordPress database abstraction object.
       
   705 	 *
   698 	 * @param array $query        Date query clause.
   706 	 * @param array $query        Date query clause.
   699 	 * @param array $parent_query Parent query of the current date query.
   707 	 * @param array $parent_query Parent query of the current date query.
   700 	 * @return string[] {
   708 	 * @return array {
   701 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   709 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   702 	 *
   710 	 *
   703 	 *     @type string $join  SQL fragment to append to the main JOIN clause.
   711 	 *     @type string[] $join  Array of SQL fragments to append to the main JOIN clause.
   704 	 *     @type string $where SQL fragment to append to the main WHERE clause.
   712 	 *     @type string[] $where Array of SQL fragments to append to the main WHERE clause.
   705 	 * }
   713 	 * }
   706 	 */
   714 	 */
   707 	protected function get_sql_for_clause( $query, $parent_query ) {
   715 	protected function get_sql_for_clause( $query, $parent_query ) {
   708 		global $wpdb;
   716 		global $wpdb;
   709 
   717 
   856 	 * either the maximum or minimum values (controlled by the $default_to parameter). Alternatively you can
   864 	 * either the maximum or minimum values (controlled by the $default_to parameter). Alternatively you can
   857 	 * pass a string that will be passed to date_create().
   865 	 * pass a string that will be passed to date_create().
   858 	 *
   866 	 *
   859 	 * @since 3.7.0
   867 	 * @since 3.7.0
   860 	 *
   868 	 *
   861 	 * @param string|array $datetime       An array of parameters or a strotime() string
   869 	 * @param string|array $datetime       An array of parameters or a strtotime() string.
   862 	 * @param bool         $default_to_max Whether to round up incomplete dates. Supported by values
   870 	 * @param bool         $default_to_max Whether to round up incomplete dates. Supported by values
   863 	 *                                     of $datetime that are arrays, or string values that are a
   871 	 *                                     of $datetime that are arrays, or string values that are a
   864 	 *                                     subset of MySQL date format ('Y', 'Y-m', 'Y-m-d', 'Y-m-d H:i').
   872 	 *                                     subset of MySQL date format ('Y', 'Y-m', 'Y-m-d', 'Y-m-d H:i').
   865 	 *                                     Default: false.
   873 	 *                                     Default: false.
   866 	 * @return string|false A MySQL format date/time or false on failure
   874 	 * @return string|false A MySQL format date/time or false on failure.
   867 	 */
   875 	 */
   868 	public function build_mysql_datetime( $datetime, $default_to_max = false ) {
   876 	public function build_mysql_datetime( $datetime, $default_to_max = false ) {
   869 		if ( ! is_array( $datetime ) ) {
   877 		if ( ! is_array( $datetime ) ) {
   870 
   878 
   871 			/*
   879 			/*
   955 	 * However if multiple values are passed, a pseudo-decimal time will be created
   963 	 * However if multiple values are passed, a pseudo-decimal time will be created
   956 	 * in order to be able to accurately compare against.
   964 	 * in order to be able to accurately compare against.
   957 	 *
   965 	 *
   958 	 * @since 3.7.0
   966 	 * @since 3.7.0
   959 	 *
   967 	 *
       
   968 	 * @global wpdb $wpdb WordPress database abstraction object.
       
   969 	 *
   960 	 * @param string   $column  The column to query against. Needs to be pre-validated!
   970 	 * @param string   $column  The column to query against. Needs to be pre-validated!
   961 	 * @param string   $compare The comparison operator. Needs to be pre-validated!
   971 	 * @param string   $compare The comparison operator. Needs to be pre-validated!
   962 	 * @param int|null $hour    Optional. An hour value (0-23).
   972 	 * @param int|null $hour    Optional. An hour value (0-23).
   963 	 * @param int|null $minute  Optional. A minute value (0-59).
   973 	 * @param int|null $minute  Optional. A minute value (0-59).
   964 	 * @param int|null $second  Optional. A second value (0-59).
   974 	 * @param int|null $second  Optional. A second value (0-59).
  1038 			$time   .= sprintf( '%02d', $second );
  1048 			$time   .= sprintf( '%02d', $second );
  1039 		}
  1049 		}
  1040 
  1050 
  1041 		return $wpdb->prepare( "DATE_FORMAT( $column, %s ) $compare %f", $format, $time );
  1051 		return $wpdb->prepare( "DATE_FORMAT( $column, %s ) $compare %f", $format, $time );
  1042 	}
  1052 	}
       
  1053 
       
  1054 	/**
       
  1055 	 * Sanitizes a 'relation' operator.
       
  1056 	 *
       
  1057 	 * @since 6.0.3
       
  1058 	 *
       
  1059 	 * @param string $relation Raw relation key from the query argument.
       
  1060 	 * @return string Sanitized relation. Either 'AND' or 'OR'.
       
  1061 	 */
       
  1062 	public function sanitize_relation( $relation ) {
       
  1063 		if ( 'OR' === strtoupper( $relation ) ) {
       
  1064 			return 'OR';
       
  1065 		} else {
       
  1066 			return 'AND';
       
  1067 		}
       
  1068 	}
  1043 }
  1069 }