PriorityConfigurationChain::mergeArrays()   B
last analyzed

Complexity

Conditions 7
Paths 12

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 22
rs 8.8333
cc 7
nc 12
nop 2
1
<?php
2
3
/**
4
 * This file is part of slick/configuration
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Configuration;
11
12
use JetBrains\PhpStorm\Pure;
13
use Slick\Configuration\Common\PriorityList;
14
use Slick\Configuration\Driver\CommonDriverMethods;
15
16
/**
17
 * PriorityConfigurationChain
18
 *
19
 * @package Slick\Configuration
20
*/
21
class PriorityConfigurationChain implements ConfigurationChainInterface
22
{
23
24
    /**
25
     * @var PriorityList
26
     */
27
    private PriorityList $priorityList;
28
29
    private bool $dirty = true;
30
31
    use CommonDriverMethods {
32
        get as private baseGet;
33
        set as private baseSet;
34
    }
35
36
    /**
37
     * Creates a Priority Configuration Chain
38
     */
39
    public function __construct()
40
    {
41
        $this->priorityList = new PriorityList();
42
    }
43
44
    /**
45
     * Returns the value store with provided key or the default value.
46
     *
47
     * @param string $key The key used to store the value in configuration
48
     * @param mixed|null $default The default value if no value was stored.
49
     *
50
     * @return mixed The stored value or the default value if key
51
     *  was not found.
52
     */
53
    public function get(string $key, mixed $default = null): mixed
54
    {
55
        if ($this->dirty) {
56
            $this->mergeValues();
57
        }
58
        return $this->baseGet($key, $default);
59
    }
60
61
    public function set(string $key, mixed $value): self
62
    {
63
        if ($this->dirty) {
64
            $this->mergeValues();
65
        }
66
        $this->baseSet($key, $value);
67
        return $this;
68
    }
69
70
    /**
71
     * Add a configuration driver to the chain
72
     *
73
     * The configuration driver will be placed according to its priority.
74
     * Highest priority will be verified first
75
     *
76
     * @param ConfigurationInterface $config
77
     * @param integer $priority
78
     *
79
     * @return ConfigurationChainInterface self
80
     */
81
    public function add(ConfigurationInterface $config, int $priority = 0): ConfigurationChainInterface
82
    {
83
        $this->priorityList->insert($config, $priority);
84
        $this->dirty = true;
85
        return $this;
86
    }
87
88
    /**
89
     * Returns the internal configuration driver chain
90
     *
91
     * @return PriorityList
92
     */
93
    public function priorityList(): PriorityList
94
    {
95
        return $this->priorityList;
96
    }
97
98
    private function mergeValues(): void
99
    {
100
        $this->dirty = false;
101
        $data = [];
102
        $elements = array_reverse($this->priorityList->asArray());
103
        foreach ($elements as $element) {
104
            $data = $this->mergeArrays($data, $element->asArray());
105
        }
106
        $this->data = $data;
107
    }
108
109
    /**
110
     * Merges two arrays recursively, overriding values from the default array with
111
     * values from the custom array.
112
     *
113
     * @param array<string, mixed> $default The default array.
114
     * @param array<string, mixed> $custom The custom array.
115
     *
116
     * @return array<string, mixed> The merged array.
117
     */
118
    private function mergeArrays(array $default, array $custom): array
119
    {
120
        $base = [];
121
        $names = [];
122
        foreach ($default as $name => $value) {
123
            $isPresent = array_key_exists($name, $custom);
124
            $names[] = $name;
125
            if (is_array($value) && $isPresent) {
126
                $base[$name] = $this->mergeArrays($value, $custom[$name]);
127
                continue;
128
            }
129
130
            $base[$name] = $isPresent ? $custom[$name] : $value;
131
        }
132
133
        foreach ($custom as $other => $value) {
134
            if (!in_array($other, $names)) {
135
                $base[$other] = $value;
136
            }
137
        }
138
139
        return $base;
140
    }
141
}
142