Passed
Pull Request — master (#1939)
by
unknown
02:54
created

Config::getEnvironment()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6

Importance

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