Skip to content

Commit 0bffd2d

Browse files
authored
Merge pull request #2365 from themeum/prepare_where_clause
Added prepare statement when building where clause
2 parents 71187a5 + 26fabbe commit 0bffd2d

3 files changed

Lines changed: 89 additions & 28 deletions

File tree

helpers/QueryHelper.php

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ public static function insert_multiple_rows( $table, $request, $return_ids = fal
252252
* Otherwise the clause would be `WHERE column_name = 'value'`
253253
*
254254
* @since 3.0.0
255+
* @since 3.9.7 added prepared statement for value.
255256
*
256257
* @param array $where The where clause array. e.g. array( 'id', 'IN', array(1, 2, 3) ) or array( 'id', '=', 1 ).
257258
*
@@ -261,8 +262,16 @@ public static function make_clause( array $where ) {
261262
list ( $field, $operator, $value ) = $where;
262263

263264
$upper_operator = strtoupper( $operator );
265+
264266
if ( in_array( $upper_operator, array( 'IN', 'NOT IN' ), true ) ) {
265267
$value = '(' . self::prepare_in_clause( $value ) . ')';
268+
} elseif ( in_array( $upper_operator, array( 'BETWEEN', 'NOT BETWEEN' ), true ) ) {
269+
$value = array_map( fn( $val ) => self::prepare_value( $val ), $value );
270+
$value = implode( ' AND ', $value );
271+
} elseif ( strtoupper( $value ) === 'NULL' ) {
272+
$value = 'NULL';
273+
} else {
274+
$value = self::prepare_value( $value );
266275
}
267276

268277
return "{$field} {$upper_operator} {$value}";
@@ -346,15 +355,13 @@ public static function prepare_where_clause( array $where ) {
346355
case 'BETWEEN':
347356
case 'NOT BETWEEN':
348357
if ( is_array( $val ) && count( $val ) === 2 ) {
349-
$val1 = is_numeric( $val[0] ) ? $val[0] : "'" . $val[0] . "'";
350-
$val2 = is_numeric( $val[1] ) ? $val[1] : "'" . $val[1] . "'";
351-
$clause = array( $field, $operator, "{$val1} AND {$val2}" );
358+
$clause = array( $field, $operator, $val );
352359
}
353360
break;
354361

355362
case 'IS':
356363
case 'IS NOT':
357-
$val = strtoupper( $val ) === 'NULL' ? 'NULL' : "'" . $val . "'";
364+
$val = strtoupper( $val ) === 'NULL' ? 'NULL' : $val;
358365
$clause = array( $field, $operator, $val );
359366
break;
360367
case 'RAW':
@@ -365,16 +372,14 @@ public static function prepare_where_clause( array $where ) {
365372
$clause = $final_query;
366373
break;
367374
default: // =, !=, <, >, <=, >=, LIKE, NOT LIKE, <>
368-
$val = is_numeric( $val ) ? $val : "'" . $val . "'";
369375
$clause = array( $field, $operator, $val );
370376
break;
371377
}
372378
} elseif ( is_array( $value ) ) {
373379
$clause = array( $field, 'IN', $value );
374380
} elseif ( 'null' === strtolower( $value ) ) {
375-
$clause = array( $field, 'IS', 'NULL' );
381+
$clause = array( $field, 'IS', 'NULL' );
376382
} else {
377-
$value = is_numeric( $value ) ? $value : "'" . $value . "'";
378383
$clause = array( $field, '=', $value );
379384
}
380385

@@ -910,32 +915,41 @@ public static function prepare_set_clause( array $data ) {
910915
return rtrim( $set, ',' );
911916
}
912917

918+
/**
919+
* Prepare value before using in query.
920+
*
921+
* @since 3.9.7
922+
*
923+
* @param string|int|float $value the value to prepare.
924+
*
925+
* @return mixed
926+
*/
927+
public static function prepare_value( $value ) {
928+
global $wpdb;
929+
$escaped_value = null;
930+
if ( is_int( $value ) ) {
931+
$escaped_value = $wpdb->prepare( '%d', $value );
932+
} elseif ( is_float( $value ) ) {
933+
list( $whole, $decimal ) = explode( '.', $value );
934+
$expression = '%.'. strlen( $decimal ) . 'f';
935+
$escaped_value = $wpdb->prepare( $expression, $value );
936+
} else {
937+
$escaped_value = $wpdb->prepare( '%s', $value );
938+
}
939+
return $escaped_value;
940+
}
941+
913942
/**
914943
* Make sanitized SQL IN clause value from an array
915944
*
945+
* @since 2.1.1
946+
*
916947
* @param array $arr a sequential array.
948+
*
917949
* @return string
918-
* @since 2.1.1
919950
*/
920951
public static function prepare_in_clause( array $arr ) {
921-
$escaped = array_map(
922-
function( $value ) {
923-
global $wpdb;
924-
$escaped_value = null;
925-
if ( is_int( $value ) ) {
926-
$escaped_value = $wpdb->prepare( '%d', $value );
927-
} else if( is_float( $value ) ) {
928-
list( $whole, $decimal ) = explode( '.', $value );
929-
$expression = '%.'. strlen( $decimal ) . 'f';
930-
$escaped_value = $wpdb->prepare( $expression, $value );
931-
} else {
932-
$escaped_value = $wpdb->prepare( '%s', $value );
933-
}
934-
return $escaped_value;
935-
},
936-
$arr
937-
);
938-
952+
$escaped = array_map( fn( $value ) => self::prepare_value( $value ), $arr );
939953
return implode( ',', $escaped );
940954
}
941955

models/CouponModel.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ public function get_coupon_details_for_checkout( $coupon_code = '' ) {
810810
} else {
811811
$coupon = $this->get_coupon(
812812
array(
813-
'coupon_code' => esc_sql( $coupon_code ),
813+
'coupon_code' => $coupon_code,
814814
'coupon_status' => self::STATUS_ACTIVE,
815815
)
816816
);

tests/phpunit/QueryHelperTest.php

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,53 @@ public function test_prepare_set_clause() {
6868
*
6969
* @return void
7070
*/
71+
public function test_prepare_where_clause() {
72+
73+
$test1 = array(
74+
'age' => 18,
75+
'height' => array( '>=', '5feet7inch' ),
76+
'hobbies' => array( 'coding', 'biking', 'swimming' ),
77+
);
78+
$expect1 = "age = 18 AND height >= '5feet7inch' AND hobbies IN ('coding','biking','swimming')";
79+
$actual1 = QueryHelper::prepare_where_clause( $test1 );
80+
81+
$test2 = array(
82+
'salary' => array( 'BETWEEN', array( 10, 20 ) ),
83+
'name' => array( 'NOT BETWEEN', array( 'b', 'c' ) ),
84+
);
85+
86+
$expect2 = "salary BETWEEN 10 AND 20 AND name NOT BETWEEN 'b' AND 'c'";
87+
$actual2 = QueryHelper::prepare_where_clause( $test2 );
88+
89+
$test3 = array(
90+
'name' => array( 'LIKE', 'test' ),
91+
'age' => array( 'NOT IN', array( 18, 19, 20 ) ),
92+
);
93+
94+
$expect3 = "name LIKE 'test' AND age NOT IN (18,19,20)";
95+
$actual3 = QueryHelper::prepare_where_clause( $test3 );
96+
97+
$test4 = array(
98+
'name' => 'NULL',
99+
'age' => array( 'IS NOT', 'NULL' ),
100+
);
101+
102+
$expect4 = 'name IS NULL AND age IS NOT NULL';
103+
$actual4 = QueryHelper::prepare_where_clause( $test4 );
104+
105+
$this->assertEquals( $expect1, trim( $actual1 ) );
106+
$this->assertEquals( $expect2, trim( $actual2 ) );
107+
$this->assertEquals( $expect3, trim( $actual3 ) );
108+
$this->assertEquals( $expect4, trim( $actual4 ) );
109+
}
110+
111+
/**
112+
* Test QueryHelper Raw query.
113+
*
114+
* @since 3.6.0
115+
*
116+
* @return void
117+
*/
71118
public function test_prepare_raw_query() {
72119
$case_1 = array(
73120
"username = '%s'" => array(
@@ -88,7 +135,7 @@ public function test_prepare_raw_query() {
88135
);
89136

90137
$case_3 = array(
91-
'id' => array(
138+
'id' => array(
92139
'BETWEEN',
93140
array( 10, 20 ),
94141
),

0 commit comments

Comments
 (0)