Completed
Pull Request — master (#552)
by Greg
03:18
created

ConfigProcessor::reduceOne()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
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
     * Get the configuration to be processed, and clear out the
75
     * 'unprocessed' list.
76
     *
77
     * @return array
78
     */
79
    protected function fetchUnprocessed()
80
    {
81
        $toBeProcessed = $this->unprocessedConfig;
82
        $this->unprocessedConfig = [];
83
        return $toBeProcessed;
84
    }
85
86
    /**
87
     * Use a map-reduce to evaluate the items to be processed,
88
     * and merge them into the processed array.
89
     *
90
     * @param array $processed
91
     * @param array $toBeProcessed
92
     * @return array
93
     */
94
    protected function process($processed, $toBeProcessed)
95
    {
96
        $toBeReduced = array_map([$this, 'preprocess'], $toBeProcessed);
97
        return array_reduce($toBeReduced, [$this, 'reduceOne'], $processed);
98
    }
99
100
    /**
101
     * Process a single configuration file from the 'to be processed'
102
     * list. By default this is a no-op. Override this method to
103
     * provide any desired configuration preprocessing, e.g. dot-notation
104
     * expansion of the configuration keys, etc.
105
     *
106
     * @param array $config
107
     * @return array
108
     */
109
    protected function preprocess($config)
110
    {
111
        return $config;
112
    }
113
114
    /**
115
     * Evaluate one item in the 'to be evaluated' list, and then
116
     * merge it into the processed configuration (the 'carry').
117
     *
118
     * @param array $processed
119
     * @param array $config
120
     * @return array
121
     */
122
    protected function reduceOne($processed, $config)
123
    {
124
        $evaluated = $this->evaluate($processed, $config);
125
        return static::arrayMergeRecursiveDistinct($processed, $evaluated);
126
    }
127
128
    /**
129
     * Evaluate one configuration item.
130
     *
131
     * @param array $processed
132
     * @param array $config
133
     * @return array
134
     */
135
    protected function evaluate($processed, $config)
136
    {
137
        return Expander::expandArrayProperties(
138
            $config,
139
            $processed
140
        );
141
    }
142
143
    /**
144
     * Merges arrays recursively while preserving.
145
     *
146
     * @param array $array1
147
     * @param array $array2
148
     *
149
     * @return array
150
     *
151
     * @see http://php.net/manual/en/function.array-merge-recursive.php#92195
152
     * @see https://github.com/grasmash/bolt/blob/robo-rebase/src/Robo/Common/ArrayManipulator.php#L22
153
     */
154
    public static function arrayMergeRecursiveDistinct(
155
        array &$array1,
156
        array &$array2
157
    ) {
158
        $merged = $array1;
159
        foreach ($array2 as $key => &$value) {
160
            if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
161
                $merged[$key] = self::arrayMergeRecursiveDistinct($merged[$key], $value);
162
            } else {
163
                $merged[$key] = $value;
164
            }
165
        }
166
        return $merged;
167
    }
168
}
169