Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f6a9530
Added Context class
patel-vansh Feb 18, 2026
30cdc19
Added some convenient methods
patel-vansh Feb 18, 2026
a166678
Enhance logger to properly include global context
patel-vansh Feb 18, 2026
43c2d52
Add unit tests for Context class functionality
patel-vansh Feb 18, 2026
b0d5848
Added tests for global context logging behaviour
patel-vansh Feb 18, 2026
f62d577
cs-fix
patel-vansh Feb 18, 2026
2e4ca31
Add docs
patel-vansh Feb 19, 2026
da3a843
Merge remote-tracking branch 'upstream/4.8' into feat/context
patel-vansh Feb 21, 2026
b055119
add context() helper function
patel-vansh Feb 21, 2026
c60b9f9
cs-fix and fixing static analysis problems
patel-vansh Feb 21, 2026
66fd4ae
Some doc fixing
patel-vansh Feb 21, 2026
daf6d6b
Fix rst errors.
patel-vansh Feb 21, 2026
1136fa0
Apply suggested changes
patel-vansh Feb 22, 2026
07f895e
Set $logGlobalContext to false by default
patel-vansh Feb 23, 2026
7bf481a
Add docs for helper method
patel-vansh Feb 23, 2026
5a6fbca
cs-fix
patel-vansh Feb 23, 2026
2f11d7c
Fix the doc issue
patel-vansh Feb 23, 2026
bf7ed1b
Merge remote-tracking branch 'upstream/4.8' into feat/context
patel-vansh Feb 24, 2026
f36449e
Applying code suggestions
patel-vansh Feb 24, 2026
4bd55ed
changed type from mixed|null to mixed
patel-vansh Feb 24, 2026
f066488
Added __serialize and __unserialize methods.
patel-vansh Feb 24, 2026
9f77ab8
Returning $default if $key is not present. And only calling ArrayHelp…
patel-vansh Feb 26, 2026
dc8ca2a
Remove contructor
patel-vansh Feb 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions app/Config/Logger.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,22 @@ 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.
*/
public bool $logGlobalContext = false;

/**
* --------------------------------------------------------------------------
* Log Handlers
Expand Down
12 changes: 12 additions & 0 deletions system/Common.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -212,6 +213,17 @@ 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.
*/
function context(): Context
{
return service('context');
}
}

if (! function_exists('cookie')) {
/**
* Simpler way to create a new Cookie instance.
Expand Down
15 changes: 15 additions & 0 deletions system/Config/Services.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -875,4 +876,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();
}
}
321 changes: 321 additions & 0 deletions system/Context/Context.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
<?php

declare(strict_types=1);

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\Context;

class Context
{
/**
* The data stored in the context.
*
* @var array<string, mixed>
*/
protected array $data;

/**
* The data that is stored, but not included in logs.
*
* @var array<string, mixed>
*/
private array $hiddenData;

/**
* Constructor
*/
public function __construct()
{
$this->data = [];
$this->hiddenData = [];
}

/**
* Set a key-value pair to the context.
*
* @param array<string, mixed>|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(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 array<string, mixed>|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(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 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 only the specified keys from the context. If a key does not exist, it will be ignored.
*
* @param list<string>|string $keys An array of keys to retrieve from the context.
*
* @return array<string, mixed> An array of key-value pairs for the specified keys that exist in the context.
*/
public function getOnly(array|string $keys): array
{
if (is_string($keys)) {
$keys = [$keys];
}

return array_filter($this->data, static fn ($k): bool => in_array($k, $keys, true), ARRAY_FILTER_USE_KEY);
}

/**
* Get all keys from the context except the specified keys.
*
* @param list<string>|string $keys An array of keys to exclude from the context.
*
* @return array<string, mixed> An array of key-value pairs for all keys in the context except the specified keys.
*/
public function getExcept(array|string $keys): array
{
if (is_string($keys)) {
$keys = [$keys];
}

return array_filter($this->data, static fn ($k): bool => ! in_array($k, $keys, true), ARRAY_FILTER_USE_KEY);
}

/**
* Get all data from the context
*
* @return array<string, mixed> 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 only the specified keys from the hidden context. If a key does not exist, it will be ignored.
*
* @param list<string>|string $keys An array of keys to retrieve from the hidden context.
*
* @return array<string, mixed> An array of key-value pairs for the specified keys that exist in the hidden context.
*/
public function getOnlyHidden(array|string $keys): array
{
if (is_string($keys)) {
$keys = [$keys];
}

return array_filter($this->hiddenData, static fn ($k): bool => in_array($k, $keys, true), ARRAY_FILTER_USE_KEY);
}

/**
* Get all keys from the hidden context except the specified keys.
*
* @param list<string>|string $keys An array of keys to exclude from the hidden context.
*
* @return array<string, mixed> An array of key-value pairs for all keys in the hidden context except the specified keys.
*/
public function getExceptHidden(array|string $keys): array
{
if (is_string($keys)) {
$keys = [$keys];
}

return array_filter($this->hiddenData, static fn ($k): bool => ! in_array($k, $keys, true), ARRAY_FILTER_USE_KEY);
}

/**
* Get all hidden data from the context
*
* @return array<string, mixed> An array of all key-value pairs in the hidden context.
*/
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.
*
* @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 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.
*
* @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 list<string>|string $key The key to identify the data to be removed from the context.
*
* @return $this
*/
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 list<string>|string $key The key to identify the data to be removed from the hidden context.
*
* @return $this
*/
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;
}

/**
* 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;
}
}
Loading
Loading