WordPress.org

Make WordPress Core

Ticket #18694: 18694.patch

File 18694.patch, 13.5 KB (added by Viper007Bond, 8 years ago)

Possibly good to go, minus phpdoc. Needs testing!

  • wp-includes/query.php

     
    869869        var $meta_query = false;
    870870
    871871        /**
     872         * Date query container
     873         *
     874         * @since 3.5.0
     875         * @access public
     876         * @var object WP_Date_Query
     877         */
     878        var $date_query = false;
     879
     880        /**
    872881         * Holds the data for a single object that is queried.
    873882         *
    874883         * Holds the contents of a post, page, category, attachment.
     
    20562065                                $where .= " AND SECOND($wpdb->posts.post_date)=" . substr($q['m'], 12, 2);
    20572066                }
    20582067
     2068                // Handle individual date parameters
     2069                $date_parameters = array();
     2070
    20592071                if ( '' !== $q['hour'] )
    2060                         $where .= " AND HOUR($wpdb->posts.post_date)='" . $q['hour'] . "'";
     2072                        $date_parameters['hour'] = $q['hour'];
    20612073
    20622074                if ( '' !== $q['minute'] )
    2063                         $where .= " AND MINUTE($wpdb->posts.post_date)='" . $q['minute'] . "'";
     2075                        $date_parameters['minute'] = $q['minute'];
    20642076
    20652077                if ( '' !== $q['second'] )
    2066                         $where .= " AND SECOND($wpdb->posts.post_date)='" . $q['second'] . "'";
     2078                        $date_parameters['second'] = $q['second'];
    20672079
    20682080                if ( $q['year'] )
    2069                         $where .= " AND YEAR($wpdb->posts.post_date)='" . $q['year'] . "'";
     2081                        $date_parameters['year'] = $q['year'];
    20702082
    20712083                if ( $q['monthnum'] )
    2072                         $where .= " AND MONTH($wpdb->posts.post_date)='" . $q['monthnum'] . "'";
     2084                        $date_parameters['month'] = $q['monthnum'];
    20732085
    20742086                if ( $q['day'] )
    2075                         $where .= " AND DAYOFMONTH($wpdb->posts.post_date)='" . $q['day'] . "'";
     2087                        $date_parameters['day'] = $q['day'];
    20762088
     2089                if ( $q['w'] )
     2090                        $date_parameters['week'] = $q['w'];
     2091
     2092                if ( ! empty( $date_parameters ) ) {
     2093                        $date_query = new WP_Date_Query( array( $date_parameters ) );
     2094                        $where .= $date_query->get_sql();
     2095                }
     2096                unset( $date_parameters );
     2097
     2098                // Handle complex date queries
     2099                if ( ! empty( $q['date_query'] ) ) {
     2100                        $this->date_query = new WP_Date_Query( $q['date_query'] );
     2101                        $where .= $this->date_query->get_sql();
     2102                }
     2103
    20772104                // If we've got a post_type AND its not "any" post_type.
    20782105                if ( !empty($q['post_type']) && 'any' != $q['post_type'] ) {
    20792106                        foreach ( (array)$q['post_type'] as $_post_type ) {
     
    21422169                        $where .= " AND $wpdb->posts.post_name = '" . $q['attachment'] . "'";
    21432170                }
    21442171
    2145                 if ( $q['w'] )
    2146                         $where .= ' AND ' . _wp_mysql_week( "`$wpdb->posts`.`post_date`" ) . " = '" . $q['w'] . "'";
    21472172
    21482173                if ( intval($q['comments_popup']) )
    21492174                        $q['p'] = absint($q['comments_popup']);
     
    34873512}
    34883513
    34893514/**
     3515 * Container class for a date query
     3516 *
     3517 * @since 3.5.0
     3518 */
     3519class WP_Date_Query {
     3520        /**
     3521        * List of date queries. A single query is an associative array:
     3522        *
     3523        * TODO: DOCUMENT AVAILABLE VARS
     3524        *
     3525        * @since 3.5.0
     3526        * @access public
     3527        * @var array
     3528        */
     3529        public $queries = array();
     3530
     3531        /**
     3532         * The relation between the queries. Can be one of 'AND' or 'OR'.
     3533         *
     3534         * @since 3.5.0
     3535         * @access public
     3536         * @var string
     3537         */
     3538        public $relation;
     3539
     3540        /**
     3541         * The column to query against. Can be changed via the query arguments.
     3542         *
     3543         * @since 3.5.0
     3544         * @access public
     3545         * @var string
     3546         */
     3547        public $column = 'post_date';
     3548
     3549        /**
     3550         * The value comparison operator. Can be changed via the query arguments.
     3551         *
     3552         * @since 3.5.0
     3553         * @access public
     3554         * @var array
     3555         */
     3556        public $compare = '=';
     3557
     3558        /**
     3559         * Constructor
     3560         *
     3561         * @param array (optional) $date_query A date query parameter array
     3562         */
     3563        function __construct( $date_query = false ) {
     3564                if ( empty( $date_query ) || ! is_array( $date_query ) )
     3565                        return false;
     3566
     3567                if ( isset( $date_query['relation'] ) && strtoupper( $date_query['relation'] ) == 'OR' ) {
     3568                        $this->relation = 'OR';
     3569                } else {
     3570                        $this->relation = 'AND';
     3571                }
     3572
     3573                $this->column  = $this->get_column( $date_query );
     3574                $this->compare = $this->get_compare( $date_query );
     3575
     3576                // If an array of arrays wasn't passed, fix it
     3577                if ( ! isset( $date_query[0] ) )
     3578                        $date_query = array( $date_query );
     3579
     3580                $this->queries = array();
     3581                foreach ( $date_query as $key => $query ) {
     3582                        if ( ! is_array( $query ) )
     3583                                continue;
     3584
     3585                        $this->queries[$key] = $query;
     3586                }
     3587        }
     3588
     3589        /**
     3590         * Determines and validates what database column to query against.
     3591         *
     3592         * @since 3.5.0
     3593         * @access public
     3594         *
     3595         * @param array $query A date query or a date subquery
     3596         * @return string The column to query against
     3597         */
     3598        public function get_column( $query ) {
     3599                if ( isset( $query['column'] ) && in_array( $query['column'], array( 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt' ) ) )
     3600                        return strtolower( $query['column'] );
     3601
     3602                return $this->column;
     3603        }
     3604
     3605        /**
     3606         * Determines and validates what comparison operator to use.
     3607         *
     3608         * @since 3.5.0
     3609         * @access public
     3610         *
     3611         * @param array $query A date query or a date subquery
     3612         * @return string The comparison operator
     3613         */
     3614        public function get_compare( $query ) {
     3615                if ( ! empty( $query['compare'] ) && in_array( $query['compare'], array( '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) )
     3616                        return strtoupper( $query['compare'] );
     3617
     3618                return $this->compare;
     3619        }
     3620
     3621        /**
     3622         * Turns an array of date query parameters into a MySQL string.
     3623         *
     3624         * @since 3.5.0
     3625         * @access public
     3626         *
     3627         * @return string MySQL WHERE parameters
     3628         */
     3629        public function get_sql() {
     3630                global $wpdb;
     3631
     3632                // The parts of the final query
     3633                $where = array();
     3634
     3635                foreach ( $this->queries as $key => $query ) {
     3636
     3637                        // The parts of a $where part (Queryception!)
     3638                        $where_parts = array();
     3639
     3640                        // Figure out what column to use in this subquery, defaulting to the overall column
     3641                        $column = $wpdb->posts . '.' . $this->get_column( $query );
     3642
     3643                        // Range queries
     3644                        if ( ! empty( $query['after'] ) || ! empty( $query['before'] ) ) {
     3645                                $lt = '<';
     3646                                $gt = '>';
     3647                                if ( ! empty( $query['inclusive'] ) ) {
     3648                                        $lt .= '=';
     3649                                        $gt .= '=';
     3650                                }
     3651
     3652                                if ( ! empty( $query['after'] ) )
     3653                                        $where_parts[] = $wpdb->prepare( "$column $gt %s", $this->build_mysql_datetime( $query['after'] ) );
     3654                               
     3655                                if ( ! empty( $query['before'] ) )
     3656                                        $where_parts[] = $wpdb->prepare( "$column $lt %s", $this->build_mysql_datetime( $query['before'] ) );
     3657                        }
     3658
     3659                        // Specific value queries
     3660                        else {
     3661                                $compare = $this->get_compare( $query );
     3662
     3663                                if ( isset( $query['year'] ) && $value = $this->build_value( $compare, $query['year'] ) )
     3664                                        $where_parts[] = "YEAR( $column ) $compare $value";
     3665
     3666                                if ( isset( $query['month'] ) && $value = $this->build_value( $compare, $query['month'] ) )
     3667                                        $where_parts[] = "MONTH( $column ) $compare $value";
     3668
     3669                                // Legacy
     3670                                if ( isset( $query['monthnum'] ) && $value = $this->build_value( $compare, $query['monthnum'] ) )
     3671                                        $where_parts[] = "MONTH( $column ) $compare $value";
     3672
     3673                                if ( isset( $query['week'] ) && false !== ( $value = $this->build_value( $compare, $query['week'] ) ) )
     3674                                        $where_parts[] = _wp_mysql_week( $column ) . " $compare $value";
     3675
     3676                                // Legacy
     3677                                if ( isset( $query['w'] ) && false !== ( $value = $this->build_value( $compare, $query['w'] ) ) )
     3678                                        $where_parts[] = _wp_mysql_week( $column ) . " $compare $value";
     3679
     3680                                if ( isset( $query['dayofyear'] ) && $value = $this->build_value( $compare, $query['dayofyear'] ) )
     3681                                        $where_parts[] = "DAYOFYEAR( $column ) $compare $value";
     3682
     3683                                if ( isset( $query['day'] ) && $value = $this->build_value( $compare, $query['day'] ) )
     3684                                        $where_parts[] = "DAYOFMONTH( $column ) $compare $value";
     3685
     3686                                if ( isset( $query['dayofweek'] ) && $value = $this->build_value( $compare, $query['dayofweek'] ) )
     3687                                        $where_parts[] = "DAYOFWEEK( $column ) $compare $value";
     3688
     3689                                if ( isset( $query['hour'] ) || isset( $query['minute'] ) || isset( $query['second'] ) ) {
     3690                                        // Avoid notices
     3691                                        foreach ( array( 'hour', 'minute', 'second' ) as $unit ) {
     3692                                                if ( ! isset( $query[$unit] ) ) {
     3693                                                        $query[$unit] = null;
     3694                                                }
     3695                                        }
     3696
     3697                                        if ( $time_query = $this->build_time_query( $column, $compare, $query['hour'], $query['minute'], $query['second'] ) ) {
     3698                                                $where_parts[] = $time_query;
     3699                                        }
     3700                                }
     3701                        }
     3702
     3703                        // Combine the parts of this subquery into a single string
     3704                        if ( ! empty( $where_parts ) )
     3705                                $where[$key] = '( ' . implode( ' AND ', $where_parts ) . ' )';
     3706                }
     3707
     3708                // Combine the subquery strings into a single string
     3709                if ( ! empty( $where ) )
     3710                        $where = ' AND ( ' . implode( " {$this->relation} ", $where ) . ' )';
     3711                else
     3712                        $where = '';
     3713
     3714                return apply_filters( 'get_date_sql', $where, $this->queries );
     3715        }
     3716
     3717        /**
     3718         * Builds and validates a value string based on the comparison operator.
     3719         *
     3720         * @since 3.5.0
     3721         * @access public
     3722         *
     3723         * @param string $compare The compare operator to use
     3724         * @param string|array $value The value
     3725         * @return string|int The value to be used in SQL
     3726         */
     3727        public function build_value( $compare, $value ) {
     3728                if ( ! isset( $value ) )
     3729                        return false;
     3730
     3731                switch ( $compare ) {
     3732                        case 'IN':
     3733                        case 'NOT IN':
     3734                                return '(' . implode( ',', array_map( 'intval', (array) $value ) ) . ')';
     3735
     3736                        case 'BETWEEN':
     3737                        case 'NOT BETWEEN':
     3738                                if ( ! is_array( $value ) || 2 != count( $value ) || ! isset( $value[0] ) || ! isset( $value[1] ) )
     3739                                        $value = array( $value, $value );
     3740
     3741                                $value = array_map( 'intval', $value );
     3742
     3743                                return $value[0] . ' AND ' . $value[1];
     3744
     3745                        default;
     3746                                return (int) $value;
     3747                }
     3748        }
     3749
     3750        /**
     3751         * Builds a MySQL format date/time based on some query parameters.
     3752         *
     3753         * You can pass an array of values (year, month, etc.) with missing parameter values being defaulted to 0
     3754         * or you can pass a string that will be run through strtotime().
     3755         *
     3756         * @since 3.5.0
     3757         * @access public
     3758         *
     3759         * @param string|array $datetime An array of parameters or a strotime() string
     3760         * @return string|false A MySQL format date/time or false on failure
     3761         */
     3762        public function build_mysql_datetime( $datetime ) {
     3763                $now = current_time( 'timestamp' );
     3764
     3765                if ( is_array( $datetime ) ) {
     3766                        if ( ! isset( $datetime['year'] ) )
     3767                                $datetime['year'] = gmdate( 'Y', $now );
     3768
     3769                        if ( ! isset( $datetime['month'] ) )
     3770                                $datetime['month'] = 1;
     3771
     3772                        if ( ! isset( $datetime['day'] ) )
     3773                                $datetime['day'] = 1;
     3774
     3775                        if ( ! isset( $datetime['hour'] ) )
     3776                                $datetime['hour'] = 0;
     3777
     3778                        if ( ! isset( $datetime['minute'] ) )
     3779                                $datetime['minute'] = 0;
     3780
     3781                        if ( ! isset( $datetime['second'] ) )
     3782                                $datetime['second'] = 0;
     3783
     3784                        $datetime = array_map( 'absint', $datetime );
     3785
     3786                        return sprintf( '%04d-%02d-%02d %02d:%02d:%02d', $datetime['year'], $datetime['month'], $datetime['day'], $datetime['hour'], $datetime['minute'], $datetime['second'] );
     3787                } else {
     3788                        // Timezone issues here possibly
     3789                        return gmdate( 'Y-m-d H:i:s', strtotime( $datetime, $now ) );
     3790                }
     3791
     3792                return false;
     3793        }
     3794
     3795        /**
     3796         * Builds a query string for comparing time values (hour, minute, second).
     3797         *
     3798         * If just hour, minute, or second is set than a normal comparison will be done.
     3799         * However if multiple values are passed, a pseudo-decimal time will be created
     3800         * in order to be able to accurately compare against.
     3801         *
     3802         * @since 3.5.0
     3803         * @access public
     3804         *
     3805         * @param string $column The column to query against. Needs to be pre-validated!
     3806         * @param string $compare The comparison operator. Needs to be pre-validated!
     3807         * @param int|null $hour Optional. An hour value (0-23).
     3808         * @param int|null $minute Optional. A minute value (0-59).
     3809         * @param int|null $second Optional. A second value (0-59).
     3810         * @return string|false A query part or false on failure.
     3811         */
     3812        public function build_time_query( $column, $compare, $hour = null, $minute = null, $second = null ) {
     3813                global $wpdb;
     3814
     3815                // Have to have at least one
     3816                if ( ! isset( $hour ) && ! isset( $minute ) && ! isset( $second ) )
     3817                        return false;
     3818
     3819                // Complex combined queries aren't supported for multi-value queries
     3820                if ( in_array( $compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {
     3821                        $return = array();
     3822
     3823                        if ( isset( $hour ) && false !== ( $value = $this->build_value( $compare, $hour ) ) )
     3824                                $return[] = "HOUR( $column ) $compare $value";
     3825
     3826                        if ( isset( $minute ) && false !== ( $value = $this->build_value( $compare, $minute ) ) )
     3827                                $return[] = "MINUTE( $column ) $compare $value";
     3828
     3829                        if ( isset( $second ) && false !== ( $value = $this->build_value( $compare, $second ) ) )
     3830                                $return[] = "SECOND( $column ) $compare $value";
     3831
     3832                        return implode( ' AND ', $return );
     3833                }
     3834
     3835                // Cases where just one unit is set
     3836                if ( isset( $hour ) && ! isset( $minute ) && ! isset( $second ) && false !== ( $value = $this->build_value( $compare, $hour ) ) ) {
     3837                        return "HOUR( $column ) $compare $value";
     3838                } elseif ( ! isset( $hour ) && isset( $minute ) && ! isset( $second ) && false !== ( $value = $this->build_value( $compare, $minute ) ) ) {
     3839                        return "MINUTE( $column ) $compare $value";
     3840                } elseif ( ! isset( $hour ) && ! isset( $minute ) && isset( $second ) && false !== ( $value = $this->build_value( $compare, $second ) ) ) {
     3841                        return "SECOND( $column ) $compare $value";
     3842                }
     3843
     3844                // Single units were already handled. Since hour & second isn't allowed, minute must to be set.
     3845                if ( ! isset( $minute ) )
     3846                        return false;
     3847
     3848                $format = $time = '';
     3849
     3850                // Hour
     3851                if ( $hour ) {
     3852                        $format .= '%H.';
     3853                        $time   .= sprintf( '%02d', $hour ) . '.';
     3854                } else {
     3855                        $format .= '0.';
     3856                        $time   .= '0.';
     3857                }
     3858
     3859                // Minute
     3860                $format .= '%i';
     3861                $time   .= sprintf( '%02d', $minute );
     3862
     3863                if ( isset( $second ) ) {
     3864                        $format .= '%s';
     3865                        $time   .= sprintf( '%02d', $second );
     3866                }
     3867
     3868                return $wpdb->prepare( "DATE_FORMAT( $column, %s ) $compare %f", $format, $time );
     3869        }
     3870}
     3871
     3872/**
    34903873 * Redirect old slugs to the correct permalink.
    34913874 *
    34923875 * Attempts to find the current slug from the past slugs.