Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 40 additions & 26 deletions helpers/QueryHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
);

if ( $wpdb->last_error ) {
throw new \Exception( $wpdb->last_error );

Check failure on line 44 in helpers/QueryHelper.php

View workflow job for this annotation

GitHub Actions / WPCS

All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found '$wpdb'.
}

return $insert ? $wpdb->insert_id : 0;
Expand Down Expand Up @@ -144,7 +144,7 @@
$wpdb->query( "DELETE FROM {$table} WHERE id IN ( $ids )");

if ( $wpdb->last_error ) {
throw new \Exception( $wpdb->last_error );

Check failure on line 147 in helpers/QueryHelper.php

View workflow job for this annotation

GitHub Actions / WPCS

All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found '$wpdb'.
}

return true;
Expand Down Expand Up @@ -229,7 +229,7 @@

// If error occurred then throw new exception.
if ( $wpdb->last_error ) {
throw new \Exception( $wpdb->last_error );

Check failure on line 232 in helpers/QueryHelper.php

View workflow job for this annotation

GitHub Actions / WPCS

All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found '$wpdb'.
}

if ( $return_ids ) {
Expand All @@ -252,6 +252,7 @@
* Otherwise the clause would be `WHERE column_name = 'value'`
*
* @since 3.0.0
* @since 3.9.7 added prepared statement for value.
*
* @param array $where The where clause array. e.g. array( 'id', 'IN', array(1, 2, 3) ) or array( 'id', '=', 1 ).
*
Expand All @@ -261,8 +262,16 @@
list ( $field, $operator, $value ) = $where;

$upper_operator = strtoupper( $operator );

if ( in_array( $upper_operator, array( 'IN', 'NOT IN' ), true ) ) {
$value = '(' . self::prepare_in_clause( $value ) . ')';
} elseif ( in_array( $upper_operator, array( 'BETWEEN', 'NOT BETWEEN' ), true ) ) {
$value = array_map( fn( $val ) => self::prepare_value( $val ), $value );
$value = implode( ' AND ', $value );
} elseif ( strtoupper( $value ) === 'NULL' ) {
$value = 'NULL';
} else {
$value = self::prepare_value( $value );
}

return "{$field} {$upper_operator} {$value}";
Expand Down Expand Up @@ -346,15 +355,13 @@
case 'BETWEEN':
case 'NOT BETWEEN':
if ( is_array( $val ) && count( $val ) === 2 ) {
$val1 = is_numeric( $val[0] ) ? $val[0] : "'" . $val[0] . "'";
$val2 = is_numeric( $val[1] ) ? $val[1] : "'" . $val[1] . "'";
$clause = array( $field, $operator, "{$val1} AND {$val2}" );
$clause = array( $field, $operator, $val );
}
break;

case 'IS':
case 'IS NOT':
$val = strtoupper( $val ) === 'NULL' ? 'NULL' : "'" . $val . "'";
$val = strtoupper( $val ) === 'NULL' ? 'NULL' : $val;
$clause = array( $field, $operator, $val );
break;
case 'RAW':
Expand All @@ -365,16 +372,14 @@
$clause = $final_query;
break;
default: // =, !=, <, >, <=, >=, LIKE, NOT LIKE, <>
$val = is_numeric( $val ) ? $val : "'" . $val . "'";
$clause = array( $field, $operator, $val );
break;
}
} elseif ( is_array( $value ) ) {
$clause = array( $field, 'IN', $value );
} elseif ( 'null' === strtolower( $value ) ) {
$clause = array( $field, 'IS', 'NULL' );
$clause = array( $field, 'IS', 'NULL' );
} else {
$value = is_numeric( $value ) ? $value : "'" . $value . "'";
$clause = array( $field, '=', $value );
}

Expand Down Expand Up @@ -910,32 +915,41 @@
return rtrim( $set, ',' );
}

