From 1ef2123269e5b68bd1b7c2a8294f95d76f039f4d Mon Sep 17 00:00:00 2001 From: Mior Muhammad Zaki Date: Thu, 23 Nov 2023 11:40:06 +0800 Subject: [PATCH] [10.x] Throw exception when trying to initiate `Collection` using `WeakMap` fixes laravel/framework#49089 Signed-off-by: Mior Muhammad Zaki --- .../Collections/Traits/EnumeratesValues.php | 3 ++ .../ShouldDispatchAfterCommitEventTest.php | 46 ++++++++++++++++--- tests/Support/SupportCollectionTest.php | 18 +++++++- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/Illuminate/Collections/Traits/EnumeratesValues.php b/src/Illuminate/Collections/Traits/EnumeratesValues.php index b9aee097b123..32fbb33124d4 100644 --- a/src/Illuminate/Collections/Traits/EnumeratesValues.php +++ b/src/Illuminate/Collections/Traits/EnumeratesValues.php @@ -11,11 +11,13 @@ use Illuminate\Support\Collection; use Illuminate\Support\Enumerable; use Illuminate\Support\HigherOrderCollectionProxy; +use InvalidArgumentException; use JsonSerializable; use Symfony\Component\VarDumper\VarDumper; use Traversable; use UnexpectedValueException; use UnitEnum; +use WeakMap; /** * @template TKey of array-key @@ -1019,6 +1021,7 @@ protected function getArrayableItems($items) } return match (true) { + $items instanceof WeakMap => throw new InvalidArgumentException('Unable to resolve array from instance of WeakMap.'), $items instanceof Enumerable => $items->all(), $items instanceof Arrayable => $items->toArray(), $items instanceof Traversable => iterator_to_array($items), diff --git a/tests/Integration/Events/ShouldDispatchAfterCommitEventTest.php b/tests/Integration/Events/ShouldDispatchAfterCommitEventTest.php index e20d65891a23..f948c9341af2 100644 --- a/tests/Integration/Events/ShouldDispatchAfterCommitEventTest.php +++ b/tests/Integration/Events/ShouldDispatchAfterCommitEventTest.php @@ -3,20 +3,32 @@ namespace Illuminate\Tests\Integration\Events; use Illuminate\Contracts\Events\ShouldDispatchAfterCommit; +use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Event; -use Mockery as m; +use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\TestCase; +#[WithMigration('queue')] class ShouldDispatchAfterCommitEventTest extends TestCase { - protected function tearDown(): void + use DatabaseMigrations; + + protected function setUp(): void { TransactionUnawareTestEvent::$ran = false; ShouldDispatchAfterCommitTestEvent::$ran = false; AnotherShouldDispatchAfterCommitTestEvent::$ran = false; - m::close(); + parent::setUp(); + } + + protected function defineEnvironment($app) + { + $app['config']->set([ + 'database.default' => 'testing', + 'queue.default' => 'database', + ]); } public function testEventIsDispatchedIfThereIsNoTransaction() @@ -112,6 +124,11 @@ public function testItOnlyDispatchesNestedTransactionsEventsAfterTheRootTransact Event::listen(AnotherShouldDispatchAfterCommitTestEvent::class, AnotherShouldDispatchAfterCommitListener::class); DB::transaction(function () { + DB::afterCommit(function () { + // The main difference with this test is that we dispatch an event on the parent transaction + // at the end. This is important due to how the DatabaseTransactionsManager works. + Event::dispatch(new AnotherShouldDispatchAfterCommitTestEvent); + }); DB::transaction(function () { Event::dispatch(new ShouldDispatchAfterCommitTestEvent); }); @@ -120,9 +137,9 @@ public function testItOnlyDispatchesNestedTransactionsEventsAfterTheRootTransact // The event dispatched on the child transaction should not have been dispatched. $this->assertFalse(ShouldDispatchAfterCommitTestEvent::$ran); - // The main difference with this test is that we dispatch an event on the parent transaction - // at the end. This is important due to how the DatabaseTransactionsManager works. - Event::dispatch(new AnotherShouldDispatchAfterCommitTestEvent); + + $this->assertFalse(ShouldDispatchAfterCommitTestEvent::$ran); + $this->assertFalse(AnotherShouldDispatchAfterCommitTestEvent::$ran); }); // Now that the parent transaction has been committed, the event @@ -130,6 +147,23 @@ public function testItOnlyDispatchesNestedTransactionsEventsAfterTheRootTransact $this->assertTrue(ShouldDispatchAfterCommitTestEvent::$ran); $this->assertTrue(AnotherShouldDispatchAfterCommitTestEvent::$ran); } + + public function testItOnlyDispatchesEventsAfterTheRootTransactionIsCommitted() + { + Event::listen(ShouldDispatchAfterCommitTestEvent::class, ShouldDispatchAfterCommitListener::class); + + DB::transaction(function () { + DB::transaction(function () {}); + + DB::afterCommit( + fn () => Event::dispatch(new ShouldDispatchAfterCommitTestEvent) + ); + + $this->assertFalse(ShouldDispatchAfterCommitTestEvent::$ran); + }); + + $this->assertTrue(ShouldDispatchAfterCommitTestEvent::$ran); + } } class TransactionUnawareTestEvent diff --git a/tests/Support/SupportCollectionTest.php b/tests/Support/SupportCollectionTest.php index 6efeeba1be36..7a8e2172a407 100755 --- a/tests/Support/SupportCollectionTest.php +++ b/tests/Support/SupportCollectionTest.php @@ -19,12 +19,14 @@ use IteratorAggregate; use JsonSerializable; use Mockery as m; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use ReflectionClass; -use stdClass; use Symfony\Component\VarDumper\VarDumper; use Traversable; use UnexpectedValueException; +use stdClass; +use WeakMap; include_once 'Enums.php'; @@ -2957,6 +2959,20 @@ public function testConstructMethodFromObject($collection) $this->assertEquals(['foo' => 'bar'], $data->all()); } + #[DataProvider('collectionClassProvider')] + public function testConstructMethodFromWeakMap($collection) + { + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage('Unable to resolve array from instance of WeakMap.'); + + $map = new WeakMap(); + $object = new stdClass; + $object->foo = 'bar'; + $map[$object] = 3; + + $data = new $collection($map); + } + public function testSplice() { $data = new Collection(['foo', 'baz']);