Skip to content

Commit 4262ff8

Browse files
committed
On source of trues
1 parent 846a924 commit 4262ff8

5 files changed

Lines changed: 95 additions & 21 deletions

File tree

src/Database/Adapter/MariaDB.php

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,13 +2111,8 @@ protected function getOperatorSQL(string $column, Operator $operator, array &$bi
21112111

21122112
case Operator::TYPE_ARRAY_FILTER:
21132113
$condition = $values[0] ?? 'equal';
2114-
$validConditions = [
2115-
'equal', 'notEqual', // Comparison
2116-
'greaterThan', 'greaterThanEqual', 'lessThan', 'lessThanEqual', // Numeric
2117-
'isNull', 'isNotNull' // Null checks
2118-
];
2119-
if (!in_array($condition, $validConditions, true)) {
2120-
throw new DatabaseException("Invalid filter condition: {$condition}. Must be one of: " . implode(', ', $validConditions));
2114+
if (!in_array($condition, Operator::ARRAY_FILTER_CONDITIONS, true)) {
2115+
throw new DatabaseException("Invalid filter condition: {$condition}. Must be one of: " . implode(', ', Operator::ARRAY_FILTER_CONDITIONS));
21212116
}
21222117
$filterValue = $values[1] ?? null;
21232118
$conditionKey = $this->registerOperatorBind($binds, $condition);

src/Database/Adapter/Postgres.php

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2661,13 +2661,8 @@ protected function getOperatorSQL(string $column, Operator $operator, array &$bi
26612661

26622662
case Operator::TYPE_ARRAY_FILTER:
26632663
$condition = $values[0] ?? 'equal';
2664-
$validConditions = [
2665-
'equal', 'notEqual', // Comparison
2666-
'greaterThan', 'greaterThanEqual', 'lessThan', 'lessThanEqual', // Numeric
2667-
'isNull', 'isNotNull' // Null checks
2668-
];
2669-
if (!in_array($condition, $validConditions, true)) {
2670-
throw new DatabaseException("Invalid filter condition: {$condition}. Must be one of: " . implode(', ', $validConditions));
2664+
if (!in_array($condition, Operator::ARRAY_FILTER_CONDITIONS, true)) {
2665+
throw new DatabaseException("Invalid filter condition: {$condition}. Must be one of: " . implode(', ', Operator::ARRAY_FILTER_CONDITIONS));
26712666
}
26722667
$filterValue = $values[1] ?? null;
26732668
$conditionKey = $this->registerOperatorBind($binds, $condition);

src/Database/Operator.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ class Operator
7272
*/
7373
public const MAX_ARRAY_OPERATOR_SIZE = 10000;
7474

75+
/**
76+
* Conditions accepted by the arrayFilter operator.
77+
*/
78+
public const ARRAY_FILTER_CONDITIONS = [
79+
'equal', 'notEqual', // comparison
80+
'greaterThan', 'greaterThanEqual', 'lessThan', 'lessThanEqual', // numeric
81+
'isNull', 'isNotNull', // null checks
82+
];
83+
7584
protected const NUMERIC_TYPES = [
7685
self::TYPE_INCREMENT,
7786
self::TYPE_DECREMENT,

src/Database/Validator/Operator.php

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,8 @@ private function validateOperatorForAttribute(
405405
return false;
406406
}
407407

408-
$validConditions = [
409-
'equal', 'notEqual', // Comparison
410-
'greaterThan', 'greaterThanEqual', 'lessThan', 'lessThanEqual', // Numeric
411-
'isNull', 'isNotNull' // Null checks
412-
];
413-
if (!\in_array($values[0], $validConditions, true)) {
414-
$this->message = "Invalid array filter condition '{$values[0]}'. Must be one of: " . \implode(', ', $validConditions);
408+
if (!\in_array($values[0], DatabaseOperator::ARRAY_FILTER_CONDITIONS, true)) {
409+
$this->message = "Invalid array filter condition '{$values[0]}'. Must be one of: " . \implode(', ', DatabaseOperator::ARRAY_FILTER_CONDITIONS);
415410
return false;
416411
}
417412

tests/e2e/Adapter/Scopes/OperatorTests.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,86 @@ public function testOperatorArraySizeLimit(): void
16951695
$database->deleteCollection($collectionId);
16961696
}
16971697

1698+
/**
1699+
* An invalid array filter condition is rejected the same way on every adapter, because the
1700+
* operator validator checks it before any adapter runs.
1701+
*/
1702+
public function testOperatorArrayFilterRejectsUnknownCondition(): void
1703+
{
1704+
$database = static::getDatabase();
1705+
1706+
if (!$database->getAdapter()->getSupportForOperators()) {
1707+
$this->expectNotToPerformAssertions();
1708+
return;
1709+
}
1710+
1711+
$collectionId = 'operator_filter_unknown_cond';
1712+
$database->createCollection($collectionId);
1713+
$database->createAttribute($collectionId, 'tags', Database::VAR_STRING, 50, false, null, true, true);
1714+
1715+
$database->createDocument($collectionId, new Document([
1716+
'$id' => 'doc',
1717+
'$permissions' => [Permission::read(Role::any()), Permission::update(Role::any())],
1718+
'tags' => ['a', 'b', 'c'],
1719+
]));
1720+
1721+
try {
1722+
$database->updateDocument($collectionId, 'doc', new Document([
1723+
'tags' => Operator::arrayFilter('bogusCondition', 'x'),
1724+
]));
1725+
$this->fail('Expected an exception for an invalid array filter condition');
1726+
} catch (DatabaseException $e) {
1727+
$this->assertStringContainsString('filter condition', $e->getMessage());
1728+
}
1729+
1730+
$database->deleteCollection($collectionId);
1731+
}
1732+
1733+
/**
1734+
* Every filter condition the validator accepts must actually work the same on all adapters.
1735+
* Each condition is applied to [1,2,3,4,5] and must keep exactly the matching elements —
1736+
* an adapter that doesn't handle a condition would keep the whole array instead.
1737+
*/
1738+
public function testOperatorArrayFilterAllConditions(): void
1739+
{
1740+
$database = static::getDatabase();
1741+
1742+
if (!$database->getAdapter()->getSupportForOperators()) {
1743+
$this->expectNotToPerformAssertions();
1744+
return;
1745+
}
1746+
1747+
$collectionId = 'operator_filter_all_conditions';
1748+
$database->createCollection($collectionId);
1749+
$database->createAttribute($collectionId, 'numbers', Database::VAR_INTEGER, 0, false, null, true, true);
1750+
$database->createDocument($collectionId, new Document([
1751+
'$id' => 'doc',
1752+
'$permissions' => [Permission::read(Role::any()), Permission::update(Role::any())],
1753+
'numbers' => [1, 2, 3, 4, 5],
1754+
]));
1755+
1756+
$cases = [
1757+
['equal', 3, [3]],
1758+
['notEqual', 3, [1, 2, 4, 5]],
1759+
['greaterThan', 3, [4, 5]],
1760+
['greaterThanEqual', 3, [3, 4, 5]],
1761+
['lessThan', 3, [1, 2]],
1762+
['lessThanEqual', 3, [1, 2, 3]],
1763+
['isNotNull', null, [1, 2, 3, 4, 5]],
1764+
['isNull', null, []],
1765+
];
1766+
1767+
foreach ($cases as [$condition, $compare, $expected]) {
1768+
$database->updateDocument($collectionId, 'doc', new Document(['numbers' => [1, 2, 3, 4, 5]])); // reset
1769+
$updated = $database->updateDocument($collectionId, 'doc', new Document([
1770+
'numbers' => Operator::arrayFilter($condition, $compare),
1771+
]));
1772+
$this->assertEquals($expected, \array_values($updated->getAttribute('numbers')), "arrayFilter('{$condition}') gave the wrong result");
1773+
}
1774+
1775+
$database->deleteCollection($collectionId);
1776+
}
1777+
16981778
public function testOperatorStringConcatComprehensive(): void
16991779
{
17001780
$database = static::getDatabase();

0 commit comments

Comments
 (0)