Completed
Pull Request — master (#47)
by Sebastian
01:45
created

Factory::loadIncludedConfigs()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 12
ccs 9
cts 9
cp 1
rs 10
c 0
b 0
f 0
cc 4
nc 8
nop 2
crap 4
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 27
    public function createConfig($path = '') : Config
51
    {
52 27
        $path = $path ?: getcwd() . DIRECTORY_SEPARATOR . CH::CONFIG;
53 27
        $file = new Json($path);
54
55 27
        return $this->setupConfig($file);
56
    }
57
58
    /**
59
     * @param  string $path
60
     * @return \CaptainHook\App\Config
61
     * @throws \Exception
62
     */
63 4
    private function includeConfig(string $path) : Config
64
    {
65 4
        $file = new Json($path);
66 4
        if (!$file->exists()) {
67 1
            throw new RuntimeException('Config to include not found: ' . $path);
68
        }
69 3
        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 27
    private function setupConfig(Json $file) : Config
81
    {
82
        // use variable to not check file system twice
83 27
        $fileExists = $file->exists();
84 27
        $config     = new Config($file->getPath(), $fileExists);
85 27
        if ($fileExists) {
86 22
            $this->loadConfigFromFile($config, $file);
87
        }
88 26
        return $config;
89
    }
90
91
    /**
92
     * Loads a given file into given the configuration
93
     *
94
     * @param  \CaptainHook\App\Config            $config
95
     * @param  \CaptainHook\App\Storage\File\Json $file
96
     * @throws \Exception
97
     */
98 22
    private function loadConfigFromFile(Config $config, Json $file) : void
99
    {
100 22
        $json = $file->readAssoc();
101 22
        Util::validateJsonConfiguration($json);
102
103 22
        foreach (HookUtil::getValidHooks() as $hook => $class) {
104 22
            if (isset($json[$hook])) {
105 22
                $this->configureHook($config->getHookConfig($hook), $json[$hook]);
106
            }
107
        }
108
109 22
        $this->appendIncludedConfigurations($config, $json);
110 21
    }
111
112
    /**
113
     * Setup a hook configuration by json data
114
     *
115
     * @param  \CaptainHook\App\Config\Hook $config
116
     * @param  array                        $json
117
     * @return void
118
     * @throws \Exception
119
     */
120 21
    private function configureHook(Config\Hook $config, array $json) : void
121
    {
122 21
        $config->setEnabled($json['enabled']);
123 21
        foreach ($json['actions'] as $actionJson) {
124 15
            $options    = isset($actionJson['options']) && is_array($actionJson['options'])
125 15
                        ? $actionJson['options']
126 15
                        : [];
127 15
            $conditions = isset($actionJson['conditions']) && is_array($actionJson['conditions'])
128 1
                        ? $actionJson['conditions']
129 15
                        : [];
130 15
            $config->addAction(new Config\Action($actionJson['action'], $options, $conditions));
131
        }
132 21
    }
133
134
    /**
135
     * Append all included configuration to the current configuration
136
     *
137
     * @param  \CaptainHook\App\Config $config
138
     * @param  array                   $json
139
     * @throws \Exception
140
     */
141 22
    private function appendIncludedConfigurations(Config $config, array $json)
142
    {
143 22
        $this->readMaxIncludeLevel($json);
144 22
        if ($this->includeLevel < $this->maxIncludeLevel) {
145 22
            $includes = $this->loadIncludedConfigs($json, $config->getPath());
146 21
            foreach (HookUtil::getValidHooks() as $hook => $class) {
147 21
                $this->appendHookActionsFromIncludes($config->getHookConfig($hook), $includes);
148
            }
149
        }
150 21
        $this->includeLevel++;
151 21
    }
152
153
    /**
154
     * Check config section for 'maxIncludeLevel' setting
155
     *
156
     * @param array $json
157
     */
158 22
    private function readMaxIncludeLevel(array $json) : void
159
    {
160
        // read the include level setting only for the actual configuration
161 22
        if ($this->includeLevel === 0 && isset($json['config']['maxIncludeLevel'])) {
162 1
            $this->maxIncludeLevel = (int) $json['config']['maxIncludeLevel'];
163
        }
164 22
    }
165
166
    /**
167
     * Append actions to a given hook config from a list of included configurations
168
     *
169
     * @param  \CaptainHook\App\Config\Hook $hook
170
     * @param  \CaptainHook\App\Config[]    $includes
171
     * @return void
172
     */
173 21
    private function appendHookActionsFromIncludes(Hook $hook, array $includes) : void
174
    {
175 21
        foreach ($includes as $includedConfig) {
176 3
            $this->copyActionsFromTo($includedConfig->getHookConfig($hook->getName()), $hook);
177
        }
178 21
    }
179
180
    /**
181
     * Return list of included configurations to add them to the main configuration afterwards
182
     *
183
     * @param  array  $json
184
     * @param  string $path
185
     * @return \CaptainHook\App\Config[]
186
     * @throws \Exception
187
     */
188 22
    protected function loadIncludedConfigs(array $json, string $path) : array
189
    {
190 22
        $includes  = [];
191 22
        $directory = dirname($path);
192 22
        $files     = isset($json['config']['includes']) && is_array($json['config']['includes'])
193 4
                   ? $json['config']['includes']
194 22
                   : [];
195
196 22
        foreach ($files as $file) {
197 4
            $includes[] = $this->includeConfig($directory . DIRECTORY_SEPARATOR . $file);
198
        }
199 21
        return $includes;
200
    }
201
202
    /**
203
     * Copy action from a given configuration to the second given configuration
204
     *
205
     * @param \CaptainHook\App\Config\Hook $sourceConfig
206
     * @param \CaptainHook\App\Config\Hook $targetConfig
207
     */
208 3
    private function copyActionsFromTo(Hook $sourceConfig, Hook $targetConfig)
209
    {
210 3
        foreach ($sourceConfig->getActions() as $action) {
211 3
            $targetConfig->addAction($action);
212
        }
213 3
    }
214
215
    /**
216
     * Config factory method
217
     *
218
     * @param  string $path
219
     * @return \CaptainHook\App\Config
220
     */
221 27
    public static function create(string $path = '') : Config
222
    {
223 27
        $factory = new static();
224 27
        return $factory->createConfig($path);
225
    }
226
}
227