Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
ArrayConfig
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
7 / 7
12
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 has
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 set
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 remove
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getIterator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toArray
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/**
6 * This file is part of php-fast-forward/config.
7 *
8 * This source file is subject to the license bundled
9 * with this source code in the file LICENSE.
10 *
11 * @link      https://github.com/php-fast-forward/config
12 * @copyright Copyright (c) 2025 Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
13 * @license   https://opensource.org/licenses/MIT MIT License
14 */
15
16namespace FastForward\Config;
17
18use Dflydev\DotAccessData\Data;
19use FastForward\Config\Exception\InvalidArgumentException;
20use FastForward\Config\Helper\ConfigHelper;
21
22/**
23 * Class ArrayConfig.
24 *
25 * Provides a configuration management system with dot notation access.
26 * This class SHALL encapsulate configuration data using the DotAccessData library.
27 * It MUST support nested keys and provide export, iteration, and dynamic update capabilities.
28 *
29 * @package FastForward\Config
30 */
31final class ArrayConfig implements ConfigInterface
32{
33    use ArrayAccessConfigTrait;
34
35    /**
36     * @var Data internal configuration storage instance
37     */
38    private Data $data;
39
40    /**
41     * Constructs the ArrayConfig instance.
42     *
43     * This constructor SHALL initialize the internal configuration store using normalized keys.
44     *
45     * @param array $config an optional initial configuration array
46     */
47    public function __construct(array $config = [])
48    {
49        $this->data = new Data(
50            data: ConfigHelper::normalize($config),
51        );
52    }
53
54    /**
55     * Determines whether the given configuration key exists.
56     *
57     * @param string $key the dot-notation configuration key to check
58     *
59     * @return bool TRUE if the key exists, FALSE otherwise
60     */
61    public function has(string $key): bool
62    {
63        return $this->data->has($key);
64    }
65
66    /**
67     * Retrieves a value from the configuration.
68     *
69     * If the value is a nested associative array, a new instance of ArrayConfig SHALL be returned.
70     * If the key is not found and no default is provided, a MissingPathException SHOULD be thrown.
71     *
72     * @param string $key     the configuration key to retrieve
73     * @param mixed  $default the default value to return if the key does not exist
74     *
75     * @return mixed|self the configuration value, or a nested ArrayConfig instance
76     */
77    public function get(string $key, mixed $default = null): mixed
78    {
79        $value = $this->data->get($key, $default);
80
81        if (ConfigHelper::isAssoc($value)) {
82            return new self($value);
83        }
84
85        return $value;
86    }
87
88    /**
89     * Sets configuration values into the internal data store.
90     *
91     * If a value is provided, the key MUST be a string. If the input is an associative array
92     * or another ConfigInterface instance, it SHALL be normalized before insertion.
93     *
94     * @param array|ConfigInterface|string $key   the configuration key(s) or configuration object
95     * @param null|mixed                   $value the value to assign to the specified key, if applicable
96     *
97     * @throws InvalidArgumentException if the key is not a string when a value is provided
98     */
99    public function set(array|ConfigInterface|string $key, mixed $value = null): void
100    {
101        if (!empty($value)) {
102            if (!\is_string($key)) {
103                throw InvalidArgumentException::forNonStringKeyWithValue();
104            }
105
106            $key = [$key => $value];
107        }
108
109        if ($key instanceof ConfigInterface) {
110            $key = $key->toArray();
111        }
112
113        $this->data->import(ConfigHelper::normalize($key));
114    }
115
116    /**
117     * Removes a configuration key and its associated value.
118     *
119     * If the key does not exist, this method SHALL do nothing.
120     *
121     * @param string $key the configuration key to remove
122     */
123    public function remove(string $key): void
124    {
125        if ($this->has($key)) {
126            $this->data->remove($key);
127        }
128    }
129
130    /**
131     * Retrieves a traversable set of flattened configuration data.
132     *
133     * This method SHALL return an iterator where each key represents
134     * the nested path in dot notation, and each value is the corresponding value.
135     *
136     * For example:
137     * ['database' => ['host' => 'localhost']] becomes ['database.host' => 'localhost'].
138     *
139     * @return \Traversable<string, mixed> an iterator of flattened key-value pairs
140     */
141    public function getIterator(): \Traversable
142    {
143        return ConfigHelper::flatten($this->toArray());
144    }
145
146    /**
147     * Converts the entire configuration to an associative array.
148     *
149     * This method MUST export the configuration in its current state.
150     *
151     * @return array the exported configuration array
152     */
153    public function toArray(): array
154    {
155        return $this->data->export();
156    }
157}