Passed
Push — master ( 1eb29c...18ff88 )
by Michael
04:59
created

Config   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 85%

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 2
dl 0
loc 166
ccs 34
cts 40
cp 0.85
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A transform() 0 19 2
A transformAndSort() 0 13 2
B sortConfigBlocks() 0 22 4
A mergeUniqueKeys() 0 4 1
A mergeByPriority() 0 17 4
1
<?php
2
3
namespace micmania1\config;
4
5
use micmania1\config\MergeStrategy\NoKeyConflict;
6
use micmania1\config\MergeStrategy\Priority;
7
use micmania1\config\Transformer\TransformerInterface;
8
9
class Config implements TransformerInterface
10
{
11
    /**
12
     * @var array
13
     */
14
    protected $transformers = [];
15
16
    /**
17
     * @var array
18
     */
19
    protected $merged = [];
20
21
    /**
22
     * This takes a list of transformaers which are responsible for fetching and tranforming
23
     * their config into PHP array.
24
     *
25
     * @param TransformerInterface $transformers
26
     */
27 1
    public function __construct(...$transformers)
28
    {
29 1
        $this->transformers = $transformers;
30 1
    }
31
32
    /**
33
     * This loops through each transformer and trnasforms the config into the common php
34
     * array format.
35
     *
36
     * @return array
37
     */
38 1
    public function transform()
39
    {
40 1
        $this->merged = [];
41
42 1
        if (empty($this->transformers)) {
43
            return $this->merged;
44
        }
45
46
        // Each transformer returns config with sorted keys. These are then
47
        // all merged together into a sorted, but unmerged config.
48 1
        $unmerged = $this->transformAndSort();
49
50
        // Merge the config by sort order. Now that we have our config ordered
51
        // by priority we can merge the config together.
52 1
        $this->merged = $this->mergeByPriority($unmerged);
53
54
        // Return the final merged config
55 1
        return $this->merged;
56
    }
57
58
    /**
59
     * This is responsible for calling each transformer and then creating an unmerged
60
     * config array in blocks ordered by sort key.
61
     *
62
     * @example
63
     * The unmerged config looks as follows:
64
     * array(
65
     * 	10 => array(...)
66
     * );
67
     *
68
     * @return array
69
     */
70 1
    protected function transformAndSort()
71
    {
72 1
        $unmerged = [];
73
74
        // Run through each transformer and merge into a sorted array of configurations.
75
        // These will then need to be merged by priorty (lower number is higher priortiy)
76 1
        foreach ($this->transformers as $transformer) {
77 1
            $config = $transformer->transform();
78 1
            $unmerged = $this->sortConfigBlocks($config, $unmerged);
79 1
        }
80
81 1
        return $unmerged;
82
    }
83
84
    /**
85
     * This method takes a config block with a key which is used to sort. Its merged into
86
     * the existing config cleanly. If the sort key exists, then we will attempt to merge
87
     * that config block. If there are any key clashes at this stage, then we will throw an
88
     * exception.
89
     *
90
     * @example
91
     * $mine = array(
92
     * 	10 => array(...)
93
     * );
94
     *
95
     * $theirs = array(
96
     * 	5 => array(...)
97
     * 	15 => array(...)
98
     * );
99
     *
100
     * In this example, $mine would be placed in between the 5 and 15 sort keys.
101
     * $return = array(
102
     * 	5 => array(...)
103
     * 	10 => array(...)
104
     * 	15 => array(...)
105
     * );
106
     *
107
     * @param array $mine
108
     * @param array $theirs
109
     *
110
     * @return array
111
     */
112 1
    protected function sortConfigBlocks($mine, $theirs)
113
    {
114 1
        foreach ($mine as $sort => $value) {
115 1
            if (!is_int($sort)) {
116
                throw new Exception('Unable to sort config. Sort key must be an integer');
117
            }
118
119 1
            if (!array_key_exists($sort, $this->merged)) {
120 1
                $this->merged[$sort] = $value;
121 1
            } else {
122
                // If we get to this point, we have a potential key clash with the same
123
                // priority. If both values are an array, we can attempt to merge.
124
                // However, if the value is a string we cannot tell which has greater
125
                // priority and therefore must throw an exception. Even if the value is an
126
                // array, we can run into a key clash when merging those and an exception
127
                // will be thrown.
128
                $this->merged[$sort] = $this->mergeUniqueKeys($mine[$sort], $theirs[$sort]);
129
            }
130 1
        }
131
132 1
        return $this->merged;
133
    }
134
135
    /**
136
     * This will merge config blocks, but throw an exception if there is a key clash.
137
     *
138
     * @param array $mine
139
     * @param array $theirs
140
     *
141
     * @return array
142
     */
143
    protected function mergeUniqueKeys($mine, $theirs)
144
    {
145
        return (new NoKeyConflict)->merge($mine, $theirs);
146
    }
147
148
    /**
149
     * This will merge by priority order and overwrite any existing values that aren't arrays
150
     * or try to merge values that are arrays recursively.
151
     *
152
     * @param array $mine
153
     * @param array $theirs
154
     *
155
     * @return array
156
     */
157 1
    protected function mergeByPriority($mine, $theirs = [])
158
    {
159 1
        $merged = [];
160
161 1
        foreach ($mine as $sort => $block) {
162 1
            foreach ($block as $key => $value) {
163 1
                if(!is_array($value)) {
164 1
                    $merged[$key] = $value;
165 1
                    continue;
166
                }
167
168
                $merged[$key] = (new Priority)->merge($value, $theirs);
169 1
            }
170 1
        }
171
172 1
        return $merged;
173
    }
174
}
175