Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ real-world PHP projects.

| Class | Description |
|---------------------|---------------------------------------------------------------------------------------------------------------------|
| **Config** | Dot-access configuration loader. |
| **Config** | Dot-access configuration loader with explicit hook-aware variants (`getWithHooks`, `setWithHooks`, `fillWithHooks`). |
| **LazyFileConfig** | First-segment lazy loader (`db.host` loads `db.php` on demand) for lower memory usage on large config trees. |
| **DynamicConfig** | Extends `Config` with **on-get/on-set hooks** to transform values dynamically (e.g., encrypt/decrypt, auto-format). |
| **BaseConfigTrait** | Shared config logic. |


Expand All @@ -59,7 +58,7 @@ real-world PHP projects.

| Trait | Description |
|---------------|------------------------------------------------------------------------------------------------|
| **HookTrait** | Generic hook system for on-get/on-set callbacks. Used by `DynamicConfig` & `HookedCollection`. |
| **HookTrait** | Generic hook system for on-get/on-set callbacks. Used by `Config`, `LazyFileConfig`, and `HookedCollection`. |
| **DTOTrait** | Utility trait for DTO-like behavior: populate, extract, cast arrays/objects easily. |


Expand Down Expand Up @@ -140,12 +139,12 @@ $flat = DotNotation::flatten($user);
// [ 'profile.name' => 'Alice', 'profile.email' => 'alice@example.com' ]
```

### 🔹 Dynamic Config with Hooks
### 🔹 Config Hooks (Explicit)

```php
use Infocyph\ArrayKit\Config\DynamicConfig;
use Infocyph\ArrayKit\Config\Config;

$config = new DynamicConfig();
$config = new Config();

// Load from file
$config->loadFile(__DIR__.'/config.php');
Expand All @@ -157,8 +156,8 @@ $config->onSet('auth.password', fn($v) => password_hash($v, PASSWORD_BCRYPT));
$config->onGet('secure.key', fn($v) => decrypt($v));

