Update filter criteria and CHANGELOG

This commit is contained in:
Younes ENNAJI
2026-03-02 03:20:33 +00:00
parent 50ffa722a5
commit 98336b98bf
9 changed files with 187 additions and 26 deletions
+2
View File
@@ -15,6 +15,8 @@
- [Notyf] Dispatch events: `flasher:notyf:click`, `flasher:notyf:dismiss` - [Notyf] Dispatch events: `flasher:notyf:click`, `flasher:notyf:dismiss`
- [Themes] Dispatch events: `flasher:theme:click` (generic) and `flasher:theme:{name}:click` (specific) - [Themes] Dispatch events: `flasher:theme:click` (generic) and `flasher:theme:{name}:click` (specific)
- [Laravel] Add LivewireListener classes for all adapters and themes to enable Livewire event handling - [Laravel] Add LivewireListener classes for all adapters and themes to enable Livewire event handling
* fix [Flasher] Fix FilterCriteria uninitialized property error when constructed with empty array
* fix [Flasher] Fix null comparison issues in PriorityCriteria, HopsCriteria, and DelayCriteria that relied on PHP's implicit null-to-0 coercion
## [v2.1.3](https://github.com/php-flasher/php-flasher/compare/v2.1.2...v2.1.3) - 2025-01-25 ## [v2.1.3](https://github.com/php-flasher/php-flasher/compare/v2.1.2...v2.1.3) - 2025-01-25
@@ -46,14 +46,9 @@ final readonly class DelayCriteria implements CriteriaInterface
$delay = $stamp->getDelay(); $delay = $stamp->getDelay();
if (null === $this->maxDelay) { $meetsMin = null === $this->minDelay || $delay >= $this->minDelay;
return $delay >= $this->minDelay; $meetsMax = null === $this->maxDelay || $delay <= $this->maxDelay;
}
if ($delay <= $this->maxDelay) { return $meetsMin && $meetsMax;
return $delay >= $this->minDelay;
}
return false;
} }
} }
@@ -11,7 +11,7 @@ final class FilterCriteria implements CriteriaInterface
/** /**
* @var \Closure[] * @var \Closure[]
*/ */
private array $callbacks; private array $callbacks = [];
/** /**
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
@@ -11,9 +11,9 @@ final readonly class HopsCriteria implements CriteriaInterface
{ {
use RangeExtractor; use RangeExtractor;
private readonly ?int $minAmount; private ?int $minAmount;
private readonly ?int $maxAmount; private ?int $maxAmount;
/** /**
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
@@ -44,14 +44,11 @@ final readonly class HopsCriteria implements CriteriaInterface
return false; return false;
} }
if (null === $this->maxAmount) { $amount = $stamp->getAmount();
return $stamp->getAmount() >= $this->minAmount;
}
if ($stamp->getAmount() <= $this->maxAmount) { $meetsMin = null === $this->minAmount || $amount >= $this->minAmount;
return $stamp->getAmount() >= $this->minAmount; $meetsMax = null === $this->maxAmount || $amount <= $this->maxAmount;
}
return false; return $meetsMin && $meetsMax;
} }
} }
@@ -46,14 +46,9 @@ final readonly class PriorityCriteria implements CriteriaInterface
$priority = $stamp->getPriority(); $priority = $stamp->getPriority();
if (null === $this->maxPriority) { $meetsMin = null === $this->minPriority || $priority >= $this->minPriority;
return $priority >= $this->minPriority; $meetsMax = null === $this->maxPriority || $priority <= $this->maxPriority;
}
if ($priority <= $this->maxPriority) { return $meetsMin && $meetsMax;
return $priority >= $this->minPriority;
}
return false;
} }
} }
@@ -248,4 +248,52 @@ final class DelayCriteriaTest extends TestCase
$this->assertCount(3, $result); $this->assertCount(3, $result);
} }
public function testMatchWithOnlyMaxAndNullMin(): void
{
// This test verifies explicit null handling for min
// Previously relied on PHP's implicit null-to-0 coercion
$envelope = new Envelope(new Notification(), [new DelayStamp(3)]);
$criteria = new DelayCriteria(['max' => 5]);
// With min=null and max=5, delay=3 should match
$this->assertTrue($criteria->match($envelope));
}
public function testMatchWithOnlyMaxRejectsHigherDelay(): void
{
$envelope = new Envelope(new Notification(), [new DelayStamp(10)]);
$criteria = new DelayCriteria(['max' => 5]);
// With min=null and max=5, delay=10 should NOT match
$this->assertFalse($criteria->match($envelope));
}
public function testMatchWithBothNullMinAndMax(): void
{
// When both min and max are null, all envelopes with delay stamp should match
$envelope = new Envelope(new Notification(), [new DelayStamp(100)]);
$criteria = new DelayCriteria([]);
$this->assertTrue($criteria->match($envelope));
}
public function testApplyWithNullMinAndMaxMatchesAll(): void
{
$envelopes = [
new Envelope(new Notification(), [new DelayStamp(0)]),
new Envelope(new Notification(), [new DelayStamp(50)]),
new Envelope(new Notification(), [new DelayStamp(100)]),
];
$criteria = new DelayCriteria([]);
$result = $criteria->apply($envelopes);
// All envelopes with delay stamp should match when no constraints
$this->assertCount(3, $result);
}
} }
@@ -166,4 +166,32 @@ final class FilterCriteriaTest extends TestCase
$this->assertSame(['first', 'second'], $order); $this->assertSame(['first', 'second'], $order);
$this->assertCount(2, $result); $this->assertCount(2, $result);
} }
public function testConstructorWithEmptyArrayDoesNotThrow(): void
{
// This test verifies the fix for uninitialized $callbacks property
// Previously, new FilterCriteria([]) would leave $callbacks uninitialized
// causing "must not be accessed before initialization" error on apply()
$criteria = new FilterCriteria([]);
$envelopes = [new Envelope(new Notification())];
$result = $criteria->apply($envelopes);
// With no callbacks, envelopes should pass through unchanged
$this->assertSame($envelopes, $result);
}
public function testApplyWithEmptyCallbacksReturnsEnvelopesUnchanged(): void
{
$criteria = new FilterCriteria([]);
$notification = new Notification();
$notification->setMessage('test');
$envelopes = [new Envelope($notification)];
$result = $criteria->apply($envelopes);
$this->assertCount(1, $result);
$this->assertSame('test', $result[0]->getMessage());
}
} }
@@ -229,4 +229,52 @@ final class HopsCriteriaTest extends TestCase
$this->assertCount(1, $result); $this->assertCount(1, $result);
} }
public function testMatchWithOnlyMaxAndNullMin(): void
{
// This test verifies explicit null handling for min
// Previously relied on PHP's implicit null-to-0 coercion
$envelope = new Envelope(new Notification(), [new HopsStamp(2)]);
$criteria = new HopsCriteria(['max' => 5]);
// With min=null and max=5, hops=2 should match
$this->assertTrue($criteria->match($envelope));
}
public function testMatchWithOnlyMaxRejectsHigherHops(): void
{
$envelope = new Envelope(new Notification(), [new HopsStamp(10)]);
$criteria = new HopsCriteria(['max' => 5]);
// With min=null and max=5, hops=10 should NOT match
$this->assertFalse($criteria->match($envelope));
}
public function testMatchWithBothNullMinAndMax(): void
{
// When both min and max are null, all envelopes with hops stamp should match
$envelope = new Envelope(new Notification(), [new HopsStamp(100)]);
$criteria = new HopsCriteria([]);
$this->assertTrue($criteria->match($envelope));
}
public function testApplyWithNullMinAndMaxMatchesAll(): void
{
$envelopes = [
new Envelope(new Notification(), [new HopsStamp(1)]),
new Envelope(new Notification(), [new HopsStamp(50)]),
new Envelope(new Notification(), [new HopsStamp(100)]),
];
$criteria = new HopsCriteria([]);
$result = $criteria->apply($envelopes);
// All envelopes with hops stamp should match when no constraints
$this->assertCount(3, $result);
}
} }
@@ -245,4 +245,52 @@ final class PriorityCriteriaTest extends TestCase
$this->assertCount(1, $result); $this->assertCount(1, $result);
} }
public function testMatchWithOnlyMaxAndNullMin(): void
{
// This test verifies explicit null handling for min
// Previously relied on PHP's implicit null-to-0 coercion
$envelope = new Envelope(new Notification(), [new PriorityStamp(3)]);
$criteria = new PriorityCriteria(['max' => 5]);
// With min=null and max=5, priority 3 should match
$this->assertTrue($criteria->match($envelope));
}
public function testMatchWithOnlyMaxRejectsHigherPriority(): void
{
$envelope = new Envelope(new Notification(), [new PriorityStamp(10)]);
$criteria = new PriorityCriteria(['max' => 5]);
// With min=null and max=5, priority 10 should NOT match
$this->assertFalse($criteria->match($envelope));
}
public function testMatchWithBothNullMinAndMax(): void
{
// When both min and max are null, all envelopes with priority stamp should match
$envelope = new Envelope(new Notification(), [new PriorityStamp(100)]);
$criteria = new PriorityCriteria([]);
$this->assertTrue($criteria->match($envelope));
}
public function testApplyWithNullMinAndMaxMatchesAll(): void
{
$envelopes = [
new Envelope(new Notification(), [new PriorityStamp(-100)]),
new Envelope(new Notification(), [new PriorityStamp(0)]),
new Envelope(new Notification(), [new PriorityStamp(100)]),
];
$criteria = new PriorityCriteria([]);
$result = $criteria->apply($envelopes);
// All envelopes with priority stamp should match when no constraints
$this->assertCount(3, $result);
}
} }