diff --git a/lib/Models/ScheduledTaskSubscriber.php b/lib/Models/ScheduledTaskSubscriber.php index 29bff1c837..2a39cf7fee 100644 --- a/lib/Models/ScheduledTaskSubscriber.php +++ b/lib/Models/ScheduledTaskSubscriber.php @@ -2,6 +2,7 @@ namespace MailPoet\Models; +use MailPoet\Util\Helpers; use MailPoet\WP\Functions as WPFunctions; /** @@ -88,7 +89,7 @@ class ScheduledTaskSubscriber extends Model { ->select('subscribers.first_name', 'firstName'); if (isset($data['search'])) { $search = trim($data['search']); - $search = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $search); // escape for 'LIKE' + $search = Helpers::escapeSearch($search); if (strlen($search) === 0) { return $query; } diff --git a/lib/Newsletter/Listing/NewsletterListingRepository.php b/lib/Newsletter/Listing/NewsletterListingRepository.php index 5c3b133336..5d43ef7cbf 100644 --- a/lib/Newsletter/Listing/NewsletterListingRepository.php +++ b/lib/Newsletter/Listing/NewsletterListingRepository.php @@ -5,6 +5,7 @@ namespace MailPoet\Newsletter\Listing; use MailPoet\Entities\NewsletterEntity; use MailPoet\Listing\ListingDefinition; use MailPoet\Listing\ListingRepository; +use MailPoet\Util\Helpers; use MailPoet\WP\Functions as WPFunctions; use MailPoetVendor\Doctrine\ORM\QueryBuilder; @@ -202,7 +203,7 @@ class NewsletterListingRepository extends ListingRepository { } protected function applySearch(QueryBuilder $queryBuilder, string $search) { - $search = str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $search); // escape for 'LIKE' + $search = Helpers::escapeSearch($search); $queryBuilder ->andWhere('n.subject LIKE :search') ->setParameter('search', "%$search%"); diff --git a/lib/Subscribers/SubscriberListingRepository.php b/lib/Subscribers/SubscriberListingRepository.php index a4860f31b5..bb80e85fb9 100644 --- a/lib/Subscribers/SubscriberListingRepository.php +++ b/lib/Subscribers/SubscriberListingRepository.php @@ -74,7 +74,7 @@ class SubscriberListingRepository extends ListingRepository { } protected function applySearch(QueryBuilder $queryBuilder, string $search) { - $search = $this->sanitizeSearch($search); + $search = Helpers::escapeSearch($search); $queryBuilder ->andWhere('s.email LIKE :search or s.firstName LIKE :search or s.lastName LIKE :search') ->setParameter('search', "%$search%"); @@ -258,10 +258,6 @@ class SubscriberListingRepository extends ListingRepository { return ['segment' => $segmentList]; } - private function sanitizeSearch(string $search): string { - return str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], trim($search)); // escape for 'LIKE' - } - private function applyDynamicSegmentsFilter( QueryBuilder $queryBuilder, ListingDefinition $definition, @@ -282,7 +278,7 @@ class SubscriberListingRepository extends ListingRepository { // Apply group, search, order and paging to fetch only necessary ids // This id done for performance reasons instead of fetching all IDs in dynamic segment if ($definition->getSearch()) { - $search = $this->sanitizeSearch((string)$definition->getSearch()); + $search = Helpers::escapeSearch((string)$definition->getSearch()); $subscribersIdsQuery ->andWhere("$subscribersTable.email LIKE :search or $subscribersTable.first_name LIKE :search or $subscribersTable.last_name LIKE :search") ->setParameter('search', "%$search%"); diff --git a/lib/Util/Helpers.php b/lib/Util/Helpers.php index fd5b92576d..fe367fd7e5 100644 --- a/lib/Util/Helpers.php +++ b/lib/Util/Helpers.php @@ -98,4 +98,8 @@ class Helpers { return trim($value); return $value; } + + public static function escapeSearch(string $search): string { + return str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], trim($search)); // escape for 'LIKE' + } } diff --git a/tests/unit/Util/HelpersTest.php b/tests/unit/Util/HelpersTest.php index fbe97abbb1..8bd3dbf6dd 100644 --- a/tests/unit/Util/HelpersTest.php +++ b/tests/unit/Util/HelpersTest.php @@ -58,4 +58,15 @@ class HelpersTest extends \MailPoetUnitTest { 'number' => 523, ]); } + + public function testSanitizeSearch() { + expect(Helpers::escapeSearch('Hello'))->equals('Hello'); + expect(Helpers::escapeSearch('Hello '))->equals('Hello'); + expect(Helpers::escapeSearch(' Hello '))->equals('Hello'); + expect(Helpers::escapeSearch('%Hello '))->equals('\%Hello'); + expect(Helpers::escapeSearch('%Hello %'))->equals('\%Hello \%'); + expect(Helpers::escapeSearch('He%llo'))->equals('He\%llo'); + expect(Helpers::escapeSearch('He_llo'))->equals('He\_llo'); + expect(Helpers::escapeSearch('He\\llo'))->equals('He\\\llo'); + } }