Completed
Branch develop (94d92d)
by Filipe
01:26
created

Configuration::fixOptions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 0
1
<?php
2
3
/**
4
 * This file is part of slick/configuration
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Configuration;
11
12
use Slick\Configuration\Driver\Environment;
13
use Slick\Configuration\Driver\Ini;
14
use Slick\Configuration\Driver\Php;
15
use Slick\Configuration\Exception\InvalidArgumentException;
16
17
/**
18
 * Configuration
19
 *
20
 * @package Slick\Configuration
21
*/
22
final class Configuration
23
{
24
    /**@#+
25
     * Known configuration drivers
26
     */
27
    const DRIVER_INI = Ini::class;
28
    const DRIVER_PHP = Php::class;
29
    const DRIVER_ENV = Environment::class;
30
    /**@#- */
31
32
    private $extensionToDriver = [
33
        'ini' => self::DRIVER_INI,
34
        'php' => self::DRIVER_PHP,
35
    ];
36
37
    /**
38
     * @var string
39
     */
40
    private $file;
41
42
    /**
43
     * @var null|string
44
     */
45
    private $driverClass;
46
47
    private static $paths = [
48
        './'
49
    ];
50
51
    /**
52
     * Creates a configuration factory
53
     *
54
     * @param string|array $options
55
     * @param null         $driverClass
56
     */
57
    public function __construct($options = null, $driverClass = null)
58
    {
59
        $this->file = $options;
0 ignored issues
show
Documentation Bug introduced by
It seems like $options can also be of type array. However, the property $file is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
60
        $this->driverClass = $driverClass;
61
        self::addPath(getcwd());
62
    }
63
64
    /**
65
     * Creates a ConfigurationInterface with passed arguments
66
     *
67
     * @param string|array $fileName
68
     * @param null         $driverClass
69
     *
70
     * @return ConfigurationInterface|PriorityConfigurationChain
71
     */
72
    public static function get($fileName, $driverClass = null)
73
    {
74
        $configuration = new Configuration($fileName, $driverClass);
75
        return $configuration->initialize();
76
    }
77
78
    /**
79
     * @return PriorityConfigurationChain|ConfigurationInterface
80
     */
81
    public function initialize()
82
    {
83
        $chain = new PriorityConfigurationChain();
84
85
        $options = $this->fixOptions();
86
87
        foreach ($options as $option) {
88
            $priority = $this->setProperties($option);
89
            $chain->add($this->createConfigurationDriver(), $priority);
90
        }
91
92
        return $chain;
93
    }
94
95
    /**
96
     * Prepends a searchable path to available paths list.
97
     *
98
     * @param string $path
99
     */
100
    public static function addPath($path)
101
    {
102
        $path = str_replace('//', '/', rtrim($path, '/'));
103
        if (!in_array($path, self::$paths)) {
104
            array_unshift(self::$paths, $path);
105
        }
106
    }
107
108
    /**
109
     * Returns the driver class to be initialized
110
     *
111
     * @return mixed|null|string
112
     */
113
    private function driverClass()
114
    {
115
        if (null == $this->driverClass) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $this->driverClass of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
116
            $this->driverClass = $this->determineDriver($this->file);
117
        }
118
        return $this->driverClass;
119
    }
120
121
    /**
122
     * Tries to determine the driver class based on given file
123
     *
124
     * @param string $file
125
     * @return mixed
126
     */
127
    private function determineDriver($file)
128
    {
129
        $exception = new InvalidArgumentException(
130
            "Cannot initialize the configuration driver. I could not determine " .
131
            "the correct driver class."
132
        );
133
134
        if (is_null($file) || ! is_string($file)) {
135
            throw $exception;
136
        }
137
138
        $nameDivision = explode('.', $file);
139
        $extension = strtolower(end($nameDivision));
140
141
        if (! array_key_exists($extension, $this->extensionToDriver)) {
142
            throw $exception;
143
        }
144
145
        return $this->extensionToDriver[$extension];
146
    }
147
148
    /**
149
     * Compose the filename with existing paths and return when match
150
     *
151
     * If no match is found the $name is returned as is;
152
     * If no extension is given it will add it from driver class map;
153
     * By default it will try to find <$name>.php file
154
     *
155
     * @param  string $name
156
     *
157
     * @return string
158
     */
159
    private function composeFileName($name)
160
    {
161
        if (is_null($name)) {
162
            return $name;
163
        }
164
165
        $ext = $this->determineExtension();
166
        $withExtension = $this->createName($name, $ext);
167
168
        list($found, $fileName) = $this->searchFor($name, $withExtension);
169
170
        return $found ? $fileName : $name;
171
    }
172
173
    /**
174
     * Determine the extension based on the driver class
175
     *
176
     * If there is no driver class given it will default to .php
177
     *
178
     * @return string
179
     */
180
    private function determineExtension()
181
    {
182
        $ext = 'php';
183
        if (in_array($this->driverClass, $this->extensionToDriver)) {
184
            $map = array_flip($this->extensionToDriver);
185
            $ext = $map[$this->driverClass];
186
        }
187
        return $ext;
188
    }
189
190
    /**
191
     * Creates the name with the extension for known names
192
     *
193
     * @param string $name
194
     * @param string $ext
195
     *
196
     * @return string
197
     */
198
    private function createName($name, $ext)
199
    {
200
        $withExtension = $name;
201
        if (!preg_match('/.*\.(ini|php)/i', $name)) {
202
            $withExtension = "{$name}.{$ext}";
203
        }
204
        return $withExtension;
205
    }
206
207
    /**
208
     * Search for name in the list of paths
209
     *
210
     * @param string $name
211
     * @param string $withExtension
212
     *
213
     * @return array
214
     */
215
    private function searchFor($name, $withExtension)
216
    {
217
        $found = false;
218
        $fileName = $name;
219
220
        foreach (self::$paths as $path) {
221
            $fileName = "{$path}/$withExtension";
222
            if (is_file($fileName)) {
223
                $found = true;
224
                break;
225
            }
226
        }
227
228
        return [$found, $fileName];
229
    }
230
231
    /**
232
     * Creates the configuration driver from current properties
233
     *
234
     * @return ConfigurationInterface
235
     */
236
    private function createConfigurationDriver()
237
    {
238
        $reflection = new \ReflectionClass($this->driverClass());
239
240
        /** @var ConfigurationInterface $config */
241
        $config = $reflection->hasMethod('__construct')
242
            ? $reflection->newInstanceArgs([$this->file])
243
            : $reflection->newInstance();
244
        return $config;
245
    }
246
247
    /**
248
     * Sets the file and driver class
249
     *
250
     * @param array $option
251
     *
252
     * @return int
253
     */
254
    private function setProperties($option)
255
    {
256
        $priority = isset($option[2]) ? $option[2] : 0;
257
        $this->driverClass = isset($option[1]) ? $option[1] : null;
258
        $this->file = isset($option[0]) ? $this->composeFileName($option[0]) : null;
259
        return $priority;
260
    }
261
262
    /**
263
     * Fixes the file for initialization
264
     *
265
     * @return array
266
     */
267
    private function fixOptions()
268
    {
269
        $options = (is_array($this->file))
270
            ? $this->file
271
            : [[$this->file]];
272
        return $options;
273
    }
274
}
275