diff --git a/src/events/src/CallQueuedListener.php b/src/events/src/CallQueuedListener.php index 1c839f7fe..129476546 100644 --- a/src/events/src/CallQueuedListener.php +++ b/src/events/src/CallQueuedListener.php @@ -4,6 +4,7 @@ namespace Hypervel\Events; +use AllowDynamicProperties; use DateTimeInterface; use Hypervel\Bus\Queueable; use Hypervel\Container\Container; @@ -13,6 +14,7 @@ use Hypervel\Queue\InteractsWithQueue; use Throwable; +#[AllowDynamicProperties] class CallQueuedListener implements ShouldQueue { use InteractsWithQueue; diff --git a/tests/Events/CallQueuedListenerTest.php b/tests/Events/CallQueuedListenerTest.php new file mode 100644 index 000000000..977461b1a --- /dev/null +++ b/tests/Events/CallQueuedListenerTest.php @@ -0,0 +1,50 @@ +assertListenerToleratesUnknownProperties( + HypervelCallQueuedListener::class + ); + } + + /** + * Simulates cross-version deserialization: a job payload serialized by a + * newer Laravel/Hypervel version (which adds extra properties to + * CallQueuedListener) is unserialized by an older version that does not + * declare those properties. Without #[AllowDynamicProperties], PHP 8.2+ + * raises an error on the dynamic property assignment during unserialize(). + */ + private function assertListenerToleratesUnknownProperties(string $class): void + { + $listener = new $class('App\Listeners\OrderShipped', 'handle', []); + $serialized = serialize($listener); + + // Inject a synthetic property absent from the current class definition. + $extra = 's:18:"newPropertyFromV11";s:5:"value";'; + $serialized = preg_replace_callback( + '/^(O:\d+:"[^"]+":)(\d+):/', + fn ($m) => $m[1] . ((int) $m[2] + 1) . ':', + $serialized + ); + $serialized = substr($serialized, 0, -1) . $extra . '}'; + + $result = unserialize($serialized); + + $this->assertInstanceOf($class, $result); + $this->assertSame('App\Listeners\OrderShipped', $result->class); + $this->assertSame('value', $result->newPropertyFromV11); + } +}