Completed
Pull Request — master (#1512)
by Elan
02:08
created

Config::getSeedBaseClassName()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 3

Importance

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