Config::getAlias()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
c 0
b 0
f 0
ccs 1
cts 1
cp 1
rs 10
cc 2
nc 2
nop 1
crap 2
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|null
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['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 (isset($this->values['environments']['default_migration_table'])) {
166 21
                $environments[$name]['default_migration_table'] =
167 21
                    $this->values['environments']['default_migration_table'];
168 21
            }
169
170 21
            if (
171
                isset($environments[$name]['adapter'])
172
                && $environments[$name]['adapter'] === 'sqlite'
173 5
                && !empty($environments[$name]['memory'])
174
            ) {
175
                $environments[$name]['name'] = SQLiteAdapter::MEMORY;
176
            }
177
178
            return $this->parseAgnosticDsn($environments[$name]);
179 9
        }
180
181 9
        return null;
182
    }
183
184
    /**
185
     * @inheritDoc
186
     */
187 20
    public function hasEnvironment($name)
188
    {
189
        return $this->getEnvironment($name) !== null;
190 20
    }
191 20
192 2
    /**
193 1
     * @inheritDoc
194
     */
195
    public function getDefaultEnvironment()
196 1
    {
197 1
        // The $PHINX_ENVIRONMENT variable overrides all other default settings
198
        $env = getenv('PHINX_ENVIRONMENT');
199 1
        if (!empty($env)) {
200
            if ($this->hasEnvironment($env)) {
201
                return $env;
202
            }
203
204 19
            throw new RuntimeException(sprintf(
205 17
                'The environment configuration (read from $PHINX_ENVIRONMENT) for \'%s\' is missing',
206 16
                $env
207
            ));
208
        }
209 1
210 1
        // deprecated: to be removed 0.13
211 1
        if (isset($this->values['environments']['default_database'])) {
212 1
            $this->values['environments']['default_environment'] = $this->values['environments']['default_database'];
213
        }
214
215
        // if the user has configured a default environment then use it,
216 2
        // providing it actually exists!
217 1
        if (isset($this->values['environments']['default_environment'])) {
218 1
            if ($this->hasEnvironment($this->values['environments']['default_environment'])) {
219
                return $this->values['environments']['default_environment'];
220
            }
221 1
222
            throw new RuntimeException(sprintf(
223
                'The environment configuration for \'%s\' is missing',
224
                $this->values['environments']['default_environment']
225
            ));
226
        }
227 7
228
        // else default to the first available one
229 7
        if (is_array($this->getEnvironments()) && count($this->getEnvironments()) > 0) {
230
            $names = array_keys($this->getEnvironments());
231
232
            return $names[0];
233
        }
234
235 448
        throw new RuntimeException('Could not find a default environment');
236
    }
237 448
238
    /**
239
     * @inheritDoc
240
     */
241
    public function getAlias($alias)
242
    {
243 423
        return !empty($this->values['aliases'][$alias]) ? $this->values['aliases'][$alias] : null;
244
    }
245 423
246 1
    /**
247
     * @inheritDoc
248
     */
249 422
    public function getAliases()
250 219
    {
251 219
        return !empty($this->values['aliases']) ? $this->values['aliases'] : [];
252
    }
253 422
254
    /**
255
     * @inheritDoc
256
     */
257
    public function getConfigFilePath()
258
    {
259
        return $this->configFilePath;
260
    }
261
262 14
    /**
263
     * @inheritDoc
264 14
     * @throws \UnexpectedValueException
265
     */
266 14
    public function getMigrationPaths()
267
    {
268
        if (!isset($this->values['paths']['migrations'])) {
269
            throw new UnexpectedValueException('Migrations path missing from config file');
270
        }
271
272 48
        if (is_string($this->values['paths']['migrations'])) {
273
            $this->values['paths']['migrations'] = [$this->values['paths']['migrations']];
274 48
        }
275 28
276
        return $this->values['paths']['migrations'];
277
    }
278 20
279 13
    /**
280 13
     * Gets the base class name for migrations.
281
     *
282 20
     * @param bool $dropNamespace Return the base migration class name without the namespace.
283
     * @return string
284
     */
285
    public function getMigrationBaseClassName($dropNamespace = true)
286
    {
287
        $className = !isset($this->values['migration_base_class']) ? 'Phinx\Migration\AbstractMigration' : $this->values['migration_base_class'];
288
289
        return $dropNamespace ? (substr(strrchr($className, '\\'), 1) ?: $className) : $className;
290 14
    }
291
292 14
    /**
293 13
     * @inheritDoc
294
     * @throws \UnexpectedValueException
295
     */
296 1
    public function getSeedPaths()
297
    {
298
        if (!isset($this->values['paths']['seeds'])) {
299
            throw new UnexpectedValueException('Seeds path missing from config file');
300
        }
301
302
        if (is_string($this->values['paths']['seeds'])) {
303
            $this->values['paths']['seeds'] = [$this->values['paths']['seeds']];
304 14
        }
305
306 14
        return $this->values['paths']['seeds'];
307 10
    }
308
309
    /**
310 4
     * Gets the base class name for seeders.
311
     *
312
     * @param bool $dropNamespace Return the base seeder class name without the namespace.
313
     * @return string
314
     */
315
    public function getSeedBaseClassName($dropNamespace = true)
316
    {
317
        $className = !isset($this->values['seed_base_class']) ? 'Phinx\Seed\AbstractSeed' : $this->values['seed_base_class'];
318 384
319
        return $dropNamespace ? substr(strrchr($className, '\\'), 1) : $className;
320 384
    }
321 162
322
    /**
323
     * Get the template file name.
324 222
     *
325
     * @return string|false
326
     */
327
    public function getTemplateFile()
328
    {
329
        if (!isset($this->values['templates']['file'])) {
330
            return false;
331
        }
332 357
333
        return $this->values['templates']['file'];
334 357
    }
335
336 357
    /**
337
     * Get the template class name.
338
     *
339
     * @return string|false
340
     */
341
    public function getTemplateClass()
342
    {
343
        if (!isset($this->values['templates']['class'])) {
344
            return false;
345
        }
346
347 448
        return $this->values['templates']['class'];
348
    }
349
350
    /**
351 448
     * {@inheritdoc}
352 448
     */
353 448
    public function getDataDomain()
354 2
    {
355 2
        if (!isset($this->values['data_domain'])) {
356 448
            return [];
357
        }
358
359 448
        return $this->values['data_domain'];
360 448
    }
361
362
    /**
363 448
     * @inheritDoc
364
     */
365
    public function getContainer()
366
    {
367
        if (!isset($this->values['container'])) {
368
            return null;
369
        }
370
371
        return $this->values['container'];
372
    }
373 448
374
    /**
375 448
     * Get the version order.
376 448
     *
377 447
     * @return string
378 446
     */
379 446
    public function getVersionOrder()
380
    {
381 446
        if (!isset($this->values['version_order'])) {
382 446
            return self::VERSION_ORDER_CREATION_TIME;
383 446
        }
384 446
385 446
        return $this->values['version_order'];
386 446
    }
387
388 43
    /**
389 448
     * Is version order creation time?
390 448
     *
391
     * @return bool
392
     */
393
    public function isVersionOrderCreationTime()
394
    {
395
        $versionOrder = $this->getVersionOrder();
396 213
397
        return $versionOrder == self::VERSION_ORDER_CREATION_TIME;
398 213
    }
399 213
400
    /**
401
     * Get the bootstrap file path
402
     *
403
     * @return string|false
404 2
     */
405
    public function getBootstrapFile()
406 2
    {
407 1
        if (!isset($this->values['paths']['bootstrap'])) {
408
            return false;
409
        }
410 1
411
        return $this->values['paths']['bootstrap'];
412
    }
413
414
    /**
415
     * Replace tokens in the specified array.
416 1
     *
417
     * @param array $arr Array to replace
418 1
     * @return array
419
     */
420
    protected function replaceTokens(array $arr)
421
    {
422
        // Get environment variables
423
        // Depending on configuration of server / OS and variables_order directive,
424 1
        // environment variables either end up in $_SERVER (most likely) or $_ENV,
425
        // so we search through both
426 1
        $tokens = [];
427 1
        foreach (array_merge($_ENV, $_SERVER) as $varname => $varvalue) {
428
            if (strpos($varname, 'PHINX_') === 0) {
429
                $tokens['%%' . $varname . '%%'] = $varvalue;
430
            }
431
        }
432
433
        // Phinx defined tokens (override env tokens)
434
        $tokens['%%PHINX_CONFIG_PATH%%'] = $this->getConfigFilePath();
435
        $tokens['%%PHINX_CONFIG_DIR%%'] = $this->getConfigFilePath() !== null ? dirname($this->getConfigFilePath()) : '';
436
437
        // Recurse the array and replace tokens
438
        return $this->recurseArrayForTokens($arr, $tokens);
439
    }
440
441
    /**
442
     * Recurse an array for the specified tokens and replace them.
443
     *
444
     * @param array $arr Array to recurse
445
     * @param string[] $tokens Array of tokens to search for
446
     * @return array
447
     */
448
    protected function recurseArrayForTokens($arr, $tokens)
449
    {
450
        $out = [];
451
        foreach ($arr as $name => $value) {
452
            if (is_array($value)) {
453
                $out[$name] = $this->recurseArrayForTokens($value, $tokens);
454
                continue;
455
            }
456
            if (is_string($value)) {
457
                foreach ($tokens as $token => $tval) {
458
                    $value = str_replace($token, $tval ?? '', $value);
459
                }
460
                $out[$name] = $value;
461
                continue;
462
            }
463
            $out[$name] = $value;
464
        }
465
466
        return $out;
467
    }
468
469
    /**
470
     * Parse a database-agnostic DSN into individual options.
471
     *
472
     * @param array $options Options
473
     * @return array
474
     */
475
    protected function parseAgnosticDsn(array $options)
476
    {
477
        $parsed = Util::parseDsn($options['dsn'] ?? '');
478
        if ($parsed) {
479
            unset($options['dsn']);
480
        }
481
482
        $options = array_merge($parsed, $options);
483
484
        return $options;
485
    }
486
487
    /**
488
     * {@inheritDoc}
489
     *
490
     * @param mixed $id ID
491
     * @param mixed $value Value
492
     * @return void
493
     */
494
    #[\ReturnTypeWillChange]
495
    public function offsetSet($id, $value)
496
    {
497
        $this->values[$id] = $value;
498
    }
499
500
    /**
501
     * {@inheritDoc}
502
     *
503
     * @param mixed $id ID
504
     * @throws \InvalidArgumentException
505
     * @return mixed
506
     */
507
    #[\ReturnTypeWillChange]
508
    public function offsetGet($id)
509
    {
510
        if (!array_key_exists($id, $this->values)) {
511
            throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
512
        }
513
514
        return $this->values[$id] instanceof Closure ? $this->values[$id]($this) : $this->values[$id];
515
    }
516
517
    /**
518
     * {@inheritDoc}
519
     *
520
     * @param mixed $id ID
521
     * @return bool
522
     */
523
    #[\ReturnTypeWillChange]
524
    public function offsetExists($id)
525
    {
526
        return isset($this->values[$id]);
527
    }
528
529
    /**
530
     * {@inheritDoc}
531
     *
532
     * @param mixed $id ID
533
     * @return void
534
     */
535
    #[\ReturnTypeWillChange]
536
    public function offsetUnset($id)
537
    {
538
        unset($this->values[$id]);
539
    }
540
}
541