Passed
Push — main ( 8dde80...7f1e83 )
by Thierry
01:42
created

ConfigSetter::isArrayOfOptions()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 14
rs 10
cc 4
nc 4
nop 1
1
<?php
2
3
/**
4
 * ConfigSetter.php - Jaxon config setter
5
 *
6
 * @package jaxon-core
7
 * @author Thierry Feuzeu <[email protected]>
8
 * @copyright 2025 Thierry Feuzeu <[email protected]>
9
 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
10
 * @link https://github.com/jaxon-php/jaxon-core
11
 */
12
13
namespace Jaxon\Utils\Config;
14
15
use Jaxon\Utils\Config\Exception\DataDepth;
16
17
use function array_keys;
18
use function array_key_exists;
19
use function array_merge;
20
use function array_pop;
21
use function count;
22
use function explode;
23
use function implode;
24
use function is_array;
25
use function is_string;
26
use function trim;
27
28
class ConfigSetter
29
{
30
    /**
31
     * Create a new config object
32
     *
33
     * @param array $aOptions The options values to be set
34
     * @param string $sNamePrefix A prefix for the config option names
35
     * @param string $sValuePrefix A prefix of the values in the input array
36
     *
37
     * @return Config
38
     * @throws DataDepth
39
     */
40
    public function newConfig(array $aOptions = [],
41
        string $sNamePrefix = '', string $sValuePrefix = ''): Config
42
    {
43
        return count($aOptions) === 0 ? new Config() :
44
            $this->setOptions(new Config(), $aOptions, $sNamePrefix, $sValuePrefix);
45
    }
46
47
    /**
48
     * @param string $sLastName
49
     * @param array $aNames
50
     *
51
     * @return int
52
     */
53
    private function pop(string &$sLastName, array &$aNames): int
54
    {
55
        $sLastName = array_pop($aNames);
56
        return count($aNames);
57
    }
58
59
    /**
60
     * Set the value of a config option
61
     *
62
     * @param array $aValues The current options values
63
     * @param string $sName The option name
64
     * @param mixed $xValue The option value
65
     *
66
     * @return array
67
     */
68
    private function setValue(array $aValues, string $sName, $xValue): array
69
    {
70
        $sOptionName = $sName;
71
        $xOptionValue = $xValue;
72
        // Given an option name like a.b.c, the values of a and a.b must also be set.
73
        $sLastName = '';
74
        $aNames = explode('.', $sName);
75
        while($this->pop($sLastName, $aNames) > 0)
76
        {
77
            $sName = implode('.', $aNames);
78
            if(!array_key_exists($sName, $aValues))
79
            {
80
                $aValues[$sName] = [];
81
            }
82
            $aValues[$sName] = array_merge($aValues[$sName], [$sLastName => $xValue]);
83
            $xValue = $aValues[$sName];
84
        }
85
86
        // Set the input option value.
87
        $aValues[$sOptionName] = $xOptionValue;
88
        return $aValues;
89
    }
90
91
    /**
92
     * Set the value of a config option
93
     *
94
     * @param Config $xConfig
95
     * @param string $sName The option name
96
     * @param mixed $xValue The option value
97
     *
98
     * @return Config
99
     */
100
    public function setOption(Config $xConfig, string $sName, $xValue): Config
101
    {
102
        return new Config($this->setValue($xConfig->getValues(), $sName, $xValue));
103
    }
104
105
    /**
106
     * Check if a value is an array of options
107
     *
108
     * @param mixed $xValue
109
     *
110
     * @return bool
111
     */
112
    private function isArrayOfOptions($xValue): bool
113
    {
114
        if(!is_array($xValue))
115
        {
116
            return false;
117
        }
118
        foreach(array_keys($xValue) as $xKey)
119
        {
120
            if(!is_string($xKey))
121
            {
122
                return false;
123
            }
124
        }
125
        return true;
126
    }
127
128
    /**
129
     * Recursively set Jaxon options from a data array
130
     *
131
     * @param array $aValues The current options values
132
     * @param array $aOptions The options values to be set
133
     * @param string $sNamePrefix The prefix for option names
134
     * @param int $nDepth The depth from the first call
135
     *
136
     * @return array
137
     * @throws DataDepth
138
     */
139
    private function setValues(array $aValues, array $aOptions,
140
        string $sNamePrefix = '', int $nDepth = 0): array
141
    {
142
        // Check the max depth
143
        if($nDepth < 0 || $nDepth > 9)
144
        {
145
            throw new DataDepth($sNamePrefix, $nDepth);
146
        }
147
148
        foreach($aOptions as $sName => $xValue)
149
        {
150
            $sName = trim($sName);
151
            if($this->isArrayOfOptions($xValue))
152
            {
153
                // Recursively set the options in the array. Important to set a new var.
154
                $sNextPrefix = $sNamePrefix . $sName . '.';
155
                $aValues = $this->setValues($aValues, $xValue, $sNextPrefix, $nDepth + 1);
156
                continue;
157
            }
158
            // Save the value of this option
159
            $sNextName = $sNamePrefix . $sName;
160
            $aValues = $this->setValue($aValues, $sNextName, $xValue);
161
        }
162
        return $aValues;
163
    }
164
165
    /**
166
     * Set the values of an array of config options
167
     *
168
     * @param Config $xConfig
169
     * @param array $aOptions The options values to be set
170
     * @param string $sNamePrefix A prefix for the config option names
171
     * @param string $sValuePrefix A prefix of the values in the input array
172
     *
173
     * @return Config
174
     * @throws DataDepth
175
     */
176
    public function setOptions(Config $xConfig, array $aOptions,
177
        string $sNamePrefix = '', string $sValuePrefix = ''): Config
178
    {
179
        // Find the config array in the input data
180
        $sValuePrefix = trim($sValuePrefix, ' .');
181
        $aKeys = explode('.', $sValuePrefix);
182
        foreach($aKeys as $sKey)
183
        {
184
            if(($sKey))
185
            {
186
                if(!isset($aOptions[$sKey]) || !is_array($aOptions[$sKey]))
187
                {
188
                    // No change if the required key is not found.
189
                    return new Config($xConfig->getValues(), false);
190
                }
191
                $aOptions = $aOptions[$sKey];
192
            }
193
        }
194
195
        $sNamePrefix = trim($sNamePrefix, ' .');
196
        if(($sNamePrefix))
197
        {
198
            $sNamePrefix .= '.';
199
        }
200
        return new Config($this->setValues($xConfig->getValues(), $aOptions, $sNamePrefix));
201
    }
202
}
203