Passed
Push — master ( 70b7f8...c79008 )
by Sebastian
01:40
created

Factory::extractSettings()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 3
nc 4
nop 1
crap 3
1
<?php
2
/**
3
 * This file is part of CaptainHook.
4
 *
5
 * (c) Sebastian Feldmann <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace CaptainHook\App\Config;
11
12
use CaptainHook\App\CH;
13
use CaptainHook\App\Config;
14
use CaptainHook\App\Hook\Util as HookUtil;
15
use CaptainHook\App\Storage\File\Json;
16
use RuntimeException;
17
18
/**
19
 * Class Factory
20
 *
21
 * @package CaptainHook
22
 * @author  Sebastian Feldmann <[email protected]>
23
 * @link    https://github.com/captainhookphp/captainhook
24
 * @since   Class available since Release 0.9.0
25
 * @internal
26
 */
27
final class Factory
28
{
29
    /**
30
     * Maximal level in including config files
31
     *
32
     * @var int
33
     */
34
    private $maxIncludeLevel = 1;
35
36
    /**
37
     * Current level of inclusion
38
     *
39
     * @var int
40
     */
41
    private $includeLevel = 0;
42
43
    /**
44
     * Create a CaptainHook configuration
45
     *
46
     * @param  string $path
47
     * @return \CaptainHook\App\Config
48
     * @throws \Exception
49
     */
50 30
    public function createConfig($path = '') : Config
51
    {
52 30
        $path = $path ?: getcwd() . DIRECTORY_SEPARATOR . CH::CONFIG;
53 30
        $file = new Json($path);
54
55 30
        return $this->setupConfig($file);
56
    }
57
58
    /**
59
     * @param  string $path
60
     * @return \CaptainHook\App\Config
61
     * @throws \Exception
62
     */
63 6
    private function includeConfig(string $path) : Config
64
    {
65 6
        $file = new Json($path);
66 6
        if (!$file->exists()) {
67 1
            throw new RuntimeException('Config to include not found: ' . $path);
68
        }
69 5
        return $this->setupConfig($file);
70
    }
71
72
73
    /**
74
     * Return a configuration with data loaded from json file it it exists
75
     *
76
     * @param  \CaptainHook\App\Storage\File\Json $file
77
     * @return \CaptainHook\App\Config
78
     * @throws \Exception
79
     */
80 30
    private function setupConfig(Json $file) : Config
81
    {
82 30
        return $file->exists() ? $this->loadConfigFromFile($file) : new Config($file->getPath());
83
    }
84
85
    /**
86
     * Loads a given file into given the configuration
87
     *
88
     * @param  \CaptainHook\App\Storage\File\Json $file
89
     * @return \CaptainHook\App\Config
90
     * @throws \Exception
91
     */
92 25
    private function loadConfigFromFile(Json $file) : Config
93
    {
94 25
        $json = $file->readAssoc();
95 25
        Util::validateJsonConfiguration($json);
96
97 25
        $config = new Config($file->getPath(), true, $this->extractSettings($json));
98
99 25
        $this->appendIncludedConfigurations($config, $json);
100
101 24
        foreach (HookUtil::getValidHooks() as $hook => $class) {
102 24
            if (isset($json[$hook])) {
103 24
                $this->configureHook($config->getHookConfig($hook), $json[$hook]);
104
            }
105
        }
106 24
        return $config;
107
    }
108
109
    /**
110
     * Return `config` section of captainhook.json
111
     *
112
     * @param  array $json
113
     * @return array
114
     */
115 25
    private function extractSettings(array $json) : array
116
    {
117 25
        return isset($json['config']) && is_array($json['config']) ? $json['config'] : [];
118
    }
119
120
    /**
121
     * Setup a hook configuration by json data
122
     *
123
     * @param  \CaptainHook\App\Config\Hook $config
124
     * @param  array                        $json
125
     * @return void
126
     * @throws \Exception
127
     */
128 23
    private function configureHook(Config\Hook $config, array $json) : void
129
    {
130 23
        $config->setEnabled($json['enabled']);
131 23
        foreach ($json['actions'] as $actionJson) {
132 17
            $options    = isset($actionJson['options']) && is_array($actionJson['options'])
133 17
                        ? $actionJson['options']
134 17
                        : [];
135 17
            $conditions = isset($actionJson['conditions']) && is_array($actionJson['conditions'])
136 1
                        ? $actionJson['conditions']
137 17
                        : [];
138 17
            $config->addAction(new Config\Action($actionJson['action'], $options, $conditions));
139
        }
140 23
    }
141
142
    /**
143
     * Append all included configuration to the current configuration
144
     *
145
     * @param  \CaptainHook\App\Config $config
146
     * @param  array                   $json
147
     * @throws \Exception
148
     */
149 25
    private function appendIncludedConfigurations(Config $config, array $json)
150
    {
151 25
        $this->readMaxIncludeLevel($json);
152 25
        if ($this->includeLevel < $this->maxIncludeLevel) {
153 25
            $includes = $this->loadIncludedConfigs($json, $config->getPath());
154 24
            foreach (HookUtil::getValidHooks() as $hook => $class) {
155 24
                $this->mergeHookConfigFromIncludes($config->getHookConfig($hook), $includes);
156
            }
157
        }
158 24
        $this->includeLevel++;
159 24
    }
160
161
    /**
162
     * Check config section for 'maxIncludeLevel' setting
163
     *
164
     * @param array $json
165
     */
166 25
    private function readMaxIncludeLevel(array $json) : void
167
    {
168
        // read the include level setting only for the actual configuration
169 25
        if ($this->includeLevel === 0 && isset($json['config']['maxIncludeLevel'])) {
170 1
            $this->maxIncludeLevel = (int) $json['config']['maxIncludeLevel'];
171
        }
172 25
    }
173
174
    /**
175
     * Merge a given hook config with the corresponding hook configs from a list of included configurations
176
     *
177
     * @param  \CaptainHook\App\Config\Hook $hook
178
     * @param  \CaptainHook\App\Config[]    $includes
179
     * @return void
180
     */
181 24
    private function mergeHookConfigFromIncludes(Hook $hook, array $includes) : void
182
    {
183 24
        foreach ($includes as $includedConfig) {
184 5
            $includedHook = $includedConfig->getHookConfig($hook->getName());
185 5
            if ($includedHook->isEnabled()) {
186 5
                $hook->setEnabled(true);
187 5
                $this->copyActionsFromTo($includedHook, $hook);
188
            }
189
        }
190 24
    }
191
192
    /**
193
     * Return list of included configurations to add them to the main configuration afterwards
194
     *
195
     * @param  array  $json
196
     * @param  string $path
197
     * @return \CaptainHook\App\Config[]
198
     * @throws \Exception
199
     */
200 25
    protected function loadIncludedConfigs(array $json, string $path) : array
201
    {
202 25
        $includes  = [];
203 25
        $directory = dirname($path);
204 25
        $files     = isset($json['config']['includes']) && is_array($json['config']['includes'])
205 6
                   ? $json['config']['includes']
206 25
                   : [];
207
208 25
        foreach ($files as $file) {
209 6
            $includes[] = $this->includeConfig($directory . DIRECTORY_SEPARATOR . $file);
210
        }
211 24
        return $includes;
212
    }
213
214
    /**
215
     * Copy action from a given configuration to the second given configuration
216
     *
217
     * @param \CaptainHook\App\Config\Hook $sourceConfig
218
     * @param \CaptainHook\App\Config\Hook $targetConfig
219
     */
220 5
    private function copyActionsFromTo(Hook $sourceConfig, Hook $targetConfig)
221
    {
222 5
        foreach ($sourceConfig->getActions() as $action) {
223 5
            $targetConfig->addAction($action);
224
        }
225 5
    }
226
227
    /**
228
     * Config factory method
229
     *
230
     * @param  string $path
231
     * @return \CaptainHook\App\Config
232
     */
233 30
    public static function create(string $path = '') : Config
234
    {
235 30
        $factory = new static();
236 30
        return $factory->createConfig($path);
237
    }
238
}
239