diff --git a/src/Internal/Declaration/Reader/ActivityReader.php b/src/Internal/Declaration/Reader/ActivityReader.php index 8d3b4c49e..541fa7b57 100644 --- a/src/Internal/Declaration/Reader/ActivityReader.php +++ b/src/Internal/Declaration/Reader/ActivityReader.php @@ -139,6 +139,10 @@ private function getMethodGroups(ClassNode $graph, \ReflectionMethod $root): arr continue; } + if ($attribute === null && $this->isMagic($method)) { + continue; + } + // // The name of the activity must be generated based on the // optional prefix on the #[ActivityInterface] attribute and diff --git a/src/Internal/Declaration/Reader/Reader.php b/src/Internal/Declaration/Reader/Reader.php index c462e53a3..44c945a2f 100644 --- a/src/Internal/Declaration/Reader/Reader.php +++ b/src/Internal/Declaration/Reader/Reader.php @@ -18,6 +18,11 @@ */ abstract class Reader { + private const MAGIC_METHODS = [ + '__construct', '__destruct', '__call', '__callStatic', '__get', '__set', '__isset', '__unset', '__sleep', + '__wakeup', '__serialize', '__unserialize', '__toString', '__invoke', '__set_state', '__clone', '__debugInfo', + ]; + protected ReaderInterface $reader; public function __construct(ReaderInterface $reader) @@ -35,4 +40,9 @@ protected function isValidMethod(\ReflectionMethod $method): bool { return !$method->isStatic() && $method->isPublic(); } + + protected function isMagic(\ReflectionMethod $method): bool + { + return \in_array($method->getName(), self::MAGIC_METHODS, true); + } } diff --git a/tests/Unit/Activity/ActivityPrototypeTestCase.php b/tests/Unit/Activity/ActivityPrototypeTestCase.php index 67ea648a7..ff5337fd2 100644 --- a/tests/Unit/Activity/ActivityPrototypeTestCase.php +++ b/tests/Unit/Activity/ActivityPrototypeTestCase.php @@ -9,16 +9,17 @@ use Spiral\Attributes\Composite\SelectiveReader; use Temporal\Internal\Declaration\Reader\ActivityReader; use Temporal\Tests\Unit\AbstractUnit; -use WeakReference; final class ActivityPrototypeTestCase extends AbstractUnit { private ActivityReader $activityReader; - protected function setUp(): void + public function testNoMagicMethods(): void { - $this->activityReader = new ActivityReader(new SelectiveReader([new AnnotationReader(), new AttributeReader()])); - parent::setUp(); + $protos = $this->activityReader->fromClass(MagicActivity::class); + + self::assertCount(1, $protos); + self::assertSame('MagicActivity.Do', $protos[0]->getID()); } public function testInstanceLeaks(): void @@ -27,11 +28,11 @@ public function testInstanceLeaks(): void $proto = $this->activityReader ->fromClass(DummyActivity::class)[0]; - $refProto = WeakReference::create($proto); - $refInstance = WeakReference::create($proto->getInstance()); - $refHandler = WeakReference::create($proto->getHandler()); - $refInstanceHandler = WeakReference::create($proto->getInstance()->getHandler()); - $refActivity = WeakReference::create($proto->getInstance()->getContext()); + $refProto = \WeakReference::create($proto); + $refInstance = \WeakReference::create($proto->getInstance()); + $refHandler = \WeakReference::create($proto->getHandler()); + $refInstanceHandler = \WeakReference::create($proto->getInstance()->getHandler()); + $refActivity = \WeakReference::create($proto->getInstance()->getContext()); unset($proto, $instance); @@ -49,8 +50,8 @@ public function testProtoWithInstanceImmutabilityAndLeaks(): void ->fromClass(DummyActivity::class)[0]; $newProto = $proto->withInstance($instance); // References - $refProto = WeakReference::create($proto); - $refNewProto = WeakReference::create($newProto); + $refProto = \WeakReference::create($proto); + $refNewProto = \WeakReference::create($newProto); // New object is result of clone operation $this->assertNotSame($proto, $newProto); @@ -80,7 +81,7 @@ public function testGetInstanceFromClass(): void public function testGetInstanceFromFactory(): void { $proto = $this->activityReader->fromClass(DummyActivity::class)[0]; - $protoWithFactory = $proto->withFactory(fn () => new DummyActivity()); + $protoWithFactory = $proto->withFactory(static fn() => new DummyActivity()); $this->assertInstanceOf(DummyActivity::class, $protoWithFactory->getInstance()->getContext()); } @@ -97,7 +98,7 @@ public function testLocalActivityFlag(): void public function testFactoryCreatesNewInstances(): void { $proto = $this->activityReader->fromClass(DummyActivity::class)[0]; - $protoWithFactory = $proto->withFactory(fn () => new DummyActivity()); + $protoWithFactory = $proto->withFactory(static fn() => new DummyActivity()); $this->assertEquals($protoWithFactory->getInstance()->getContext(), $protoWithFactory->getInstance()->getContext()); $this->assertNotSame($protoWithFactory->getInstance()->getContext(), $protoWithFactory->getInstance()->getContext()); @@ -106,7 +107,7 @@ public function testFactoryCreatesNewInstances(): void public function testFactoryAcceptsReflectionClassOfActivity(): void { $proto = $this->activityReader->fromClass(DummyActivity::class)[0]; - $protoWithFactory = $proto->withFactory(fn (\ReflectionClass $reflectionClass) => $reflectionClass->newInstance()); + $protoWithFactory = $proto->withFactory(static fn(\ReflectionClass $reflectionClass) => $reflectionClass->newInstance()); $this->assertEquals($protoWithFactory->getInstance()->getContext(), $protoWithFactory->getInstance()->getContext()); $this->assertNotSame($protoWithFactory->getInstance()->getContext(), $protoWithFactory->getInstance()->getContext()); @@ -115,9 +116,15 @@ public function testFactoryAcceptsReflectionClassOfActivity(): void public function testGetFactory(): void { $proto = $this->activityReader->fromClass(DummyActivity::class)[0]; - $protoWithFactory = $proto->withFactory(fn (\ReflectionClass $reflectionClass) => $reflectionClass->newInstance()); + $protoWithFactory = $proto->withFactory(static fn(\ReflectionClass $reflectionClass) => $reflectionClass->newInstance()); $this->assertNull($proto->getFactory()); $this->assertNotNull($protoWithFactory->getFactory()); } + + protected function setUp(): void + { + $this->activityReader = new ActivityReader(new SelectiveReader([new AnnotationReader(), new AttributeReader()])); + parent::setUp(); + } } diff --git a/tests/Unit/Activity/DummyActivity.php b/tests/Unit/Activity/DummyActivity.php index 59c81444e..6aff7ddb7 100644 --- a/tests/Unit/Activity/DummyActivity.php +++ b/tests/Unit/Activity/DummyActivity.php @@ -11,7 +11,5 @@ final class DummyActivity { #[ActivityMethod(name: "DoNothing")] - public function doNothing(): void - { - } + public function doNothing(): void {} } diff --git a/tests/Unit/Activity/MagicActivity.php b/tests/Unit/Activity/MagicActivity.php new file mode 100644 index 000000000..58c0af18e --- /dev/null +++ b/tests/Unit/Activity/MagicActivity.php @@ -0,0 +1,19 @@ +services->activities->addFinalizer( + static function () use (&$finalizerWasCalled): void { + $finalizerWasCalled = true; + }, + ); + + $this->activityContext->getInfo()->type->name = 'DummyActivityDoNothing'; + $request = new Request('DummyActivityDoNothing', EncodedValues::fromValues([])); + $this->router->handle($request, [], new Deferred()); + $this->assertTrue($finalizerWasCalled); + } + + public function testFinalizerIsCalledOnFailedActivityInvocation(): void + { + $finalizerWasCalled = false; + $this->services->activities->addFinalizer( + function (\Throwable $error) use (&$finalizerWasCalled): void { + $finalizerWasCalled = true; + $this->assertInstanceOf(\RuntimeException::class, $error); + $this->assertSame('Failed', $error->getMessage()); + }, + ); + + $this->activityContext->getInfo()->type->name = 'DummyActivityDoFail'; + $request = new Request('DummyActivityDoFail', EncodedValues::fromValues([])); + $this->router->handle($request, [], new Deferred()); + $this->assertTrue($finalizerWasCalled); + } + protected function setUp(): void { $rpc = $this->createMock(RPCConnectionInterface::class); @@ -70,37 +101,4 @@ protected function setUp(): void parent::setUp(); } - - - public function testFinalizerIsCalledOnSuccessActivityInvocation(): void - { - $finalizerWasCalled = false; - $this->services->activities->addFinalizer( - function () use (&$finalizerWasCalled) { - $finalizerWasCalled = true; - } - ); - - $this->activityContext->getInfo()->type->name = 'DummyActivityDoNothing'; - $request = new Request('DummyActivityDoNothing', EncodedValues::fromValues([])); - $this->router->handle($request, [], new Deferred()); - $this->assertTrue($finalizerWasCalled); - } - - public function testFinalizerIsCalledOnFailedActivityInvocation(): void - { - $finalizerWasCalled = false; - $this->services->activities->addFinalizer( - function (\Throwable $error) use (&$finalizerWasCalled) { - $finalizerWasCalled = true; - $this->assertInstanceOf(RuntimeException::class, $error); - $this->assertSame('Failed', $error->getMessage()); - } - ); - - $this->activityContext->getInfo()->type->name = 'DummyActivityDoFail'; - $request = new Request('DummyActivityDoFail', EncodedValues::fromValues([])); - $this->router->handle($request, [], new Deferred()); - $this->assertTrue($finalizerWasCalled); - } }