/**
* Prepare value before using in query.
*
* @since 3.9.7
*
* @param string|int|float $value the value to prepare.
*
* @return mixed
*/
public static function prepare_value( $value ) {
global $wpdb;
$escaped_value = null;
if ( is_int( $value ) ) {
$escaped_value = $wpdb->prepare( '%d', $value );
} elseif ( is_float( $value ) ) {
list( $whole, $decimal ) = explode( '.', $value );
$expression = '%.'. strlen( $decimal ) . 'f';
$escaped_value = $wpdb->prepare( $expression, $value );
} else {
$escaped_value = $wpdb->prepare( '%s', $value );
}
return $escaped_value;
}

/**
* Make sanitized SQL IN clause value from an array
*
* @since 2.1.1
*
* @param array $arr a sequential array.
*
* @return string
* @since 2.1.1
*/
public static function prepare_in_clause( array $arr ) {
$escaped = array_map(
function( $value ) {
global $wpdb;
$escaped_value = null;
if ( is_int( $value ) ) {
$escaped_value = $wpdb->prepare( '%d', $value );
} else if( is_float( $value ) ) {
list( $whole, $decimal ) = explode( '.', $value );
$expression = '%.'. strlen( $decimal ) . 'f';
$escaped_value = $wpdb->prepare( $expression, $value );
} else {
$escaped_value = $wpdb->prepare( '%s', $value );
}
return $escaped_value;
},
$arr
);

$escaped = array_map( fn( $value ) => self::prepare_value( $value ), $arr );
return implode( ',', $escaped );
}

Expand Down
2 changes: 1 addition & 1 deletion models/CouponModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@
try {
return QueryHelper::insert( $this->table_name, $data );
} catch ( \Throwable $th ) {
throw new \Exception( $th->getMessage() );

Check failure on line 326 in models/CouponModel.php

View workflow job for this annotation

GitHub Actions / WPCS

All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found '$th'.
}
}

Expand Down Expand Up @@ -810,7 +810,7 @@
} else {
$coupon = $this->get_coupon(
array(
'coupon_code' => esc_sql( $coupon_code ),
'coupon_code' => $coupon_code,
'coupon_status' => self::STATUS_ACTIVE,
)
);
Expand Down
49 changes: 48 additions & 1 deletion tests/phpunit/QueryHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,53 @@ public function test_prepare_set_clause() {
*
* @return void
*/
public function test_prepare_where_clause() {

$test1 = array(
'age' => 18,
'height' => array( '>=', '5feet7inch' ),
'hobbies' => array( 'coding', 'biking', 'swimming' ),
);
$expect1 = "age = 18 AND height >= '5feet7inch' AND hobbies IN ('coding','biking','swimming')";
$actual1 = QueryHelper::prepare_where_clause( $test1 );

$test2 = array(
'salary' => array( 'BETWEEN', array( 10, 20 ) ),
'name' => array( 'NOT BETWEEN', array( 'b', 'c' ) ),
);

$expect2 = "salary BETWEEN 10 AND 20 AND name NOT BETWEEN 'b' AND 'c'";
$actual2 = QueryHelper::prepare_where_clause( $test2 );

$test3 = array(
'name' => array( 'LIKE', 'test' ),
'age' => array( 'NOT IN', array( 18, 19, 20 ) ),
);

$expect3 = "name LIKE 'test' AND age NOT IN (18,19,20)";
$actual3 = QueryHelper::prepare_where_clause( $test3 );

$test4 = array(
'name' => 'NULL',
'age' => array( 'IS NOT', 'NULL' ),
);

$expect4 = 'name IS NULL AND age IS NOT NULL';
$actual4 = QueryHelper::prepare_where_clause( $test4 );

$this->assertEquals( $expect1, trim( $actual1 ) );
$this->assertEquals( $expect2, trim( $actual2 ) );
$this->assertEquals( $expect3, trim( $actual3 ) );
$this->assertEquals( $expect4, trim( $actual4 ) );
}

/**
* Test QueryHelper Raw query.
*
* @since 3.6.0
*
* @return void
*/
public function test_prepare_raw_query() {
$case_1 = array(
"username = '%s'" => array(
Expand All @@ -88,7 +135,7 @@ public function test_prepare_raw_query() {
);

$case_3 = array(
'id' => array(
'id' => array(
'BETWEEN',
array( 10, 20 ),
),
Expand Down
Loading