From e7d4091805a1d0280539d11aa1029d688f38e084 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish Date: Thu, 9 Oct 2025 16:09:12 -0400 Subject: [PATCH 01/11] contextable --- .../Log/Context/ContextLogProcessor.php | 2 +- .../Log/Context/Contracts/Contextable.php | 8 ++ src/Illuminate/Log/Context/Repository.php | 71 +++++++++++++++++- tests/Log/ContextTest.php | Bin 23551 -> 25225 bytes 4 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 src/Illuminate/Log/Context/Contracts/Contextable.php diff --git a/src/Illuminate/Log/Context/ContextLogProcessor.php b/src/Illuminate/Log/Context/ContextLogProcessor.php index 9ac3e97a77dd..c325a19b2820 100644 --- a/src/Illuminate/Log/Context/ContextLogProcessor.php +++ b/src/Illuminate/Log/Context/ContextLogProcessor.php @@ -25,7 +25,7 @@ public function __invoke(LogRecord $record): LogRecord return $record->with(extra: [ ...$record->extra, - ...$app->get(ContextRepository::class)->all(), + ...$app->get(ContextRepository::class)->allWithContextables(), ]); } } diff --git a/src/Illuminate/Log/Context/Contracts/Contextable.php b/src/Illuminate/Log/Context/Contracts/Contextable.php new file mode 100644 index 000000000000..5a90f99704c1 --- /dev/null +++ b/src/Illuminate/Log/Context/Contracts/Contextable.php @@ -0,0 +1,8 @@ +, Contracts\Contextable> + */ + protected $contextables = []; + /** * The callback that should handle unserialize exceptions. * @@ -109,6 +115,17 @@ public function all() return $this->data; } + public function allWithContextables() + { + $data = $this->data; + + foreach($this->contextables as $contextable) { + $data = array_merge($data, $contextable->data()); + } + + return $data; + } + /** * Retrieve all the hidden context data. * @@ -370,6 +387,48 @@ public function push($key, ...$values) return $this; } + /** + * Register a contextable. + * + * @param array|Contextable $contextable + * @return $this + */ + public function contextable($contextable) + { + $contextables = is_array($contextable) ? $contextable : [$contextable]; + + foreach($contextables as $contextable) { + $this->contextables[$contextable::class] = $contextable; + } + + return $this; + } + + /** + * Retrieve all registered contextables. + * + * @return array, Contextable> + */ + public function getContextables() + { + return $this->contextables; + } + + /** + * Remove a Contextable. + * + * @param class-string|Contextable $contextable + * @return $this + */ + public function forgetContextable($contextable) + { + $class = is_string($contextable) ? $contextable : $contextable::class; + + unset($this->contextables[$class]); + + return $this; + } + /** * Pop the latest value from the key's stack. * @@ -572,7 +631,7 @@ public function scope(callable $callback, array $data = [], array $hidden = []) */ public function isEmpty() { - return $this->all() === [] && $this->allHidden() === []; + return $this->all() === [] && $this->allHidden() === [] && $this->getContextables() === []; } /** @@ -623,6 +682,7 @@ public function flush() { $this->data = []; $this->hidden = []; + $this->contextables = []; return $this; } @@ -638,7 +698,8 @@ public function dehydrate() { $instance = (new static($this->events)) ->add($this->all()) - ->addHidden($this->allHidden()); + ->addHidden($this->allHidden()) + ->contextable($this->getContextables()); $instance->events->dispatch(new Dehydrating($instance)); @@ -647,6 +708,7 @@ public function dehydrate() return $instance->isEmpty() ? null : [ 'data' => array_map($serialize, $instance->all()), 'hidden' => array_map($serialize, $instance->allHidden()), + 'contextables' => array_map($serialize, $instance->getContextables()), ]; } @@ -686,13 +748,14 @@ public function hydrate($context) } }; - [$data, $hidden] = [ + [$data, $hidden, $contextable] = [ (new Collection($context['data'] ?? []))->map(fn ($value, $key) => $unserialize($value, $key, false))->all(), (new Collection($context['hidden'] ?? []))->map(fn ($value, $key) => $unserialize($value, $key, true))->all(), + (new Collection($context['contextables'] ?? []))->map(fn ($value, $key) => $unserialize($value, $key, false))->all(), ]; $this->events->dispatch(new Hydrated( - $this->flush()->add($data)->addHidden($hidden) + $this->flush()->add($data)->addHidden($hidden)->contextable($contextable) )); return $this; diff --git a/tests/Log/ContextTest.php b/tests/Log/ContextTest.php index 23d54e0d695111fe70784a5c437f6d5724fff660..bcda69eb076f1f8168731f3af3900750e8bd38cc 100644 GIT binary patch delta 1288 zcmaJ>&rcIU6s81_v>GT-1cGGPY}lz5r4|TG(_YkIVvHmxY6{}gj&zf4x7nG3wP_5A zi81k@dH;j)V2p7Oo=m)W@FrJp9*kE=Jgd9Y(hjR-vxm+5@xAxH_r3k|A@J;T;N5T_ za>*)kThDT~=6%tJT;DV)qyy(1HE^PD88sd?+w!6mD$*TvlQJ&lV2x4R&`sl!&JC-G zxa-zpNaJ~fA@?nnH%%i8dpI&8VPP2<4ni}|o&MLIVXvwtg)#UzaC*{Lk&vvZGlo)W zq}?Eid7``w4~?(+q7-tGR2i8~5T(QQ-}n1Fwso^il_Vqa1)}^rgYe9`9;-l?#SE*> zgju2-9ZyNt;OEr|=o-wxtFZ{YUFw;@ne`ZI5Jkuql}Te5mQTi{0a>vBtBi1 zb=%e}8xIVdaoiqYw|CU5v(RP%YL4bBIa}v6C!tTL-A2S97#aWs`*!7SuU)c_4A`4G z>*&Kj#^FgQjI%AN;QtCGPfgR(qHunmLMpimzY|~jVoH4vL=j%Nw^)Pyl`dy#u1|$; zQ9V@ta>dd07z*vKG(FG72S=7HE;PL&Ak=^gM6YC0{z qk|L_+)n3i%T*pyis5WcZl$Y%y$}|n)ztN&xmB3n^tv7IkwcuYM_PVbC delta 76 zcmV-S0JHyz#R31f0k9SUlZ^omvsM8I50fw$6q7m(8M6uzr~;F@6kn5u3@EdG6|(}9 iBnv0AQ5oeNlU+s$vwJpp0h3Tie6xQz6C|^bUa11zaT+lI From bb85f84e6c5b1039a14230b062d62fe5ab9f8b94 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish Date: Thu, 9 Oct 2025 16:16:01 -0400 Subject: [PATCH 02/11] rename method --- .../Log/Context/Contracts/Contextable.php | 7 ++++++- src/Illuminate/Log/Context/Repository.php | 2 +- tests/Log/ContextTest.php | Bin 25225 -> 25232 bytes 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Log/Context/Contracts/Contextable.php b/src/Illuminate/Log/Context/Contracts/Contextable.php index 5a90f99704c1..8028828e8219 100644 --- a/src/Illuminate/Log/Context/Contracts/Contextable.php +++ b/src/Illuminate/Log/Context/Contracts/Contextable.php @@ -4,5 +4,10 @@ interface Contextable { - public function data(); + /** + * The data to append to your log output. + * + * @return array + */ + public function contextData(); } diff --git a/src/Illuminate/Log/Context/Repository.php b/src/Illuminate/Log/Context/Repository.php index b1b7ca2c5666..b4749e8ab0eb 100644 --- a/src/Illuminate/Log/Context/Repository.php +++ b/src/Illuminate/Log/Context/Repository.php @@ -120,7 +120,7 @@ public function allWithContextables() $data = $this->data; foreach($this->contextables as $contextable) { - $data = array_merge($data, $contextable->data()); + $data = array_merge($data, $contextable->contextData()); } return $data; diff --git a/tests/Log/ContextTest.php b/tests/Log/ContextTest.php index bcda69eb076f1f8168731f3af3900750e8bd38cc..fb72e35a73dda2909e1b89606e06082832e7d0f6 100644 GIT binary patch delta 22 dcmeA?$~fUD;|Aj-j^zBjlGKV4m&qncp#W&R2)Y0O delta 15 WcmbPml(F+D;|Aj-#+1p{NudBT#Rb&> From 41be7dc532eb438312dd3d90ff39c9e6c9aae767 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish Date: Thu, 9 Oct 2025 16:31:27 -0400 Subject: [PATCH 03/11] failing test --- tests/Integration/Log/ContextIntegrationTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Integration/Log/ContextIntegrationTest.php b/tests/Integration/Log/ContextIntegrationTest.php index 2d7fe074f0e5..db3e5699fcc9 100644 --- a/tests/Integration/Log/ContextIntegrationTest.php +++ b/tests/Integration/Log/ContextIntegrationTest.php @@ -21,6 +21,7 @@ public function test_it_can_hydrate_null() { Context::hydrate(null); $this->assertEquals([], Context::all()); + $this->assertEquals([], Context::getContextables()); } public function test_it_handles_eloquent() @@ -37,6 +38,7 @@ public function test_it_handles_eloquent() 'number' => 'i:55;', ], 'hidden' => [], + 'contextables' => [], ], $dehydrated); Context::flush(); From 017b5d7aa2561ef8ecff512be16f53fb13eff758 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish Date: Thu, 9 Oct 2025 22:34:57 -0400 Subject: [PATCH 04/11] some PR feedback --- .../Log/Context/ContextLogProcessor.php | 2 +- .../Log/Context/Contracts/Contextable.php | 5 +- src/Illuminate/Log/Context/Repository.php | 43 ++++++++++-------- tests/Log/ContextTest.php | Bin 25232 -> 25204 bytes 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/Illuminate/Log/Context/ContextLogProcessor.php b/src/Illuminate/Log/Context/ContextLogProcessor.php index c325a19b2820..9ac3e97a77dd 100644 --- a/src/Illuminate/Log/Context/ContextLogProcessor.php +++ b/src/Illuminate/Log/Context/ContextLogProcessor.php @@ -25,7 +25,7 @@ public function __invoke(LogRecord $record): LogRecord return $record->with(extra: [ ...$record->extra, - ...$app->get(ContextRepository::class)->allWithContextables(), + ...$app->get(ContextRepository::class)->all(), ]); } } diff --git a/src/Illuminate/Log/Context/Contracts/Contextable.php b/src/Illuminate/Log/Context/Contracts/Contextable.php index 8028828e8219..e88e2a1105f0 100644 --- a/src/Illuminate/Log/Context/Contracts/Contextable.php +++ b/src/Illuminate/Log/Context/Contracts/Contextable.php @@ -7,7 +7,8 @@ interface Contextable /** * The data to append to your log output. * - * @return array + * @param \Illuminate\Log\Context\Repository $repository + * @return array|null */ - public function contextData(); + public function context($repository); } diff --git a/src/Illuminate/Log/Context/Repository.php b/src/Illuminate/Log/Context/Repository.php index b4749e8ab0eb..07e793745fb2 100644 --- a/src/Illuminate/Log/Context/Repository.php +++ b/src/Illuminate/Log/Context/Repository.php @@ -42,7 +42,7 @@ class Repository protected $hidden = []; /** - * @var array, Contracts\Contextable> + * @var array */ protected $contextables = []; @@ -111,16 +111,11 @@ public function missingHidden($key) * @return array */ public function all() - { - return $this->data; - } - - public function allWithContextables() { $data = $this->data; foreach($this->contextables as $contextable) { - $data = array_merge($data, $contextable->contextData()); + $data = array_merge($data, $contextable->context($this) ?? []); } return $data; @@ -235,16 +230,20 @@ public function exceptHidden($keys) /** * Add a context value. * - * @param string|array $key + * @param string|array|\Illuminate\Log\Context\Contracts\Contextable $key * @param mixed $value * @return $this */ public function add($key, $value = null) { - $this->data = array_merge( - $this->data, - is_array($key) ? $key : [$key => $value] - ); + if ($key instanceof Contextable) { + $this->contextables[] = $key; + } else { + $this->data = array_merge( + $this->data, + is_array($key) ? $key : [$key => $value] + ); + } return $this; } @@ -398,7 +397,7 @@ public function contextable($contextable) $contextables = is_array($contextable) ? $contextable : [$contextable]; foreach($contextables as $contextable) { - $this->contextables[$contextable::class] = $contextable; + $this->contextables[] = $contextable; } return $this; @@ -407,7 +406,7 @@ public function contextable($contextable) /** * Retrieve all registered contextables. * - * @return array, Contextable> + * @return array */ public function getContextables() { @@ -417,14 +416,18 @@ public function getContextables() /** * Remove a Contextable. * - * @param class-string|Contextable $contextable + * @param class-string<\Illuminate\Log\Context\Contracts\Contextable>|\Illuminate\Log\Context\Contracts\Contextable $contextable * @return $this */ - public function forgetContextable($contextable) + public function forgetContextable($contextableToRemove) { - $class = is_string($contextable) ? $contextable : $contextable::class; + foreach($this->contextables as $i => $contextable) { + if ((is_string($contextableToRemove) && is_a($contextable, $contextableToRemove, true)) || ($contextableToRemove === $contextable)) { + unset($this->contextables[$i]); + } + } - unset($this->contextables[$class]); + $this->contextables = array_values($this->contextables); return $this; } @@ -697,7 +700,7 @@ public function flush() public function dehydrate() { $instance = (new static($this->events)) - ->add($this->all()) + ->add($this->data) ->addHidden($this->allHidden()) ->contextable($this->getContextables()); @@ -706,7 +709,7 @@ public function dehydrate() $serialize = fn ($value) => serialize($instance->getSerializedPropertyValue($value, withRelations: false)); return $instance->isEmpty() ? null : [ - 'data' => array_map($serialize, $instance->all()), + 'data' => array_map($serialize, $instance->data), 'hidden' => array_map($serialize, $instance->allHidden()), 'contextables' => array_map($serialize, $instance->getContextables()), ]; diff --git a/tests/Log/ContextTest.php b/tests/Log/ContextTest.php index fb72e35a73dda2909e1b89606e06082832e7d0f6..6fffbf3738dee17752313fa503d1300fadeddd8b 100644 GIT binary patch delta 62 zcmbPml<~_E#tpZGS=IfmjLjyW7ZTqrAoAIBa-SaOWDQT=&6;t7EFu~zMX3e(#hE4f SMU|RX3b~mTsVS2kk^%tE3KjqW delta 69 zcmexzgmJ=A#tpZGB|}n+OPurbN>VFI5|eULt*nxB5{ru!Z0$DR6c+Ge3C}Fam>eJ> ZIr)t)=jQ*hf-Gzhp From cec331b49282999703aed99335c509f734028a70 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish Date: Fri, 10 Oct 2025 08:55:01 -0400 Subject: [PATCH 05/11] docblocks and integration test --- src/Illuminate/Log/Context/Repository.php | 4 +- .../Log/ContextIntegrationTest.php | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Log/Context/Repository.php b/src/Illuminate/Log/Context/Repository.php index 07e793745fb2..c733c75e2924 100644 --- a/src/Illuminate/Log/Context/Repository.php +++ b/src/Illuminate/Log/Context/Repository.php @@ -42,7 +42,7 @@ class Repository protected $hidden = []; /** - * @var array + * @var list<\Illuminate\Log\Context\Contracts\Contextable> */ protected $contextables = []; @@ -653,7 +653,7 @@ public function dehydrating($callback) /** * Execute the given callback when context has been hydrated. * - * @param callable $callback + * @param callable(\Illuminate\Log\Context\Repository): mixed $callback * @return $this */ public function hydrated($callback) diff --git a/tests/Integration/Log/ContextIntegrationTest.php b/tests/Integration/Log/ContextIntegrationTest.php index db3e5699fcc9..756090175265 100644 --- a/tests/Integration/Log/ContextIntegrationTest.php +++ b/tests/Integration/Log/ContextIntegrationTest.php @@ -6,6 +6,10 @@ use Illuminate\Database\Eloquent\Collection as EloquentCollection; use Illuminate\Foundation\Auth\User; use Illuminate\Foundation\Testing\LazilyRefreshDatabase; +use Illuminate\Log\Context\Contracts\Contextable; +use Illuminate\Log\Context\Repository; +use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Context; use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\Factories\UserFactory; @@ -151,4 +155,49 @@ public function test_it_can_handle_unserialize_exceptions_manually() Context::handleUnserializeExceptionsUsing(null); } + + public function test_it_can_serialize_a_contextable_object() + { + $user = UserFactory::new()->create(['id' => 99, 'name' => 'Luke']); + Context::add(new MyContextableClass($user, 'you have been replaced')); + + $dehydrated = Context::dehydrate(); + + $this->assertEquals([ + 'data' => [], + 'hidden' => [], + 'contextables' => [ + 'O:51:"Illuminate\Tests\Integration\Log\MyContextableClass":2:{s:4:"user";O:45:"Illuminate\Contracts\Database\ModelIdentifier":5:{s:5:"class";s:31:"Illuminate\Foundation\Auth\User";s:2:"id";i:99;s:9:"relations";a:0:{}s:10:"connection";s:7:"testing";s:15:"collectionClass";N;}s:5:"other";s:22:"you have been replaced";}', + ], + ], $dehydrated); + + Context::hydrated(function (Repository $context) { + App::instance(MyContextableClass::class, $context->getContextables()[0]); + }); + + Context::hydrate($dehydrated); + + $this->assertSame(resolve(MyContextableClass::class), $contextable = Context::getContextables()[0]); + $this->assertTrue($user->is($contextable->user)); + } +} + +class MyContextableClass implements Contextable +{ + use SerializesModels; + + public function __construct( + public readonly User $user, + public readonly string $other = 'replace me', + ) { + } + + #[\Override] + public function context($repository) + { + return [ + 'user_id' => $this->user->id, + 'other' => $this->other, + ]; + } } From 42bc349a8ce867279135b122073c737e03c13905 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish Date: Fri, 10 Oct 2025 08:59:46 -0400 Subject: [PATCH 06/11] integration test addition --- tests/Integration/Log/ContextIntegrationTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Integration/Log/ContextIntegrationTest.php b/tests/Integration/Log/ContextIntegrationTest.php index 756090175265..1c232f10c74b 100644 --- a/tests/Integration/Log/ContextIntegrationTest.php +++ b/tests/Integration/Log/ContextIntegrationTest.php @@ -171,6 +171,8 @@ public function test_it_can_serialize_a_contextable_object() ], ], $dehydrated); + $this->assertEquals(['user_id' => 99, 'other' => 'you have been replaced'], Context::all()); + Context::hydrated(function (Repository $context) { App::instance(MyContextableClass::class, $context->getContextables()[0]); }); From f174873fb622e30fb3681ac505ce57d7c690c2f9 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish Date: Fri, 10 Oct 2025 09:02:05 -0400 Subject: [PATCH 07/11] no more binary --- tests/Log/ContextTest.php | Bin 25204 -> 25164 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/Log/ContextTest.php b/tests/Log/ContextTest.php index 6fffbf3738dee17752313fa503d1300fadeddd8b..a9c11420750038e6676e2e5dca695eb0a3535303 100644 GIT binary patch delta 29 lcmexzgz?M~#tl3o%%)aKo6SUwd|6AAax#-Q=OwZ+0sx~33CI8d delta 38 ucmX?egz?J}#tl3otR}`*N(_@93ac Date: Fri, 10 Oct 2025 09:03:07 -0400 Subject: [PATCH 08/11] test all --- tests/Log/ContextTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Log/ContextTest.php b/tests/Log/ContextTest.php index a9c114207500..7a707d354d09 100644 --- a/tests/Log/ContextTest.php +++ b/tests/Log/ContextTest.php @@ -721,6 +721,7 @@ public function test_it_can_store_contextable() $this->assertEquals(['value' => 'hello', 'woody' => 'guthrie'], Context::all()); Context::forgetContextable($contextable::class); $this->assertEmpty(Context::getContextables()); + $this->assertEquals(['woody' => 'guthrie'], Context::all()); } } From 68aa8583f9a35250f891264dfd5037020a103947 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish <42181698+cosmastech@users.noreply.github.com> Date: Fri, 10 Oct 2025 09:42:42 -0400 Subject: [PATCH 09/11] use list --- src/Illuminate/Log/Context/Repository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Log/Context/Repository.php b/src/Illuminate/Log/Context/Repository.php index c733c75e2924..25e53431a98a 100644 --- a/src/Illuminate/Log/Context/Repository.php +++ b/src/Illuminate/Log/Context/Repository.php @@ -406,7 +406,7 @@ public function contextable($contextable) /** * Retrieve all registered contextables. * - * @return array + * @return list<\Illuminate\Log\Context\Contracts\Contextable> */ public function getContextables() { From 8a931c0b88c8181b4018249471fc2be966f4a095 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish <42181698+cosmastech@users.noreply.github.com> Date: Fri, 10 Oct 2025 09:43:45 -0400 Subject: [PATCH 10/11] fix docblock --- src/Illuminate/Log/Context/Repository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Log/Context/Repository.php b/src/Illuminate/Log/Context/Repository.php index 25e53431a98a..d4c9a7eab426 100644 --- a/src/Illuminate/Log/Context/Repository.php +++ b/src/Illuminate/Log/Context/Repository.php @@ -416,7 +416,7 @@ public function getContextables() /** * Remove a Contextable. * - * @param class-string<\Illuminate\Log\Context\Contracts\Contextable>|\Illuminate\Log\Context\Contracts\Contextable $contextable + * @param class-string<\Illuminate\Log\Context\Contracts\Contextable>|\Illuminate\Log\Context\Contracts\Contextable $contextableToRemove * @return $this */ public function forgetContextable($contextableToRemove) From 90f5dee9cfedc4d965dad86cb08e715e42217795 Mon Sep 17 00:00:00 2001 From: Luke Kuzmish Date: Fri, 10 Oct 2025 09:51:53 -0400 Subject: [PATCH 11/11] safety through runtime checks --- src/Illuminate/Log/Context/Repository.php | 7 +++++++ tests/Log/ContextTest.php | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/Illuminate/Log/Context/Repository.php b/src/Illuminate/Log/Context/Repository.php index d4c9a7eab426..ca77c37344a4 100644 --- a/src/Illuminate/Log/Context/Repository.php +++ b/src/Illuminate/Log/Context/Repository.php @@ -13,6 +13,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Traits\Conditionable; use Illuminate\Support\Traits\Macroable; +use InvalidArgumentException; use RuntimeException; use Throwable; @@ -391,12 +392,18 @@ public function push($key, ...$values) * * @param array|Contextable $contextable * @return $this + * + * @throws \InvalidArgumentException */ public function contextable($contextable) { $contextables = is_array($contextable) ? $contextable : [$contextable]; foreach($contextables as $contextable) { + if (! $contextable instanceof Contextable) { + throw new InvalidArgumentException('Only Contextable classes can be registered.'); + } + $this->contextables[] = $contextable; } diff --git a/tests/Log/ContextTest.php b/tests/Log/ContextTest.php index 7a707d354d09..38e9d79c522d 100644 --- a/tests/Log/ContextTest.php +++ b/tests/Log/ContextTest.php @@ -15,6 +15,7 @@ use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; +use InvalidArgumentException; use Monolog\LogRecord; use Orchestra\Testbench\TestCase; use RuntimeException; @@ -723,6 +724,16 @@ public function test_it_can_store_contextable() $this->assertEmpty(Context::getContextables()); $this->assertEquals(['woody' => 'guthrie'], Context::all()); } + + public function test_registering_a_non_contextable_throws_exception() + { + try { + Context::contextable((object) ['foo' => 'bar']); + $this->fail('Did not throw expected exception'); + } catch (InvalidArgumentException $e) { + $this->assertSame('Only Contextable classes can be registered.', $e->getMessage()); + } + } } enum Suit