Completed
Push — master ( 8b9b51...6bee3d )
by mark
02:06 queued 10s
created

Config::getEnvironment()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 10
cts 10
cp 1
rs 8.9297
c 0
b 0
f 0
cc 6
nc 5
nop 1
crap 6
1
<?php
2
3
/**
4
 * MIT License
5
 * For full license information, please view the LICENSE file that was distributed with this source code.
6
 */
7
8
namespace Phinx\Config;
9
10
use Closure;
11
use InvalidArgumentException;
12
use Phinx\Db\Adapter\SQLiteAdapter;
13
use Phinx\Util\Util;
14
use RuntimeException;
15
use Symfony\Component\Yaml\Yaml;
16
use UnexpectedValueException;
17
18
/**
19
 * Phinx configuration class.
20
 *
21
 * @package Phinx
22
 * @author Rob Morgan
23
 */
24
class Config implements ConfigInterface, NamespaceAwareInterface
25
{
26
    use NamespaceAwareTrait;
27
28
    /**
29
     * The value that identifies a version order by creation time.
30
     */
31
    public const VERSION_ORDER_CREATION_TIME = 'creation';
32
33
    /**
34
     * The value that identifies a version order by execution time.
35
     */
36
    public const VERSION_ORDER_EXECUTION_TIME = 'execution';
37
38
    /**
39
     * @var array
40
     */
41
    protected $values = [];
42
43
    /**
44
     * @var string
45
     */
46
    protected $configFilePath;
47
48
    /**
49
     * @inheritDoc
50
     */
51
    public function __construct(array $configArray, $configFilePath = null)
52
    {
53
        $this->configFilePath = $configFilePath;
54
        $this->values = $this->replaceTokens($configArray);
55
    }
56
57
    /**
58
     * Create a new instance of the config class using a Yaml file path.
59
     *
60
     * @param string $configFilePath Path to the Yaml File
61
     *
62
     * @throws \RuntimeException
63
     *
64
     * @return \Phinx\Config\Config
65
     */
66 448
    public static function fromYaml($configFilePath)
67
    {
68 448
        if (!class_exists('Symfony\\Component\\Yaml\\Yaml', true)) {
69 448
            throw new RuntimeException('Missing yaml parser, symfony/yaml package is not installed.');
70 448
        }
71
72
        $configFile = file_get_contents($configFilePath);
73
        $configArray = Yaml::parse($configFile);
74
75
        if (!is_array($configArray)) {
76
            throw new RuntimeException(sprintf(
77
                'File \'%s\' must be valid YAML',
78
                $configFilePath
79 2
            ));
80
        }
81 2
82 2
        return new static($configArray, $configFilePath);
83
    }
84 2
85 1
    /**
86 1
     * Create a new instance of the config class using a JSON file path.
87
     *
88 1
     * @param string $configFilePath Path to the JSON File
89
     *
90 1
     * @throws \RuntimeException
91
     *
92
     * @return \Phinx\Config\Config
93
     */
94
    public static function fromJson($configFilePath)
95
    {
96
        if (!function_exists('json_decode')) {
97
            throw new RuntimeException("Need to install JSON PHP extension to use JSON config");
98
        }
99
100 2
        $configArray = json_decode(file_get_contents($configFilePath), true);
101
        if (!is_array($configArray)) {
102 2
            throw new RuntimeException(sprintf(
103 2
                'File \'%s\' must be valid JSON',
104 1
                $configFilePath
105 1
            ));
106
        }
107 1
108
        return new static($configArray, $configFilePath);
109 1
    }
110
111
    /**
112
     * Create a new instance of the config class using a PHP file path.
113
     *
114
     * @param string $configFilePath Path to the PHP File
115
     *
116
     * @throws \RuntimeException
117
     *
118
     * @return \Phinx\Config\Config
119 3
     */
120
    public static function fromPhp($configFilePath)
121 3
    {
122
        ob_start();
123 3
        /** @noinspection PhpIncludeInspection */
124
        $configArray = include($configFilePath);
125
126 3
        // Hide console output
127
        ob_end_clean();
128 3
129 2
        if (!is_array($configArray)) {
130 2
            throw new RuntimeException(sprintf(
131
                'PHP file \'%s\' must return an array',
132 2
                $configFilePath
133
            ));
134
        }
135 1
136
        return new static($configArray, $configFilePath);
137
    }
138
139
    /**
140
     * @inheritDoc
141 25
     */
142
    public function getEnvironments()
143 25
    {
144 24
        if (isset($this->values) && isset($this->values['environments'])) {
145 24
            $environments = [];
146 24
            foreach ($this->values['environments'] as $key => $value) {
147 24
                if (is_array($value)) {
148 24
                    $environments[$key] = $value;
149 24
                }
150
            }
151 24
152
            return $environments;
153
        }
154 1
155
        return null;
156
    }
157
158
    /**
159
     * @inheritDoc
160 25
     */
161
    public function getEnvironment($name)
162 25
    {
163
        $environments = $this->getEnvironments();
164 25
165 21
        if (isset($environments[$name])) {
166 21
            if (isset($this->values['environments']['default_migration_table'])) {
167 21
                $environments[$name]['default_migration_table'] =
168 21
                    $this->values['environments']['default_migration_table'];
169
            }
170 21
171
            if (
172
                isset($environments[$name]['adapter'])
173 5
                && $environments[$name]['adapter'] === 'sqlite'
174
                && !empty($environments[$name]['memory'])
175
            ) {
176
                $environments[$name]['name'] = SQLiteAdapter::MEMORY;
177
            }
178
179 9
            return $this->parseAgnosticDsn($environments[$name]);
180
        }
181 9
182
        return null;
183
    }
184
185
    /**
186
     * @inheritDoc
187 20
     */
188
    public function hasEnvironment($name)
189
    {
190 20
        return ($this->getEnvironment($name) !== null);
191 20
    }
192 2
193 1
    /**
194
     * @inheritDoc
195
     */
196 1
    public function getDefaultEnvironment()
197 1
    {
198
        // The $PHINX_ENVIRONMENT variable overrides all other default settings
199 1
        $env = getenv('PHINX_ENVIRONMENT');
200
        if (!empty($env)) {
201
            if ($this->hasEnvironment($env)) {
202
                return $env;
203
            }
204 19
205 17
            throw new RuntimeException(sprintf(
206 16
                'The environment configuration (read from $PHINX_ENVIRONMENT) for \'%s\' is missing',
207
                $env
208
            ));
209 1
        }
210 1
211 1
        // deprecated: to be removed 0.13
212 1
        if (isset($this->values['environments']['default_database'])) {
213
            $this->values['environments']['default_environment'] = $this->values['environments']['default_database'];
214
        }
215
216 2
        // if the user has configured a default environment then use it,
217 1
        // providing it actually exists!
218 1
        if (isset($this->values['environments']['default_environment'])) {
219
            if ($this->getEnvironment($this->values['environments']['default_environment'])) {
220
                return $this->values['environments']['default_environment'];
221 1
            }
222
223
            throw new RuntimeException(sprintf(
224
                'The environment configuration for \'%s\' is missing',
225
                $this->values['environments']['default_environment']
226
            ));
227 7
        }
228
229 7
        // else default to the first available one
230
        if (is_array($this->getEnvironments()) && count($this->getEnvironments()) > 0) {
231
            $names = array_keys($this->getEnvironments());
232
233
            return $names[0];
234
        }
235 448
236
        throw new RuntimeException('Could not find a default environment');
237 448
    }
238
239
    /**
240
     * @inheritDoc
241
     */
242
    public function getAlias($alias)
243 423
    {
244
        return !empty($this->values['aliases'][$alias]) ? $this->values['aliases'][$alias] : null;
245 423
    }
246 1
247
    /**
248
     * @inheritDoc
249 422
     */
250 219
    public function getAliases()
251 219
    {
252
        return !empty($this->values['aliases']) ? $this->values['aliases'] : [];
253 422
    }
254
255
    /**
256
     * @inheritDoc
257
     */
258
    public function getConfigFilePath()
259
    {
260
        return $this->configFilePath;
261
    }
262 14
263
    /**
264 14
     * @inheritDoc
265
     *
266 14
     * @throws \UnexpectedValueException
267
     */
268
    public function getMigrationPaths()
269
    {
270
        if (!isset($this->values['paths']['migrations'])) {
271
            throw new UnexpectedValueException('Migrations path missing from config file');
272 48
        }
273
274 48
        if (is_string($this->values['paths']['migrations'])) {
275 28
            $this->values['paths']['migrations'] = [$this->values['paths']['migrations']];
276
        }
277
278 20
        return $this->values['paths']['migrations'];
279 13
    }
280 13
281
    /**
282 20
     * Gets the base class name for migrations.
283
     *
284
     * @param bool $dropNamespace Return the base migration class name without the namespace.
285
     *
286
     * @return string
287
     */
288
    public function getMigrationBaseClassName($dropNamespace = true)
289
    {
290 14
        $className = !isset($this->values['migration_base_class']) ? 'Phinx\Migration\AbstractMigration' : $this->values['migration_base_class'];
291
292 14
        return $dropNamespace ? substr(strrchr($className, '\\'), 1) ?: $className : $className;
293 13
    }
294
295
    /**
296 1
     * @inheritDoc
297
     *
298
     * @throws \UnexpectedValueException
299
     */
300
    public function getSeedPaths()
301
    {
302
        if (!isset($this->values['paths']['seeds'])) {
303
            throw new UnexpectedValueException('Seeds path missing from config file');
304 14
        }
305
306 14
        if (is_string($this->values['paths']['seeds'])) {
307 10
            $this->values['paths']['seeds'] = [$this->values['paths']['seeds']];
308
        }
309
310 4
        return $this->values['paths']['seeds'];
311
    }
312
313
    /**
314
     * Gets the base class name for seeders.
315
     *
316
     * @param bool $dropNamespace Return the base seeder class name without the namespace.
317
     * @return string
318 384
     */
319
    public function getSeedBaseClassName($dropNamespace = true)
320 384
    {
321 162
        $className = !isset($this->values['seed_base_class']) ? 'Phinx\Seed\AbstractSeed' : $this->values['seed_base_class'];
322
323
        return $dropNamespace ? substr(strrchr($className, '\\'), 1) : $className;
324 222
    }
325
326
    /**
327
     * Get the template file name.
328
     *
329
     * @return string|false
330
     */
331
    public function getTemplateFile()
332 357
    {
333
        if (!isset($this->values['templates']['file'])) {
334 357
            return false;
335
        }
336 357
337
        return $this->values['templates']['file'];
338
    }
339
340
    /**
341
     * Get the template class name.
342
     *
343
     * @return string|false
344
     */
345
    public function getTemplateClass()
346
    {
347 448
        if (!isset($this->values['templates']['class'])) {
348
            return false;
349
        }
350
351 448
        return $this->values['templates']['class'];
352 448
    }
353 448
354 2
    /**
355 2
     * {@inheritdoc}
356 448
     */
357
    public function getDataDomain()
358
    {
359 448
        if (!isset($this->values['data_domain'])) {
360 448
            return [];
361
        }
362
363 448
        return $this->values['data_domain'];
364
    }
365
366
    /**
367
     * @inheritDoc
368
     */
369
    public function getContainer()
370
    {
371
        if (!isset($this->values['container'])) {
372
            return null;
373 448
        }
374
375 448
        return $this->values['container'];
376 448
    }
377 447
378 446
    /**
379 446
     * Get the version order.
380
     *
381 446
     * @return string
382 446
     */
383 446
    public function getVersionOrder()
384 446
    {
385 446
        if (!isset($this->values['version_order'])) {
386 446
            return self::VERSION_ORDER_CREATION_TIME;
387
        }
388 43
389 448
        return $this->values['version_order'];
390 448
    }
391
392
    /**
393
     * Is version order creation time?
394
     *
395
     * @return bool
396 213
     */
397
    public function isVersionOrderCreationTime()
398 213
    {
399 213
        $versionOrder = $this->getVersionOrder();
400
401
        return $versionOrder == self::VERSION_ORDER_CREATION_TIME;
402
    }
403
404 2
    /**
405
     * Get the bootstrap file path
406 2
     *
407 1
     * @return string|false
408
     */
409
    public function getBootstrapFile()
410 1
    {
411
        if (!isset($this->values['paths']['bootstrap'])) {
412
            return false;
413
        }
414
415
        return $this->values['paths']['bootstrap'];
416 1
    }
417
418 1
    /**
419
     * Replace tokens in the specified array.
420
     *
421
     * @param array $arr Array to replace
422
     *
423
     * @return array
424 1
     */
425
    protected function replaceTokens(array $arr)
426 1
    {
427 1
        // Get environment variables
428
        // Depending on configuration of server / OS and variables_order directive,
429
        // environment variables either end up in $_SERVER (most likely) or $_ENV,
430
        // so we search through both
431
        $tokens = [];
432
        foreach (array_merge($_ENV, $_SERVER) as $varname => $varvalue) {
433
            if (strpos($varname, 'PHINX_') === 0) {
434
                $tokens['%%' . $varname . '%%'] = $varvalue;
435
            }
436
        }
437
438
        // Phinx defined tokens (override env tokens)
439
        $tokens['%%PHINX_CONFIG_PATH%%'] = $this->getConfigFilePath();
440
        $tokens['%%PHINX_CONFIG_DIR%%'] = dirname($this->getConfigFilePath());
441
442
        // Recurse the array and replace tokens
443
        return $this->recurseArrayForTokens($arr, $tokens);
444
    }
445
446
    /**
447
     * Recurse an array for the specified tokens and replace them.
448
     *
449
     * @param array $arr Array to recurse
450
     * @param string[] $tokens Array of tokens to search for
451
     *
452
     * @return array
453
     */
454
    protected function recurseArrayForTokens($arr, $tokens)
455
    {
456
        $out = [];
457
        foreach ($arr as $name => $value) {
458
            if (is_array($value)) {
459
                $out[$name] = $this->recurseArrayForTokens($value, $tokens);
460
                continue;
461
            }
462
            if (is_string($value)) {
463
                foreach ($tokens as $token => $tval) {
464
                    $value = str_replace($token, $tval, $value);
465
                }
466
                $out[$name] = $value;
467
                continue;
468
            }
469
            $out[$name] = $value;
470
        }
471
472
        return $out;
473
    }
474
475
    /**
476
     * Parse a database-agnostic DSN into individual options.
477
     *
478
     * @param array $options Options
479
     *
480
     * @return array
481
     */
482
    protected function parseAgnosticDsn(array $options)
483
    {
484
        $parsed = Util::parseDsn($options['dsn'] ?? '');
485
        if ($parsed) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsed of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
486
            unset($options['dsn']);
487
        }
488
489
        $options = array_merge($parsed, $options);
490
491
        return $options;
492
    }
493
494
    /**
495
     * @inheritDoc
496
     */
497
    public function offsetSet($id, $value)
498
    {
499
        $this->values[$id] = $value;
500
    }
501
502
    /**
503
     * @inheritDoc
504
     *
505
     * @throws \InvalidArgumentException
506
     */
507
    public function offsetGet($id)
508
    {
509
        if (!array_key_exists($id, $this->values)) {
510
            throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
511
        }
512
513
        return $this->values[$id] instanceof Closure ? $this->values[$id]($this) : $this->values[$id];
514
    }
515
516
    /**
517
     * @inheritDoc
518
     */
519
    public function offsetExists($id)
520
    {
521
        return isset($this->values[$id]);
522
    }
523
524
    /**
525
     * @inheritDoc
526
     */
527
    public function offsetUnset($id)
528
    {
529
        unset($this->values[$id]);
530
    }
531
}
532