diff --git a/CRM/Contact/BAO/Query.php b/CRM/Contact/BAO/Query.php index 58b54ca1dfe941bab79df43037b11a354f34fa3c..cbe7debf28a06ac4cb131785994ffe8c30ad6dfc 100644 --- a/CRM/Contact/BAO/Query.php +++ b/CRM/Contact/BAO/Query.php @@ -5668,7 +5668,10 @@ civicrm_relationship.start_date > {$today} } throw new CRM_Core_Exception(ts('Failed to interpret input for search')); } - + $emojiWhere = CRM_Utils_SQL::handleEmojiInQuery($value); + if ($emojiWhere === '0 = 1') { + $value = $emojiWhere; + } $value = CRM_Utils_Type::escape($value, $dataType); // if we don't have a dataType we should assume if ($dataType == 'String' || $dataType == 'Text') { diff --git a/CRM/Core/DAO.php b/CRM/Core/DAO.php index 7ac51a518f715c4e4d9fae150d7f58560ba657b9..5bb34367c713f0f0d8045ec04c2e7a186fed7db7 100644 --- a/CRM/Core/DAO.php +++ b/CRM/Core/DAO.php @@ -2881,20 +2881,9 @@ SELECT contact_id */ public static function createSQLFilter($fieldName, $filter, $type = NULL, $alias = NULL, $returnSanitisedArray = FALSE) { foreach ($filter as $operator => $criteria) { - if (!CRM_Core_BAO_SchemaHandler::databaseSupportsUTF8MB4()) { - foreach ((array) $criteria as $criterion) { - if (!empty($criterion) && !is_numeric($criterion) - // The first 2 criteria are redundant but are added as they - // seem like they would - // be quicker than this 3rd check. - && max(array_map('ord', str_split($criterion))) >= 240) { - // String contains unsupported emojis. - // We return a clause that resolves to false as an emoji string by definition cannot be saved. - // note that if we return just 0 for false if gets lost in empty checks. - // https://stackoverflow.com/questions/16496554/can-php-detect-4-byte-encoded-utf8-chars - return '0 = 1'; - } - } + $emojiFilter = CRM_Utils_SQL::handleEmojiInQuery($criteria); + if ($emojiFilter === '0 = 1') { + return $emojiFilter; } if (in_array($operator, self::acceptedSQLOperators(), TRUE)) { diff --git a/CRM/Utils/SQL.php b/CRM/Utils/SQL.php index 3b9167e6265f5301a81023024ff084d4134dbce2..1ca298e96ad37d881c25c64cde4aeed53709bb33 100644 --- a/CRM/Utils/SQL.php +++ b/CRM/Utils/SQL.php @@ -203,4 +203,30 @@ class CRM_Utils_SQL { return $dsn; } + /** + * Filter out Emojis in where clause if the database (determined by checking the create table for civicrm_contact) + * cannot support emojis + * @param mixed $criteria - filter criteria to check + * + * @return bool|string + */ + public static function handleEmojiInQuery($criteria) { + if (!CRM_Core_BAO_SchemaHandler::databaseSupportsUTF8MB4()) { + foreach ((array) $criteria as $criterion) { + if (!empty($criterion) && !is_numeric($criterion) + // The first 2 criteria are redundant but are added as they + // seem like they would + // be quicker than this 3rd check. + && max(array_map('ord', str_split($criterion))) >= 240) { + // String contains unsupported emojis. + // We return a clause that resolves to false as an emoji string by definition cannot be saved. + // note that if we return just 0 for false if gets lost in empty checks. + // https://stackoverflow.com/questions/16496554/can-php-detect-4-byte-encoded-utf8-chars + return '0 = 1'; + } + } + return TRUE; + } + } + } diff --git a/tests/phpunit/api/v3/ContactTest.php b/tests/phpunit/api/v3/ContactTest.php index 79b3e54caf4c4809b26ec9c36064e627d42662dc..6eb0b086247ff0f5932463e130e0bac46ea36ee3 100644 --- a/tests/phpunit/api/v3/ContactTest.php +++ b/tests/phpunit/api/v3/ContactTest.php @@ -5196,11 +5196,27 @@ class api_v3_ContactTest extends CiviUnitTestCase { * out when we are. */ public function testEmojiInWhereClause(): void { + $schemaNeedsAlter = \CRM_Core_BAO_SchemaHandler::databaseSupportsUTF8MB4(); + if ($schemaNeedsAlter) { + CRM_Core_DAO::executeQuery(" + ALTER TABLE civicrm_contact MODIFY COLUMN + `first_name` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'First Name.', + CHARSET utf8 COLLATE utf8_unicode_ci + "); + Civi::$statics['CRM_Core_BAO_SchemaHandler'] = []; + } $this->callAPISuccess('Contact', 'get', [ 'debug' => 1, 'first_name' => '🦉Claire', - 'version' => 4, ]); + if ($schemaNeedsAlter) { + CRM_Core_DAO::executeQuery(" + ALTER TABLE civicrm_contact MODIFY COLUMN + `first_name` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'First Name.', + CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci + "); + Civi::$statics['CRM_Core_BAO_SchemaHandler'] = []; + } } /** diff --git a/tests/phpunit/api/v4/Action/ContactGetTest.php b/tests/phpunit/api/v4/Action/ContactGetTest.php index 8754771ca382140e72fe4b311a4265e4804a4067..2e5075ea03258b8fe1761c44ce011c3cf079c88c 100644 --- a/tests/phpunit/api/v4/Action/ContactGetTest.php +++ b/tests/phpunit/api/v4/Action/ContactGetTest.php @@ -117,8 +117,9 @@ class ContactGetTest extends \api\v4\UnitTestCase { \CRM_Core_DAO::executeQuery(" ALTER TABLE civicrm_contact MODIFY COLUMN `first_name` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'First Name.', - CHARSET utf8 + CHARSET utf8 COLLATE utf8_unicode_ci "); + \Civi::$statics['CRM_Core_BAO_SchemaHandler'] = []; } \Civi::$statics['CRM_Core_BAO_SchemaHandler'] = []; Contact::get() @@ -129,7 +130,7 @@ class ContactGetTest extends \api\v4\UnitTestCase { \CRM_Core_DAO::executeQuery(" ALTER TABLE civicrm_contact MODIFY COLUMN `first_name` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'First Name.', - CHARSET utf8mb4 + CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci "); } }