Commit 60312771 by Klimov Paul

Escaping of the special characters at 'MATCH' statement reworked

parent 38a6b4fa
...@@ -132,15 +132,16 @@ class Connection extends \yii\db\Connection ...@@ -132,15 +132,16 @@ class Connection extends \yii\db\Connection
/** /**
* Escapes all special characters from 'MATCH' statement argument. * Escapes all special characters from 'MATCH' statement argument.
* Make sure you are using this method whenever composing 'MATCH' search statement. * Make sure you are using this method whenever composing 'MATCH' search statement.
* Note: this method does not perform quoting, you should place the result in the quotes manually. * Note: this method does not perform quoting, you should place the result in the quotes
* an perform additional escaping for it manually, the best way to do it is using PDO parameter.
* @param string $str string to be escaped. * @param string $str string to be escaped.
* @return string the properly escaped string. * @return string the properly escaped string.
*/ */
public function escapeMatchValue($str) public function escapeMatchValue($str)
{ {
return str_replace( return str_replace(
['\\', "'", '/', '"', '(', ')', '|', '-', '!', '@', '~', '&', '^', '$', '=', "\x00", "\n", "\r", "\x1a"], ['\\', '/', '"', '(', ')', '|', '-', '!', '@', '~', '&', '^', '$', '=', '>', '<', "\x00", "\n", "\r", "\x1a"],
['\\\\', "\\'", '\\\\/', '\\\\"', '\\\\(', '\\\\)', '\\\\|', '\\\\-', '\\\\!', '\\\\@', '\\\\~', '\\\\&', '\\\\^', '\\\\$', '\\\\=', "\\x00", "\\n", "\\r", "\\x1a"], ['\\\\', '\\/', '\\"', '\\(', '\\)', '\\|', '\\-', '\\!', '\\@', '\\~', '\\&', '\\^', '\\$', '\\=', '\\>', '\\<', "\\x00", "\\n", "\\r", "\\x1a"],
$str $str
); );
} }
......
...@@ -75,7 +75,8 @@ class Query extends Component implements QueryInterface ...@@ -75,7 +75,8 @@ class Query extends Component implements QueryInterface
* @var string|Expression 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. * This value will be composed into MATCH operator inside the WHERE clause.
* Note: this value will be processed by [[Connection::escapeMatchValue()]], * Note: this value will be processed by [[Connection::escapeMatchValue()]],
* if you need to compose complex match condition use [[Expression]]. * if you need to compose complex match condition use [[Expression]],
* see [[match()]] for details.
*/ */
public $match; public $match;
/** /**
...@@ -384,7 +385,14 @@ class Query extends Component implements QueryInterface ...@@ -384,7 +385,14 @@ class Query extends Component implements QueryInterface
* Sets the fulltext query text. This text will be composed into * Sets the fulltext query text. This text will be composed into
* MATCH operator inside the WHERE clause. * MATCH operator inside the WHERE clause.
* Note: this value will be processed by [[Connection::escapeMatchValue()]], * Note: this value will be processed by [[Connection::escapeMatchValue()]],
* if you need to compose complex match condition use [[Expression]]. * if you need to compose complex match condition use [[Expression]]:
* ```
* $query = new Query;
* $query->from('my_index')
* ->match(new Expression(':match', ['match' => '@(content) ' . Yii::$app->sphinx->escapeMatchValue($matchValue)]))
* ->all();
* ```
*
* @param string $query fulltext query text. * @param string $query fulltext query text.
* @return static the query object itself * @return static the query object itself
*/ */
......
...@@ -67,7 +67,9 @@ class QueryBuilder extends Object ...@@ -67,7 +67,9 @@ class QueryBuilder extends Object
$query->andWhere('MATCH(' . $query->match->expression . ')'); $query->andWhere('MATCH(' . $query->match->expression . ')');
$params = array_merge($params, $query->match->params); $params = array_merge($params, $query->match->params);
} else { } else {
$query->andWhere("MATCH('" . $this->db->escapeMatchValue($query->match) . "')"); $phName = self::PARAM_PREFIX . count($params);
$params[$phName] = $this->db->escapeMatchValue($query->match);
$query->andWhere('MATCH(' . $phName . ')');
} }
} }
......
...@@ -42,7 +42,7 @@ class QueryTest extends SphinxTestCase ...@@ -42,7 +42,7 @@ class QueryTest extends SphinxTestCase
$command = $query->createCommand($this->getConnection(false)); $command = $query->createCommand($this->getConnection(false));
$this->assertContains('MATCH(', $command->getSql(), 'No MATCH operator present!'); $this->assertContains('MATCH(', $command->getSql(), 'No MATCH operator present!');
$this->assertContains($match, $command->getSql(), 'No match query in SQL!'); $this->assertContains($match, $command->params, 'No match query among params!');
} }
public function testWhere() public function testWhere()
...@@ -258,14 +258,13 @@ class QueryTest extends SphinxTestCase ...@@ -258,14 +258,13 @@ class QueryTest extends SphinxTestCase
/** /**
* @depends testRun * @depends testRun
*/ */
public function testWhereQuotedValue() public function testWhereSpecialCharValue()
{ {
$connection = $this->getConnection(); $connection = $this->getConnection();
$query = new Query; $query = new Query;
$rows = $query->from('yii2_test_article_index') $rows = $query->from('yii2_test_article_index')
->andWhere(['author_id' => 'some"']) ->andWhere(['author_id' => 'some"'])
//->match('about"')
->all($connection); ->all($connection);
$this->assertEmpty($rows); $this->assertEmpty($rows);
} }
...@@ -282,11 +281,14 @@ class QueryTest extends SphinxTestCase ...@@ -282,11 +281,14 @@ class QueryTest extends SphinxTestCase
['@'], ['@'],
['\\'], ['\\'],
['()'], ['()'],
['\\' . "'"], ['<<<'],
['>>>'],
["\x00"], ["\x00"],
["\n"], ["\n"],
["\r"], ["\r"],
["\x1a"] ["\x1a"],
['\\' . "'"],
['\\' . '"'],
]; ];
} }
...@@ -318,7 +320,7 @@ class QueryTest extends SphinxTestCase ...@@ -318,7 +320,7 @@ class QueryTest extends SphinxTestCase
$query = new Query; $query = new Query;
$rows = $query->from('yii2_test_article_index') $rows = $query->from('yii2_test_article_index')
->match(new Expression("'@(content) " . $connection->escapeMatchValue('about"') . "'")) ->match(new Expression(':match', ['match' => '@(content) ' . $connection->escapeMatchValue('about\\"')]))
->all($connection); ->all($connection);
$this->assertNotEmpty($rows); $this->assertNotEmpty($rows);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment