Passed
Pull Request — master (#2007)
by
unknown
02:49
created

Config::getEnvironment()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 7

Importance

Changes 0
Metric Value
eloc 14
c 0
b 0
f 0
dl 0
loc 25
ccs 11
cts 11
cp 1
rs 8.8333
cc 7
nc 5
nop 1
crap 7
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
     * @throws \RuntimeException
63
     * @return \Phinx\Config\Config
64
     */
65
    public static function fromYaml($configFilePath)
66 448
    {
67
        if (!class_exists('Symfony\\Component\\Yaml\\Yaml', true)) {
68 448
            // @codeCoverageIgnoreStart
69 448
            throw new RuntimeException('Missing yaml parser, symfony/yaml package is not installed.');
70 448
            // @codeCoverageIgnoreEnd
71
        }
72
73
        $configFile = file_get_contents($configFilePath);
74
        $configArray = Yaml::parse($configFile);
75
76
        if (!is_array($configArray)) {
77
            throw new RuntimeException(sprintf(
78
                'File \'%s\' must be valid YAML',
79 2
                $configFilePath
80
            ));
81 2
        }
82 2
83
        return new static($configArray, $configFilePath);
84 2
    }
85 1
86 1
    /**
87
     * Create a new instance of the config class using a JSON file path.
88 1
     *
89
     * @param string $configFilePath Path to the JSON File
90 1
     * @throws \RuntimeException
91
     * @return \Phinx\Config\Config
92
     */
93
    public static function fromJson($configFilePath)
94
    {
95
        if (!function_exists('json_decode')) {
96
            // @codeCoverageIgnoreStart
97
            throw new RuntimeException('Need to install JSON PHP extension to use JSON config');
98
            // @codeCoverageIgnoreEnd
99
        }
100 2
101
        $configArray = json_decode(file_get_contents($configFilePath), true);
102 2
        if (!is_array($configArray)) {
103 2
            throw new RuntimeException(sprintf(
104 1
                'File \'%s\' must be valid JSON',
105 1
                $configFilePath
106
            ));
107 1
        }
108
109 1
        return new static($configArray, $configFilePath);
110
    }
111
112
    /**
113
     * Create a new instance of the config class using a PHP file path.
114
     *
115
     * @param string $configFilePath Path to the PHP File
116
     * @throws \RuntimeException
117
     * @return \Phinx\Config\Config
118
     */
119 3
    public static function fromPhp($configFilePath)
120
    {
121 3
        ob_start();
122
        /** @noinspection PhpIncludeInspection */
123 3
        $configArray = include $configFilePath;
124
125
        // Hide console output
126 3
        ob_end_clean();
127
128 3
        if (!is_array($configArray)) {
129 2
            throw new RuntimeException(sprintf(
130 2
                'PHP file \'%s\' must return an array',
131
                $configFilePath
132 2
            ));
133
        }
134
135 1
        return new static($configArray, $configFilePath);
136
    }
137
138
    /**
139
     * @inheritDoc
140
     */
141 25
    public function getEnvironments()
142
    {
143 25
        if (isset($this->values) && isset($this->values['environments'])) {
144 24
            $environments = [];
145 24
            foreach ($this->values['environments'] as $key => $value) {
146 24
                if (is_array($value)) {
147 24
                    $environments[$key] = $value;
148 24
                }
149 24
            }
150
151 24
            return $environments;
152
        }
153
154 1
        return null;
155
    }
156
157
    /**
158
     * @inheritDoc
159
     */
160 25
    public function getEnvironment($name)
161
    {
162 25
        $environments = $this->getEnvironments();
163
164 25
        if (isset($environments[$name])) {
165 21
            if (
166 21
                isset($this->values['environments']['default_migration_table'])
167 21
                && !isset($environments[$name]['migration_table'])
168 21
            ) {
169
                $environments[$name]['migration_table'] =
170 21
                    $this->values['environments']['default_migration_table'];
171
            }
172
173 5
            if (
174
                isset($environments[$name]['adapter'])
175
                && $environments[$name]['adapter'] === 'sqlite'
176
                && !empty($environments[$name]['memory'])
177
            ) {
178
                $environments[$name]['name'] = SQLiteAdapter::MEMORY;
179 9
            }
180
181 9
            return $this->parseAgnosticDsn($environments[$name]);
182
        }
183
184
        return null;
185
    }
186
187 20
    /**
188
     * @inheritDoc
189
     */
190 20
    public function hasEnvironment($name)
191 20
    {
192 2
        return $this->getEnvironment($name) !== null;
193 1
    }
194
195
    /**
196 1
     * @inheritDoc
197 1
     */
198
    public function getDefaultEnvironment()
199 1
    {
200
        // The $PHINX_ENVIRONMENT variable overrides all other default settings
201
        $env = getenv('PHINX_ENVIRONMENT');
202
        if (!empty($env)) {
203
            if ($this->hasEnvironment($env)) {
204 19
                return $env;
205 17
            }
206 16
207
            throw new RuntimeException(sprintf(
208
                'The environment configuration (read from $PHINX_ENVIRONMENT) for \'%s\' is missing',
209 1
                $env
210 1
            ));
211 1
        }
212 1
213
        // deprecated: to be removed 0.13
214
        if (isset($this->values['environments']['default_database'])) {
215
            trigger_error('default_database in the config has been deprecated since 0.12, use default_environment instead.', E_USER_DEPRECATED);
216 2
            $this->values['environments']['default_environment'] = $this->values['environments']['default_database'];
217 1
        }
218 1
219
        // if the user has configured a default environment then use it,
220
        // providing it actually exists!
221 1
        if (isset($this->values['environments']['default_environment'])) {
222
            if ($this->hasEnvironment($this->values['environments']['default_environment'])) {
223
                return $this->values['environments']['default_environment'];
224
            }
225
226
            throw new RuntimeException(sprintf(
227 7
                'The environment configuration for \'%s\' is missing',
228
                $this->values['environments']['default_environment']
229 7
            ));
230
        }
231
232
        // else default to the first available one
233
        if (is_array($this->getEnvironments()) && count($this->getEnvironments()) > 0) {
234
            $names = array_keys($this->getEnvironments());
235 448
236
            return $names[0];
237 448
        }
238
239
        throw new RuntimeException('Could not find a default environment');
240
    }
241
242
    /**
243 423
     * @inheritDoc
244
     */
245 423
    public function getAlias($alias)
246 1
    {
247
        return !empty($this->values['aliases'][$alias]) ? $this->values['aliases'][$alias] : null;
248
    }
249 422
250 219
    /**
251 219
     * @inheritDoc
252
     */
253 422
    public function getAliases()
254
    {
255
        return !empty($this->values['aliases']) ? $this->values['aliases'] : [];
256
    }
257
258
    /**
259
     * @inheritDoc
260
     */
261
    public function getConfigFilePath()
262 14
    {
263
        return $this->configFilePath;
264 14
    }
265
266 14
    /**
267
     * @inheritDoc
268
     * @throws \UnexpectedValueException
269
     */
270
    public function getMigrationPaths()
271
    {
272 48
        if (!isset($this->values['paths']['migrations'])) {
273
            throw new UnexpectedValueException('Migrations path missing from config file');
274 48
        }
275 28
276
        if (is_string($this->values['paths']['migrations'])) {
277
            $this->values['paths']['migrations'] = [$this->values['paths']['migrations']];
278 20
        }
279 13
280 13
        return $this->values['paths']['migrations'];
281
    }
282 20
283
    /**
284
     * Gets the base class name for migrations.
285
     *
286
     * @param bool $dropNamespace Return the base migration class name without the namespace.
287
     * @return string
288
     */
289
    public function getMigrationBaseClassName($dropNamespace = true)
290 14
    {
291
        $className = !isset($this->values['migration_base_class']) ? 'Phinx\Migration\AbstractMigration' : $this->values['migration_base_class'];
292 14
293 13
        return $dropNamespace ? (substr(strrchr($className, '\\'), 1) ?: $className) : $className;
294
    }
295
296 1
    /**
297
     * @inheritDoc
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
     * @return array
423
     */
424 1
    protected function replaceTokens(array $arr)
425
    {
426 1
        // Get environment variables
427 1
        // Depending on configuration of server / OS and variables_order directive,
428
        // environment variables either end up in $_SERVER (most likely) or $_ENV,
429
        // so we search through both
430
        $tokens = [];
431
        foreach (array_merge($_ENV, $_SERVER) as $varname => $varvalue) {
432
            if (strpos($varname, 'PHINX_') === 0) {
433
                $tokens['%%' . $varname . '%%'] = $varvalue;
434
            }
435
        }
436
437
        // Phinx defined tokens (override env tokens)
438
        $tokens['%%PHINX_CONFIG_PATH%%'] = $this->getConfigFilePath();
439
        $tokens['%%PHINX_CONFIG_DIR%%'] = dirname($this->getConfigFilePath());
440
441
        // Recurse the array and replace tokens
442
        return $this->recurseArrayForTokens($arr, $tokens);
443
    }
444
445
    /**
446
     * Recurse an array for the specified tokens and replace them.
447
     *
448
     * @param array $arr Array to recurse
449
     * @param string[] $tokens Array of tokens to search for
450
     * @return array
451
     */
452
    protected function recurseArrayForTokens($arr, $tokens)
453
    {
454
        $out = [];
455
        foreach ($arr as $name => $value) {
456
            if (is_array($value)) {
457
                $out[$name] = $this->recurseArrayForTokens($value, $tokens);
458
                continue;
459
            }
460
            if (is_string($value)) {
461
                foreach ($tokens as $token => $tval) {
462
                    $value = str_replace($token, $tval, $value);
463
                }
464
                $out[$name] = $value;
465
                continue;
466
            }
467
            $out[$name] = $value;
468
        }
469
470
        return $out;
471
    }
472
473
    /**
474
     * Parse a database-agnostic DSN into individual options.
475
     *
476
     * @param array $options Options
477
     * @return array
478
     */
479
    protected function parseAgnosticDsn(array $options)
480
    {
481
        $parsed = Util::parseDsn($options['dsn'] ?? '');
482
        if ($parsed) {
483
            unset($options['dsn']);
484
        }
485
486
        $options = array_merge($parsed, $options);
487
488
        return $options;
489
    }
490
491
    /**
492
     * {@inheritDoc}
493
     *
494
     * @param mixed $id ID
495
     * @param mixed $value Value
496
     * @return void
497
     */
498
    public function offsetSet($id, $value)
499
    {
500
        $this->values[$id] = $value;
501
    }
502
503
    /**
504
     * {@inheritDoc}
505
     *
506
     * @param mixed $id ID
507
     * @throws \InvalidArgumentException
508
     * @return mixed
509
     */
510
    public function offsetGet($id)
511
    {
512
        if (!array_key_exists($id, $this->values)) {
513
            throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
514
        }
515
516
        return $this->values[$id] instanceof Closure ? $this->values[$id]($this) : $this->values[$id];
517
    }
518
519
    /**
520
     * {@inheritDoc}
521
     *
522
     * @param mixed $id ID
523
     * @return bool
524
     */
525
    public function offsetExists($id)
526
    {
527
        return isset($this->values[$id]);
528
    }
529
530
    /**
531
     * {@inheritDoc}
532
     *
533
     * @param mixed $id ID
534
     * @return void
535
     */
536
    public function offsetUnset($id)
537
    {
538
        unset($this->values[$id]);
539
    }
540
}
541