Completed
Pull Request — master (#552)
by Greg
02:57
created

ConfigProcessor::process()   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->add($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
     * @param string $source
33
     */
34
    public function add($data, $source = '')
35
    {
36
        if (empty($source)) {
37
            $this->unprocessedConfig[] = $data;
38
            return $this;
39
        }
40
        $this->unprocessedConfig[$source] = $data;
41
        return $this;
42
    }
43
44
    /**
45
     * Process all of the configuration that has been collected,
46
     * and return a nested array.
47
     *
48
     * @return array
49
     */
50
    public function export()
51
    {
52
        if (!empty($this->unprocessedConfig)) {
53
            $this->processedConfig = $this->process(
54
                $this->processedConfig,
55
                $this->fetchUnprocessed()
56
            );
57
        }
58
        return $this->processedConfig;
59
    }
60
61
    /**
62
     * Get the configuration to be processed, and clear out the
63
     * 'unprocessed' list.
64
     *
65
     * @return array
66
     */
67
    protected function fetchUnprocessed()
68
    {
69
        $toBeProcessed = $this->unprocessedConfig;
70
        $this->unprocessedConfig = [];
71
        return $toBeProcessed;
72
    }
73
74
    /**
75
     * Use a map-reduce to evaluate the items to be processed,
76
     * and merge them into the processed array.
77
     *
78
     * @param array $processed
79
     * @param array $toBeProcessed
80
     * @return array
81
     */
82
    protected function process($processed, $toBeProcessed)
83
    {
84
        $toBeReduced = array_map([$this, 'preprocess'], $toBeProcessed);
85
        return array_reduce($toBeReduced, [$this, 'reduceOne'], $processed);
86
    }
87
88
    /**
89
     * Process a single configuration file from the 'to be processed'
90
     * list. By default this is a no-op. Override this method to
91
     * provide any desired configuration preprocessing, e.g. dot-notation
92
     * expansion of the configuration keys, etc.
93
     *
94
     * @param array $config
95
     * @return array
96
     */
97
    protected function preprocess($config)
98
    {
99
        return $config;
100
    }
101
102
    /**
103
     * Evaluate one item in the 'to be evaluated' list, and then
104
     * merge it into the processed configuration (the 'carry').
105
     *
106
     * @param array $processed
107
     * @param array $config
108
     * @return array
109
     */
110
    protected function reduceOne($processed, $config)
111
    {
112
        $evaluated = $this->evaluate($processed, $config);
113
        return static::arrayMergeRecursiveDistinct($processed, $evaluated);
114
    }
115
116
    /**
117
     * Evaluate one configuration item.
118
     *
119
     * @param array $processed
120
     * @param array $config
121
     * @return array
122
     */
123
    protected function evaluate($processed, $config)
124
    {
125
        return Expander::expandArrayProperties(
126
            $config,
127
            $processed
128
        );
129
    }
130
131
    /**
132
     * Merges arrays recursively while preserving.
133
     *
134
     * @param array $array1
135
     * @param array $array2
136
     *
137
     * @return array
138
     *
139
     * @see http://php.net/manual/en/function.array-merge-recursive.php#92195
140
     * @see https://github.com/grasmash/bolt/blob/robo-rebase/src/Robo/Common/ArrayManipulator.php#L22
141
     */
142
    public static function arrayMergeRecursiveDistinct(
143
        array &$array1,
144
        array &$array2
145
    ) {
146
        $merged = $array1;
147
        foreach ($array2 as $key => &$value) {
148
            if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
149
                $merged[$key] = self::arrayMergeRecursiveDistinct($merged[$key], $value);
150
            } else {
151
                $merged[$key] = $value;
152
            }
153
        }
154
        return $merged;
155
    }
156
}
157