Skip to content

Commit d815d2e

Browse files
committed
Query: Validate relation in WP_Date_Query.
Props dd32, johnjamesjacoby, martinkrcho, ehtis, paulkevan, peterwilsoncc. git-svn-id: https://develop.svn.wordpress.org/trunk@54530 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 3765886 commit d815d2e

File tree

3 files changed

+224
-2
lines changed

3 files changed

+224
-2
lines changed

src/wp-includes/class-wp-date-query.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ public function __construct( $date_query, $default_column = 'post_date' ) {
150150
return;
151151
}
152152

153-
if ( isset( $date_query['relation'] ) && 'OR' === strtoupper( $date_query['relation'] ) ) {
154-
$this->relation = 'OR';
153+
if ( isset( $date_query['relation'] ) ) {
154+
$this->relation = $this->sanitize_relation( $date_query['relation'] );
155155
} else {
156156
$this->relation = 'AND';
157157
}
@@ -220,6 +220,9 @@ public function sanitize_query( $queries, $parent_query = null ) {
220220
$this->validate_date_values( $queries );
221221
}
222222

223+
// Sanitize the relation parameter.
224+
$queries['relation'] = $this->sanitize_relation( $queries['relation'] );
225+
223226
foreach ( $queries as $key => $q ) {
224227
if ( ! is_array( $q ) || in_array( $key, $this->time_keys, true ) ) {
225228
// This is a first-order query. Trust the values and sanitize when building SQL.
@@ -1041,4 +1044,20 @@ public function build_time_query( $column, $compare, $hour = null, $minute = nul
10411044

10421045
return $wpdb->prepare( "DATE_FORMAT( $column, %s ) $compare %f", $format, $time );
10431046
}
1047+
1048+
/**
1049+
* Sanitizes a 'relation' operator.
1050+
*
1051+
* @since 6.0.3
1052+
*
1053+
* @param string $relation Raw relation key from the query argument.
1054+
* @return string Sanitized relation ('AND' or 'OR').
1055+
*/
1056+
public function sanitize_relation( $relation ) {
1057+
if ( 'OR' === strtoupper( $relation ) ) {
1058+
return 'OR';
1059+
} else {
1060+
return 'AND';
1061+
}
1062+
}
10441063
}

tests/phpunit/tests/date/query.php

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,4 +1145,142 @@ public function test_validate_date_values_should_process_array_value_for_day_whe
11451145
// MySQL ignores the invalid clause.
11461146
$this->assertSame( array( $p1, $p2 ), $q->posts );
11471147
}
1148+
1149+
/**
1150+
* @covers WP_Date_Query::get_sql
1151+
*/
1152+
public function test_relation_in_query_and() {
1153+
$date_query = array(
1154+
'relation' => 'AND',
1155+
array(
1156+
'before' => array(
1157+
'year' => 2021,
1158+
'month' => 9,
1159+
'day' => 20,
1160+
),
1161+
'after' => array(
1162+
'year' => 2019,
1163+
'month' => 2,
1164+
'day' => 25,
1165+
),
1166+
'inclusive' => true,
1167+
),
1168+
array(
1169+
'before' => array(
1170+
'year' => 2016,
1171+
'month' => 9,
1172+
'day' => 11,
1173+
),
1174+
'after' => array(
1175+
'year' => 2014,
1176+
'month' => 5,
1177+
'day' => 12,
1178+
),
1179+
'inclusive' => false,
1180+
),
1181+
);
1182+
1183+
$q = new WP_Date_Query( $date_query );
1184+
1185+
$sql = $q->get_sql();
1186+
1187+
$parts = mb_split( '\)\s+AND\s+\(', $sql );
1188+
$this->assertIsArray( $parts, 'SQL query cannot be split into multiple parts using operator AND.' );
1189+
$this->assertEquals( 2, count( $parts ), 'SQL query does not contain correct number of AND operators.' );
1190+
1191+
$this->assertStringNotContainsString( 'OR', $sql, 'SQL query contains conditions joined by operator OR.' );
1192+
}
1193+
1194+
/**
1195+
* @covers WP_Date_Query::get_sql
1196+
*/
1197+
public function test_relation_in_query_or() {
1198+
$date_query = array(
1199+
'relation' => 'OR',
1200+
array(
1201+
'before' => array(
1202+
'year' => 2021,
1203+
'month' => 9,
1204+
'day' => 20,
1205+
),
1206+
'after' => array(
1207+
'year' => 2019,
1208+
'month' => 2,
1209+
'day' => 25,
1210+
),
1211+
'inclusive' => true,
1212+
),
1213+
array(
1214+
'before' => array(
1215+
'year' => 2016,
1216+
'month' => 9,
1217+
'day' => 11,
1218+
),
1219+
'after' => array(
1220+
'year' => 2014,
1221+
'month' => 5,
1222+
'day' => 12,
1223+
),
1224+
'inclusive' => false,
1225+
),
1226+
);
1227+
1228+
$q = new WP_Date_Query( $date_query );
1229+
1230+
$sql = $q->get_sql();
1231+
1232+
$this->assertStringContainsString( 'OR', $sql, 'SQL query does not contain conditions joined by operator OR.' );
1233+
1234+
$parts = mb_split( '\)\s+OR\s+\(', $sql );
1235+
$this->assertIsArray( $parts, 'SQL query cannot be split into multiple parts using operator OR.' );
1236+
$this->assertEquals( 2, count( $parts ), 'SQL query does not contain correct number of OR operators.' );
1237+
1238+
// Checking number of occurrences of AND while skipping the one at the beginning.
1239+
$this->assertSame( 2, substr_count( substr( $sql, 5 ), 'AND' ), 'SQL query does not contain expected number conditions joined by operator AND.' );
1240+
}
1241+
1242+
/**
1243+
* @covers WP_Date_Query::get_sql
1244+
*/
1245+
public function test_relation_in_query_unsupported() {
1246+
$date_query = array(
1247+
'relation' => 'UNSUPPORTED',
1248+
array(
1249+
'before' => array(
1250+
'year' => 2021,
1251+
'month' => 9,
1252+
'day' => 20,
1253+
),
1254+
'after' => array(
1255+
'year' => 2019,
1256+
'month' => 2,
1257+
'day' => 25,
1258+
),
1259+
'inclusive' => true,
1260+
),
1261+
array(
1262+
'before' => array(
1263+
'year' => 2016,
1264+
'month' => 9,
1265+
'day' => 11,
1266+
),
1267+
'after' => array(
1268+
'year' => 2014,
1269+
'month' => 5,
1270+
'day' => 12,
1271+
),
1272+
'inclusive' => false,
1273+
),
1274+
);
1275+
1276+
$q = new WP_Date_Query( $date_query );
1277+
1278+
$sql = $q->get_sql();
1279+
1280+
$parts = mb_split( '\)\s+AND\s+\(', $sql );
1281+
$this->assertIsArray( $parts, 'SQL query cannot be split into multiple parts using operator AND.' );
1282+
$this->assertEquals( 2, count( $parts ), 'SQL query does not contain correct number of AND operators.' );
1283+
1284+
$this->assertStringNotContainsString( 'OR', $sql, 'SQL query contains conditions joined by operator OR.' );
1285+
}
11481286
}

