From f6a95308e9b26909e973dbd5ee4c8b844c26778b Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 18 Feb 2026 10:04:13 +0530 Subject: [PATCH 01/16] Added Context class --- app/Config/Logger.php | 18 ++++ system/Config/Services.php | 15 +++ system/Context/Context.php | 189 +++++++++++++++++++++++++++++++++++++ system/Log/Logger.php | 15 +++ 4 files changed, 237 insertions(+) create mode 100644 system/Context/Context.php diff --git a/app/Config/Logger.php b/app/Config/Logger.php index 799dc2c39080..d971ecc43fae 100644 --- a/app/Config/Logger.php +++ b/app/Config/Logger.php @@ -51,6 +51,24 @@ class Logger extends BaseConfig */ public string $dateFormat = 'Y-m-d H:i:s'; + /** + * -------------------------------------------------------------------------- + * Whether to log the global context + * -------------------------------------------------------------------------- + * + * You can enable/disable logging of global context data, which comes from the + * `CodeIgniter\Context\Context` class. This data is automatically included in + * logs, and can be set using the `set()` method of the Context class. This is + * useful for including additional information in your logs, such as user IDs, + * request IDs, etc. + * + * **NOTE:** This **DOES NOT** include any data that has been marked as hidden + * using the `setHidden()` method of the Context class. + * + * @var bool + */ + public bool $logGlobalContext = false; + /** * -------------------------------------------------------------------------- * Log Handlers diff --git a/system/Config/Services.php b/system/Config/Services.php index 3878911c8cbf..07f5d9bd2398 100644 --- a/system/Config/Services.php +++ b/system/Config/Services.php @@ -18,6 +18,7 @@ use CodeIgniter\Cache\ResponseCache; use CodeIgniter\CLI\Commands; use CodeIgniter\CodeIgniter; +use CodeIgniter\Context\Context; use CodeIgniter\Database\ConnectionInterface; use CodeIgniter\Database\MigrationRunner; use CodeIgniter\Debug\Exceptions; @@ -872,4 +873,18 @@ public static function typography(bool $getShared = true) return new Typography(); } + + /** + * The Context class provides a way to store and retrieve static data throughout requests. + * + * @return Context + */ + public static function context(bool $getShared = true) + { + if ($getShared) { + return static::getSharedInstance('context'); + } + + return new Context(); + } } diff --git a/system/Context/Context.php b/system/Context/Context.php new file mode 100644 index 000000000000..208880f98e5c --- /dev/null +++ b/system/Context/Context.php @@ -0,0 +1,189 @@ + + */ + protected array $data; + + /** + * The data that is stored, but not included in logs. + * + * @var array + */ + private array $hiddenData; + + /** + * Constructor + */ + public function __construct() + { + $this->data = []; + $this->hiddenData = []; + } + + /** + * Set a key-value pair to the context. + * + * @param string|array $key The key to identify the data. Can be a string or an array of key-value pairs. + * @param mixed $value The value to be stored in the context. + * @return $this + */ + public function set(string|array $key, mixed $value): self + { + if (is_array($key)) { + $this->data = array_merge($this->data, $key); + return $this; + } + + $this->data[$key] = $value; + return $this; + } + + /** + * Set a hidden key-value pair to the context. This data will not be included in logs. + * + * @param string|array $key The key to identify the data. Can be a string or an array of key-value pairs. + * @param mixed $value The value to be stored in the context. + * @return $this + */ + public function setHidden(string|array $key, mixed $value): self + { + if (is_array($key)) { + $this->hiddenData = array_merge($this->hiddenData, $key); + return $this; + } + + $this->hiddenData[$key] = $value; + return $this; + } + + /** + * Get a value from the context by its key, or return a default value if the key does not exist. + * + * @param string $key The key to identify the data. + * @param mixed|null $default The default value to return if the key does not exist in the context. + * @return mixed The value associated with the key, or the default value if the key does not exist. + */ + public function get(string $key, mixed $default = null): mixed + { + return $this->data[$key] ?? $default; + } + + /** + * Get all data from the context + * + * @return array An array of all key-value pairs in the context. + */ + public function getAll(): array + { + return $this->data; + } + + /** + * Get a hidden value from the context by its key, or return a default value if the key does not exist. + * + * @param string $key The key to identify the data. + * @param mixed|null $default The default value to return if the key does not exist in the context. + * @return mixed The value associated with the key, or the default value if the key does not exist. + */ + public function getHidden(string $key, mixed $default = null): mixed + { + return $this->hiddenData[$key] ?? $default; + } + + /** + * Get all hidden data from the context + * + * @return array An array of all key-value pairs in the hidden context. + */ + public function getAllHidden(): array + { + return $this->hiddenData; + } + + /** + * Check if a key exists in the context. + * + * @param string $key The key to check for existence in the context. + * @return bool True if the key exists in the context, false otherwise. + */ + public function has(string $key): bool + { + return array_key_exists($key, $this->data); + } + + /** + * Check if a key exists in the hidden context. + * + * @param string $key The key to check for existence in the hidden context. + * @return bool True if the key exists in the hidden context, false otherwise. + */ + public function hasHidden(string $key): bool + { + return array_key_exists($key, $this->hiddenData); + } + + /** + * Remove a key-value pair from the context by its key. + * + * @param string $key The key to identify the data to be removed from the context. + * @return $this + */ + public function remove(string $key): self + { + unset($this->data[$key]); + return $this; + } + + /** + * Remove a key-value pair from the hidden context by its key. + * + * @param string $key The key to identify the data to be removed from the hidden context. + * @return $this + */ + public function removeHidden(string $key): self + { + unset($this->hiddenData[$key]); + return $this; + } + + /** + * Clear all data from the context, including hidden data. + * + * @return $this + */ + public function clearAll(): self + { + $this->clear(); + $this->clearHidden(); + return $this; + } + + /** + * Clear all data from the context. + * + * @return $this + */ + public function clear(): self + { + $this->data = []; + return $this; + } + + /** + * Clear all hidden data from the context. + * + * @return $this + */ + public function clearHidden(): self + { + $this->hiddenData = []; + return $this; + } +} diff --git a/system/Log/Logger.php b/system/Log/Logger.php index 8308ddaf94f7..c1e868a944dd 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -114,6 +114,15 @@ class Logger implements LoggerInterface */ protected $cacheLogs = false; + /** + * Whether to log the global context data. + * + * Set in app/Config/Logger.php + * + * @var bool + */ + protected $logGlobalContext; + /** * Constructor. * @@ -154,6 +163,8 @@ public function __construct($config, bool $debug = CI_DEBUG) if ($this->cacheLogs) { $this->logCache = []; } + + $this->logGlobalContext = $config->logGlobalContext; } /** @@ -252,6 +263,10 @@ public function log($level, string|Stringable $message, array $context = []): vo $message = $this->interpolate($message, $context); + if ($this->logGlobalContext) { + $message .= ' ' . json_encode(service('context')->getAll()); + } + if ($this->cacheLogs) { $this->logCache[] = ['level' => $level, 'msg' => $message]; } From 30cdc191acff06a6cc24679add35de5cc9ca649d Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 18 Feb 2026 11:59:00 +0530 Subject: [PATCH 02/16] Added some convenient methods --- system/Context/Context.php | 104 ++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 6 deletions(-) diff --git a/system/Context/Context.php b/system/Context/Context.php index 208880f98e5c..b9b77ab7846f 100644 --- a/system/Context/Context.php +++ b/system/Context/Context.php @@ -34,7 +34,7 @@ public function __construct() * @param mixed $value The value to be stored in the context. * @return $this */ - public function set(string|array $key, mixed $value): self + public function set(string|array $key, mixed $value = null): self { if (is_array($key)) { $this->data = array_merge($this->data, $key); @@ -52,7 +52,7 @@ public function set(string|array $key, mixed $value): self * @param mixed $value The value to be stored in the context. * @return $this */ - public function setHidden(string|array $key, mixed $value): self + public function setHidden(string|array $key, mixed $value = null): self { if (is_array($key)) { $this->hiddenData = array_merge($this->hiddenData, $key); @@ -75,6 +75,34 @@ public function get(string $key, mixed $default = null): mixed return $this->data[$key] ?? $default; } + /** + * Get only the specified keys from the context. If a key does not exist, it will be ignored. + * + * @param string|array $keys An array of keys to retrieve from the context. + * @return array An array of key-value pairs for the specified keys that exist in the context. + */ + public function getOnly(string|array $keys): array + { + if (is_string($keys)) { + $keys = [$keys]; + } + return array_filter($this->data, fn($k) => in_array($k, $keys), ARRAY_FILTER_USE_KEY); + } + + /** + * Get all keys from the context except the specified keys. + * + * @param string|array $keys An array of keys to exclude from the context. + * @return array An array of key-value pairs for all keys in the context except the specified keys. + */ + public function getExcept(string|array $keys): array + { + if (is_string($keys)) { + $keys = [$keys]; + } + return array_filter($this->data, fn($k) => !in_array($k, $keys), ARRAY_FILTER_USE_KEY); + } + /** * Get all data from the context * @@ -97,6 +125,34 @@ public function getHidden(string $key, mixed $default = null): mixed return $this->hiddenData[$key] ?? $default; } + /** + * Get only the specified keys from the hidden context. If a key does not exist, it will be ignored. + * + * @param string|array $keys An array of keys to retrieve from the hidden context. + * @return array An array of key-value pairs for the specified keys that exist in the hidden context. + */ + public function getOnlyHidden(string|array $keys): array + { + if (is_string($keys)) { + $keys = [$keys]; + } + return array_filter($this->hiddenData, fn($k) => in_array($k, $keys), ARRAY_FILTER_USE_KEY); + } + + /** + * Get all keys from the hidden context except the specified keys. + * + * @param string|array $keys An array of keys to exclude from the hidden context. + * @return array An array of key-value pairs for all keys in the hidden context except the specified keys. + */ + public function getExceptHidden(string|array $keys): array + { + if (is_string($keys)) { + $keys = [$keys]; + } + return array_filter($this->hiddenData, fn($k) => !in_array($k, $keys), ARRAY_FILTER_USE_KEY); + } + /** * Get all hidden data from the context * @@ -107,6 +163,17 @@ public function getAllHidden(): array return $this->hiddenData; } + /** + * Check if a key does not exist in the context. Exactly the opposite of `has()`. + * + * @param string $key The key to check for non-existence in the context. + * @return bool True if the key does not exist in the context, false otherwise. + */ + public function missing(string $key): bool + { + return !$this->has($key); + } + /** * Check if a key exists in the context. * @@ -118,6 +185,17 @@ public function has(string $key): bool return array_key_exists($key, $this->data); } + /** + * Check if a key does not exist in the hidden context. Exactly the opposite of `hasHidden()`. + * + * @param string $key The key to check for non-existence in the hidden context. + * @return bool True if the key does not exist in the hidden context, false otherwise. + */ + public function missingHidden(string $key): bool + { + return !$this->hasHidden($key); + } + /** * Check if a key exists in the hidden context. * @@ -132,11 +210,18 @@ public function hasHidden(string $key): bool /** * Remove a key-value pair from the context by its key. * - * @param string $key The key to identify the data to be removed from the context. + * @param string|array $key The key to identify the data to be removed from the context. * @return $this */ - public function remove(string $key): self + public function remove(string|array $key): self { + if (is_array($key)) { + foreach ($key as $k) { + unset($this->data[$k]); + } + return $this; + } + unset($this->data[$key]); return $this; } @@ -144,11 +229,18 @@ public function remove(string $key): self /** * Remove a key-value pair from the hidden context by its key. * - * @param string $key The key to identify the data to be removed from the hidden context. + * @param string|array $key The key to identify the data to be removed from the hidden context. * @return $this */ - public function removeHidden(string $key): self + public function removeHidden(string|array $key): self { + if (is_array($key)) { + foreach ($key as $k) { + unset($this->hiddenData[$k]); + } + return $this; + } + unset($this->hiddenData[$key]); return $this; } From a1666782c9374e9ba6b7eb60154cd3daaf984b0b Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 18 Feb 2026 11:59:30 +0530 Subject: [PATCH 03/16] Enhance logger to properly include global context --- system/Log/Logger.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/system/Log/Logger.php b/system/Log/Logger.php index c1e868a944dd..22eb3de48637 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -264,7 +264,10 @@ public function log($level, string|Stringable $message, array $context = []): vo $message = $this->interpolate($message, $context); if ($this->logGlobalContext) { - $message .= ' ' . json_encode(service('context')->getAll()); + $globalContext = service('context')->getAll(); + if (is_array($globalContext) && $globalContext !== []) { + $message .= ' ' . json_encode($globalContext); + } } if ($this->cacheLogs) { From 43c2d5202ecca8768db9e79d01f42a250cbc69be Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 18 Feb 2026 11:59:51 +0530 Subject: [PATCH 04/16] Add unit tests for Context class functionality --- tests/system/Context/ContextTest.php | 409 +++++++++++++++++++++++++++ 1 file changed, 409 insertions(+) create mode 100644 tests/system/Context/ContextTest.php diff --git a/tests/system/Context/ContextTest.php b/tests/system/Context/ContextTest.php new file mode 100644 index 000000000000..d3c47b36cda0 --- /dev/null +++ b/tests/system/Context/ContextTest.php @@ -0,0 +1,409 @@ +assertSame([], $context->getAll()); + $this->assertSame([], $context->getAllHidden()); + } + + public function testSetAndGetSingleValue(): void + { + $context = service('context'); + $context->set('user_id', 123); + + $this->assertSame(123, $context->get('user_id')); + $this->assertNull($context->getHidden('user_id')); // Normal value should not be retrievable with getHidden() + } + + public function testSetAndGetMultipleValues(): void + { + $context = service('context'); + $context->set([ + 'user_id' => 123, + 'username' => 'john_doe', + ]); + + $this->assertSame(123, $context->get('user_id')); + $this->assertSame('john_doe', $context->get('username')); + $this->assertNull($context->getHidden('user_id')); + $this->assertNull($context->getHidden('username')); + } + + public function testSetAndGetSingleHiddenValue(): void + { + $context = service('context'); + $context->setHidden('api_key', 'secret'); + + $this->assertSame('secret', $context->getHidden('api_key')); + $this->assertNull($context->get('api_key')); // Hidden value should not be retrievable with get() + } + + public function testSetAndGetMultipleHiddenValues(): void + { + $context = service('context'); + $context->setHidden([ + 'api_key' => 'secret', + 'token' => 'abc123', + ]); + + $this->assertSame('secret', $context->getHidden('api_key')); + $this->assertSame('abc123', $context->getHidden('token')); + $this->assertNull($context->get('api_key')); + $this->assertNull($context->get('token')); + } + + public function testClear(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->set('username', 'john_doe'); + + $context->clear(); + + $this->assertNull($context->get('user_id')); + $this->assertNull($context->get('username')); + } + + public function testClearDoesntAffectHidden(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->setHidden('api_key', 'secret123'); + + $context->clear(); + + $this->assertNull($context->get('user_id')); + $this->assertSame('secret123', $context->getHidden('api_key')); // Hidden value should still be retrievable after clear() + } + + public function testClearHidden(): void + { + $context = service('context'); + $context->setHidden('api_key', 'abcdef'); + $context->setHidden('token', 'abc123'); + + $context->clearHidden(); + + $this->assertNull($context->getHidden('api_key')); + $this->assertNull($context->getHidden('token')); + } + + public function testClearHiddenDoesntAffectNormalValues(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->setHidden('api_key', 'secret123'); + + $context->clearHidden(); + + $this->assertSame(123, $context->get('user_id')); // Normal value should still be retrievable after clearHidden() + $this->assertNull($context->getHidden('api_key')); // Hidden value should be cleared + } + + public function testClearAll(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->setHidden('api_key', 'secret'); + + $context->clearAll(); + + $this->assertNull($context->get('user_id')); + $this->assertNull($context->getHidden('api_key')); + } + + public function testGetWithDefaultValue(): void + { + $context = service('context'); + + $context->set('user_id', 123); + + $this->assertSame(123, $context->get('user_id', 'default')); // Existing key should return its value, not the default + $this->assertSame('default', $context->get('non_existent_key', 'default')); + } + + public function testGetOnlySingleKey(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->set('username', 'john_doe'); + $context->setHidden('api_key', 'secret'); + + $this->assertSame(['user_id' => 123], $context->getOnly('user_id')); + $this->assertSame(['username' => 'john_doe'], $context->getOnly('username')); + $this->assertSame([], $context->getOnly('non_existent_key')); + } + + public function testGetOnlyMultipleKeys(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->set('username', 'john_doe'); + $context->setHidden('api_key', 'secret'); + + $expected = [ + 'user_id' => 123, + 'username' => 'john_doe', + ]; + $this->assertSame($expected, $context->getOnly(['user_id', 'username', 'non_existent_key'])); // non_existent_key should be ignored + } + + public function testGetExceptSingleKey(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->set('username', 'john_doe'); + $context->setHidden('api_key', 'secret'); + + $expected = [ + 'username' => 'john_doe', + ]; + $this->assertSame($expected, $context->getExcept('user_id')); // user_id should be excluded + } + + public function testGetExceptMultipleKeys(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->set('username', 'john_doe'); + $context->setHidden('api_key', 'secret'); + + $expected = [ + 'username' => 'john_doe', + ]; + $this->assertSame($expected, $context->getExcept(['user_id', 'non_existent_key'])); // user_id should be excluded, non_existent_key should be ignored + } + + public function testGetAll(): void + { + $context = service('context'); + $context->set([ + 'user_id' => 123, + 'username' => 'john_doe', + ]); + + $expected = [ + 'user_id' => 123, + 'username' => 'john_doe', + ]; + + $this->assertSame($expected, $context->getAll()); + } + + public function testGetHiddenWithDefaultValue(): void + { + $context = service('context'); + + $context->setHidden('some_secret_token', '123456abcdefghij'); + + $this->assertSame('123456abcdefghij', $context->getHidden('some_secret_token', 'foo')); // Existing key should return its value, not the default + $this->assertSame('foo', $context->getHidden('api_key', 'foo')); + } + + public function testGetOnlyHiddenSingleKey(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->setHidden('api_key', 'some_secret_api_key_here'); + + $this->assertSame(['api_key' => 'some_secret_api_key_here'], $context->getOnlyHidden('api_key')); + $this->assertSame([], $context->getOnlyHidden('some_token')); + } + + public function testGetOnlyHiddenMultipleKeys(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->setHidden('api_key', 'secret'); + $context->setHidden('token', 'abc123'); + + $expected = [ + 'api_key' => 'secret', + 'token' => 'abc123', + ]; + $this->assertSame($expected, $context->getOnlyHidden(['api_key', 'token', 'non_existent_key'])); // non_existent_key should be ignored + } + + public function testGetExceptHiddenSingleKey(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->setHidden('some_sensitive_user_info', 'abcdefghij'); + $context->setHidden('api_key', 'some_secret_api_key_here'); + + $expected = [ + 'some_sensitive_user_info' => 'abcdefghij', + ]; + + $this->assertSame($expected, $context->getExceptHidden('api_key')); + } + + public function testGetExceptHiddenMultipleKeys(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->setHidden('token', 'abc123'); + $context->setHidden('api_key', 'secret'); + + $expected = [ + 'token' => 'abc123', + ]; + $this->assertSame($expected, $context->getExceptHidden(['api_key', 'non_existent_key'])); // token should be excluded, non_existent_key should be ignored + } + + public function testGetAllHidden(): void + { + $context = service('context'); + $context->setHidden([ + 'api_key' => 'secret', + 'token' => 'abc123', + ]); + + $expected = [ + 'api_key' => 'secret', + 'token' => 'abc123', + ]; + + $this->assertSame($expected, $context->getAllHidden()); + } + + public function testOverwriteExistingValue(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->set('user_id', 456); // Overwrite existing value + + $this->assertSame(456, $context->get('user_id')); + } + + public function testOverwriteExistingHiddenValue(): void + { + $context = service('context'); + $context->setHidden('api_key', 'secret'); + $context->setHidden('api_key', 'new_secret'); // Overwrite existing hidden value + + $this->assertSame('new_secret', $context->getHidden('api_key')); + } + + public function testSetHiddenDoesNotAffectNormalValues(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->setHidden('user_id', 'hidden_value'); + + $this->assertSame(123, $context->get('user_id')); // Normal value should still be retrievable + $this->assertSame('hidden_value', $context->getHidden('user_id')); // Hidden value should be retrievable with getHidden() + } + + public function testHasKey(): void + { + $context = service('context'); + $this->assertFalse($context->has('user_id')); + + $context->set('user_id', 123); + + $this->assertTrue($context->has('user_id')); + } + + public function testMissingKey(): void + { + $context = service('context'); + $this->assertTrue($context->missing('user_id')); + + $context->set('user_id', 123); + $this->assertFalse($context->missing('user_id')); + } + + public function testHasHiddenKey(): void + { + $context = service('context'); + $this->assertFalse($context->hasHidden('api_key')); + + $context->setHidden('api_key', 'secret'); + $this->assertTrue($context->hasHidden('api_key')); + } + + public function testMissingHiddenKey(): void + { + $context = service('context'); + $this->assertTrue($context->missingHidden('api_key')); + + $context->setHidden('api_key', 'secret'); + $this->assertFalse($context->missingHidden('api_key')); + } + + public function testRemoveSingleValue(): void + { + $context = service('context'); + $context->set('user_id', 123); + $context->set('username', 'john_doe'); + $context->remove('user_id'); + + $this->assertNull($context->get('user_id')); + $this->assertSame('john_doe', $context->get('username')); // Ensure other values are unaffected + } + + public function testRemoveMultipleValues(): void + { + $context = service('context'); + $context->set([ + 'user_id' => 123, + 'username' => 'john_doe', + 'email' => 'john@example.com', + ]); + + $context->remove(['user_id', 'username']); + + $this->assertNull($context->get('user_id')); + $this->assertNull($context->get('username')); + $this->assertSame('john@example.com', $context->get('email')); // Ensure other values are unaffected + } + + public function testRemoveHiddenValue(): void + { + $context = service('context'); + $context->setHidden('api_key', 'secret'); + $context->setHidden('token', 'abc123'); + + $context->removeHidden('api_key'); + $this->assertNull($context->getHidden('api_key')); + $this->assertSame('abc123', $context->getHidden('token')); // Ensure other hidden values are unaffected + } + + public function testRemoveMultipleHiddenValues(): void + { + $context = service('context'); + $context->setHidden([ + 'api_key' => 'secret', + 'token' => 'abc123', + 'session_id' => 'xyz789', + ]); + + $context->removeHidden(['api_key', 'token']); + + $this->assertNull($context->getHidden('api_key')); + $this->assertNull($context->getHidden('token')); + $this->assertSame('xyz789', $context->getHidden('session_id')); // Ensure other hidden values are unaffected + } + + protected function tearDown(): void + { + parent::tearDown(); + + // Clear the context after each test to ensure isolation. + $context = service('context'); + $context->clearAll(); + } +} From b0d5848d230a77df67b4a719e699c5268ae61898 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 18 Feb 2026 12:00:07 +0530 Subject: [PATCH 05/16] Added tests for global context logging behaviour --- tests/system/Log/LoggerTest.php | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/system/Log/LoggerTest.php b/tests/system/Log/LoggerTest.php index 80d42d6c83b3..ec71b95bcefd 100644 --- a/tests/system/Log/LoggerTest.php +++ b/tests/system/Log/LoggerTest.php @@ -36,6 +36,8 @@ protected function tearDown(): void // Reset the current time. Time::setTestNow(); + + service('context')->clearAll(); // Clear any context data that may have been set during tests. } public function testThrowsExceptionWithBadHandlerSettings(): void @@ -438,4 +440,67 @@ public function testDetermineFileNoStackTrace(): void $this->assertSame($expected, $logger->determineFile()); } + + public function testLogsGlobalContext(): void + { + $config = new LoggerConfig(); + $config->logGlobalContext = true; + + $logger = new Logger($config); + + Time::setTestNow('2026-02-18 12:00:00'); + + service('context')->set('foo', 'bar'); + + $expected = 'DEBUG - ' . Time::now()->format('Y-m-d') . ' --> Test message {"foo":"bar"}'; + + $logger->log('debug', 'Test message'); + + $logs = TestHandler::getLogs(); + + $this->assertCount(1, $logs); + $this->assertSame($expected, $logs[0]); + } + + public function testDoesNotLogGlobalContext(): void + { + $config = new LoggerConfig(); + $config->logGlobalContext = false; + + $logger = new Logger($config); + + Time::setTestNow('2026-02-18 12:00:00'); + + service('context')->set('foo', 'bar'); + + $expected = 'DEBUG - ' . Time::now()->format('Y-m-d') . ' --> Test message'; + + $logger->log('debug', 'Test message'); + + $logs = TestHandler::getLogs(); + + $this->assertCount(1, $logs); + $this->assertSame($expected, $logs[0]); + } + + public function testDoesNotLogHiddenGlobalContext(): void + { + $config = new LoggerConfig(); + $config->logGlobalContext = true; + + $logger = new Logger($config); + + Time::setTestNow('2026-02-18 12:00:00'); + + service('context')->setHidden('secret', 'hidden value'); + + $expected = 'DEBUG - ' . Time::now()->format('Y-m-d') . ' --> Test message'; + + $logger->log('debug', 'Test message'); + + $logs = TestHandler::getLogs(); + + $this->assertCount(1, $logs); + $this->assertSame($expected, $logs[0]); + } } From f62d577c994ae332713b854183ff72804a31745c Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Wed, 18 Feb 2026 12:02:33 +0530 Subject: [PATCH 06/16] cs-fix --- app/Config/Logger.php | 2 - system/Context/Context.php | 92 ++++++++++++++++++++-------- tests/system/Context/ContextTest.php | 33 ++++++---- tests/system/Log/LoggerTest.php | 6 +- 4 files changed, 89 insertions(+), 44 deletions(-) diff --git a/app/Config/Logger.php b/app/Config/Logger.php index d971ecc43fae..e303965a397b 100644 --- a/app/Config/Logger.php +++ b/app/Config/Logger.php @@ -64,8 +64,6 @@ class Logger extends BaseConfig * * **NOTE:** This **DOES NOT** include any data that has been marked as hidden * using the `setHidden()` method of the Context class. - * - * @var bool */ public bool $logGlobalContext = false; diff --git a/system/Context/Context.php b/system/Context/Context.php index b9b77ab7846f..1ff836226965 100644 --- a/system/Context/Context.php +++ b/system/Context/Context.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + namespace CodeIgniter\Context; class Context @@ -23,51 +32,58 @@ class Context */ public function __construct() { - $this->data = []; + $this->data = []; $this->hiddenData = []; } /** * Set a key-value pair to the context. * - * @param string|array $key The key to identify the data. Can be a string or an array of key-value pairs. - * @param mixed $value The value to be stored in the context. + * @param array|string $key The key to identify the data. Can be a string or an array of key-value pairs. + * @param mixed $value The value to be stored in the context. + * * @return $this */ - public function set(string|array $key, mixed $value = null): self + public function set(array|string $key, mixed $value = null): self { if (is_array($key)) { $this->data = array_merge($this->data, $key); + return $this; } $this->data[$key] = $value; + return $this; } /** * Set a hidden key-value pair to the context. This data will not be included in logs. * - * @param string|array $key The key to identify the data. Can be a string or an array of key-value pairs. - * @param mixed $value The value to be stored in the context. + * @param array|string $key The key to identify the data. Can be a string or an array of key-value pairs. + * @param mixed $value The value to be stored in the context. + * * @return $this */ - public function setHidden(string|array $key, mixed $value = null): self + public function setHidden(array|string $key, mixed $value = null): self { if (is_array($key)) { $this->hiddenData = array_merge($this->hiddenData, $key); + return $this; } $this->hiddenData[$key] = $value; + return $this; } /** * Get a value from the context by its key, or return a default value if the key does not exist. * - * @param string $key The key to identify the data. + * @param string $key The key to identify the data. * @param mixed|null $default The default value to return if the key does not exist in the context. + * * @return mixed The value associated with the key, or the default value if the key does not exist. */ public function get(string $key, mixed $default = null): mixed @@ -78,29 +94,33 @@ public function get(string $key, mixed $default = null): mixed /** * Get only the specified keys from the context. If a key does not exist, it will be ignored. * - * @param string|array $keys An array of keys to retrieve from the context. + * @param list|string $keys An array of keys to retrieve from the context. + * * @return array An array of key-value pairs for the specified keys that exist in the context. */ - public function getOnly(string|array $keys): array + public function getOnly(array|string $keys): array { if (is_string($keys)) { $keys = [$keys]; } - return array_filter($this->data, fn($k) => in_array($k, $keys), ARRAY_FILTER_USE_KEY); + + return array_filter($this->data, static fn ($k) => in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); } /** * Get all keys from the context except the specified keys. * - * @param string|array $keys An array of keys to exclude from the context. + * @param list|string $keys An array of keys to exclude from the context. + * * @return array An array of key-value pairs for all keys in the context except the specified keys. */ - public function getExcept(string|array $keys): array + public function getExcept(array|string $keys): array { if (is_string($keys)) { $keys = [$keys]; } - return array_filter($this->data, fn($k) => !in_array($k, $keys), ARRAY_FILTER_USE_KEY); + + return array_filter($this->data, static fn ($k) => ! in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); } /** @@ -116,8 +136,9 @@ public function getAll(): array /** * Get a hidden value from the context by its key, or return a default value if the key does not exist. * - * @param string $key The key to identify the data. + * @param string $key The key to identify the data. * @param mixed|null $default The default value to return if the key does not exist in the context. + * * @return mixed The value associated with the key, or the default value if the key does not exist. */ public function getHidden(string $key, mixed $default = null): mixed @@ -128,29 +149,33 @@ public function getHidden(string $key, mixed $default = null): mixed /** * Get only the specified keys from the hidden context. If a key does not exist, it will be ignored. * - * @param string|array $keys An array of keys to retrieve from the hidden context. + * @param list|string $keys An array of keys to retrieve from the hidden context. + * * @return array An array of key-value pairs for the specified keys that exist in the hidden context. */ - public function getOnlyHidden(string|array $keys): array + public function getOnlyHidden(array|string $keys): array { if (is_string($keys)) { $keys = [$keys]; } - return array_filter($this->hiddenData, fn($k) => in_array($k, $keys), ARRAY_FILTER_USE_KEY); + + return array_filter($this->hiddenData, static fn ($k) => in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); } /** * Get all keys from the hidden context except the specified keys. * - * @param string|array $keys An array of keys to exclude from the hidden context. + * @param list|string $keys An array of keys to exclude from the hidden context. + * * @return array An array of key-value pairs for all keys in the hidden context except the specified keys. */ - public function getExceptHidden(string|array $keys): array + public function getExceptHidden(array|string $keys): array { if (is_string($keys)) { $keys = [$keys]; } - return array_filter($this->hiddenData, fn($k) => !in_array($k, $keys), ARRAY_FILTER_USE_KEY); + + return array_filter($this->hiddenData, static fn ($k) => ! in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); } /** @@ -167,17 +192,19 @@ public function getAllHidden(): array * Check if a key does not exist in the context. Exactly the opposite of `has()`. * * @param string $key The key to check for non-existence in the context. + * * @return bool True if the key does not exist in the context, false otherwise. */ public function missing(string $key): bool { - return !$this->has($key); + return ! $this->has($key); } /** * Check if a key exists in the context. * * @param string $key The key to check for existence in the context. + * * @return bool True if the key exists in the context, false otherwise. */ public function has(string $key): bool @@ -189,17 +216,19 @@ public function has(string $key): bool * Check if a key does not exist in the hidden context. Exactly the opposite of `hasHidden()`. * * @param string $key The key to check for non-existence in the hidden context. + * * @return bool True if the key does not exist in the hidden context, false otherwise. */ public function missingHidden(string $key): bool { - return !$this->hasHidden($key); + return ! $this->hasHidden($key); } /** * Check if a key exists in the hidden context. * * @param string $key The key to check for existence in the hidden context. + * * @return bool True if the key exists in the hidden context, false otherwise. */ public function hasHidden(string $key): bool @@ -210,38 +239,44 @@ public function hasHidden(string $key): bool /** * Remove a key-value pair from the context by its key. * - * @param string|array $key The key to identify the data to be removed from the context. + * @param list|string $key The key to identify the data to be removed from the context. + * * @return $this */ - public function remove(string|array $key): self + public function remove(array|string $key): self { if (is_array($key)) { foreach ($key as $k) { unset($this->data[$k]); } + return $this; } unset($this->data[$key]); + return $this; } /** * Remove a key-value pair from the hidden context by its key. * - * @param string|array $key The key to identify the data to be removed from the hidden context. + * @param list|string $key The key to identify the data to be removed from the hidden context. + * * @return $this */ - public function removeHidden(string|array $key): self + public function removeHidden(array|string $key): self { if (is_array($key)) { foreach ($key as $k) { unset($this->hiddenData[$k]); } + return $this; } unset($this->hiddenData[$key]); + return $this; } @@ -254,6 +289,7 @@ public function clearAll(): self { $this->clear(); $this->clearHidden(); + return $this; } @@ -265,6 +301,7 @@ public function clearAll(): self public function clear(): self { $this->data = []; + return $this; } @@ -276,6 +313,7 @@ public function clear(): self public function clearHidden(): self { $this->hiddenData = []; + return $this; } } diff --git a/tests/system/Context/ContextTest.php b/tests/system/Context/ContextTest.php index d3c47b36cda0..09f50b8561d9 100644 --- a/tests/system/Context/ContextTest.php +++ b/tests/system/Context/ContextTest.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + namespace CodeIgniter\Context; use CodeIgniter\Test\CIUnitTestCase; @@ -31,7 +40,7 @@ public function testSetAndGetMultipleValues(): void { $context = service('context'); $context->set([ - 'user_id' => 123, + 'user_id' => 123, 'username' => 'john_doe', ]); @@ -55,7 +64,7 @@ public function testSetAndGetMultipleHiddenValues(): void $context = service('context'); $context->setHidden([ 'api_key' => 'secret', - 'token' => 'abc123', + 'token' => 'abc123', ]); $this->assertSame('secret', $context->getHidden('api_key')); @@ -154,7 +163,7 @@ public function testGetOnlyMultipleKeys(): void $context->setHidden('api_key', 'secret'); $expected = [ - 'user_id' => 123, + 'user_id' => 123, 'username' => 'john_doe', ]; $this->assertSame($expected, $context->getOnly(['user_id', 'username', 'non_existent_key'])); // non_existent_key should be ignored @@ -190,12 +199,12 @@ public function testGetAll(): void { $context = service('context'); $context->set([ - 'user_id' => 123, + 'user_id' => 123, 'username' => 'john_doe', ]); $expected = [ - 'user_id' => 123, + 'user_id' => 123, 'username' => 'john_doe', ]; @@ -231,7 +240,7 @@ public function testGetOnlyHiddenMultipleKeys(): void $expected = [ 'api_key' => 'secret', - 'token' => 'abc123', + 'token' => 'abc123', ]; $this->assertSame($expected, $context->getOnlyHidden(['api_key', 'token', 'non_existent_key'])); // non_existent_key should be ignored } @@ -268,12 +277,12 @@ public function testGetAllHidden(): void $context = service('context'); $context->setHidden([ 'api_key' => 'secret', - 'token' => 'abc123', + 'token' => 'abc123', ]); $expected = [ 'api_key' => 'secret', - 'token' => 'abc123', + 'token' => 'abc123', ]; $this->assertSame($expected, $context->getAllHidden()); @@ -359,9 +368,9 @@ public function testRemoveMultipleValues(): void { $context = service('context'); $context->set([ - 'user_id' => 123, + 'user_id' => 123, 'username' => 'john_doe', - 'email' => 'john@example.com', + 'email' => 'john@example.com', ]); $context->remove(['user_id', 'username']); @@ -386,8 +395,8 @@ public function testRemoveMultipleHiddenValues(): void { $context = service('context'); $context->setHidden([ - 'api_key' => 'secret', - 'token' => 'abc123', + 'api_key' => 'secret', + 'token' => 'abc123', 'session_id' => 'xyz789', ]); diff --git a/tests/system/Log/LoggerTest.php b/tests/system/Log/LoggerTest.php index ec71b95bcefd..136dc4332bd5 100644 --- a/tests/system/Log/LoggerTest.php +++ b/tests/system/Log/LoggerTest.php @@ -443,7 +443,7 @@ public function testDetermineFileNoStackTrace(): void public function testLogsGlobalContext(): void { - $config = new LoggerConfig(); + $config = new LoggerConfig(); $config->logGlobalContext = true; $logger = new Logger($config); @@ -464,7 +464,7 @@ public function testLogsGlobalContext(): void public function testDoesNotLogGlobalContext(): void { - $config = new LoggerConfig(); + $config = new LoggerConfig(); $config->logGlobalContext = false; $logger = new Logger($config); @@ -485,7 +485,7 @@ public function testDoesNotLogGlobalContext(): void public function testDoesNotLogHiddenGlobalContext(): void { - $config = new LoggerConfig(); + $config = new LoggerConfig(); $config->logGlobalContext = true; $logger = new Logger($config); From 2e4ca31977256804d0fb223bd3137ebd35cb0d24 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Thu, 19 Feb 2026 14:43:59 +0530 Subject: [PATCH 07/16] Add docs --- user_guide_src/source/general/context.rst | 370 ++++++++++++++++++ user_guide_src/source/general/context/001.php | 4 + user_guide_src/source/general/context/002.php | 4 + user_guide_src/source/general/context/003.php | 9 + user_guide_src/source/general/context/004.php | 6 + user_guide_src/source/general/context/005.php | 5 + user_guide_src/source/general/context/006.php | 5 + user_guide_src/source/general/context/007.php | 5 + user_guide_src/source/general/context/008.php | 9 + user_guide_src/source/general/context/009.php | 9 + user_guide_src/source/general/context/010.php | 6 + user_guide_src/source/general/context/011.php | 6 + user_guide_src/source/general/context/012.php | 4 + user_guide_src/source/general/context/013.php | 4 + user_guide_src/source/general/context/014.php | 4 + user_guide_src/source/general/context/015.php | 4 + user_guide_src/source/general/context/016.php | 8 + user_guide_src/source/general/context/017.php | 5 + user_guide_src/source/general/context/018.php | 14 + user_guide_src/source/general/context/019.php | 10 + user_guide_src/source/general/context/020.php | 8 + user_guide_src/source/general/context/021.php | 4 + user_guide_src/source/general/context/022.php | 4 + user_guide_src/source/general/context/023.php | 15 + user_guide_src/source/general/context/024.php | 8 + user_guide_src/source/general/index.rst | 1 + 26 files changed, 531 insertions(+) create mode 100644 user_guide_src/source/general/context.rst create mode 100644 user_guide_src/source/general/context/001.php create mode 100644 user_guide_src/source/general/context/002.php create mode 100644 user_guide_src/source/general/context/003.php create mode 100644 user_guide_src/source/general/context/004.php create mode 100644 user_guide_src/source/general/context/005.php create mode 100644 user_guide_src/source/general/context/006.php create mode 100644 user_guide_src/source/general/context/007.php create mode 100644 user_guide_src/source/general/context/008.php create mode 100644 user_guide_src/source/general/context/009.php create mode 100644 user_guide_src/source/general/context/010.php create mode 100644 user_guide_src/source/general/context/011.php create mode 100644 user_guide_src/source/general/context/012.php create mode 100644 user_guide_src/source/general/context/013.php create mode 100644 user_guide_src/source/general/context/014.php create mode 100644 user_guide_src/source/general/context/015.php create mode 100644 user_guide_src/source/general/context/016.php create mode 100644 user_guide_src/source/general/context/017.php create mode 100644 user_guide_src/source/general/context/018.php create mode 100644 user_guide_src/source/general/context/019.php create mode 100644 user_guide_src/source/general/context/020.php create mode 100644 user_guide_src/source/general/context/021.php create mode 100644 user_guide_src/source/general/context/022.php create mode 100644 user_guide_src/source/general/context/023.php create mode 100644 user_guide_src/source/general/context/024.php diff --git a/user_guide_src/source/general/context.rst b/user_guide_src/source/general/context.rst new file mode 100644 index 000000000000..30170b6e54ec --- /dev/null +++ b/user_guide_src/source/general/context.rst @@ -0,0 +1,370 @@ +################### +Context +################### + +.. contents:: + :local: + :depth: 2 + +*********** +What is it? +*********** + +The Context class provides a simple, convenient way to store and retrieve user-defined data throughout a single request. It functions as a key-value store that can hold any data you need to access across different parts of your application during the request lifecycle. + +The Context class is particularly useful for: + +- Storing request-specific metadata (user IDs, request IDs, correlation IDs) +- Passing data between filters, controllers, and other components +- Adding contextual information to your logs automatically +- Storing sensitive data that should not appear in logs + +****************** +Accessing Context +****************** + +You can access the Context service anywhere in your application using the ``service()`` function: + +.. literalinclude:: context/001.php + +********************* +Setting Context Data +********************* + +Setting a Single Value +====================== + +You can store a single key-value pair using the ``set()`` method: + +.. literalinclude:: context/002.php + +Setting Multiple Values +======================= + +You can also set multiple values at once by passing an array: + +.. literalinclude:: context/003.php + +The ``set()`` method returns the Context instance, allowing you to chain multiple calls: + +.. literalinclude:: context/004.php + +********************* +Getting Context Data +********************* + +Retrieving a Single Value +========================== + +Use the ``get()`` method to retrieve a value by its key: + +.. literalinclude:: context/005.php + +You can provide a default value as the second parameter, which will be returned if the key doesn't exist: + +.. literalinclude:: context/006.php + +Retrieving All Data +=================== + +To get all stored context data: + +.. literalinclude:: context/007.php + +Retrieving Specific Keys +========================= + +You can retrieve only specific keys using ``getOnly()``: + +.. literalinclude:: context/008.php + +If you need all data except specific keys, use ``getExcept()``: + +.. literalinclude:: context/009.php + +********************** +Checking for Data +********************** + +You can check if a key exists in the context: + +.. literalinclude:: context/010.php + +Or check if a key is missing (the opposite of ``has()``): + +.. literalinclude:: context/011.php + +********************* +Removing Context Data +********************* + +Removing a Single Value +======================== + +You can remove data from the context using the ``remove()`` method: + +.. literalinclude:: context/012.php + +Removing Multiple Values +========================= + +To remove multiple keys at once, pass an array: + +.. literalinclude:: context/013.php + +Clearing All Data +================= + +To remove all context data: + +.. literalinclude:: context/014.php + +********************* +Hidden Context Data +********************* + +The Context class provides a separate storage area for sensitive data that should not be included in logs. This is useful for storing API keys, passwords, tokens, or other sensitive information that you need to access during the request but don't want to expose in log files. + +Setting Hidden Data +=================== + +Use the ``setHidden()`` method to store sensitive data: + +.. literalinclude:: context/015.php + +You can also set multiple hidden values at once: + +.. literalinclude:: context/016.php + +Getting Hidden Data +=================== + +Retrieve hidden data using ``getHidden()``: + +.. literalinclude:: context/017.php + +The same methods available for regular data also work with hidden data: + +.. literalinclude:: context/018.php + +Checking Hidden Data +==================== + +Check if a hidden key exists: + +.. literalinclude:: context/019.php + +Removing Hidden Data +==================== + +Remove hidden data using ``removeHidden()``: + +.. literalinclude:: context/020.php + +Clearing Hidden Data +==================== + +To clear all hidden data without affecting regular context data: + +.. literalinclude:: context/021.php + +To clear both regular and hidden data: + +.. literalinclude:: context/022.php + +.. important:: Regular data and hidden data are stored separately. A key can exist in both regular and hidden storage with different values. Use ``get()`` for regular data and ``getHidden()`` for hidden data. + +*********************************** +Integration with Logging +*********************************** + +The Context class integrates seamlessly with CodeIgniter's logging system. When enabled, context data is automatically appended to log messages, providing additional information for debugging and monitoring. + +Enabling Global Context Logging +================================ + +To enable automatic logging of context data, set the ``$logGlobalContext`` property to ``true`` in your **app/Config/Logger.php** file: + +.. literalinclude:: context/023.php + +When enabled, all context data (excluding hidden data) will be automatically appended to your log messages as JSON: + +.. literalinclude:: context/024.php + +This would produce a log entry like: + +.. code-block:: text + + ERROR - 2026-02-18 --> Payment processing failed {"user_id":123,"transaction_id":"txn_12345"} + +.. note:: Hidden data set with ``setHidden()`` is **never** included in logs, even when ``$logGlobalContext`` is enabled. This ensures sensitive information like API keys or tokens remain secure. + +*************** +Important Notes +*************** + +- Context data persists only for the duration of a single request. It is not shared between requests. +- The Context service is shared by default, meaning there is one instance per request. +- Hidden data is never included in logs, regardless of the logging configuration. +- Regular context data and hidden context data are stored separately and can have overlapping keys. +- Context is cleared automatically at the end of each request. +- In testing environments, remember to clear context data between tests using ``clearAll()`` to ensure test isolation. + +*************** +Class Reference +*************** + +.. php:namespace:: CodeIgniter\Context + +.. php:class:: Context + + .. php:method:: set($key[, $value = null]) + + :param array|string $key: The key or an array of key-value pairs + :param mixed $value: The value to store (ignored if $key is an array) + :returns: Context instance for method chaining + :rtype: Context + + Sets one or more key-value pairs in the context. + + .. php:method:: setHidden($key[, $value = null]) + + :param array|string $key: The key or an array of key-value pairs + :param mixed $value: The value to store (ignored if $key is an array) + :returns: Context instance for method chaining + :rtype: Context + + Sets one or more key-value pairs in the hidden context. + + .. php:method:: get($key[, $default = null]) + + :param string $key: The key to retrieve + :param mixed $default: Default value if key doesn't exist + :returns: The value or default + :rtype: mixed + + Gets a value from the context. + + .. php:method:: getHidden($key[, $default = null]) + + :param string $key: The key to retrieve + :param mixed $default: Default value if key doesn't exist + :returns: The value or default + :rtype: mixed + + Gets a value from the hidden context. + + .. php:method:: getOnly($keys) + + :param array|string $keys: Key or array of keys to retrieve + :returns: Array of key-value pairs + :rtype: array + + Gets only the specified keys from the context. + + .. php:method:: getOnlyHidden($keys) + + :param array|string $keys: Key or array of keys to retrieve + :returns: Array of key-value pairs + :rtype: array + + Gets only the specified keys from the hidden context. + + .. php:method:: getExcept($keys) + + :param array|string $keys: Key or array of keys to exclude + :returns: Array of key-value pairs + :rtype: array + + Gets all context data except the specified keys. + + .. php:method:: getExceptHidden($keys) + + :param array|string $keys: Key or array of keys to exclude + :returns: Array of key-value pairs + :rtype: array + + Gets all hidden context data except the specified keys. + + .. php:method:: getAll() + + :returns: All context data + :rtype: array + + Gets all data from the context. + + .. php:method:: getAllHidden() + + :returns: All hidden context data + :rtype: array + + Gets all data from the hidden context. + + .. php:method:: has($key) + + :param string $key: The key to check + :returns: True if key exists, false otherwise + :rtype: bool + + Checks if a key exists in the context. + + .. php:method:: hasHidden($key) + + :param string $key: The key to check + :returns: True if key exists, false otherwise + :rtype: bool + + Checks if a key exists in the hidden context. + + .. php:method:: missing($key) + + :param string $key: The key to check + :returns: True if key doesn't exist, false otherwise + :rtype: bool + + Checks if a key doesn't exist in the context. Opposite of ``has()``. + + .. php:method:: missingHidden($key) + + :param string $key: The key to check + :returns: True if key doesn't exist, false otherwise + :rtype: bool + + Checks if a key doesn't exist in the hidden context. Opposite of ``hasHidden()``. + + .. php:method:: remove($key) + + :param array|string $key: The key or array of keys to remove + :returns: Context instance for method chaining + :rtype: Context + + Removes one or more keys from the context. + + .. php:method:: removeHidden($key) + + :param array|string $key: The key or array of keys to remove + :returns: Context instance for method chaining + :rtype: Context + + Removes one or more keys from the hidden context. + + .. php:method:: clear() + + :returns: Context instance for method chaining + :rtype: Context + + Clears all data from the context (does not affect hidden data). + + .. php:method:: clearHidden() + + :returns: Context instance for method chaining + :rtype: Context + + Clears all data from the hidden context (does not affect regular data). + + .. php:method:: clearAll() + + :returns: Context instance for method chaining + :rtype: Context + + Clears all data from both the context and hidden context. diff --git a/user_guide_src/source/general/context/001.php b/user_guide_src/source/general/context/001.php new file mode 100644 index 000000000000..30e6e9d52b36 --- /dev/null +++ b/user_guide_src/source/general/context/001.php @@ -0,0 +1,4 @@ +set('user_id', 123); + diff --git a/user_guide_src/source/general/context/003.php b/user_guide_src/source/general/context/003.php new file mode 100644 index 000000000000..b6eb5fb23b53 --- /dev/null +++ b/user_guide_src/source/general/context/003.php @@ -0,0 +1,9 @@ +set([ + 'user_id' => 123, + 'username' => 'john_doe', + 'request_id' => 'req_abc123', + 'correlation_id' => 'corr_xyz789', +]); + diff --git a/user_guide_src/source/general/context/004.php b/user_guide_src/source/general/context/004.php new file mode 100644 index 000000000000..f68cdc50c198 --- /dev/null +++ b/user_guide_src/source/general/context/004.php @@ -0,0 +1,6 @@ +set('user_id', 123) + ->set('username', 'john_doe') + ->set('request_id', 'req_abc123'); + diff --git a/user_guide_src/source/general/context/005.php b/user_guide_src/source/general/context/005.php new file mode 100644 index 000000000000..5a496c58b501 --- /dev/null +++ b/user_guide_src/source/general/context/005.php @@ -0,0 +1,5 @@ +get('user_id'); +// $userId = 123 + diff --git a/user_guide_src/source/general/context/006.php b/user_guide_src/source/general/context/006.php new file mode 100644 index 000000000000..9703e14d98f7 --- /dev/null +++ b/user_guide_src/source/general/context/006.php @@ -0,0 +1,5 @@ +get('user_role', 'guest'); +// If 'user_role' doesn't exist, $role will be 'guest' + diff --git a/user_guide_src/source/general/context/007.php b/user_guide_src/source/general/context/007.php new file mode 100644 index 000000000000..1112e327b5bb --- /dev/null +++ b/user_guide_src/source/general/context/007.php @@ -0,0 +1,5 @@ +getAll(); +// Returns: ['user_id' => 123, 'username' => 'john_doe', ...] + diff --git a/user_guide_src/source/general/context/008.php b/user_guide_src/source/general/context/008.php new file mode 100644 index 000000000000..070cd5f80db0 --- /dev/null +++ b/user_guide_src/source/general/context/008.php @@ -0,0 +1,9 @@ +getOnly(['user_id', 'username']); +// Returns: ['user_id' => 123, 'username' => 'john_doe'] + +// You can also pass a single key as a string +$userId = $context->getOnly('user_id'); +// Returns: ['user_id' => 123] + diff --git a/user_guide_src/source/general/context/009.php b/user_guide_src/source/general/context/009.php new file mode 100644 index 000000000000..7365457a4d63 --- /dev/null +++ b/user_guide_src/source/general/context/009.php @@ -0,0 +1,9 @@ +getExcept(['password', 'api_key']); +// Returns all data except 'password' and 'api_key' + +// You can also pass a single key as a string +$data = $context->getExcept('password'); +// Returns all data except 'password' + diff --git a/user_guide_src/source/general/context/010.php b/user_guide_src/source/general/context/010.php new file mode 100644 index 000000000000..09541e2b40df --- /dev/null +++ b/user_guide_src/source/general/context/010.php @@ -0,0 +1,6 @@ +has('user_id')) { + // Do something with user_id +} + diff --git a/user_guide_src/source/general/context/011.php b/user_guide_src/source/general/context/011.php new file mode 100644 index 000000000000..a1968abd8746 --- /dev/null +++ b/user_guide_src/source/general/context/011.php @@ -0,0 +1,6 @@ +missing('user_id')) { + // user_id hasn't been set yet +} + diff --git a/user_guide_src/source/general/context/012.php b/user_guide_src/source/general/context/012.php new file mode 100644 index 000000000000..96559acb0dbf --- /dev/null +++ b/user_guide_src/source/general/context/012.php @@ -0,0 +1,4 @@ +remove('user_id'); + diff --git a/user_guide_src/source/general/context/013.php b/user_guide_src/source/general/context/013.php new file mode 100644 index 000000000000..8ce31709a0a4 --- /dev/null +++ b/user_guide_src/source/general/context/013.php @@ -0,0 +1,4 @@ +remove(['user_id', 'username', 'request_id']); + diff --git a/user_guide_src/source/general/context/014.php b/user_guide_src/source/general/context/014.php new file mode 100644 index 000000000000..1fac5ae27cfd --- /dev/null +++ b/user_guide_src/source/general/context/014.php @@ -0,0 +1,4 @@ +clear(); + diff --git a/user_guide_src/source/general/context/015.php b/user_guide_src/source/general/context/015.php new file mode 100644 index 000000000000..dfb657040641 --- /dev/null +++ b/user_guide_src/source/general/context/015.php @@ -0,0 +1,4 @@ +setHidden('api_key', 'sk_live_abc123xyz789'); + diff --git a/user_guide_src/source/general/context/016.php b/user_guide_src/source/general/context/016.php new file mode 100644 index 000000000000..e2d25b392e8f --- /dev/null +++ b/user_guide_src/source/general/context/016.php @@ -0,0 +1,8 @@ +setHidden([ + 'api_key' => 'sk_live_abc123xyz789', + 'api_secret' => 'secret_key_here', + 'db_password' => 'database_password', +]); + diff --git a/user_guide_src/source/general/context/017.php b/user_guide_src/source/general/context/017.php new file mode 100644 index 000000000000..39f304bab02e --- /dev/null +++ b/user_guide_src/source/general/context/017.php @@ -0,0 +1,5 @@ +getHidden('api_key'); +// $apiKey = 'sk_live_abc123xyz789' + diff --git a/user_guide_src/source/general/context/018.php b/user_guide_src/source/general/context/018.php new file mode 100644 index 000000000000..017ef1a5e766 --- /dev/null +++ b/user_guide_src/source/general/context/018.php @@ -0,0 +1,14 @@ +getHidden('api_key', 'default_key'); + +// Get only specific hidden keys +$credentials = $context->getOnlyHidden(['api_key', 'api_secret']); + +// Get all hidden data except specific keys +$data = $context->getExceptHidden(['db_password']); + +// Get all hidden data +$allHidden = $context->getAllHidden(); + diff --git a/user_guide_src/source/general/context/019.php b/user_guide_src/source/general/context/019.php new file mode 100644 index 000000000000..234b6ef39678 --- /dev/null +++ b/user_guide_src/source/general/context/019.php @@ -0,0 +1,10 @@ +hasHidden('api_key')) { + // API key is set +} + +if ($context->missingHidden('api_key')) { + // API key is not set +} + diff --git a/user_guide_src/source/general/context/020.php b/user_guide_src/source/general/context/020.php new file mode 100644 index 000000000000..18ae63a9f588 --- /dev/null +++ b/user_guide_src/source/general/context/020.php @@ -0,0 +1,8 @@ +removeHidden('api_key'); + +// Remove multiple hidden values +$context->removeHidden(['api_key', 'api_secret']); + diff --git a/user_guide_src/source/general/context/021.php b/user_guide_src/source/general/context/021.php new file mode 100644 index 000000000000..b5fb29c0d163 --- /dev/null +++ b/user_guide_src/source/general/context/021.php @@ -0,0 +1,4 @@ +clearHidden(); + diff --git a/user_guide_src/source/general/context/022.php b/user_guide_src/source/general/context/022.php new file mode 100644 index 000000000000..fb90f6adf686 --- /dev/null +++ b/user_guide_src/source/general/context/022.php @@ -0,0 +1,4 @@ +clearAll(); + diff --git a/user_guide_src/source/general/context/023.php b/user_guide_src/source/general/context/023.php new file mode 100644 index 000000000000..3803bed019b1 --- /dev/null +++ b/user_guide_src/source/general/context/023.php @@ -0,0 +1,15 @@ +set('user_id', 123); +$context->set('transaction_id', 'txn_12345'); + +log_message('error', 'Payment processing failed'); + diff --git a/user_guide_src/source/general/index.rst b/user_guide_src/source/general/index.rst index b6f148ca77d4..ca14915a39b4 100644 --- a/user_guide_src/source/general/index.rst +++ b/user_guide_src/source/general/index.rst @@ -12,6 +12,7 @@ General Topics logging errors caching + context ajax modules managing_apps From b055119b7d8607ac3268df152c3ebc160696d2bc Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 21 Feb 2026 15:09:33 +0530 Subject: [PATCH 08/16] add context() helper function --- system/Common.php | 14 ++++++++++++++ tests/system/CommonFunctionsTest.php | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/system/Common.php b/system/Common.php index bcf2a5c14db9..ebafa173afe5 100644 --- a/system/Common.php +++ b/system/Common.php @@ -14,6 +14,7 @@ use CodeIgniter\Cache\CacheInterface; use CodeIgniter\Config\BaseConfig; use CodeIgniter\Config\Factories; +use CodeIgniter\Context\Context; use CodeIgniter\Cookie\Cookie; use CodeIgniter\Cookie\CookieStore; use CodeIgniter\Cookie\Exceptions\CookieException; @@ -212,6 +213,19 @@ function config(string $name, bool $getShared = true) } } +if (! function_exists('context')) { + /** + * Provides access to the Context object, which is used to store + * contextual data during a request that can be accessed globally. + * + * @return Context + */ + function context(): Context + { + return service('context'); + } +} + if (! function_exists('cookie')) { /** * Simpler way to create a new Cookie instance. diff --git a/tests/system/CommonFunctionsTest.php b/tests/system/CommonFunctionsTest.php index d437f985ebd5..da9574ab4bad 100644 --- a/tests/system/CommonFunctionsTest.php +++ b/tests/system/CommonFunctionsTest.php @@ -821,4 +821,11 @@ public function testRenderBacktrace(): void $this->assertMatchesRegularExpression('/^\s*\d* .+(?:\(\d+\))?: \S+(?:(?:\->|::)\S+)?\(.*\)$/', $render); } } + + public function testContext(): void + { + service('context')->set('foo', 'bar'); + + $this->assertSame('bar', context()->get('foo')); + } } From c60b9f9add08b4c556209fdeaaa18d746fe1857e Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 21 Feb 2026 15:13:40 +0530 Subject: [PATCH 09/16] cs-fix and fixing static analysis problems --- system/Common.php | 2 -- system/Context/Context.php | 10 ++++++---- tests/system/Context/ContextTest.php | 2 ++ user_guide_src/source/general/context/001.php | 1 - user_guide_src/source/general/context/002.php | 1 - user_guide_src/source/general/context/003.php | 1 - user_guide_src/source/general/context/004.php | 1 - user_guide_src/source/general/context/005.php | 1 - user_guide_src/source/general/context/006.php | 1 - user_guide_src/source/general/context/007.php | 1 - user_guide_src/source/general/context/008.php | 1 - user_guide_src/source/general/context/009.php | 1 - user_guide_src/source/general/context/010.php | 1 - user_guide_src/source/general/context/011.php | 1 - user_guide_src/source/general/context/012.php | 1 - user_guide_src/source/general/context/013.php | 1 - user_guide_src/source/general/context/014.php | 1 - user_guide_src/source/general/context/015.php | 1 - user_guide_src/source/general/context/016.php | 7 +++---- user_guide_src/source/general/context/017.php | 1 - user_guide_src/source/general/context/018.php | 1 - user_guide_src/source/general/context/019.php | 1 - user_guide_src/source/general/context/020.php | 1 - user_guide_src/source/general/context/021.php | 1 - user_guide_src/source/general/context/022.php | 1 - user_guide_src/source/general/context/023.php | 1 - user_guide_src/source/general/context/024.php | 1 - 27 files changed, 11 insertions(+), 33 deletions(-) diff --git a/system/Common.php b/system/Common.php index ebafa173afe5..04115993ef87 100644 --- a/system/Common.php +++ b/system/Common.php @@ -217,8 +217,6 @@ function config(string $name, bool $getShared = true) /** * Provides access to the Context object, which is used to store * contextual data during a request that can be accessed globally. - * - * @return Context */ function context(): Context { diff --git a/system/Context/Context.php b/system/Context/Context.php index 1ff836226965..24852b14a8eb 100644 --- a/system/Context/Context.php +++ b/system/Context/Context.php @@ -1,5 +1,7 @@ data, static fn ($k) => in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); + return array_filter($this->data, static fn ($k): bool => in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); } /** @@ -120,7 +122,7 @@ public function getExcept(array|string $keys): array $keys = [$keys]; } - return array_filter($this->data, static fn ($k) => ! in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); + return array_filter($this->data, static fn ($k): bool => ! in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); } /** @@ -159,7 +161,7 @@ public function getOnlyHidden(array|string $keys): array $keys = [$keys]; } - return array_filter($this->hiddenData, static fn ($k) => in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); + return array_filter($this->hiddenData, static fn ($k): bool => in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); } /** @@ -175,7 +177,7 @@ public function getExceptHidden(array|string $keys): array $keys = [$keys]; } - return array_filter($this->hiddenData, static fn ($k) => ! in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); + return array_filter($this->hiddenData, static fn ($k): bool => ! in_array($k, $keys, true), ARRAY_FILTER_USE_KEY); } /** diff --git a/tests/system/Context/ContextTest.php b/tests/system/Context/ContextTest.php index 09f50b8561d9..c315519d81b4 100644 --- a/tests/system/Context/ContextTest.php +++ b/tests/system/Context/ContextTest.php @@ -1,5 +1,7 @@ set('user_id', 123); - diff --git a/user_guide_src/source/general/context/003.php b/user_guide_src/source/general/context/003.php index b6eb5fb23b53..60d2ca87ee2f 100644 --- a/user_guide_src/source/general/context/003.php +++ b/user_guide_src/source/general/context/003.php @@ -6,4 +6,3 @@ 'request_id' => 'req_abc123', 'correlation_id' => 'corr_xyz789', ]); - diff --git a/user_guide_src/source/general/context/004.php b/user_guide_src/source/general/context/004.php index f68cdc50c198..29dd1e38cafc 100644 --- a/user_guide_src/source/general/context/004.php +++ b/user_guide_src/source/general/context/004.php @@ -3,4 +3,3 @@ $context->set('user_id', 123) ->set('username', 'john_doe') ->set('request_id', 'req_abc123'); - diff --git a/user_guide_src/source/general/context/005.php b/user_guide_src/source/general/context/005.php index 5a496c58b501..1ddd78820180 100644 --- a/user_guide_src/source/general/context/005.php +++ b/user_guide_src/source/general/context/005.php @@ -2,4 +2,3 @@ $userId = $context->get('user_id'); // $userId = 123 - diff --git a/user_guide_src/source/general/context/006.php b/user_guide_src/source/general/context/006.php index 9703e14d98f7..c56335f6bc27 100644 --- a/user_guide_src/source/general/context/006.php +++ b/user_guide_src/source/general/context/006.php @@ -2,4 +2,3 @@ $role = $context->get('user_role', 'guest'); // If 'user_role' doesn't exist, $role will be 'guest' - diff --git a/user_guide_src/source/general/context/007.php b/user_guide_src/source/general/context/007.php index 1112e327b5bb..9ad340998671 100644 --- a/user_guide_src/source/general/context/007.php +++ b/user_guide_src/source/general/context/007.php @@ -2,4 +2,3 @@ $allData = $context->getAll(); // Returns: ['user_id' => 123, 'username' => 'john_doe', ...] - diff --git a/user_guide_src/source/general/context/008.php b/user_guide_src/source/general/context/008.php index 070cd5f80db0..519ecf64e174 100644 --- a/user_guide_src/source/general/context/008.php +++ b/user_guide_src/source/general/context/008.php @@ -6,4 +6,3 @@ // You can also pass a single key as a string $userId = $context->getOnly('user_id'); // Returns: ['user_id' => 123] - diff --git a/user_guide_src/source/general/context/009.php b/user_guide_src/source/general/context/009.php index 7365457a4d63..2c5158dd36b0 100644 --- a/user_guide_src/source/general/context/009.php +++ b/user_guide_src/source/general/context/009.php @@ -6,4 +6,3 @@ // You can also pass a single key as a string $data = $context->getExcept('password'); // Returns all data except 'password' - diff --git a/user_guide_src/source/general/context/010.php b/user_guide_src/source/general/context/010.php index 09541e2b40df..61b10e76a4c5 100644 --- a/user_guide_src/source/general/context/010.php +++ b/user_guide_src/source/general/context/010.php @@ -3,4 +3,3 @@ if ($context->has('user_id')) { // Do something with user_id } - diff --git a/user_guide_src/source/general/context/011.php b/user_guide_src/source/general/context/011.php index a1968abd8746..534b4e32e388 100644 --- a/user_guide_src/source/general/context/011.php +++ b/user_guide_src/source/general/context/011.php @@ -3,4 +3,3 @@ if ($context->missing('user_id')) { // user_id hasn't been set yet } - diff --git a/user_guide_src/source/general/context/012.php b/user_guide_src/source/general/context/012.php index 96559acb0dbf..2c6c3598ab91 100644 --- a/user_guide_src/source/general/context/012.php +++ b/user_guide_src/source/general/context/012.php @@ -1,4 +1,3 @@ remove('user_id'); - diff --git a/user_guide_src/source/general/context/013.php b/user_guide_src/source/general/context/013.php index 8ce31709a0a4..5922fd6605ac 100644 --- a/user_guide_src/source/general/context/013.php +++ b/user_guide_src/source/general/context/013.php @@ -1,4 +1,3 @@ remove(['user_id', 'username', 'request_id']); - diff --git a/user_guide_src/source/general/context/014.php b/user_guide_src/source/general/context/014.php index 1fac5ae27cfd..18746e68281d 100644 --- a/user_guide_src/source/general/context/014.php +++ b/user_guide_src/source/general/context/014.php @@ -1,4 +1,3 @@ clear(); - diff --git a/user_guide_src/source/general/context/015.php b/user_guide_src/source/general/context/015.php index dfb657040641..560f233a600a 100644 --- a/user_guide_src/source/general/context/015.php +++ b/user_guide_src/source/general/context/015.php @@ -1,4 +1,3 @@ setHidden('api_key', 'sk_live_abc123xyz789'); - diff --git a/user_guide_src/source/general/context/016.php b/user_guide_src/source/general/context/016.php index e2d25b392e8f..b7b3a906ac78 100644 --- a/user_guide_src/source/general/context/016.php +++ b/user_guide_src/source/general/context/016.php @@ -1,8 +1,7 @@ setHidden([ - 'api_key' => 'sk_live_abc123xyz789', - 'api_secret' => 'secret_key_here', - 'db_password' => 'database_password', + 'api_key' => 'sk_live_abc123xyz789', + 'api_secret' => 'secret_key_here', + 'db_password' => 'database_password', ]); - diff --git a/user_guide_src/source/general/context/017.php b/user_guide_src/source/general/context/017.php index 39f304bab02e..e26ceda619ff 100644 --- a/user_guide_src/source/general/context/017.php +++ b/user_guide_src/source/general/context/017.php @@ -2,4 +2,3 @@ $apiKey = $context->getHidden('api_key'); // $apiKey = 'sk_live_abc123xyz789' - diff --git a/user_guide_src/source/general/context/018.php b/user_guide_src/source/general/context/018.php index 017ef1a5e766..9c52af5e6b05 100644 --- a/user_guide_src/source/general/context/018.php +++ b/user_guide_src/source/general/context/018.php @@ -11,4 +11,3 @@ // Get all hidden data $allHidden = $context->getAllHidden(); - diff --git a/user_guide_src/source/general/context/019.php b/user_guide_src/source/general/context/019.php index 234b6ef39678..4e55c610538f 100644 --- a/user_guide_src/source/general/context/019.php +++ b/user_guide_src/source/general/context/019.php @@ -7,4 +7,3 @@ if ($context->missingHidden('api_key')) { // API key is not set } - diff --git a/user_guide_src/source/general/context/020.php b/user_guide_src/source/general/context/020.php index 18ae63a9f588..a06a3f6d9c3b 100644 --- a/user_guide_src/source/general/context/020.php +++ b/user_guide_src/source/general/context/020.php @@ -5,4 +5,3 @@ // Remove multiple hidden values $context->removeHidden(['api_key', 'api_secret']); - diff --git a/user_guide_src/source/general/context/021.php b/user_guide_src/source/general/context/021.php index b5fb29c0d163..7fca9aa5869f 100644 --- a/user_guide_src/source/general/context/021.php +++ b/user_guide_src/source/general/context/021.php @@ -1,4 +1,3 @@ clearHidden(); - diff --git a/user_guide_src/source/general/context/022.php b/user_guide_src/source/general/context/022.php index fb90f6adf686..bc26dead4e00 100644 --- a/user_guide_src/source/general/context/022.php +++ b/user_guide_src/source/general/context/022.php @@ -1,4 +1,3 @@ clearAll(); - diff --git a/user_guide_src/source/general/context/023.php b/user_guide_src/source/general/context/023.php index 3803bed019b1..7e9f878c30bb 100644 --- a/user_guide_src/source/general/context/023.php +++ b/user_guide_src/source/general/context/023.php @@ -12,4 +12,3 @@ class Logger extends BaseConfig // ... } - diff --git a/user_guide_src/source/general/context/024.php b/user_guide_src/source/general/context/024.php index 1afc0dad17f3..cb730f8d20fc 100644 --- a/user_guide_src/source/general/context/024.php +++ b/user_guide_src/source/general/context/024.php @@ -5,4 +5,3 @@ $context->set('transaction_id', 'txn_12345'); log_message('error', 'Payment processing failed'); - From 66fd4aea03e83037f921a68a851a592cac519ba5 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 21 Feb 2026 15:30:02 +0530 Subject: [PATCH 10/16] Some doc fixing --- user_guide_src/source/changelogs/v4.8.0.rst | 2 ++ user_guide_src/source/general/context.rst | 26 ++++++++++++------ user_guide_src/source/general/logging.rst | 29 +++++++++++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/user_guide_src/source/changelogs/v4.8.0.rst b/user_guide_src/source/changelogs/v4.8.0.rst index b45b1fda6b16..5404ace41547 100644 --- a/user_guide_src/source/changelogs/v4.8.0.rst +++ b/user_guide_src/source/changelogs/v4.8.0.rst @@ -74,6 +74,8 @@ Model Libraries ========= +- **Context**: This new feature allows you to easily set and retrieve normal or hidden contextual data for the current request. See :ref:`Context ` for details. + Helpers and Functions ===================== diff --git a/user_guide_src/source/general/context.rst b/user_guide_src/source/general/context.rst index 30170b6e54ec..db813c14d106 100644 --- a/user_guide_src/source/general/context.rst +++ b/user_guide_src/source/general/context.rst @@ -1,7 +1,11 @@ +.. _context: + ################### Context ################### +.. versionadded:: 4.8.0 + .. contents:: :local: :depth: 2 @@ -19,9 +23,9 @@ The Context class is particularly useful for: - Adding contextual information to your logs automatically - Storing sensitive data that should not appear in logs -****************** -Accessing Context -****************** +*********************** +Accessing Context Class +*********************** You can access the Context service anywhere in your application using the ``service()`` function: @@ -123,7 +127,9 @@ To remove all context data: Hidden Context Data ********************* -The Context class provides a separate storage area for sensitive data that should not be included in logs. This is useful for storing API keys, passwords, tokens, or other sensitive information that you need to access during the request but don't want to expose in log files. +The Context class provides a separate storage area for sensitive data that should not be included in logs. +This is useful for storing API keys, passwords, tokens, or other sensitive information that you need to access +during the request but don't want to expose in log files. Setting Hidden Data =================== @@ -172,18 +178,21 @@ To clear both regular and hidden data: .. literalinclude:: context/022.php -.. important:: Regular data and hidden data are stored separately. A key can exist in both regular and hidden storage with different values. Use ``get()`` for regular data and ``getHidden()`` for hidden data. +.. important:: Regular data and hidden data are stored separately. A key can exist in both regular and hidden storage +with different values. Use ``get()`` for regular data and ``getHidden()`` for hidden data. *********************************** Integration with Logging *********************************** -The Context class integrates seamlessly with CodeIgniter's logging system. When enabled, context data is automatically appended to log messages, providing additional information for debugging and monitoring. +The Context class integrates seamlessly with CodeIgniter's logging system. When enabled, context data is automatically +appended to log messages, providing additional information for debugging and monitoring. Enabling Global Context Logging ================================ -To enable automatic logging of context data, set the ``$logGlobalContext`` property to ``true`` in your **app/Config/Logger.php** file: +To enable automatic logging of context data, set the ``$logGlobalContext`` property to ``true`` in your +**app/Config/Logger.php** file: .. literalinclude:: context/023.php @@ -197,7 +206,8 @@ This would produce a log entry like: ERROR - 2026-02-18 --> Payment processing failed {"user_id":123,"transaction_id":"txn_12345"} -.. note:: Hidden data set with ``setHidden()`` is **never** included in logs, even when ``$logGlobalContext`` is enabled. This ensures sensitive information like API keys or tokens remain secure. +.. note:: Hidden data set with ``setHidden()`` is **never** included in logs, even when ``$logGlobalContext`` is enabled. +This ensures sensitive information like API keys or tokens remain secure. *************** Important Notes diff --git a/user_guide_src/source/general/logging.rst b/user_guide_src/source/general/logging.rst index 6e159827b1e9..5f21ad7888b8 100644 --- a/user_guide_src/source/general/logging.rst +++ b/user_guide_src/source/general/logging.rst @@ -118,6 +118,35 @@ Several core placeholders exist that will be automatically expanded for you base | {env:foo} | The value of 'foo' in $_ENV | +----------------+---------------------------------------------------+ +.. _logging-global-context: + +Global Context Logging +---------------------- + +.. versionadded:: 4.8.0 + +You can automatically append context data to all log messages by enabling the ``$logGlobalContext`` +property in **app/Config/Logger.php**: + +.. literalinclude:: context/023.php + +When enabled, all regular context data (set via the :ref:`Context class `) is automatically +appended to every log message as a JSON string: + +.. literalinclude:: context/024.php + +This would produce a log entry like: + +.. code-block:: text + + ERROR - 2026-02-18 --> Payment processing failed {"user_id":123,"transaction_id":"txn_12345"} + +.. note:: Hidden data set with ``setHidden()`` is **never** included in log output, even when + ``$logGlobalContext`` is enabled. This protects sensitive information such as API keys + and tokens from appearing in log files. + +See :ref:`context` for full documentation on storing and managing context data. + Using Third-Party Loggers ========================= From daf6d6b5a7fbaff41c31c36150be4eebc0b9bd6c Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sat, 21 Feb 2026 15:50:10 +0530 Subject: [PATCH 11/16] Fix rst errors. --- user_guide_src/source/general/context.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/user_guide_src/source/general/context.rst b/user_guide_src/source/general/context.rst index db813c14d106..17dc17a502fc 100644 --- a/user_guide_src/source/general/context.rst +++ b/user_guide_src/source/general/context.rst @@ -178,8 +178,7 @@ To clear both regular and hidden data: .. literalinclude:: context/022.php -.. important:: Regular data and hidden data are stored separately. A key can exist in both regular and hidden storage -with different values. Use ``get()`` for regular data and ``getHidden()`` for hidden data. +.. important:: Regular data and hidden data are stored separately. A key can exist in both regular and hidden storage with different values. Use ``get()`` for regular data and ``getHidden()`` for hidden data. *********************************** Integration with Logging @@ -206,8 +205,7 @@ This would produce a log entry like: ERROR - 2026-02-18 --> Payment processing failed {"user_id":123,"transaction_id":"txn_12345"} -.. note:: Hidden data set with ``setHidden()`` is **never** included in logs, even when ``$logGlobalContext`` is enabled. -This ensures sensitive information like API keys or tokens remain secure. +.. note:: Hidden data set with ``setHidden()`` is **never** included in logs, even when ``$logGlobalContext`` is enabled. This ensures sensitive information like API keys or tokens remain secure. *************** Important Notes From 1136fa0f7af7d4c5466976bf8d596098d70d4634 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Sun, 22 Feb 2026 15:33:59 +0530 Subject: [PATCH 12/16] Apply suggested changes --- app/Config/Logger.php | 2 +- system/Context/Context.php | 35 ++++--------------- system/Log/Logger.php | 6 ++-- tests/system/Context/ContextTest.php | 18 ---------- user_guide_src/source/general/context.rst | 30 +++++++--------- user_guide_src/source/general/context/011.php | 4 +-- user_guide_src/source/general/context/012.php | 2 +- user_guide_src/source/general/context/013.php | 2 +- user_guide_src/source/general/context/014.php | 2 +- user_guide_src/source/general/context/015.php | 6 +++- user_guide_src/source/general/context/016.php | 7 ++-- user_guide_src/source/general/context/017.php | 13 +++++-- user_guide_src/source/general/context/018.php | 14 ++------ user_guide_src/source/general/context/019.php | 10 +++--- user_guide_src/source/general/context/020.php | 6 +--- user_guide_src/source/general/context/021.php | 2 +- user_guide_src/source/general/context/022.php | 13 ++++++- user_guide_src/source/general/context/023.php | 15 +++----- user_guide_src/source/general/context/024.php | 7 ---- 19 files changed, 71 insertions(+), 123 deletions(-) delete mode 100644 user_guide_src/source/general/context/024.php diff --git a/app/Config/Logger.php b/app/Config/Logger.php index e303965a397b..16f2fa9aa890 100644 --- a/app/Config/Logger.php +++ b/app/Config/Logger.php @@ -65,7 +65,7 @@ class Logger extends BaseConfig * **NOTE:** This **DOES NOT** include any data that has been marked as hidden * using the `setHidden()` method of the Context class. */ - public bool $logGlobalContext = false; + public bool $logGlobalContext = true; /** * -------------------------------------------------------------------------- diff --git a/system/Context/Context.php b/system/Context/Context.php index 24852b14a8eb..2cd29957c8a5 100644 --- a/system/Context/Context.php +++ b/system/Context/Context.php @@ -13,6 +13,8 @@ namespace CodeIgniter\Context; +use CodeIgniter\Helpers\Array\ArrayHelper; + class Context { /** @@ -82,6 +84,7 @@ public function setHidden(array|string $key, mixed $value = null): self /** * Get a value from the context by its key, or return a default value if the key does not exist. + * Supports dot notation for nested arrays (e.g., 'user.profile.name' to access $data['user']['profile']['name']). * * @param string $key The key to identify the data. * @param mixed|null $default The default value to return if the key does not exist in the context. @@ -90,7 +93,7 @@ public function setHidden(array|string $key, mixed $value = null): self */ public function get(string $key, mixed $default = null): mixed { - return $this->data[$key] ?? $default; + return ArrayHelper::dotSearch($key, $this->data) ?? $default; } /** @@ -145,7 +148,7 @@ public function getAll(): array */ public function getHidden(string $key, mixed $default = null): mixed { - return $this->hiddenData[$key] ?? $default; + return ArrayHelper::dotSearch($key, $this->hiddenData) ?? $default; } /** @@ -190,18 +193,6 @@ public function getAllHidden(): array return $this->hiddenData; } - /** - * Check if a key does not exist in the context. Exactly the opposite of `has()`. - * - * @param string $key The key to check for non-existence in the context. - * - * @return bool True if the key does not exist in the context, false otherwise. - */ - public function missing(string $key): bool - { - return ! $this->has($key); - } - /** * Check if a key exists in the context. * @@ -211,19 +202,7 @@ public function missing(string $key): bool */ public function has(string $key): bool { - return array_key_exists($key, $this->data); - } - - /** - * Check if a key does not exist in the hidden context. Exactly the opposite of `hasHidden()`. - * - * @param string $key The key to check for non-existence in the hidden context. - * - * @return bool True if the key does not exist in the hidden context, false otherwise. - */ - public function missingHidden(string $key): bool - { - return ! $this->hasHidden($key); + return ArrayHelper::dotKeyExists($key, $this->data); } /** @@ -235,7 +214,7 @@ public function missingHidden(string $key): bool */ public function hasHidden(string $key): bool { - return array_key_exists($key, $this->hiddenData); + return ArrayHelper::dotKeyExists($key, $this->hiddenData); } /** diff --git a/system/Log/Logger.php b/system/Log/Logger.php index 22eb3de48637..1bb94e95ee26 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -121,7 +121,7 @@ class Logger implements LoggerInterface * * @var bool */ - protected $logGlobalContext; + protected bool $logGlobalContext = true; /** * Constructor. @@ -164,7 +164,7 @@ public function __construct($config, bool $debug = CI_DEBUG) $this->logCache = []; } - $this->logGlobalContext = $config->logGlobalContext; + $this->logGlobalContext = $config->logGlobalContext ?? $this->logGlobalContext; } /** @@ -265,7 +265,7 @@ public function log($level, string|Stringable $message, array $context = []): vo if ($this->logGlobalContext) { $globalContext = service('context')->getAll(); - if (is_array($globalContext) && $globalContext !== []) { + if ($globalContext !== []) { $message .= ' ' . json_encode($globalContext); } } diff --git a/tests/system/Context/ContextTest.php b/tests/system/Context/ContextTest.php index c315519d81b4..dacb6615fbfd 100644 --- a/tests/system/Context/ContextTest.php +++ b/tests/system/Context/ContextTest.php @@ -328,15 +328,6 @@ public function testHasKey(): void $this->assertTrue($context->has('user_id')); } - public function testMissingKey(): void - { - $context = service('context'); - $this->assertTrue($context->missing('user_id')); - - $context->set('user_id', 123); - $this->assertFalse($context->missing('user_id')); - } - public function testHasHiddenKey(): void { $context = service('context'); @@ -346,15 +337,6 @@ public function testHasHiddenKey(): void $this->assertTrue($context->hasHidden('api_key')); } - public function testMissingHiddenKey(): void - { - $context = service('context'); - $this->assertTrue($context->missingHidden('api_key')); - - $context->setHidden('api_key', 'secret'); - $this->assertFalse($context->missingHidden('api_key')); - } - public function testRemoveSingleValue(): void { $context = service('context'); diff --git a/user_guide_src/source/general/context.rst b/user_guide_src/source/general/context.rst index 17dc17a502fc..bee097946596 100644 --- a/user_guide_src/source/general/context.rst +++ b/user_guide_src/source/general/context.rst @@ -94,10 +94,6 @@ You can check if a key exists in the context: .. literalinclude:: context/010.php -Or check if a key is missing (the opposite of ``has()``): - -.. literalinclude:: context/011.php - ********************* Removing Context Data ********************* @@ -107,21 +103,21 @@ Removing a Single Value You can remove data from the context using the ``remove()`` method: -.. literalinclude:: context/012.php +.. literalinclude:: context/011.php Removing Multiple Values ========================= To remove multiple keys at once, pass an array: -.. literalinclude:: context/013.php +.. literalinclude:: context/012.php Clearing All Data ================= To remove all context data: -.. literalinclude:: context/014.php +.. literalinclude:: context/013.php ********************* Hidden Context Data @@ -136,47 +132,47 @@ Setting Hidden Data Use the ``setHidden()`` method to store sensitive data: -.. literalinclude:: context/015.php +.. literalinclude:: context/014.php You can also set multiple hidden values at once: -.. literalinclude:: context/016.php +.. literalinclude:: context/015.php Getting Hidden Data =================== Retrieve hidden data using ``getHidden()``: -.. literalinclude:: context/017.php +.. literalinclude:: context/016.php The same methods available for regular data also work with hidden data: -.. literalinclude:: context/018.php +.. literalinclude:: context/017.php Checking Hidden Data ==================== Check if a hidden key exists: -.. literalinclude:: context/019.php +.. literalinclude:: context/018.php Removing Hidden Data ==================== Remove hidden data using ``removeHidden()``: -.. literalinclude:: context/020.php +.. literalinclude:: context/019.php Clearing Hidden Data ==================== To clear all hidden data without affecting regular context data: -.. literalinclude:: context/021.php +.. literalinclude:: context/020.php To clear both regular and hidden data: -.. literalinclude:: context/022.php +.. literalinclude:: context/021.php .. important:: Regular data and hidden data are stored separately. A key can exist in both regular and hidden storage with different values. Use ``get()`` for regular data and ``getHidden()`` for hidden data. @@ -193,11 +189,11 @@ Enabling Global Context Logging To enable automatic logging of context data, set the ``$logGlobalContext`` property to ``true`` in your **app/Config/Logger.php** file: -.. literalinclude:: context/023.php +.. literalinclude:: context/022.php When enabled, all context data (excluding hidden data) will be automatically appended to your log messages as JSON: -.. literalinclude:: context/024.php +.. literalinclude:: context/023.php This would produce a log entry like: diff --git a/user_guide_src/source/general/context/011.php b/user_guide_src/source/general/context/011.php index 534b4e32e388..2c6c3598ab91 100644 --- a/user_guide_src/source/general/context/011.php +++ b/user_guide_src/source/general/context/011.php @@ -1,5 +1,3 @@ missing('user_id')) { - // user_id hasn't been set yet -} +$context->remove('user_id'); diff --git a/user_guide_src/source/general/context/012.php b/user_guide_src/source/general/context/012.php index 2c6c3598ab91..5922fd6605ac 100644 --- a/user_guide_src/source/general/context/012.php +++ b/user_guide_src/source/general/context/012.php @@ -1,3 +1,3 @@ remove('user_id'); +$context->remove(['user_id', 'username', 'request_id']); diff --git a/user_guide_src/source/general/context/013.php b/user_guide_src/source/general/context/013.php index 5922fd6605ac..18746e68281d 100644 --- a/user_guide_src/source/general/context/013.php +++ b/user_guide_src/source/general/context/013.php @@ -1,3 +1,3 @@ remove(['user_id', 'username', 'request_id']); +$context->clear(); diff --git a/user_guide_src/source/general/context/014.php b/user_guide_src/source/general/context/014.php index 18746e68281d..560f233a600a 100644 --- a/user_guide_src/source/general/context/014.php +++ b/user_guide_src/source/general/context/014.php @@ -1,3 +1,3 @@ clear(); +$context->setHidden('api_key', 'sk_live_abc123xyz789'); diff --git a/user_guide_src/source/general/context/015.php b/user_guide_src/source/general/context/015.php index 560f233a600a..b7b3a906ac78 100644 --- a/user_guide_src/source/general/context/015.php +++ b/user_guide_src/source/general/context/015.php @@ -1,3 +1,7 @@ setHidden('api_key', 'sk_live_abc123xyz789'); +$context->setHidden([ + 'api_key' => 'sk_live_abc123xyz789', + 'api_secret' => 'secret_key_here', + 'db_password' => 'database_password', +]); diff --git a/user_guide_src/source/general/context/016.php b/user_guide_src/source/general/context/016.php index b7b3a906ac78..e26ceda619ff 100644 --- a/user_guide_src/source/general/context/016.php +++ b/user_guide_src/source/general/context/016.php @@ -1,7 +1,4 @@ setHidden([ - 'api_key' => 'sk_live_abc123xyz789', - 'api_secret' => 'secret_key_here', - 'db_password' => 'database_password', -]); +$apiKey = $context->getHidden('api_key'); +// $apiKey = 'sk_live_abc123xyz789' diff --git a/user_guide_src/source/general/context/017.php b/user_guide_src/source/general/context/017.php index e26ceda619ff..9c52af5e6b05 100644 --- a/user_guide_src/source/general/context/017.php +++ b/user_guide_src/source/general/context/017.php @@ -1,4 +1,13 @@ getHidden('api_key'); -// $apiKey = 'sk_live_abc123xyz789' +// Get with default value +$apiKey = $context->getHidden('api_key', 'default_key'); + +// Get only specific hidden keys +$credentials = $context->getOnlyHidden(['api_key', 'api_secret']); + +// Get all hidden data except specific keys +$data = $context->getExceptHidden(['db_password']); + +// Get all hidden data +$allHidden = $context->getAllHidden(); diff --git a/user_guide_src/source/general/context/018.php b/user_guide_src/source/general/context/018.php index 9c52af5e6b05..b5084a23f143 100644 --- a/user_guide_src/source/general/context/018.php +++ b/user_guide_src/source/general/context/018.php @@ -1,13 +1,5 @@ getHidden('api_key', 'default_key'); - -// Get only specific hidden keys -$credentials = $context->getOnlyHidden(['api_key', 'api_secret']); - -// Get all hidden data except specific keys -$data = $context->getExceptHidden(['db_password']); - -// Get all hidden data -$allHidden = $context->getAllHidden(); +if ($context->hasHidden('api_key')) { + // API key is set +} diff --git a/user_guide_src/source/general/context/019.php b/user_guide_src/source/general/context/019.php index 4e55c610538f..a06a3f6d9c3b 100644 --- a/user_guide_src/source/general/context/019.php +++ b/user_guide_src/source/general/context/019.php @@ -1,9 +1,7 @@ hasHidden('api_key')) { - // API key is set -} +// Remove a single hidden value +$context->removeHidden('api_key'); -if ($context->missingHidden('api_key')) { - // API key is not set -} +// Remove multiple hidden values +$context->removeHidden(['api_key', 'api_secret']); diff --git a/user_guide_src/source/general/context/020.php b/user_guide_src/source/general/context/020.php index a06a3f6d9c3b..7fca9aa5869f 100644 --- a/user_guide_src/source/general/context/020.php +++ b/user_guide_src/source/general/context/020.php @@ -1,7 +1,3 @@ removeHidden('api_key'); - -// Remove multiple hidden values -$context->removeHidden(['api_key', 'api_secret']); +$context->clearHidden(); diff --git a/user_guide_src/source/general/context/021.php b/user_guide_src/source/general/context/021.php index 7fca9aa5869f..bc26dead4e00 100644 --- a/user_guide_src/source/general/context/021.php +++ b/user_guide_src/source/general/context/021.php @@ -1,3 +1,3 @@ clearHidden(); +$context->clearAll(); diff --git a/user_guide_src/source/general/context/022.php b/user_guide_src/source/general/context/022.php index bc26dead4e00..7e9f878c30bb 100644 --- a/user_guide_src/source/general/context/022.php +++ b/user_guide_src/source/general/context/022.php @@ -1,3 +1,14 @@ clearAll(); +namespace Config; + +use CodeIgniter\Config\BaseConfig; + +class Logger extends BaseConfig +{ + // ... + + public bool $logGlobalContext = true; + + // ... +} diff --git a/user_guide_src/source/general/context/023.php b/user_guide_src/source/general/context/023.php index 7e9f878c30bb..cb730f8d20fc 100644 --- a/user_guide_src/source/general/context/023.php +++ b/user_guide_src/source/general/context/023.php @@ -1,14 +1,7 @@ set('user_id', 123); +$context->set('transaction_id', 'txn_12345'); -use CodeIgniter\Config\BaseConfig; - -class Logger extends BaseConfig -{ - // ... - - public bool $logGlobalContext = true; - - // ... -} +log_message('error', 'Payment processing failed'); diff --git a/user_guide_src/source/general/context/024.php b/user_guide_src/source/general/context/024.php deleted file mode 100644 index cb730f8d20fc..000000000000 --- a/user_guide_src/source/general/context/024.php +++ /dev/null @@ -1,7 +0,0 @@ -set('user_id', 123); -$context->set('transaction_id', 'txn_12345'); - -log_message('error', 'Payment processing failed'); From 07f895e31fbf1b226641f452f33e60d15061872a Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Mon, 23 Feb 2026 15:02:56 +0530 Subject: [PATCH 13/16] Set $logGlobalContext to false by default --- app/Config/Logger.php | 2 +- system/Log/Logger.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Config/Logger.php b/app/Config/Logger.php index 16f2fa9aa890..e303965a397b 100644 --- a/app/Config/Logger.php +++ b/app/Config/Logger.php @@ -65,7 +65,7 @@ class Logger extends BaseConfig * **NOTE:** This **DOES NOT** include any data that has been marked as hidden * using the `setHidden()` method of the Context class. */ - public bool $logGlobalContext = true; + public bool $logGlobalContext = false; /** * -------------------------------------------------------------------------- diff --git a/system/Log/Logger.php b/system/Log/Logger.php index 1bb94e95ee26..85f7a0f1e9a4 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -121,7 +121,7 @@ class Logger implements LoggerInterface * * @var bool */ - protected bool $logGlobalContext = true; + protected bool $logGlobalContext = false; /** * Constructor. From 7bf481aa105cbd813dd537f9555e6e6ebc725bc9 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Mon, 23 Feb 2026 15:04:12 +0530 Subject: [PATCH 14/16] Add docs for helper method --- user_guide_src/source/general/context.rst | 2 +- user_guide_src/source/general/context/001.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/user_guide_src/source/general/context.rst b/user_guide_src/source/general/context.rst index bee097946596..5c9dac35b16b 100644 --- a/user_guide_src/source/general/context.rst +++ b/user_guide_src/source/general/context.rst @@ -27,7 +27,7 @@ The Context class is particularly useful for: Accessing Context Class *********************** -You can access the Context service anywhere in your application using the ``service()`` function: +You can access the Context service anywhere in your application using the ``service()`` function or ``context()`` helper: .. literalinclude:: context/001.php diff --git a/user_guide_src/source/general/context/001.php b/user_guide_src/source/general/context/001.php index f424ec9dc35f..53004b9f25c5 100644 --- a/user_guide_src/source/general/context/001.php +++ b/user_guide_src/source/general/context/001.php @@ -1,3 +1,6 @@ Date: Mon, 23 Feb 2026 15:09:28 +0530 Subject: [PATCH 15/16] cs-fix --- system/Log/Logger.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/system/Log/Logger.php b/system/Log/Logger.php index 85f7a0f1e9a4..87edf8fbc14c 100644 --- a/system/Log/Logger.php +++ b/system/Log/Logger.php @@ -118,8 +118,6 @@ class Logger implements LoggerInterface * Whether to log the global context data. * * Set in app/Config/Logger.php - * - * @var bool */ protected bool $logGlobalContext = false; From 2f11d7c792ec8d8991a92266ad4e264cf57c5046 Mon Sep 17 00:00:00 2001 From: patel-vansh Date: Mon, 23 Feb 2026 15:15:24 +0530 Subject: [PATCH 16/16] Fix the doc issue --- user_guide_src/source/general/logging.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_guide_src/source/general/logging.rst b/user_guide_src/source/general/logging.rst index 5f21ad7888b8..1c2fc453eb64 100644 --- a/user_guide_src/source/general/logging.rst +++ b/user_guide_src/source/general/logging.rst @@ -133,7 +133,7 @@ property in **app/Config/Logger.php**: When enabled, all regular context data (set via the :ref:`Context class `) is automatically appended to every log message as a JSON string: -.. literalinclude:: context/024.php +.. literalinclude:: context/023.php This would produce a log entry like: