Ticket #18694: 18694.patch

File 18694.patch, 13.5 KB (added by Viper007Bond, 15 months 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.