tests/phpunit/tests/term/taxQuery.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ public function test_get_sql_relation_or_operator_in() {
335335

336336
/**
337337
* @ticket 18105
338+
* @covers WP_Tax_Query::get_sql
338339
*/
339340
public function test_get_sql_relation_and_operator_in() {
340341
register_taxonomy( 'wptests_tax', 'post' );
@@ -381,11 +382,17 @@ public function test_get_sql_relation_and_operator_in() {
381382

382383
$this->assertSame( 3, substr_count( $sql['join'], 'JOIN' ) );
383384

385+
// Checking number of occurrences of AND while skipping the one at the beginning.
386+
$this->assertSame( 2, substr_count( substr( $sql['where'], 5 ), 'AND' ), 'SQL query does not contain expected number conditions joined by operator AND.' );
387+
388+
$this->assertStringNotContainsString( 'OR', $sql['where'], 'SQL query contains conditions joined by operator OR.' );
389+
384390
_unregister_taxonomy( 'wptests_tax' );
385391
}
386392

387393
/**
388394
* @ticket 18105
395+
* @covers WP_Tax_Query::get_sql
389396
*/
390397
public function test_get_sql_nested_relation_or_operator_in() {
391398
register_taxonomy( 'wptests_tax', 'post' );
@@ -434,6 +441,8 @@ public function test_get_sql_nested_relation_or_operator_in() {
434441
$sql = $tq->get_sql( $wpdb->posts, 'ID' );
435442

436443
$this->assertSame( 2, substr_count( $sql['join'], 'JOIN' ) );
444+
$this->assertSame( 2, substr_count( $sql['where'], 'OR' ), 'SQL query does not contain expected number conditions joined by operator OR.' );
445+
$this->assertStringNotContainsString( 'AND', substr( $sql['where'], 5 ), 'SQL query contains conditions joined by operator AND.' );
437446

438447
_unregister_taxonomy( 'wptests_tax' );
439448
}
@@ -495,4 +504,60 @@ public function test_get_sql_operator_and_empty_terms() {
495504

496505
_unregister_taxonomy( 'wptests_tax' );
497506
}
507+
508+
/**
509+
* @ticket 18105
510+
* @covers WP_Tax_Query::get_sql
511+
*/
512+
public function test_get_sql_relation_unsupported() {
513+
register_taxonomy( 'wptests_tax', 'post' );
514+
515+
$t1 = self::factory()->term->create(
516+
array(
517+
'taxonomy' => 'wptests_tax',
518+
)
519+
);
520+
$t2 = self::factory()->term->create(
521+
array(
522+
'taxonomy' => 'wptests_tax',
523+
)
524+
);
525+
$t3 = self::factory()->term->create(
526+
array(
527+
'taxonomy' => 'wptests_tax',
528+
)
529+
);
530+
531+
$tq = new WP_Tax_Query(
532+
array(
533+
'relation' => 'UNSUPPORTED',
534+
array(
535+
'taxonomy' => 'wptests_tax',
536+
'field' => 'term_id',
537+
'terms' => $t1,
538+
),
539+
array(
540+
'taxonomy' => 'wptests_tax',
541+
'field' => 'term_id',
542+
'terms' => $t2,
543+
),
544+
array(
545+
'taxonomy' => 'wptests_tax',
546+
'field' => 'term_id',
547+
'terms' => $t3,
548+
),
549+
)
550+
);
551+
552+
global $wpdb;
553+
$sql = $tq->get_sql( $wpdb->posts, 'ID' );
554+
555+
// Checking number of occurrences of AND while skipping the one at the beginning.
556+
$this->assertSame( 2, substr_count( substr( $sql['where'], 5 ), 'AND' ), 'SQL query does not contain expected number conditions joined by operator AND.' );
557+
558+
$this->assertStringNotContainsString( 'UNSUPPORTED', $sql['where'], 'SQL query contains unsupported relation operator.' );
559+
$this->assertStringNotContainsString( 'OR', $sql['where'], 'SQL query contains conditions joined by operator OR.' );
560+
561+
_unregister_taxonomy( 'wptests_tax' );
562+
}
498563
}

0 commit comments

Comments
 (0)