Configuration   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 1
dl 0
loc 209
ccs 0
cts 54
cp 0
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A setMapping() 0 5 1
A setBaseDirectories() 0 11 4
C load() 0 56 13
A loadFromDir() 0 12 2
A getConfig() 0 4 1
1
<?php
2
3
namespace Anax\Configure;
4
5
/**
6
 * Load configuration for a specified item, look in several places for the
7
 * configuration files or directories. Return the configuration as the value
8
 * received from the configuration file.
9
 */
10
class Configuration
11
{
12
    /**
13
     * @var array $config to save the latest loaded config to ease
14
     *                    access.
15
     */
16
    protected $config = [];
17
18
19
20
    /**
21
     * @var array $dirs where to look for configuration items.
22
     */
23
    protected $dirs = [];
24
25
26
27
    /**
28
     * @var array $mapping mapping items to specific configuration file, mainly
29
     *                     useful for testing various configuration files.
30
     */
31
    protected $mapping = [];
32
33
34
35
    /**
36
     * Set a specific configuration file to load for a particluar item.
37
     *
38
     * @param string $item the item to map.
39
     * @param string $file file to load configuration from.
40
     *
41
     * @return self to allow chaining.
42
     */
43
    public function setMapping(string $item, string $file) : object
44
    {
45
        $this->mapping[$item] = $file;
46
        return $this;
47
    }
48
49
50
51
    /**
52
     * Set the directories where to look for configuration
53
     * items (files, directories) to load.
54
     *
55
     * @throws Exception when the path to any of the directories are incorrect.
56
     *
57
     * @param array $dirs with the path to the config directories to search in.
58
     *
59
     * @return self to allow chaining.
60
     */
61
    public function setBaseDirectories(array $dirs): object
62
    {
63
        foreach ($dirs as $dir) {
64
            if (!(is_readable($dir) && is_dir($dir))) {
65
                throw new Exception("The configuration dir '$dir' is not a valid path.");
66
            }
67
        }
68
69
        $this->dirs = $dirs;
70
        return $this;
71
    }
72
73
74
75
    /**
76
     * Read configuration from file or directory, if a file, look though all
77
     * base dirs and use the first configuration that is found. A configuration
78
     * item can be combined from a file and a directory, when available in the
79
     * same base directory.
80
     *
81
     * The resulting configuration is always an array, its structure contains
82
     * values from each individual configuration file, like this.
83
     *
84
     * $config = [
85
     *      "file" => filename for file.php,
86
     *      "config" => result returned from file.php,
87
     *      "items" => [
88
     *          [
89
     *              "file" => filename for dir/file1.php,
90
     *              "config" => result returned from dir/file1.php,
91
     *          ],
92
     *          [
93
     *              "file" => filename for dir/file2.php,
94
     *              "config" => result returned from dir/file2.php,
95
     *          ],
96
     *      ].
97
     * ]
98
     *
99
     * The configuration files in the directory are loaded per alphabetical
100
     * order.
101
     *
102
     * @param string $item is a name representing the module and is used to
103
     *                     combine the path to search for.
104
     *
105
     * @throws Exception when configuration item can not be found.
106
     * @throws Exception when $dirs are empty.
107
     *
108
     * @return array with returned value from the loaded configuration.
109
     */
110
    public function load(string $item) : array
111
    {
112
        $found = false;
113
        $config = [];
114
        $this->config = $config;
115
116
        $mapping = $this->mapping[$item] ?? null;
117
        if ($mapping) {
118
            $config["file"] = $mapping;
119
            $config["config"] = require $mapping;
120
            $this->config = $config;
121
            return $config;
122
        }
123
124
        // The configuration is found by absolute path
125
        if (is_readable($item) && is_file($item)) {
126
            $found = true;
127
            $config["file"] = $item;
128
            $config["config"] = require $item;
129
            $this->config = $config;
130
            return $config;
131
        }
132
133
        foreach ($this->dirs as $dir) {
134
            $path = "$dir/$item";
135
            $file = "$path.php";
136
137
            // The configuration is found in a file
138
            if (is_readable($file) && is_file($file)) {
139
                $found = true;
140
                $config["file"] = $file;
141
                $config["config"] = require $file;
142
            } elseif (is_readable($path) && is_file($path)) {
143
                $found = true;
144
                $config["file"] = $path;
145
                $config["config"] = require $path;
146
            }
147
148
            // The configuration is found in a directory
149
            if (is_readable($path) && is_dir($path)) {
150
                $found = true;
151
                $config["items"] = $this->loadFromDir($path);
152
            }
153
154
            if ($found) {
155
                break;
156
            }
157
        }
158
159
        if (!$found) {
160
            throw new Exception("Configure item '$item' can not be found.");
161
        }
162
163
        $this->config = $config;
164
        return $config;
165
    }
166
167
168
169
    /**
170
     * Read configuration a directory, loop through all files and add
171
     * them into the $config array as:
172
     * [
173
     *      [
174
     *          "file" => filename for dir/file1.php,
175
     *          "config" => result returned from dir/file1.php,
176
     *      ],
177
     *      [
178
     *          "file" => filename for dir/file2.php,
179
     *          "config" => result returned from dir/file2.php,
180
     *      ],
181
     * ].
182
     *
183
     * @param string $path is the path to the directory containing config files.
184
     *
185
     * @return array with configuration for each file.
186
     */
187
    public function loadFromDir(string $path): array
188
    {
189
        $config = [];
190
        foreach (glob("$path/*.php") as $file) {
191
            $config[] = [
192
                "file" => $file,
193
                "config" => require $file,
194
            ];
195
        }
196
197
        return $config;
198
    }
199
200
201
202
    /**
203
     * Helper function for reading values from the configuration and apply
204
     * default values where configuration item is missing. This
205
     * helper only works when there are single configuration files,
206
     * it does not work when multiple configuration files are loaded
207
     * from a directory.
208
     *
209
     * @param string $key     matching a key in the config array.
210
     * @param string $default value returned when config item is not found.
211
     *
212
     * @return mixed or null if key does not exists.
213
     */
214
    public function getConfig($key, $default = null)
215
    {
216
        return $this->config["config"][$key] ?? $default;
217
    }
218
}
219