diff --git a/changelog.txt b/changelog.txt index 3df9a2d7..0bb6e484 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ This file shows the changes in this release of xPDO. +Unreleased +==================================== +- [#54] Fix updateCollection/set() when string value contains SQL operator keywords (e.g. IN, LIKE) + xPDO 3.1.7 (December 4, 2025) ==================================== - PHP 8.5 compatibility (#264) diff --git a/src/xPDO/Om/mysql/xPDOQuery.php b/src/xPDO/Om/mysql/xPDOQuery.php index 38988237..356bcd43 100644 --- a/src/xPDO/Om/mysql/xPDOQuery.php +++ b/src/xPDO/Om/mysql/xPDOQuery.php @@ -78,8 +78,8 @@ public function construct() { foreach ($this->query['set'] as $setKey => $setVal) { $value = $setVal['value']; $type = $setVal['type']; - if ($value !== null && in_array($type, array(\PDO::PARAM_INT, \PDO::PARAM_STR))) { - $value = $this->xpdo->quote($value, $type); + if ($value !== null && ($type === null || in_array($type, array(\PDO::PARAM_INT, \PDO::PARAM_STR)))) { + $value = $this->xpdo->quote($value, $type === null ? \PDO::PARAM_STR : $type); } elseif ($value === null) { $value = 'NULL'; } diff --git a/src/xPDO/Om/pgsql/xPDOQuery.php b/src/xPDO/Om/pgsql/xPDOQuery.php index 095eeec0..80a08a05 100644 --- a/src/xPDO/Om/pgsql/xPDOQuery.php +++ b/src/xPDO/Om/pgsql/xPDOQuery.php @@ -69,8 +69,8 @@ public function construct() { foreach ($this->query['set'] as $setKey => $setVal) { $value = $setVal['value']; $type = $setVal['type']; - if ($value !== null && in_array($type, array(\PDO::PARAM_INT, \PDO::PARAM_STR))) { - $value = $this->xpdo->quote($value, $type); + if ($value !== null && ($type === null || in_array($type, array(\PDO::PARAM_INT, \PDO::PARAM_STR)))) { + $value = $this->xpdo->quote($value, $type === null ? \PDO::PARAM_STR : $type); } elseif ($value === null) { $value = 'NULL'; } diff --git a/src/xPDO/Om/sqlite/xPDOQuery.php b/src/xPDO/Om/sqlite/xPDOQuery.php index f2364402..3e944803 100644 --- a/src/xPDO/Om/sqlite/xPDOQuery.php +++ b/src/xPDO/Om/sqlite/xPDOQuery.php @@ -78,8 +78,8 @@ public function construct() { foreach ($this->query['set'] as $setKey => $setVal) { $value = $setVal['value']; $type = $setVal['type']; - if ($value !== null && in_array($type, array(\PDO::PARAM_INT, \PDO::PARAM_STR))) { - $value = $this->xpdo->quote($value, $type); + if ($value !== null && ($type === null || in_array($type, array(\PDO::PARAM_INT, \PDO::PARAM_STR)))) { + $value = $this->xpdo->quote($value, $type === null ? \PDO::PARAM_STR : $type); } elseif ($value === null) { $value = 'NULL'; } diff --git a/src/xPDO/Om/sqlsrv/xPDOQuery.php b/src/xPDO/Om/sqlsrv/xPDOQuery.php index d191ea95..86c725d4 100644 --- a/src/xPDO/Om/sqlsrv/xPDOQuery.php +++ b/src/xPDO/Om/sqlsrv/xPDOQuery.php @@ -249,8 +249,8 @@ public function construct() { foreach ($this->query['set'] as $setKey => $setVal) { $value = $setVal['value']; $type = $setVal['type']; - if ($value !== null && in_array($type, array(\PDO::PARAM_INT, \PDO::PARAM_STR))) { - $value = $this->xpdo->quote($value, $type); + if ($value !== null && ($type === null || in_array($type, array(\PDO::PARAM_INT, \PDO::PARAM_STR)))) { + $value = $this->xpdo->quote($value, $type === null ? \PDO::PARAM_STR : $type); } elseif ($value === null) { $value = 'NULL'; } diff --git a/src/xPDO/Om/xPDOQuery.php b/src/xPDO/Om/xPDOQuery.php index c4e98907..59307fc3 100644 --- a/src/xPDO/Om/xPDOQuery.php +++ b/src/xPDO/Om/xPDOQuery.php @@ -264,7 +264,7 @@ public function set(array $values) { elseif (!in_array($fieldMeta[$key]['phptype'], $this->_quotable)) { $type= \PDO::PARAM_INT; } - elseif (strpos($value, '(') === false && !$this->isConditionalClause($value)) { + else { $type= \PDO::PARAM_STR; } $this->query['set'][$key]= array('value' => $value, 'type' => $type); diff --git a/test/xPDO/Legacy/Om/xPDOObjectTest.php b/test/xPDO/Legacy/Om/xPDOObjectTest.php index 7d336fbc..7c4779c2 100644 --- a/test/xPDO/Legacy/Om/xPDOObjectTest.php +++ b/test/xPDO/Legacy/Om/xPDOObjectTest.php @@ -630,20 +630,56 @@ public function testUpdateCollection($class, $set, $criteria, $expected) public function providerUpdateCollection() { return array( - array('Person', array('dob' => '2011-08-09'), array('dob:<' => '1951-01-01'), array(1, array())), - array('Person', array('security_level' => 5), array('security_level' => 3), array(1, array())), - array( + 'no matches' => array('Person', array('dob' => '2011-08-09'), array('dob:<' => '1951-01-01'), array(1, array())), + 'integer field' => array('Person', array('security_level' => 5), array('security_level' => 3), array(1, array())), + 'update all records' => array( 'Person', array('date_of_birth' => '2011-09-01'), null, array(2, array(array('date_of_birth' => '2011-09-01'), array('date_of_birth' => '2011-09-01'))) ), - array( + 'null value' => array( 'Person', array('date_of_birth' => null), array('security_level' => 3), array(1, array(array('date_of_birth' => null))) ), + 'string with IN' => array( + 'Person', + array('first_name' => 'The word IN is IN this strINg'), + array('security_level' => 3), + array(1, array(array('first_name' => 'The word IN is IN this strINg'))) + ), + 'string with LIKE' => array( + 'Person', + array('first_name' => 'Something LIKE %test%'), + array('security_level' => 3), + array(1, array(array('first_name' => 'Something LIKE %test%'))) + ), + 'string with BETWEEN' => array( + 'Person', + array('first_name' => 'Value BETWEEN 1 AND 10'), + array('security_level' => 3), + array(1, array(array('first_name' => 'Value BETWEEN 1 AND 10'))) + ), + 'string with equals' => array( + 'Person', + array('first_name' => 'x = y'), + array('security_level' => 3), + array(1, array(array('first_name' => 'x = y'))) + ), + 'empty string' => array( + 'Person', + array('middle_name' => ''), + array('security_level' => 3), + array(1, array(array('middle_name' => ''))) + ), + 'apostrophe SQL injection' => array( + 'Person', + array('first_name' => "O'Brien"), + array('security_level' => 3), + array(1, array(array('first_name' => "O'Brien"))) + ), ); } diff --git a/test/xPDO/Test/Om/xPDOObjectTest.php b/test/xPDO/Test/Om/xPDOObjectTest.php index 2117a2f3..ebaa1b2e 100644 --- a/test/xPDO/Test/Om/xPDOObjectTest.php +++ b/test/xPDO/Test/Om/xPDOObjectTest.php @@ -661,20 +661,56 @@ public function testUpdateCollection($class, $set, $criteria, $expected) public function providerUpdateCollection() { return array( - array('xPDO\\Test\\Sample\\Person', array('dob' => '2011-08-09'), array('dob:<' => '1951-01-01'), array(1, array())), - array('xPDO\\Test\\Sample\\Person', array('security_level' => 5), array('security_level' => 3), array(1, array())), - array( + 'no matches' => array('xPDO\\Test\\Sample\\Person', array('dob' => '2011-08-09'), array('dob:<' => '1951-01-01'), array(1, array())), + 'integer field' => array('xPDO\\Test\\Sample\\Person', array('security_level' => 5), array('security_level' => 3), array(1, array())), + 'update all records' => array( 'xPDO\\Test\\Sample\\Person', array('date_of_birth' => '2011-09-01'), null, array(2, array(array('date_of_birth' => '2011-09-01'), array('date_of_birth' => '2011-09-01'))) ), - array( + 'null value' => array( 'xPDO\\Test\\Sample\\Person', array('date_of_birth' => null), array('security_level' => 3), array(1, array(array('date_of_birth' => null))) ), + 'string with IN' => array( + 'xPDO\\Test\\Sample\\Person', + array('first_name' => 'The word IN is IN this strINg'), + array('security_level' => 3), + array(1, array(array('first_name' => 'The word IN is IN this strINg'))) + ), + 'string with LIKE' => array( + 'xPDO\\Test\\Sample\\Person', + array('first_name' => 'Something LIKE %test%'), + array('security_level' => 3), + array(1, array(array('first_name' => 'Something LIKE %test%'))) + ), + 'string with BETWEEN' => array( + 'xPDO\\Test\\Sample\\Person', + array('first_name' => 'Value BETWEEN 1 AND 10'), + array('security_level' => 3), + array(1, array(array('first_name' => 'Value BETWEEN 1 AND 10'))) + ), + 'string with equals' => array( + 'xPDO\\Test\\Sample\\Person', + array('first_name' => 'x = y'), + array('security_level' => 3), + array(1, array(array('first_name' => 'x = y'))) + ), + 'empty string' => array( + 'xPDO\\Test\\Sample\\Person', + array('middle_name' => ''), + array('security_level' => 3), + array(1, array(array('middle_name' => ''))) + ), + 'apostrophe SQL injection' => array( + 'xPDO\\Test\\Sample\\Person', + array('first_name' => "O'Brien"), + array('security_level' => 3), + array(1, array(array('first_name' => "O'Brien"))) + ), ); }