Completed
Pull Request — master (#1320)
by
unknown
01:34
created

Config::getDataDomain()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 1
cts 1
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
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 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
    const VERSION_ORDER_CREATION_TIME = 'creation';
30
31
    /**
32
     * The value that identifies a version order by execution time.
33
     */
34
    const VERSION_ORDER_EXECUTION_TIME = 'execution';
35
36
    /**
37
     * @var array
38
     */
39
    private $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 View Code Duplication
    public static function fromYaml($configFilePath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
65
    {
66 448
        $configFile = file_get_contents($configFilePath);
67
        $configArray = Yaml::parse($configFile);
68 448
69 448
        if (!is_array($configArray)) {
70 448
            throw new RuntimeException(sprintf(
71
                'File \'%s\' must be valid YAML',
72
                $configFilePath
73
            ));
74
        }
75
76
        return new static($configArray, $configFilePath);
77
    }
78
79 2
    /**
80
     * Create a new instance of the config class using a JSON file path.
81 2
     *
82 2
     * @param string $configFilePath Path to the JSON File
83
     *
84 2
     * @throws \RuntimeException
85 1
     *
86 1
     * @return \Phinx\Config\Config
87
     */
88 1
    public static function fromJson($configFilePath)
89
    {
90 1
        if (!function_exists('json_decode')) {
91
            throw new RuntimeException("Need to install JSON PHP extension to use JSON config");
92
        }
93
94
        $configArray = json_decode(file_get_contents($configFilePath), true);
95
        if (!is_array($configArray)) {
96
            throw new RuntimeException(sprintf(
97
                'File \'%s\' must be valid JSON',
98
                $configFilePath
99
            ));
100 2
        }
101
102 2
        return new static($configArray, $configFilePath);
103 2
    }
104 1
105 1
    /**
106
     * Create a new instance of the config class using a PHP file path.
107 1
     *
108
     * @param string $configFilePath Path to the PHP File
109 1
     *
110
     * @throws \RuntimeException
111
     *
112
     * @return \Phinx\Config\Config
113
     */
114 View Code Duplication
    public static function fromPhp($configFilePath)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
115
    {
116
        ob_start();
117
        /** @noinspection PhpIncludeInspection */
118
        $configArray = include($configFilePath);
119 3
120
        // Hide console output
121 3
        ob_end_clean();
122
123 3
        if (!is_array($configArray)) {
124
            throw new RuntimeException(sprintf(
125
                'PHP file \'%s\' must return an array',
126 3
                $configFilePath
127
            ));
128 3
        }
129 2
130 2
        return new static($configArray, $configFilePath);
131
    }
132 2
133
    /**
134
     * @inheritDoc
135 1
     */
136
    public function getEnvironments()
137
    {
138
        if (isset($this->values) && isset($this->values['environments'])) {
139
            $environments = [];
140
            foreach ($this->values['environments'] as $key => $value) {
141 25
                if (is_array($value)) {
142
                    $environments[$key] = $value;
143 25
                }
144 24
            }
145 24
146 24
            return $environments;
147 24
        }
148 24
149 24
        return null;
150
    }
151 24
152
    /**
153
     * @inheritDoc
154 1
     */
155
    public function getEnvironment($name)
156
    {
157
        $environments = $this->getEnvironments();
158
159
        if (isset($environments[$name])) {
160 25
            if (isset($this->values['environments']['default_migration_table'])) {
161
                $environments[$name]['default_migration_table'] =
162 25
                    $this->values['environments']['default_migration_table'];
163
            }
164 25
165 21
            return $this->parseAgnosticDsn($environments[$name]);
166 21
        }
167 21
168 21
        return null;
169
    }
170 21
171
    /**
172
     * @inheritDoc
173 5
     */
174
    public function hasEnvironment($name)
175
    {
176
        return ($this->getEnvironment($name) !== null);
177
    }
178
179 9
    /**
180
     * @inheritDoc
181 9
     */
182
    public function getDefaultEnvironment()
183
    {
184
        // The $PHINX_ENVIRONMENT variable overrides all other default settings
185
        $env = getenv('PHINX_ENVIRONMENT');
186
        if (!empty($env)) {
187 20
            if ($this->hasEnvironment($env)) {
188
                return $env;
189
            }
190 20
191 20
            throw new RuntimeException(sprintf(
192 2
                'The environment configuration (read from $PHINX_ENVIRONMENT) for \'%s\' is missing',
193 1
                $env
194
            ));
195
        }
196 1
197 1
        // if the user has configured a default database then use it,
198
        // providing it actually exists!
199 1
        if (isset($this->values['environments']['default_database'])) {
200
            if ($this->getEnvironment($this->values['environments']['default_database'])) {
201
                return $this->values['environments']['default_database'];
202
            }
203
204 19
            throw new RuntimeException(sprintf(
205 17
                'The environment configuration for \'%s\' is missing',
206 16
                $this->values['environments']['default_database']
207
            ));
208
        }
209 1
210 1
        // else default to the first available one
211 1
        if (is_array($this->getEnvironments()) && count($this->getEnvironments()) > 0) {
212 1
            $names = array_keys($this->getEnvironments());
213
214
            return $names[0];
215
        }
216 2
217 1
        throw new RuntimeException('Could not find a default environment');
218 1
    }
219
220
    /**
221 1
     * @inheritDoc
222
     */
223
    public function getAlias($alias)
224
    {
225
        return !empty($this->values['aliases'][$alias]) ? $this->values['aliases'][$alias] : null;
226
    }
227 7
228
    /**
229 7
     * @inheritDoc
230
     */
231
    public function getAliases()
232
    {
233
        return !empty($this->values['aliases']) ? $this->values['aliases'] : [];
234
    }
235 448
236
    /**
237 448
     * @inheritDoc
238
     */
239
    public function getConfigFilePath()
240
    {
241
        return $this->configFilePath;
242
    }
243 423
244
    /**
245 423
     * @inheritDoc
246 1
     *
247
     * @throws \UnexpectedValueException
248
     */
249 422 View Code Duplication
    public function getMigrationPaths()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
250 219
    {
251 219
        if (!isset($this->values['paths']['migrations'])) {
252
            throw new UnexpectedValueException('Migrations path missing from config file');
253 422
        }
254
255
        if (is_string($this->values['paths']['migrations'])) {
256
            $this->values['paths']['migrations'] = [$this->values['paths']['migrations']];
257
        }
258
259
        return $this->values['paths']['migrations'];
260
    }
261
262 14
    /**
263
     * Gets the base class name for migrations.
264 14
     *
265
     * @param bool $dropNamespace Return the base migration class name without the namespace.
266 14
     *
267
     * @return string
268
     */
269
    public function getMigrationBaseClassName($dropNamespace = true)
270
    {
271
        $className = !isset($this->values['migration_base_class']) ? 'Phinx\Migration\AbstractMigration' : $this->values['migration_base_class'];
272 48
273
        return $dropNamespace ? substr(strrchr($className, '\\'), 1) ?: $className : $className;
274 48
    }
275 28
276
    /**
277
     * @inheritDoc
278 20
     *
279 13
     * @throws \UnexpectedValueException
280 13
     */
281 View Code Duplication
    public function getSeedPaths()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
282 20
    {
283
        if (!isset($this->values['paths']['seeds'])) {
284
            throw new UnexpectedValueException('Seeds path missing from config file');
285
        }
286
287
        if (is_string($this->values['paths']['seeds'])) {
288
            $this->values['paths']['seeds'] = [$this->values['paths']['seeds']];
289
        }
290 14
291
        return $this->values['paths']['seeds'];
292 14
    }
293 13
294
    /**
295
     * Get the template file name.
296 1
     *
297
     * @return string|false
298
     */
299 View Code Duplication
    public function getTemplateFile()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
300
    {
301
        if (!isset($this->values['templates']['file'])) {
302
            return false;
303
        }
304 14
305
        return $this->values['templates']['file'];
306 14
    }
307 10
308
    /**
309
     * Get the template class name.
310 4
     *
311
     * @return string|false
312
     */
313 View Code Duplication
    public function getTemplateClass()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
314
    {
315
        if (!isset($this->values['templates']['class'])) {
316
            return false;
317
        }
318 384
319
        return $this->values['templates']['class'];
320 384
    }
321 162
322
    /**
323
     * {@inheritdoc}
324 222
     */
325
    public function getDataDomain()
326
    {
327
        if (!isset($this->values['data_domain'])) {
328
            return [];
329
        }
330
331
        return $this->values['data_domain'];
332 357
    }
333
334 357
    /**
335
     * Get the version order.
336 357
     *
337
     * @return string
338
     */
339
    public function getVersionOrder()
340
    {
341
        if (!isset($this->values['version_order'])) {
342
            return self::VERSION_ORDER_CREATION_TIME;
343
        }
344
345
        return $this->values['version_order'];
346
    }
347 448
348
    /**
349
     * Is version order creation time?
350
     *
351 448
     * @return bool
352 448
     */
353 448
    public function isVersionOrderCreationTime()
354 2
    {
355 2
        $versionOrder = $this->getVersionOrder();
356 448
357
        return $versionOrder == self::VERSION_ORDER_CREATION_TIME;
358
    }
359 448
360 448
    /**
361
     * Get the bootstrap file path
362
     *
363 448
     * @return string|false
364
     */
365
    public function getBootstrapFile()
366
    {
367
        if (!isset($this->values['paths']['bootstrap'])) {
368
            return false;
369
        }
370
371
        return $this->values['paths']['bootstrap'];
372
    }
373 448
374
    /**
375 448
     * Replace tokens in the specified array.
376 448
     *
377 447
     * @param array $arr Array to replace
378 446
     *
379 446
     * @return array
380
     */
381 446
    protected function replaceTokens(array $arr)
382 446
    {
383 446
        // Get environment variables
384 446
        // Depending on configuration of server / OS and variables_order directive,
385 446
        // environment variables either end up in $_SERVER (most likely) or $_ENV,
386 446
        // so we search through both
387
        $tokens = [];
388 43
        foreach (array_merge($_ENV, $_SERVER) as $varname => $varvalue) {
389 448
            if (strpos($varname, 'PHINX_') === 0) {
390 448
                $tokens['%%' . $varname . '%%'] = $varvalue;
391
            }
392
        }
393
394
        // Phinx defined tokens (override env tokens)
395
        $tokens['%%PHINX_CONFIG_PATH%%'] = $this->getConfigFilePath();
396 213
        $tokens['%%PHINX_CONFIG_DIR%%'] = dirname($this->getConfigFilePath());
397
398 213
        // Recurse the array and replace tokens
399 213
        return $this->recurseArrayForTokens($arr, $tokens);
400
    }
401
402
    /**
403
     * Recurse an array for the specified tokens and replace them.
404 2
     *
405
     * @param array $arr Array to recurse
406 2
     * @param array $tokens Array of tokens to search for
407 1
     *
408
     * @return array
409
     */
410 1
    protected function recurseArrayForTokens($arr, $tokens)
411
    {
412
        $out = [];
413
        foreach ($arr as $name => $value) {
414
            if (is_array($value)) {
415
                $out[$name] = $this->recurseArrayForTokens($value, $tokens);
416 1
                continue;
417
            }
418 1
            if (is_string($value)) {
419
                foreach ($tokens as $token => $tval) {
420
                    $value = str_replace($token, $tval, $value);
421
                }
422
                $out[$name] = $value;
423
                continue;
424 1
            }
425
            $out[$name] = $value;
426 1
        }
427 1
428
        return $out;
429
    }
430
431
    /**
432
     * Parse a database-agnostic DSN into individual options.
433
     *
434
     * @param array $options Options
435
     *
436
     * @return array
437
     */
438
    protected function parseAgnosticDsn(array $options)
439
    {
440
        if (isset($options['dsn']) && is_string($options['dsn'])) {
441
            $regex = '#^(?P<adapter>[^\\:]+)\\://(?:(?P<user>[^\\:@]+)(?:\\:(?P<pass>[^@]*))?@)?'
442
                   . '(?P<host>[^\\:@/]+)(?:\\:(?P<port>[1-9]\\d*))?/(?P<name>[^\?]+)(?:\?(?P<query>.*))?$#';
443
            if (preg_match($regex, trim($options['dsn']), $parsedOptions)) {
444
                $additionalOpts = [];
445
                if (isset($parsedOptions['query'])) {
446
                    parse_str($parsedOptions['query'], $additionalOpts);
447
                }
448
                $validOptions = ['adapter', 'user', 'pass', 'host', 'port', 'name'];
449
                $parsedOptions = array_filter(array_intersect_key($parsedOptions, array_flip($validOptions)));
450
                $options = array_merge($additionalOpts, $parsedOptions, $options);
451
                unset($options['dsn']);
452
            }
453
        }
454
455
        return $options;
456
    }
457
458
    /**
459
     * @inheritDoc
460
     */
461
    public function offsetSet($id, $value)
462
    {
463
        $this->values[$id] = $value;
464
    }
465
466
    /**
467
     * @inheritDoc
468
     *
469
     * @throws \InvalidArgumentException
470
     */
471
    public function offsetGet($id)
472
    {
473
        if (!array_key_exists($id, $this->values)) {
474
            throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
475
        }
476
477
        return $this->values[$id] instanceof Closure ? $this->values[$id]($this) : $this->values[$id];
478
    }
479
480
    /**
481
     * @inheritDoc
482
     */
483
    public function offsetExists($id)
484
    {
485
        return isset($this->values[$id]);
486
    }
487
488
    /**
489
     * @inheritDoc
490
     */
491
    public function offsetUnset($id)
492
    {
493
        unset($this->values[$id]);
494
    }
495
}
496