3 namespace Drupal\Tests\Core\Database;
5 use Drupal\Core\Database\Connection;
6 use Drupal\Core\Database\Query\Condition;
7 use Drupal\Core\Database\Query\PlaceholderInterface;
8 use Drupal\Tests\UnitTestCase;
12 * @coversDefaultClass \Drupal\Core\Database\Query\Condition
16 class ConditionTest extends UnitTestCase {
19 * Provides a list of known operations and the expected output.
22 * - Expected result for the string version of the condition.
23 * - The field name to input in the condition.
25 public function providerSimpleCondition() {
27 ['name = :db_condition_placeholder_0', 'name'],
28 ['name123 = :db_condition_placeholder_0', 'name-123'],
34 * @dataProvider providerSimpleCondition()
36 public function testSimpleCondition($expected, $field_name) {
37 $connection = $this->prophesize(Connection::class);
38 $connection->escapeField($field_name)->will(function ($args) {
39 return preg_replace('/[^A-Za-z0-9_.]+/', '', $args[0]);
41 $connection->mapConditionOperator('=')->willReturn(['operator' => '=']);
42 $connection = $connection->reveal();
44 $query_placeholder = $this->prophesize(PlaceholderInterface::class);
47 $query_placeholder->nextPlaceholder()->will(function () use (&$counter) {
50 $query_placeholder->uniqueIdentifier()->willReturn(4);
51 $query_placeholder = $query_placeholder->reveal();
53 $condition = new Condition('AND');
54 $condition->condition($field_name, ['value']);
55 $condition->compile($connection, $query_placeholder);
57 $this->assertEquals($expected, $condition->__toString());
58 $this->assertEquals([':db_condition_placeholder_0' => 'value'], $condition->arguments());
64 * @dataProvider dataProviderTestCompileWithKnownOperators()
66 * @param string $expected
67 * The expected generated SQL condition.
68 * @param string $field
69 * The field to pass into the condition() method.
71 * The value to pass into the condition() method.
72 * @param string $operator
73 * The operator to pass into the condition() method.
74 * @param mixed $expected_arguments
75 * (optional) The expected set arguments.
77 public function testCompileWithKnownOperators($expected, $field, $value, $operator, $expected_arguments = NULL) {
78 $connection = $this->prophesize(Connection::class);
79 $connection->escapeField(Argument::any())->will(function ($args) {
80 return preg_replace('/[^A-Za-z0-9_.]+/', '', $args[0]);
82 $connection->mapConditionOperator(Argument::any())->willReturn(NULL);
83 $connection = $connection->reveal();
85 $query_placeholder = $this->prophesize(PlaceholderInterface::class);
88 $query_placeholder->nextPlaceholder()->will(function () use (&$counter) {
91 $query_placeholder->uniqueIdentifier()->willReturn(4);
92 $query_placeholder = $query_placeholder->reveal();
94 $condition = new Condition('AND');
95 $condition->condition($field, $value, $operator);
96 $condition->compile($connection, $query_placeholder);
98 $this->assertEquals($expected, $condition->__toString());
99 if (isset($expected_arguments)) {
100 $this->assertEquals($expected_arguments, $condition->arguments());
105 * Provides a list of known operations and the expected output.
109 public function dataProviderTestCompileWithKnownOperators() {
110 // Below are a list of commented out test cases, which should work but
111 // aren't directly supported by core, but instead need manual handling with
112 // prefix/suffix at the moment.
114 $data[] = ['name = :db_condition_placeholder_0', 'name', 'value', '='];
115 $data[] = ['name != :db_condition_placeholder_0', 'name', 'value', '!='];
116 $data[] = ['name <> :db_condition_placeholder_0', 'name', 'value', '<>'];
117 $data[] = ['name >= :db_condition_placeholder_0', 'name', 'value', '>='];
118 $data[] = ['name > :db_condition_placeholder_0', 'name', 'value', '>'];
119 $data[] = ['name <= :db_condition_placeholder_0', 'name', 'value', '<='];
120 $data[] = ['name < :db_condition_placeholder_0', 'name', 'value', '<'];
121 // $data[] = ['GREATEST (1, 2, 3)', '', [1, 2, 3], 'GREATEST'];
122 $data[] = ['name IN (:db_condition_placeholder_0, :db_condition_placeholder_1, :db_condition_placeholder_2)', 'name', ['1', '2', '3'], 'IN'];
123 $data[] = ['name NOT IN (:db_condition_placeholder_0, :db_condition_placeholder_1, :db_condition_placeholder_2)', 'name', ['1', '2', '3'], 'NOT IN'];
124 // $data[] = ['INTERVAL (1, 2, 3)', '', [1, 2, 3], 'INTERVAL'];
125 $data[] = ['name IS NULL', 'name', NULL, 'IS NULL'];
126 $data[] = ['name IS NOT NULL', 'name', NULL, 'IS NOT NULL'];
127 $data[] = ['name IS :db_condition_placeholder_0', 'name', 'TRUE', 'IS'];
128 // $data[] = ['LEAST (1, 2, 3)', '', [1, 2, 3], 'LEAST'];
129 $data[] = ["name LIKE :db_condition_placeholder_0 ESCAPE '\\\\'", 'name', '%muh%', 'LIKE', [':db_condition_placeholder_0' => '%muh%']];
130 $data[] = ["name NOT LIKE :db_condition_placeholder_0 ESCAPE '\\\\'", 'name', '%muh%', 'NOT LIKE', [':db_condition_placeholder_0' => '%muh%']];
131 $data[] = ["name BETWEEN :db_condition_placeholder_0 AND :db_condition_placeholder_1", 'name', [1, 2], 'BETWEEN', [':db_condition_placeholder_0' => 1, ':db_condition_placeholder_1' => 2]];
132 $data[] = ["name NOT BETWEEN :db_condition_placeholder_0 AND :db_condition_placeholder_1", 'name', [1, 2], 'NOT BETWEEN', [':db_condition_placeholder_0' => 1, ':db_condition_placeholder_1' => 2]];
133 // $data[] = ['STRCMP (name, :db_condition_placeholder_0)', '', ['test-string'], 'STRCMP', [':db_condition_placeholder_0' => 'test-string']];
134 // $data[] = ['EXISTS', '', NULL, 'EXISTS'];
135 // $data[] = ['name NOT EXISTS', 'name', NULL, 'NOT EXISTS'];
143 * @dataProvider providerTestCompileWithSqlInjectionForOperator
145 public function testCompileWithSqlInjectionForOperator($operator) {
146 $connection = $this->prophesize(Connection::class);
147 $connection->escapeField(Argument::any())->will(function ($args) {
148 return preg_replace('/[^A-Za-z0-9_.]+/', '', $args[0]);
150 $connection->mapConditionOperator(Argument::any())->willReturn(NULL);
151 $connection = $connection->reveal();
153 $query_placeholder = $this->prophesize(PlaceholderInterface::class);
156 $query_placeholder->nextPlaceholder()->will(function () use (&$counter) {
159 $query_placeholder->uniqueIdentifier()->willReturn(4);
160 $query_placeholder = $query_placeholder->reveal();
162 $condition = new Condition('AND');
163 $condition->condition('name', 'value', $operator);
164 $this->setExpectedException(\PHPUnit_Framework_Error::class);
165 $condition->compile($connection, $query_placeholder);
168 public function providerTestCompileWithSqlInjectionForOperator() {
170 $data[] = ["IS NOT NULL) ;INSERT INTO {test} (name) VALUES ('test12345678'); -- "];
171 $data[] = ["IS NOT NULL) UNION ALL SELECT name, pass FROM {users_field_data} -- "];
172 $data[] = ["IS NOT NULL) UNION ALL SELECT name FROM {TEST_UPPERCASE} -- "];
173 $data[] = ["= 1 UNION ALL SELECT password FROM user WHERE uid ="];