Passed
Push — main ( 673af6...acd800 )
by Thierry
01:40
created

ConfigSetter::setOptions()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 26
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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