Completed
Pull Request — master (#1241)
by
unknown
01:56
created

Config::recurseArrayForTokens()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 0
cts 0
cp 0
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 13
nc 5
nop 2
crap 30
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 getStorageConfigs(array $envOptions)
182
    {
183
184
        $configs = [];
185
186
        if (isset($envOptions['default_database'])) {
187 20
            unset($envOptions['default_database']);
188
        }
189
190 20
        if (isset($envOptions['default_migration_table'])) {
191 20
            unset($envOptions['default_migration_table']);
192 2
        }
193 1
194
        if (count($envOptions) > 0) {
195
            foreach ($envOptions as $dbRef => $adapterOptions) {
196 1
                if (!is_array($adapterOptions)) {
197 1
                    $configs [$dbRef]= $envOptions;
198
                    break;
199 1
                } else {
200
                    $configs [$dbRef]= array_merge($adapterOptions, ['dbRef' => $dbRef]);
201
                }
202
            }
203
        }
204 19
205 17
        return $configs;
206 16
207
    }
208
209 1
    /**
210 1
     * {@inheritdoc}
211 1
     */
212 1
    public function hasEnvironment($name)
213
    {
214
        return ($this->getEnvironment($name) !== null);
215
    }
216 2
217 1
    /**
218 1
     * {@inheritdoc}
219
     */
220
    public function getDefaultEnvironment()
221 1
    {
222
        // The $PHINX_ENVIRONMENT variable overrides all other default settings
223
        $env = getenv('PHINX_ENVIRONMENT');
224
        if (!empty($env)) {
225
            if ($this->hasEnvironment($env)) {
226
                return $env;
227 7
            }
228
229 7
            throw new \RuntimeException(sprintf(
230
                'The environment configuration (read from $PHINX_ENVIRONMENT) for \'%s\' is missing',
231
                $env
232
            ));
233
        }
234
235 448
        // if the user has configured a default database then use it,
236
        // providing it actually exists!
237 448
        if (isset($this->values['environments']['default_database'])) {
238
            if ($this->getEnvironment($this->values['environments']['default_database'])) {
239
                return $this->values['environments']['default_database'];
240
            }
241
242
            throw new \RuntimeException(sprintf(
243 423
                'The environment configuration for \'%s\' is missing',
244
                $this->values['environments']['default_database']
245 423
            ));
246 1
        }
247
248
        // else default to the first available one
249 422
        if (is_array($this->getEnvironments()) && count($this->getEnvironments()) > 0) {
250 219
            $names = array_keys($this->getEnvironments());
251 219
252
            return $names[0];
253 422
        }
254
255
        throw new \RuntimeException('Could not find a default environment');
256
    }
257
258
    /**
259
     * {@inheritdoc}
260
     */
261
    public function getAlias($alias)
262 14
    {
263
        return !empty($this->values['aliases'][$alias]) ? $this->values['aliases'][$alias] : null;
264 14
    }
265
266 14
    /**
267
     * {@inheritdoc}
268
     */
269
    public function getConfigFilePath()
270
    {
271
        return $this->configFilePath;
272 48
    }
273
274 48
    /**
275 28
     * {@inheritdoc}
276
     */
277
    public function getMigrationPaths($environment = null, $dbReference = null)
278 20
    {
279 13
        $paths = [];
280 13
281
        if (!isset($this->values['paths']['migrations'])) {
282 20
            throw new \UnexpectedValueException('Default migrations path missing from config file');
283
        }
284
285
        if (is_string($this->values['paths']['migrations'])) {
286
            $this->values['paths']['migrations'] = [$this->values['paths']['migrations']];
287
        }
288
289
        if ($environment !== null && $dbReference !== null) {
290 14
291
            $environment = $this->getEnvironment($environment);
292 14 View Code Duplication
            if (isset($environment[$dbReference]['paths']['migrations'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
293 13
                if (!is_array($environment[$dbReference]['paths']['migrations'])) {
294
                    return [$environment[$dbReference]['paths']['migrations']];
295
                } else {
296 1
                    foreach ($environment[$dbReference]['paths']['migrations'] as $namespace => $migrationPath) {
297
                        $paths[$namespace]= $migrationPath;
298
                    }
299
                    if (count($paths) > 0) {
300
                        $this->values['paths']['migrations']= $paths;
301
                    }
302
                }
303
            }
304 14
        } elseif (is_null($environment) && is_null($dbReference)) {
305
            if (isset($this->values['environments']) && is_array($this->values['environments'])) {
306 14
                $environments = array_keys($this->values['environments']);
307 10
308
                foreach ($environments as $env) {
309
                    if (is_array($this->values['environments'][$env])) {
310 4
                        foreach ($this->values['environments'][$env] as $dbReference => $properties) {
311
                            if (!is_array($properties)) {
312
                                continue;
313
                            }
314
315
                            if (isset($this->values['environments'][$env][$dbReference]['paths']) && !is_array($this->values['environments'][$env][$dbReference]['paths']['migrations'])) {
316
                                $paths []= $this->values['environments'][$env][$dbReference]['paths']['migrations'];
317 View Code Duplication
                            } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
318 384
                                foreach ($this->values['environments'][$env][$dbReference]['paths']['migrations'] as $namespace => $migrationPath) {
319
                                    $paths[$namespace]= $migrationPath;
320 384
                                }
321 162
                            }
322
                        }
323
                    }
324 222
                }
325
                if (count($paths) > 0) {
326
                    $this->values['paths']['migrations']= $paths;
327
                }
328
            }
329
        }
330
331
        return $this->values['paths']['migrations'];
332 357
    }
333
334 357
    /**
335
     * Gets the base class name for migrations.
336 357
     *
337
     * @param bool $dropNamespace Return the base migration class name without the namespace.
338
     * @return string
339
     */
340
    public function getMigrationBaseClassName($dropNamespace = true)
341
    {
342
        $className = !isset($this->values['migration_base_class']) ? 'Phinx\Migration\AbstractMigration' : $this->values['migration_base_class'];
343
344
        return $dropNamespace ? substr(strrchr($className, '\\'), 1) ?: $className : $className;
345
    }
346
347 448
    /**
348
     * {@inheritdoc}
349
     */
350
    public function getSeedPaths($environment = null, $dbReference = null)
351 448
    {
352 448
353 448
        $paths = [];
354 2
355 2
        if (!isset($this->values['paths']['seeds'])) {
356 448
            throw new \UnexpectedValueException('Seeds path missing from config file');
357
        }
358
359 448
        if (is_string($this->values['paths']['seeds'])) {
360 448
            $this->values['paths']['seeds'] = [$this->values['paths']['seeds']];
361
        }
362
363 448
        if ($environment !== null && $dbReference !== null) {
364
            $environment = $this->getEnvironment($environment);
365 View Code Duplication
            if (isset($environment[$dbReference]['paths']['seeds'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
366
367
                if (!is_array($environment[$dbReference]['paths']['seeds'])) {
368
                    return [$environment[$dbReference]['paths']['seeds']];
369
                } else {
370
                    foreach ($environment[$dbReference]['paths']['seeds'] as $namespace => $seedPath) {
371
                        $paths[$namespace]= $seedPath;
372
                    }
373 448
                    if (count($paths) > 0) {
374
                        $this->values['paths']['seeds']= $paths;
375 448
                    }
376 448
                }
377 447
            }
378 446
        } elseif (is_null($environment) && is_null($dbReference)) {
379 446
            if (isset($this->values['environments']) && is_array($this->values['environments'])) {
380
                $environments = array_keys($this->values['environments']);
381 446
                foreach ($environments as $env) {
382 446
                    if (is_array($this->values['environments'][$env])) {
383 446
                        foreach ($this->values['environments'][$env] as $dbReference => $properties) {
384 446
                            if (!is_array($properties)) {
385 446
                                continue;
386 446
                            }
387
388 43
                            if (!is_array($this->values['environments'][$env][$dbReference]['paths']['seeds'])) {
389 448
                                $paths []= $this->values['environments'][$env][$dbReference]['paths']['seeds'];
390 448 View Code Duplication
                            } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
391
                                foreach ($this->values['environments'][$env][$dbReference]['paths']['seeds'] as $namespace => $migrationPath) {
392
                                    $paths[$namespace]= $migrationPath;
393
                                }
394
                            }
395
                        }
396 213
                    }
397
                }
398 213
            }
399 213
            if (count($paths) > 0) {
400
                $this->values['paths']['seeds']= $paths;
401
            }
402
        }
403
404 2
        return $this->values['paths']['seeds'];
405
    }
406 2
407 1
    /**
408
     * Get the template file name.
409
     *
410 1
     * @return string|false
411
     */
412 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...
413
    {
414
        if (!isset($this->values['templates']['file'])) {
415
            return false;
416 1
        }
417
418 1
        return $this->values['templates']['file'];
419
    }
420
421
    /**
422
     * Get the template class name.
423
     *
424 1
     * @return string|false
425
     */
426 1 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...
427 1
    {
428
        if (!isset($this->values['templates']['class'])) {
429
            return false;
430
        }
431
432
        return $this->values['templates']['class'];
433
    }
434
435
    /**
436
     * Get the version order.
437
     *
438
     * @return string
439
     */
440
    public function getVersionOrder()
441
    {
442
        if (!isset($this->values['version_order'])) {
443
            return self::VERSION_ORDER_CREATION_TIME;
444
        }
445
446
        return $this->values['version_order'];
447
    }
448
449
    /**
450
     * Is version order creation time?
451
     *
452
     * @return bool
453
     */
454
    public function isVersionOrderCreationTime()
455
    {
456
        $versionOrder = $this->getVersionOrder();
457
458
        return $versionOrder == self::VERSION_ORDER_CREATION_TIME;
459
    }
460
461
    /**
462
     * Replace tokens in the specified array.
463
     *
464
     * @param array $arr Array to replace
465
     * @return array
466
     */
467
    protected function replaceTokens(array $arr)
0 ignored issues
show
Coding Style introduced by
replaceTokens uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
468
    {
469
        // Get environment variables
470
        // $_ENV is empty because variables_order does not include it normally
471
        $tokens = [];
472
        foreach ($_SERVER as $varname => $varvalue) {
473
            if (0 === strpos($varname, 'PHINX_')) {
474
                $tokens['%%' . $varname . '%%'] = $varvalue;
475
            }
476
        }
477
478
        // Phinx defined tokens (override env tokens)
479
        $tokens['%%PHINX_CONFIG_PATH%%'] = $this->getConfigFilePath();
480
        $tokens['%%PHINX_CONFIG_DIR%%'] = dirname($this->getConfigFilePath());
481
482
        // Recurse the array and replace tokens
483
        return $this->recurseArrayForTokens($arr, $tokens);
484
    }
485
486
    /**
487
     * Recurse an array for the specified tokens and replace them.
488
     *
489
     * @param array $arr Array to recurse
490
     * @param array $tokens Array of tokens to search for
491
     * @return array
492
     */
493
    protected function recurseArrayForTokens($arr, $tokens)
494
    {
495
        $out = [];
496
        foreach ($arr as $name => $value) {
497
            if (is_array($value)) {
498
                $out[$name] = $this->recurseArrayForTokens($value, $tokens);
499
                continue;
500
            }
501
            if (is_string($value)) {
502
                foreach ($tokens as $token => $tval) {
503
                    $value = str_replace($token, $tval, $value);
504
                }
505
                $out[$name] = $value;
506
                continue;
507
            }
508
            $out[$name] = $value;
509
        }
510
511
        return $out;
512
    }
513
514
    /**
515
     * {@inheritdoc}
516
     */
517
    public function offsetSet($id, $value)
518
    {
519
        $this->values[$id] = $value;
520
    }
521
522
    /**
523
     * {@inheritdoc}
524
     */
525
    public function offsetGet($id)
526
    {
527
        if (!array_key_exists($id, $this->values)) {
528
            throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
529
        }
530
531
        return $this->values[$id] instanceof \Closure ? $this->values[$id]($this) : $this->values[$id];
532
    }
533
534
    /**
535
     * {@inheritdoc}
536
     */
537
    public function offsetExists($id)
538
    {
539
        return isset($this->values[$id]);
540
    }
541
542
    /**
543
     * {@inheritdoc}
544
     */
545
    public function offsetUnset($id)
546
    {
547
        unset($this->values[$id]);
548
    }
549
}
550