Completed
Pull Request — master (#1512)
by Elan
03:44
created

Config   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 419
Duplicated Lines 19.57 %

Coupling/Cohesion

Components 3
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 62
lcom 3
cbo 2
dl 82
loc 419
ccs 141
cts 141
cp 1
rs 3.44
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A fromYaml() 13 14 2
A fromJson() 11 12 2
A fromPhp() 18 18 2
A getEnvironments() 0 15 5
A getEnvironment() 0 15 3
A hasEnvironment() 0 4 1
B getDefaultEnvironment() 0 37 7
A getAlias() 0 4 2
A getConfigFilePath() 0 4 1
A getMigrationBaseClassName() 0 6 4
A getSeedBaseClassName() 0 6 3
A getTemplateFile() 8 8 2
A getTemplateClass() 8 8 2
A getBootstrapFile() 0 8 2
A replaceTokens() 0 18 3
A recurseArrayForTokens() 0 20 5
A offsetSet() 0 4 1
A offsetGet() 0 8 3
A offsetExists() 0 4 1
A offsetUnset() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Config often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Config, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Config
28
 */
29
namespace Phinx\Config;
30
31
use Symfony\Component\Yaml\Yaml;
32
33
/**
34
 * Phinx configuration class.
35
 *
36
 * @package Phinx
37
 * @author Rob Morgan
38
 */
39
class Config implements ConfigInterface, NamespaceAwareInterface
40
{
41
    use NamespaceAwareTrait;
42
43
    /**
44
     * The value that identifies a version order by creation time.
45
     */
46
    const VERSION_ORDER_CREATION_TIME = 'creation';
47
48
    /**
49
     * The value that identifies a version order by execution time.
50
     */
51
    const VERSION_ORDER_EXECUTION_TIME = 'execution';
52
53
    /**
54
     * @var array
55
     */
56
    private $values = [];
57
58
    /**
59
     * @var string
60
     */
61
    protected $configFilePath;
62
63
    /**
64
     * {@inheritdoc}
65
     */
66 448
    public function __construct(array $configArray, $configFilePath = null)
67
    {
68 448
        $this->configFilePath = $configFilePath;
69 448
        $this->values = $this->replaceTokens($configArray);
70 448
    }
71
72
    /**
73
     * Create a new instance of the config class using a Yaml file path.
74
     *
75
     * @param  string $configFilePath Path to the Yaml File
76
     * @throws \RuntimeException
77
     * @return \Phinx\Config\Config
78
     */
79 2 View Code Duplication
    public static function fromYaml($configFilePath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
80
    {
81 2
        $configFile = file_get_contents($configFilePath);
82 2
        $configArray = Yaml::parse($configFile);
83
84 2
        if (!is_array($configArray)) {
85 1
            throw new \RuntimeException(sprintf(
86 1
                'File \'%s\' must be valid YAML',
87
                $configFilePath
88 1
            ));
89
        }
90 1
91
        return new static($configArray, $configFilePath);
92
    }
93
94
    /**
95
     * Create a new instance of the config class using a JSON file path.
96
     *
97
     * @param  string $configFilePath Path to the JSON File
98
     * @throws \RuntimeException
99
     * @return \Phinx\Config\Config
100 2
     */
101 View Code Duplication
    public static function fromJson($configFilePath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
102 2
    {
103 2
        $configArray = json_decode(file_get_contents($configFilePath), true);
104 1
        if (!is_array($configArray)) {
105 1
            throw new \RuntimeException(sprintf(
106
                'File \'%s\' must be valid JSON',
107 1
                $configFilePath
108
            ));
109 1
        }
110
111
        return new static($configArray, $configFilePath);
112
    }
113
114
    /**
115
     * Create a new instance of the config class using a PHP file path.
116
     *
117
     * @param  string $configFilePath Path to the PHP File
118
     * @throws \RuntimeException
119 3
     * @return \Phinx\Config\Config
120
     */
121 3 View Code Duplication
    public static function fromPhp($configFilePath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
122
    {
123 3
        ob_start();
124
        /** @noinspection PhpIncludeInspection */
125
        $configArray = include($configFilePath);
126 3
127
        // Hide console output
128 3
        ob_end_clean();
129 2
130 2
        if (!is_array($configArray)) {
131
            throw new \RuntimeException(sprintf(
132 2
                'PHP file \'%s\' must return an array',
133
                $configFilePath
134
            ));
135 1
        }
136
137
        return new static($configArray, $configFilePath);
138
    }
139
140
    /**
141 25
     * {@inheritdoc}
142
     */
143 25
    public function getEnvironments()
144 24
    {
145 24
        if (isset($this->values) && isset($this->values['environments'])) {
146 24
            $environments = [];
147 24
            foreach ($this->values['environments'] as $key => $value) {
148 24
                if (is_array($value)) {
149 24
                    $environments[$key] = $value;
150
                }
151 24
            }
152
153
            return $environments;
154 1
        }
155
156
        return null;
157
    }
158
159
    /**
160 25
     * {@inheritdoc}
161
     */
162 25
    public function getEnvironment($name)
163
    {
164 25
        $environments = $this->getEnvironments();
165 21
166 21
        if (isset($environments[$name])) {
167 21
            if (isset($this->values['environments']['default_migration_table'])) {
168 21
                $environments[$name]['default_migration_table'] =
169
                    $this->values['environments']['default_migration_table'];
170 21
            }
171
172
            return $environments[$name];
173 5
        }
174
175
        return null;
176
    }
177
178
    /**
179 9
     * {@inheritdoc}
180
     */
181 9
    public function hasEnvironment($name)
182
    {
183
        return ($this->getEnvironment($name) !== null);
184
    }
185
186
    /**
187 20
     * {@inheritdoc}
188
     */
189
    public function getDefaultEnvironment()
190 20
    {
191 20
        // The $PHINX_ENVIRONMENT variable overrides all other default settings
192 2
        $env = getenv('PHINX_ENVIRONMENT');
193 1
        if (!empty($env)) {
194
            if ($this->hasEnvironment($env)) {
195
                return $env;
196 1
            }
197 1
198
            throw new \RuntimeException(sprintf(
199 1
                'The environment configuration (read from $PHINX_ENVIRONMENT) for \'%s\' is missing',
200
                $env
201
            ));
202
        }
203
204 19
        // if the user has configured a default database then use it,
205 17
        // providing it actually exists!
206 16
        if (isset($this->values['environments']['default_database'])) {
207
            if ($this->getEnvironment($this->values['environments']['default_database'])) {
208
                return $this->values['environments']['default_database'];
209 1
            }
210 1
211 1
            throw new \RuntimeException(sprintf(
212 1
                'The environment configuration for \'%s\' is missing',
213
                $this->values['environments']['default_database']
214
            ));
215
        }
216 2
217 1
        // else default to the first available one
218 1
        if (is_array($this->getEnvironments()) && count($this->getEnvironments()) > 0) {
219
            $names = array_keys($this->getEnvironments());
220
221 1
            return $names[0];
222
        }
223
224
        throw new \RuntimeException('Could not find a default environment');
225
    }
226
227 7
    /**
228
     * {@inheritdoc}
229 7
     */
230
    public function getAlias($alias)
231
    {
232
        return !empty($this->values['aliases'][$alias]) ? $this->values['aliases'][$alias] : null;
233
    }
234
235 448
    /**
236
     * {@inheritdoc}
237 448
     */
238
    public function getConfigFilePath()
239
    {
240
        return $this->configFilePath;
241
    }
242
243 423
    /**
244
     * {@inheritdoc}
245 423
     */
246 1 View Code Duplication
    public function getMigrationPaths()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
247
    {
248
        if (!isset($this->values['paths']['migrations'])) {
249 422
            throw new \UnexpectedValueException('Migrations path missing from config file');
250 219
        }
251 219
252
        if (is_string($this->values['paths']['migrations'])) {
253 422
            $this->values['paths']['migrations'] = [$this->values['paths']['migrations']];
254
        }
255
256
        return $this->values['paths']['migrations'];
257
    }
258
259
    /**
260
     * Gets the base class name for migrations.
261
     *
262 14
     * @param bool $dropNamespace Return the base migration class name without the namespace.
263
     * @return string
264 14
     */
265
    public function getMigrationBaseClassName($dropNamespace = true)
266 14
    {
267
        $className = !isset($this->values['migration_base_class']) ? 'Phinx\Migration\AbstractMigration' : $this->values['migration_base_class'];
268
269
        return $dropNamespace ? substr(strrchr($className, '\\'), 1) ?: $className : $className;
270
    }
271
272 48
    /**
273
     * {@inheritdoc}
274 48
     */
275 28 View Code Duplication
    public function getSeedPaths()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
276
    {
277
        if (!isset($this->values['paths']['seeds'])) {
278 20
            throw new \UnexpectedValueException('Seeds path missing from config file');
279 13
        }
280 13
281
        if (is_string($this->values['paths']['seeds'])) {
282 20
            $this->values['paths']['seeds'] = [$this->values['paths']['seeds']];
283
        }
284
285
        return $this->values['paths']['seeds'];
286
    }
287
288
    /**
289
     * Gets the base class name for seeders.
290 14
     *
291
     * @param boolean $dropNamespace Return the base sedder class name without the namespace.
292 14
     * @return string
293 13
     */
294
    public function getSeedBaseClassName($dropNamespace = true)
295
    {
296 1
        $className = !isset($this->values['seed_base_class']) ? 'Phinx\Seed\AbstractSeed' : $this->values['seed_base_class'];
297
298
        return $dropNamespace ? substr(strrchr($className, '\\'), 1) : $className;
299
    }
300
301
    /**
302
     * Get the template file name.
303
     *
304 14
     * @return string|false
305
     */
306 14 View Code Duplication
    public function getTemplateFile()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
307 10
    {
308
        if (!isset($this->values['templates']['file'])) {
309
            return false;
310 4
        }
311
312
        return $this->values['templates']['file'];
313
    }
314
315
    /**
316
     * Get the template class name.
317
     *
318 384
     * @return string|false
319
     */
320 384 View Code Duplication
    public function getTemplateClass()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
321 162
    {
322
        if (!isset($this->values['templates']['class'])) {
323
            return false;
324 222
        }
325
326
        return $this->values['templates']['class'];
327
    }
328
329
    /**
330
     * Get the version order.
331
     *
332 357
     * @return string
333
     */
334 357
    public function getVersionOrder()
335
    {
336 357
        if (!isset($this->values['version_order'])) {
337
            return self::VERSION_ORDER_CREATION_TIME;
338
        }
339
340
        return $this->values['version_order'];
341
    }
342
343
    /**
344
     * Is version order creation time?
345
     *
346
     * @return bool
347 448
     */
348
    public function isVersionOrderCreationTime()
349
    {
350
        $versionOrder = $this->getVersionOrder();
351 448
352 448
        return $versionOrder == self::VERSION_ORDER_CREATION_TIME;
353 448
    }
354 2
355 2
    /**
356 448
     * Get the bootstrap file path
357
     *
358
     * @return string|false
359 448
     */
360 448
    public function getBootstrapFile()
361
    {
362
        if (!isset($this->values['paths']['bootstrap'])) {
363 448
            return false;
364
        }
365
366
        return $this->values['paths']['bootstrap'];
367
    }
368
369
    /**
370
     * Replace tokens in the specified array.
371
     *
372
     * @param array $arr Array to replace
373 448
     * @return array
374
     */
375 448
    protected function replaceTokens(array $arr)
376 448
    {
377 447
        // Get environment variables
378 446
        // $_ENV is empty because variables_order does not include it normally
379 446
        $tokens = [];
380
        foreach ($_SERVER as $varname => $varvalue) {
381 446
            if (0 === strpos($varname, 'PHINX_')) {
382 446
                $tokens['%%' . $varname . '%%'] = $varvalue;
383 446
            }
384 446
        }
385 446
386 446
        // Phinx defined tokens (override env tokens)
387
        $tokens['%%PHINX_CONFIG_PATH%%'] = $this->getConfigFilePath();
388 43
        $tokens['%%PHINX_CONFIG_DIR%%'] = dirname($this->getConfigFilePath());
389 448
390 448
        // Recurse the array and replace tokens
391
        return $this->recurseArrayForTokens($arr, $tokens);
392
    }
393
394
    /**
395
     * Recurse an array for the specified tokens and replace them.
396 213
     *
397
     * @param array $arr Array to recurse
398 213
     * @param array $tokens Array of tokens to search for
399 213
     * @return array
400
     */
401
    protected function recurseArrayForTokens($arr, $tokens)
402
    {
403
        $out = [];
404 2
        foreach ($arr as $name => $value) {
405
            if (is_array($value)) {
406 2
                $out[$name] = $this->recurseArrayForTokens($value, $tokens);
407 1
                continue;
408
            }
409
            if (is_string($value)) {
410 1
                foreach ($tokens as $token => $tval) {
411
                    $value = str_replace($token, $tval, $value);
412
                }
413
                $out[$name] = $value;
414
                continue;
415
            }
416 1
            $out[$name] = $value;
417
        }
418 1
419
        return $out;
420
    }
421
422
    /**
423
     * {@inheritdoc}
424 1
     */
425
    public function offsetSet($id, $value)
426 1
    {
427 1
        $this->values[$id] = $value;
428
    }
429
430
    /**
431
     * {@inheritdoc}
432
     */
433
    public function offsetGet($id)
434
    {
435
        if (!array_key_exists($id, $this->values)) {
436
            throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
437
        }
438
439
        return $this->values[$id] instanceof \Closure ? $this->values[$id]($this) : $this->values[$id];
440
    }
441
442
    /**
443
     * {@inheritdoc}
444
     */
445
    public function offsetExists($id)
446
    {
447
        return isset($this->values[$id]);
448
    }
449
450
    /**
451
     * {@inheritdoc}
452
     */
453
    public function offsetUnset($id)
454
    {
455
        unset($this->values[$id]);
456
    }
457
}
458