Completed
Push — master ( 5e7886...331704 )
by Greg
03:33
created

ConfigProcessor   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 2
dl 0
loc 192
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A extend() 0 4 1
A add() 0 5 1
A addFromSource() 0 8 2
A export() 0 10 2
A sources() 0 11 3
A fetchUnprocessed() 0 6 1
A process() 0 6 1
A preprocess() 0 4 1
A reduceOne() 0 4 1
A evaluate() 0 7 1
B arrayMergeRecursiveDistinct() 0 14 5
A arrayReplaceValueRecursive() 0 12 3
1
<?php
2
3
namespace Robo\Config;
4
5
use Grasmash\YamlExpander\Expander;
6
7
/**
8
 * The config processor combines multiple configuration
9
 * files together, and processes them as necessary.
10
 */
11
class ConfigProcessor
12
{
13
    protected $processedConfig = [];
14
    protected $unprocessedConfig = [];
15
16
    /**
17
     * Extend the configuration to be processed with the
18
     * configuration provided by the specified loader.
19
     *
20
     * @param ConfigLoaderInterface $loader
21
     */
22
    public function extend(ConfigLoaderInterface $loader)
23
    {
24
        return $this->addFromSource($loader->export(), $loader->getSourceName());
25
    }
26
27
    /**
28
     * Extend the configuration to be processed with
29
     * the provided nested array.
30
     *
31
     * @param array $data
32
     */
33
    public function add($data)
34
    {
35
        $this->unprocessedConfig[] = $data;
36
        return $this;
37
    }
38
39
    /**
40
     * Extend the configuration to be processed with
41
     * the provided nested array. Also record the name
42
     * of the data source, if applicable.
43
     *
44
     * @param array $data
45
     * @param string $source
46
     */
47
    protected function addFromSource($data, $source = '')
48
    {
49
        if (empty($source)) {
50
            return $this->add($data);
51
        }
52
        $this->unprocessedConfig[$source] = $data;
53
        return $this;
54
    }
55
56
    /**
57
     * Process all of the configuration that has been collected,
58
     * and return a nested array.
59
     *
60
     * @return array
61
     */
62
    public function export()
63
    {
64
        if (!empty($this->unprocessedConfig)) {
65
            $this->processedConfig = $this->process(
66
                $this->processedConfig,
67
                $this->fetchUnprocessed()
68
            );
69
        }
70
        return $this->processedConfig;
71
    }
72
73
    /**
74
     * To aid in debugging: return the source of each configuration item.
75
     * n.b. Must call this function *before* export and save the result
76
     * if persistence is desired.
77
     */
78
    public function sources()
79
    {
80
        $sources = [];
81
        foreach ($this->unprocessedConfig as $sourceName => $config) {
82
            if (!empty($sourceName)) {
83
                $configSources = static::arrayReplaceValueRecursive($config, $sourceName);
84
                $sources = static::arrayMergeRecursiveDistinct($sources, $configSources);
85
            }
86
        }
87
        return $sources;
88
    }
89
90
    /**
91
     * Get the configuration to be processed, and clear out the
92
     * 'unprocessed' list.
93
     *
94
     * @return array
95
     */
96
    protected function fetchUnprocessed()
97
    {
98
        $toBeProcessed = $this->unprocessedConfig;
99
        $this->unprocessedConfig = [];
100
        return $toBeProcessed;
101
    }
102
103
    /**
104
     * Use a map-reduce to evaluate the items to be processed,
105
     * and merge them into the processed array.
106
     *
107
     * @param array $processed
108
     * @param array $toBeProcessed
109
     * @return array
110
     */
111
    protected function process($processed, $toBeProcessed)
112
    {
113
        $toBeReduced = array_map([$this, 'preprocess'], $toBeProcessed);
114
        $reduced = array_reduce($toBeReduced, [$this, 'reduceOne'], $processed);
115
        return $this->evaluate($reduced);
116
    }
117
118
    /**
119
     * Process a single configuration file from the 'to be processed'
120
     * list. By default this is a no-op. Override this method to
121
     * provide any desired configuration preprocessing, e.g. dot-notation
122
     * expansion of the configuration keys, etc.
123
     *
124
     * @param array $config
125
     * @return array
126
     */
127
    protected function preprocess($config)
128
    {
129
        return $config;
130
    }
131
132
    /**
133
     * Evaluate one item in the 'to be evaluated' list, and then
134
     * merge it into the processed configuration (the 'carry').
135
     *
136
     * @param array $processed
137
     * @param array $config
138
     * @return array
139
     */
140
    protected function reduceOne($processed, $config)
141
    {
142
        return static::arrayMergeRecursiveDistinct($processed, $config);
143
    }
144
145
    /**
146
     * Evaluate one configuration item.
147
     *
148
     * @param array $processed
0 ignored issues
show
Bug introduced by
There is no parameter named $processed. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
149
     * @param array $config
150
     * @return array
151
     */
152
    protected function evaluate($config)
153
    {
154
        return Expander::expandArrayProperties(
155
            $config,
156
            []
157
        );
158
    }
159
160
    /**
161
     * Merges arrays recursively while preserving.
162
     *
163
     * @param array $array1
164
     * @param array $array2
165
     *
166
     * @return array
167
     *
168
     * @see http://php.net/manual/en/function.array-merge-recursive.php#92195
169
     * @see https://github.com/grasmash/bolt/blob/robo-rebase/src/Robo/Common/ArrayManipulator.php#L22
170
     */
171
    public static function arrayMergeRecursiveDistinct(
172
        array &$array1,
173
        array &$array2
174
    ) {
175
        $merged = $array1;
176
        foreach ($array2 as $key => &$value) {
177
            if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
178
                $merged[$key] = self::arrayMergeRecursiveDistinct($merged[$key], $value);
179
            } else {
180
                $merged[$key] = $value;
181
            }
182
        }
183
        return $merged;
184
    }
185
186
    /**
187
     * Replaces all of the leaf-node values of a nested array with the
188
     * provided replacement value.
189
     */
190
    public static function arrayReplaceValueRecursive(array $data, $replacement)
191
    {
192
        $result = [];
193
        foreach ($data as $key => $value) {
194
            if (is_array($value)) {
195
                $result[$key] = self::arrayReplaceValueRecursive($value, $replacement);
196
            } else {
197
                $result[$key] = $replacement;
198
            }
199
        }
200
        return $result;
201
    }
202
}
203