From d414e206eb8c23f99089cee4316b0076b19716d3 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Jul 2023 15:25:35 +0200 Subject: [PATCH 1/3] Filter: Remove multi value support for row values Not used anywhere at the moment and in order to sync the match mechanics with what a sql database supports, this needs to go. --- src/Filter.php | 24 ++---------------- tests/FilterTest.php | 58 -------------------------------------------- 2 files changed, 2 insertions(+), 80 deletions(-) diff --git a/src/Filter.php b/src/Filter.php index 9b60f8e..ae6a1ff 100644 --- a/src/Filter.php +++ b/src/Filter.php @@ -181,17 +181,7 @@ protected function matchEqual($rule, $row) $value = $rule->getValue(); $this->normalizeTypes($rowValue, $value); - if (! is_array($rowValue)) { - $rowValue = [$rowValue]; - } - - foreach ($rowValue as $rowVal) { - if ($this->performEqualityMatch($value, $rowVal, $rule->ignoresCase())) { - return true; - } - } - - return false; + return $this->performEqualityMatch($value, $rowValue, $rule->ignoresCase()); } /** @@ -232,17 +222,7 @@ protected function matchSimilar($rule, $row) $value = $rule->getValue(); $this->normalizeTypes($rowValue, $value); - if (! is_array($rowValue)) { - $rowValue = [$rowValue]; - } - - foreach ($rowValue as $rowVal) { - if ($this->performSimilarityMatch($value, $rowVal, $rule->ignoresCase())) { - return true; - } - } - - return false; + return $this->performSimilarityMatch($value, $rowValue, $rule->ignoresCase()); } /** diff --git a/tests/FilterTest.php b/tests/FilterTest.php index f7fc277..a3ce701 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -151,35 +151,6 @@ public function testLikeIgnoresCase() $this->assertTrue(Filter::match($like, $this->row(0))); } - public function testEqualMatchesMultiValuedColumns() - { - $this->assertTrue(Filter::match(Filter::equal('foo', 'bar'), [ - 'foo' => ['foo', 'bar'] - ])); - $this->assertTrue(Filter::match(Filter::equal('foo', 'BAR')->ignoreCase(), [ - 'foo' => ['FoO', 'bAr'] - ])); - $this->assertTrue(Filter::match(Filter::equal('foo', ['bar', 'boar']), [ - 'foo' => ['foo', 'bar'] - ])); - } - - public function testLikeMatchesMultiValuedColumns() - { - $this->assertTrue(Filter::match(Filter::like('foo', 'bar'), [ - 'foo' => ['foo', 'bar'] - ])); - $this->assertTrue(Filter::match(Filter::like('foo', 'ba*'), [ - 'foo' => ['foo', 'bar'] - ])); - $this->assertTrue(Filter::match(Filter::like('foo', 'BAR')->ignoreCase(), [ - 'foo' => ['FoO', 'bAr'] - ])); - $this->assertTrue(Filter::match(Filter::like('foo', ['bar', 'boar']), [ - 'foo' => ['foo', 'bar'] - ])); - } - public function testUnequalMatches() { $unequal = Filter::unequal('problem', '0'); @@ -236,35 +207,6 @@ public function testUnlikeIgnoresCase() $this->assertFalse(Filter::match($unlike, $this->row(0))); } - public function testUnequalMatchesMultiValuedColumns() - { - $this->assertFalse(Filter::match(Filter::unequal('foo', 'bar'), [ - 'foo' => ['foo', 'bar'] - ])); - $this->assertFalse(Filter::match(Filter::unequal('foo', 'BAR')->ignoreCase(), [ - 'foo' => ['FoO', 'bAr'] - ])); - $this->assertFalse(Filter::match(Filter::unequal('foo', ['bar', 'boar']), [ - 'foo' => ['foo', 'bar'] - ])); - } - - public function testUnlikeMatchesMultiValuedColumns() - { - $this->assertFalse(Filter::match(Filter::unlike('foo', 'bar'), [ - 'foo' => ['foo', 'bar'] - ])); - $this->assertFalse(Filter::match(Filter::unlike('foo', 'ba*'), [ - 'foo' => ['foo', 'bar'] - ])); - $this->assertFalse(Filter::match(Filter::unlike('foo', 'BAR')->ignoreCase(), [ - 'foo' => ['FoO', 'bAr'] - ])); - $this->assertFalse(Filter::match(Filter::unlike('foo', ['bar', 'boar']), [ - 'foo' => ['foo', 'bar'] - ])); - } - public function testGreaterThanMatches() { $greaterThan = Filter::greaterThan('state', 1); From a5b3d4c14f326ea3e5f31310a3ee8425bafc1c78 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Jul 2023 15:35:20 +0200 Subject: [PATCH 2/3] Filter: Drop array support for similarity matches Not used anywhere at the moment and in order to sync the match mechanics with what a sql database supports, this needs to go. --- src/Filter.php | 22 +++++++++++----------- tests/FilterTest.php | 42 +++++++++++------------------------------- 2 files changed, 22 insertions(+), 42 deletions(-) diff --git a/src/Filter.php b/src/Filter.php index ae6a1ff..17d69c0 100644 --- a/src/Filter.php +++ b/src/Filter.php @@ -218,11 +218,17 @@ protected function matchSimilar($rule, $row) )); } - $rowValue = $this->extractValue($rule->getColumn(), $row); $value = $rule->getValue(); + if (is_array($value)) { + throw new InvalidArgumentException( + 'Cannot perform a similarity match if the expression is an array' + ); + } + + $rowValue = $this->extractValue($rule->getColumn(), $row); $this->normalizeTypes($rowValue, $value); - return $this->performSimilarityMatch($value, $rowValue, $rule->ignoresCase()); + return $this->performSimilarityMatch((string) $value, (string) $rowValue, $rule->ignoresCase()); } /** @@ -257,23 +263,17 @@ protected function performEqualityMatch($value, $rowValue, $ignoreCase = false) /** * Apply similarity matching rules on the given row value * - * @param string|string[] $value + * @param string $value * @param string $rowValue * @param bool $ignoreCase * * @return bool */ - protected function performSimilarityMatch($value, $rowValue, $ignoreCase = false) + protected function performSimilarityMatch(string $value, string $rowValue, bool $ignoreCase = false): bool { if ($ignoreCase) { $rowValue = strtolower($rowValue); - $value = is_array($value) - ? array_map('strtolower', $value) - : strtolower($value); - } - - if (is_array($value)) { - return in_array($rowValue, $value, true); + $value = strtolower($value); } $wildcardSubSegments = preg_split('~\*~', $value); diff --git a/tests/FilterTest.php b/tests/FilterTest.php index a3ce701..a468bdd 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -139,16 +139,10 @@ public function testLikeMismatches() public function testLikeIgnoresCase() { - // single string $like = Filter::like('host', '*LOCAL*') ->ignoreCase(); $this->assertTrue(Filter::match($like, $this->row(0))); - - // string array - $like->setValue(['LoCaLhOsT', '127.0.0.1']); - - $this->assertTrue(Filter::match($like, $this->row(0))); } public function testUnequalMatches() @@ -195,16 +189,10 @@ public function testUnlikeMismatches() public function testUnlikeIgnoresCase() { - // single string $unlike = Filter::unlike('host', '*LOCAL*') ->ignoreCase(); $this->assertFalse(Filter::match($unlike, $this->row(0))); - - // string array - $unlike->setValue(['LoCaLhOsT', '127.0.0.1']); - - $this->assertFalse(Filter::match($unlike, $this->row(0))); } public function testGreaterThanMatches() @@ -323,18 +311,14 @@ public function testEqualWithArrayMismatches() $this->assertFalse(Filter::match($equal, $this->row(0))); } - public function testLikeWithArrayMatches() + public function testLikeWithArrayThrows() { - $like = Filter::like('host', ['127.0.0.1', 'localhost']); + $like = Filter::like('host', ['127.0.0.*', 'local*']); - $this->assertTrue(Filter::match($like, $this->row(0))); - } + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Cannot perform a similarity match if the expression is an array'); - public function testLikeWithArrayMismatches() - { - $like = Filter::like('host', ['10.0.10.20', '10.0.10.21']); - - $this->assertFalse(Filter::match($like, $this->row(0))); + Filter::match($like, $this->row(0)); } public function testUnequalWithArrayMatches() @@ -344,13 +328,6 @@ public function testUnequalWithArrayMatches() $this->assertTrue(Filter::match($unequal, $this->row(0))); } - public function testUnlikeWithArrayMatches() - { - $unlike = Filter::unlike('host', ['10.0.20.10', '10.0.20.11']); - - $this->assertTrue(Filter::match($unlike, $this->row(0))); - } - public function testUnequalWithArrayMismatches() { $unequal = Filter::unequal('host', ['127.0.0.1', 'localhost']); @@ -358,11 +335,14 @@ public function testUnequalWithArrayMismatches() $this->assertFalse(Filter::match($unequal, $this->row(0))); } - public function testUnlikeWithArrayMismatches() + public function testUnlikeWithArrayThrows() { - $unlike = Filter::unlike('host', ['127.0.0.1', 'localhost']); + $unlike = Filter::unlike('host', ['127.0.0.*', 'local*']); - $this->assertFalse(Filter::match($unlike, $this->row(0))); + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Cannot perform a similarity match if the expression is an array'); + + Filter::match($unlike, $this->row(0)); } public function testConditionsAreValueTypeAgnostic() From 4ffff1047d9e793679072e8a0582c72b935247a2 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Jul 2023 15:37:00 +0200 Subject: [PATCH 3/3] Filter: Add support for multiple columns in equal/unequal comparisons array values were already supported. Now also the column can be an array, in which case multiple row values are extracted and compared with the value directly. --- src/Filter.php | 17 ++++++++++++++--- tests/FilterTest.php | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Filter.php b/src/Filter.php index 17d69c0..afffed8 100644 --- a/src/Filter.php +++ b/src/Filter.php @@ -242,14 +242,16 @@ protected function matchSimilar($rule, $row) */ protected function performEqualityMatch($value, $rowValue, $ignoreCase = false) { - if ($ignoreCase && is_string($rowValue)) { - $rowValue = strtolower($rowValue); + if ($ignoreCase) { + $rowValue = is_array($rowValue) + ? array_map('strtolower', $rowValue) + : strtolower($rowValue); $value = is_array($value) ? array_map('strtolower', $value) : strtolower($value); } - if (is_array($value)) { + if (is_array($value) && ! is_array($rowValue)) { return in_array($rowValue, $value, true); } elseif (! is_string($value)) { if (is_string($rowValue)) { @@ -510,6 +512,15 @@ protected function performMatch(Rule $rule, $row) */ protected function extractValue($column, $row) { + if (is_array($column)) { + $value = []; + foreach ($column as $name) { + $value[] = $this->extractValue($name, $row); + } + + return $value; + } + try { return $row->{$column}; } catch (Exception $_) { diff --git a/tests/FilterTest.php b/tests/FilterTest.php index a468bdd..269af78 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -27,6 +27,13 @@ class FilterTest extends TestCase 'service' => 'www.icinga.com', 'state' => 1, 'handled' => '0' + ], + [ + 'host' => 'LocalHost', + 'problem' => '1', + 'service' => 'Ping', + 'state' => 3, + 'handled' => '1' ] ]; @@ -311,6 +318,34 @@ public function testEqualWithArrayMismatches() $this->assertFalse(Filter::match($equal, $this->row(0))); } + public function testEqualWithArrayComparisonMatches() + { + $equal = Filter::equal(['host', 'service'], ['localhost', 'ping']); + + $this->assertTrue(Filter::match($equal, $this->row(0))); + } + + public function testEqualWithCaseInsensitiveArrayComparisonMatches() + { + $equal = Filter::equal(['host', 'service'], ['lOcAlHost', 'PiNg'])->ignoreCase(); + + $this->assertTrue(Filter::match($equal, $this->row(3))); + } + + public function testUnequalWithArrayComparisonMatches() + { + $unequal = Filter::unequal(['host', 'service'], ['ping', 'localhost']); + + $this->assertTrue(Filter::match($unequal, $this->row(0))); + } + + public function testUnequalWithCaseInsensitiveArrayComparisonMatches() + { + $unequal = Filter::unequal(['host', 'service'], ['PiNg', 'lOcAlHost'])->ignoreCase(); + + $this->assertTrue(Filter::match($unequal, $this->row(3))); + } + public function testLikeWithArrayThrows() { $like = Filter::like('host', ['127.0.0.*', 'local*']);