Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.92% covered (success)
97.92%
47 / 48
93.75% covered (success)
93.75%
15 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractApplication
97.92% covered (success)
97.92%
47 / 48
93.75% covered (success)
93.75%
15 / 16
34
0.00% covered (danger)
0.00%
0 / 1
 setName
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setFullName
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getFullName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasFullName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setVersion
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getVersion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasVersion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 config
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 load
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 registerConfig
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 addConfigValue
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 updateConfigValue
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 deleteConfigValue
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 mergeConfig
94.74% covered (success)
94.74%
18 / 19
0.00% covered (danger)
0.00%
0 / 1
14.03
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop;
16
17use Pop\Config;
18use InvalidArgumentException;
19
20/**
21 * Abstract application class
22 *
23 * @category   Pop
24 * @package    Pop
25 * @author     Nick Sagona, III <nick@popphp.org>
26 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
27 * @license    https://www.popphp.org/license     New BSD License
28 * @version    5.0.0
29 */
30abstract class AbstractApplication implements ApplicationInterface
31{
32
33    /**
34     * Name (Slug, e.g. "my-app")
35     * @var ?string
36     */
37    protected ?string $name = null;
38
39    /**
40     * Full Name (Human-readable, e.g., "My Application")
41     * @var ?string
42     */
43    protected ?string $fullName = null;
44
45    /**
46     * Version
47     * @var ?string
48     */
49    protected ?string $version = null;
50
51    /**
52     * Application config
53     * @var mixed
54     */
55    protected mixed $config = null;
56
57    /**
58     * Set name
59     *
60     * @param  string $name
61     * @return static
62     */
63    public function setName(string $name): static
64    {
65        $this->name = $name;
66        return $this;
67    }
68
69    /**
70     * Get name
71     *
72     * @return string
73     */
74    public function getName(): string
75    {
76        return $this->name;
77    }
78
79    /**
80     * Determine if the name is set
81     *
82     * @return bool
83     */
84    public function hasName(): bool
85    {
86        return ($this->name !== null);
87    }
88
89    /**
90     * Set full name
91     *
92     * @param  string $fullName
93     * @return static
94     */
95    public function setFullName(string $fullName): static
96    {
97        $this->fullName = $fullName;
98        return $this;
99    }
100
101    /**
102     * Get full name
103     *
104     * @return string
105     */
106    public function getFullName(): string
107    {
108        return $this->fullName;
109    }
110
111    /**
112     * Determine if the full name is set
113     *
114     * @return bool
115     */
116    public function hasFullName(): bool
117    {
118        return ($this->fullName !== null);
119    }
120
121    /**
122     * Set version
123     *
124     * @param  string $version
125     * @return static
126     */
127    public function setVersion(string $version): static
128    {
129        $this->version = $version;
130        return $this;
131    }
132
133    /**
134     * Get version
135     *
136     * @return string
137     */
138    public function getVersion(): string
139    {
140        return $this->version;
141    }
142
143    /**
144     * Determine if version has been set
145     *
146     * @return bool
147     */
148    public function hasVersion(): bool
149    {
150        return ($this->version !== null);
151    }
152
153    /**
154     * Access application config
155     *
156     * @return mixed
157     */
158    public function config(): mixed
159    {
160        return $this->config;
161    }
162
163    /**
164     * Optional method that can be used to load custom operations/configurations for an application to run
165     *
166     * @return AbstractApplication
167     */
168    public function load(): AbstractApplication
169    {
170        return $this;
171    }
172
173    /**
174     * Register a new configuration with the application
175     *
176     * @param  mixed $config
177     * @throws InvalidArgumentException
178     * @return AbstractApplication
179     */
180    public function registerConfig(mixed $config): AbstractApplication
181    {
182        if (!is_array($config) && !($config instanceof \ArrayAccess)) {
183            throw new \InvalidArgumentException(
184                'Error: The config must be either an array itself, implement ArrayAccess or extend ArrayObject'
185            );
186        }
187
188        $this->config = $config;
189
190        return $this;
191    }
192
193    /**
194     * Add new value to config
195     *
196     * @param  string $name
197     * @param  string $value
198     * @return AbstractApplication
199     */
200    public function addConfigValue(string $name, string $value): AbstractApplication
201    {
202        if (!isset($this->config[$name])) {
203            $this->config[$name] = $value;
204        }
205        return $this;
206    }
207
208    /**
209     * Update existing value in config
210     *
211     * @param  string $name
212     * @param  string $value
213     * @return AbstractApplication
214     */
215    public function updateConfigValue(string $name, string $value): AbstractApplication
216    {
217        if (isset($this->config[$name])) {
218            $this->config[$name] = $value;
219        }
220        return $this;
221    }
222
223    /**
224     * Replace existing value in config
225     *
226     * @param  string $name
227     * @return AbstractApplication
228     */
229    public function deleteConfigValue(string $name): AbstractApplication
230    {
231        if (isset($this->config[$name])) {
232            unset($this->config[$name]);
233        }
234        return $this;
235    }
236
237    /**
238     * Merge new or altered config values with the existing config values
239     *
240     * @param  mixed $config
241     * @param  bool  $preserve
242     * @param  array $exclude
243     * @throws Config\Exception
244     * @return AbstractApplication
245     */
246    public function mergeConfig(mixed $config, bool $preserve = false, array $exclude = []): AbstractApplication
247    {
248        // Apply exclusions only to array-like config (Config\Config, array, ArrayAccess, ArrayObject)
249        if (!empty($exclude) && (is_array($config) || ($config instanceof \ArrayAccess))) {
250            // Materialize a plain-array COPY before stripping excluded keys, regardless of the
251            // original type, so the unset() below never mutates the caller's live config object
252            // (e.g. an ArrayObject or other ArrayAccess handle passed in by reference/identity).
253            if ($config instanceof Config\Config) {
254                $config = $config->toArray();
255            } else if ($config instanceof \ArrayObject) {
256                $config = $config->getArrayCopy();
257            } else if ($config instanceof \Traversable) {
258                $config = iterator_to_array($config);
259            }
260            foreach ($exclude as $key) {
261                unset($config[$key]);
262            }
263        }
264
265        if ($this->config instanceof Config\Config) {
266            $this->config->merge($config, $preserve);
267        } else if (is_array($config) || ($config instanceof \ArrayAccess)) {
268            if ($config instanceof Config\Config) {
269                $config = $config->toArray();
270            }
271            if ($this->config !== null) {
272                $this->config = ($preserve) ? array_merge_recursive($this->config, $config) :
273                    array_replace_recursive($this->config, $config);
274            } else {
275                $this->config = $config;
276            }
277        }
278
279        return $this;
280    }
281
282}