// Use it
$config->set('auth.password', 'secret123');
$hashed = $config->get('auth.password');
$config->setWithHooks('auth.password', 'secret123');
$hashed = $config->getWithHooks('auth.password');
```

### 🔹 Hooked Collection
Expand Down
37 changes: 19 additions & 18 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ Classes:

- ``Infocyph\ArrayKit\Config\Config``
- ``Infocyph\ArrayKit\Config\LazyFileConfig``
- ``Infocyph\ArrayKit\Config\DynamicConfig``

``DynamicConfig`` extends ``Config`` by adding value hooks.
``Config`` supports optional hooks via explicit ``getWithHooks()``,
``setWithHooks()``, and ``fillWithHooks()`` methods.
``LazyFileConfig`` loads namespace files only on first keyed access.

Loading Configuration
Expand Down Expand Up @@ -129,23 +129,24 @@ Array-Value Helpers
// ['cors', 'auth', 'throttle']
$middleware = $config->get('middleware');

DynamicConfig Hooks
-------------------
Config Hooks (Explicit)
-----------------------

``DynamicConfig`` allows per-key transformation on read/write.
``Config`` allows per-key transformation on read/write, while keeping
``get()/set()/fill()`` hook-free for maximum base-path performance.

.. code-block:: php

<?php
use Infocyph\ArrayKit\Config\DynamicConfig;
use Infocyph\ArrayKit\Config\Config;

$config = new DynamicConfig();
$config = new Config();

$config->onSet('user.name', fn ($v) => strtoupper((string) $v));
$config->onGet('user.name', fn ($v) => strtolower((string) $v));

$config->set('user.name', 'Alice');
echo $config->get('user.name'); // alice
$config->setWithHooks('user.name', 'Alice');
echo $config->getWithHooks('user.name'); // alice

Bulk operations with hooks:

Expand All @@ -154,12 +155,12 @@ Bulk operations with hooks:
<?php
$config->onSet('user.email', fn ($v) => trim((string) $v));

$config->set([
$config->setWithHooks([
'user.name' => 'JOHN',
'user.email' => ' john@example.com ',
]);

$vals = $config->get(['user.name', 'user.email']);
$vals = $config->getWithHooks(['user.name', 'user.email']);

Practical Pattern
-----------------
Expand All @@ -169,15 +170,15 @@ Use config as a mutable runtime container for app setup:
.. code-block:: php

<?php
$config = new DynamicConfig();
$config = new Config();
$config->loadFile(__DIR__.'/config.php');

// Normalize selected runtime values
$config->onSet('app.timezone', fn ($v) => trim((string) $v));
$config->onGet('app.timezone', fn ($v) => strtoupper((string) $v));

$config->set('app.timezone', ' utc ');
$tz = $config->get('app.timezone'); // UTC
$config->setWithHooks('app.timezone', ' utc ');
$tz = $config->getWithHooks('app.timezone'); // UTC

LazyFileConfig
--------------
Expand Down Expand Up @@ -231,9 +232,9 @@ LazyFileConfig methods:
- ``preload()``, ``isLoaded()``, ``loadedNamespaces()``
- ``all()`` (throws by design)

DynamicConfig methods:
Hook-aware methods (Config and LazyFileConfig):

- ``get()`` (hook-aware override)
- ``set()`` (hook-aware override)
- ``fill()`` (hook-aware override)
- ``getWithHooks()``
- ``setWithHooks()``
- ``fillWithHooks()``
- ``onGet()``, ``onSet()``
6 changes: 3 additions & 3 deletions docs/quick-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ Config + Hooks Example
.. code-block:: php

<?php
use Infocyph\ArrayKit\Config\DynamicConfig;
use Infocyph\ArrayKit\Config\Config;

$config = new DynamicConfig();
$config = new Config();
$config->set('auth.password', 'secret');
$config->onGet('auth.password', fn ($v) => strtoupper((string) $v));
echo $config->get('auth.password'); // SECRET
echo $config->getWithHooks('auth.password'); // SECRET

Global Helper Example
---------------------
Expand Down
16 changes: 8 additions & 8 deletions docs/rule-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ Runtime configuration with hooks:
.. code-block:: php

<?php
$config = new \Infocyph\ArrayKit\Config\DynamicConfig();
$config = new \Infocyph\ArrayKit\Config\Config();
$config->onSet('app.name', fn ($v) => trim((string) $v));
$config->set('app.name', ' ArrayKit ');
echo $config->get('app.name'); // ArrayKit
$config->setWithHooks('app.name', ' ArrayKit ');
echo $config->getWithHooks('app.name'); // ArrayKit

Static array utilities for data shaping:

Expand Down Expand Up @@ -376,16 +376,16 @@ LazyFileConfig loads top-level config files on first keyed access:
public function loadedNamespaces(): array
public function all(): array // throws (design choice)

DynamicConfig
Config Hook-Aware Variants
--------------------------------------

DynamicConfig extends Config behavior with hooks and overrides:
Both Config and LazyFileConfig expose explicit hook-aware methods:

.. code-block:: php

public function get(int|string|array|null $key = null, mixed $default = null): mixed
public function set(string|array|null $key = null, mixed $value = null, bool $overwrite = true): bool
public function fill(string|array $key, mixed $value = null): bool
public function getWithHooks(int|string|array|null $key = null, mixed $default = null): mixed
public function setWithHooks(string|array|null $key = null, mixed $value = null, bool $overwrite = true): bool
public function fillWithHooks(string|array $key, mixed $value = null): bool
public function onGet(string $offset, callable $callback): static
public function onSet(string $offset, callable $callback): static

Expand Down
17 changes: 9 additions & 8 deletions docs/traits-and-helpers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ Main methods:
``HookTrait`` is used internally by:

- ``Infocyph\ArrayKit\Collection\HookedCollection``
- ``Infocyph\ArrayKit\Config\DynamicConfig``
- ``Infocyph\ArrayKit\Config\Config``
- ``Infocyph\ArrayKit\Config\LazyFileConfig``

HookedCollection Integration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -103,20 +104,20 @@ HookedCollection Integration
$c['role'] = 'admin';
echo $c['role']; // Role: admin

DynamicConfig Integration
~~~~~~~~~~~~~~~~~~~~~~~~~
Config Integration
~~~~~~~~~~~~~~~~~~

.. code-block:: php

<?php
use Infocyph\ArrayKit\Config\DynamicConfig;
use Infocyph\ArrayKit\Config\Config;

$config = new DynamicConfig();
$config = new Config();
$config->onSet('user.email', fn ($v) => trim((string) $v));
$config->onGet('user.email', fn ($v) => strtolower((string) $v));

$config->set('user.email', ' ALICE@EXAMPLE.COM ');
echo $config->get('user.email'); // alice@example.com
$config->setWithHooks('user.email', ' ALICE@EXAMPLE.COM ');
echo $config->getWithHooks('user.email'); // alice@example.com

Multiple Hooks on Same Key
~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand All @@ -129,7 +130,7 @@ Hooks run in registration order:
$config->onSet('username', fn ($v) => trim((string) $v));
$config->onSet('username', fn ($v) => strtolower((string) $v));

$config->set('username', ' ALICE '); // becomes "alice"
$config->setWithHooks('username', ' ALICE '); // becomes "alice"

Global Helper Functions
-----------------------
Expand Down
67 changes: 64 additions & 3 deletions src/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,75 @@

namespace Infocyph\ArrayKit\Config;

use Infocyph\ArrayKit\traits\HookTrait;

/**
* Class Config
*
* Example usage of the BaseConfigTrait to provide
* core configuration handling, plus any additional
* features from the Multi trait.
* Provides base configuration storage with optional hook-aware variants.
*
* Core methods from BaseConfigTrait (`get`, `set`, `fill`) remain fast and
* hook-free. Hook processing is explicit through `getWithHooks`,
* `setWithHooks`, and `fillWithHooks`.
*/
class Config
{
use BaseConfigTrait;
use HookTrait;

/**
* Hook-aware variant of fill().
*/
public function fillWithHooks(string|array $key, mixed $value = null): bool
{
if (is_array($key)) {
$processed = [];
foreach ($key as $path => $entry) {
$processed[$path] = $this->processValue($path, $entry, 'set');
}

return $this->fill($processed);
}

$processed = $this->processValue($key, $value, 'set');

return $this->fill($key, $processed);
}

/**
* Hook-aware variant of get().
*/
public function getWithHooks(int|string|array|null $key = null, mixed $default = null): mixed
{
$value = $this->get($key, $default);

if (is_array($key)) {
foreach ($value as $path => $entry) {
$value[$path] = $this->processValue($path, $entry, 'get');
}

return $value;
}

return $this->processValue($key, $value, 'get');
}

/**
* Hook-aware variant of set().
*/
public function setWithHooks(string|array|null $key = null, mixed $value = null, bool $overwrite = true): bool
{
if (is_array($key)) {
$processed = [];
foreach ($key as $path => $entry) {
$processed[$path] = $this->processValue($path, $entry, 'set');
}

return $this->set($processed, null, $overwrite);
}

$processedValue = $this->processValue($key, $value, 'set');

return $this->set($key, $processedValue, $overwrite);
}
}
Loading
Loading