diff --git a/extensions/sphinx/CHANGELOG.md b/extensions/sphinx/CHANGELOG.md index 7524e97782..78a444681c 100644 --- a/extensions/sphinx/CHANGELOG.md +++ b/extensions/sphinx/CHANGELOG.md @@ -4,6 +4,7 @@ Yii Framework 2 sphinx extension Change Log 2.0.0-rc under development -------------------------- +- Bug #3668: Escaping of the special characters at 'MATCH' statement added (klimov-paul) - Enh: Added support for using sub-queries when building a DB query with `IN` condition (qiangxue) diff --git a/extensions/sphinx/Connection.php b/extensions/sphinx/Connection.php index 22309f806c..d5896ce14d 100644 --- a/extensions/sphinx/Connection.php +++ b/extensions/sphinx/Connection.php @@ -128,4 +128,19 @@ class Connection extends \yii\db\Connection { throw new NotSupportedException('"' . __METHOD__ . '" is not supported.'); } + + /** + * Escapes all special characters from 'MATCH' statement argument. + * Make sure you are using this method whenever composing 'MATCH' search statement. + * @param string $str string to be escaped. + * @return string the properly escaped string. + */ + public function escapeMatchValue($str) + { + return str_replace( + ['/', '"', "'", '(', ')', '|', '-', '!', '@', '~', '&', '^', '$', '=', "\x00", "\n", "\r", "\x1a"], + ['\\/', '\\"', "\\'", '\\(', '\\)', '\\|', '\\-', '\\!', '\\@', '\\~', '\\&', '\\^', '\\$', '\\=', "\\x00", "\\n", "\\r", "\\x1a"], + $str + ); + } } diff --git a/extensions/sphinx/Query.php b/extensions/sphinx/Query.php index daa9a70dbd..cf12fcd929 100644 --- a/extensions/sphinx/Query.php +++ b/extensions/sphinx/Query.php @@ -72,8 +72,10 @@ class Query extends Component implements QueryInterface */ public $from; /** - * @var string text, which should be searched in fulltext mode. + * @var string|Expression text, which should be searched in fulltext mode. * This value will be composed into MATCH operator inside the WHERE clause. + * Note: this value will be processed by [[Connection::escapeMatchValue()]], + * if you need to compose complex match condition use [[Expression]]. */ public $match; /** @@ -381,13 +383,14 @@ class Query extends Component implements QueryInterface /** * Sets the fulltext query text. This text will be composed into * MATCH operator inside the WHERE clause. + * Note: this value will be processed by [[Connection::escapeMatchValue()]], + * if you need to compose complex match condition use [[Expression]]. * @param string $query fulltext query text. * @return static the query object itself */ public function match($query) { $this->match = $query; - return $this; } diff --git a/extensions/sphinx/QueryBuilder.php b/extensions/sphinx/QueryBuilder.php index 23b1963af0..b4d346dcae 100644 --- a/extensions/sphinx/QueryBuilder.php +++ b/extensions/sphinx/QueryBuilder.php @@ -63,9 +63,14 @@ class QueryBuilder extends Object $params = empty($params) ? $query->params : array_merge($params, $query->params); if ($query->match !== null) { - $phName = self::PARAM_PREFIX . count($params); - $params[$phName] = (string) $query->match; - $query->andWhere('MATCH(' . $phName . ')'); + if ($query->match instanceof Expression) { + $query->andWhere('MATCH(' . $query->match->expression . ')'); + $params = array_merge($query->match->params); + } else { + $phName = self::PARAM_PREFIX . count($params); + $params[$phName] = $this->db->escapeMatchValue($query->match); + $query->andWhere('MATCH(' . $phName . ')'); + } } $from = $query->from; diff --git a/tests/unit/extensions/sphinx/QueryTest.php b/tests/unit/extensions/sphinx/QueryTest.php index 88f39938f6..217a3b8fdc 100644 --- a/tests/unit/extensions/sphinx/QueryTest.php +++ b/tests/unit/extensions/sphinx/QueryTest.php @@ -253,4 +253,35 @@ class QueryTest extends SphinxTestCase ->count('*', $connection); $this->assertEquals(2, $count); } + + /** + * @depends testRun + */ + public function testWhereQuotedValue() + { + $connection = $this->getConnection(); + + $query = new Query; + $rows = $query->from('yii2_test_article_index') + ->andWhere(['author_id' => 'some"']) + //->match('about"') + ->all($connection); + $this->assertEmpty($rows); + } + + /** + * @depends testRun + * + * @see https://github.com/yiisoft/yii2/issues/3668 + */ + public function testMatchSpecialCharValue() + { + $connection = $this->getConnection(); + + $query = new Query; + $rows = $query->from('yii2_test_article_index') + ->match('about"@^') + ->all($connection); + $this->assertNotEmpty($rows); + } }