Completed
Push — master ( 529697...360c85 )
by José
10:58 queued 09:33
created

Environment::parseAgnosticDsn()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 3
cts 3
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 13
nc 4
nop 1
crap 5
1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Migration\Manager
28
 */
29
namespace Phinx\Migration\Manager;
30
31
use Phinx\Db\Adapter\AdapterFactory;
32
use Phinx\Db\Adapter\AdapterInterface;
33
use Phinx\Migration\MigrationInterface;
34
use Phinx\Seed\SeedInterface;
35
use Symfony\Component\Console\Input\InputInterface;
36
use Symfony\Component\Console\Output\OutputInterface;
37
38
class Environment
39
{
40
    /**
41
     * @var string
42
     */
43
    protected $name;
44
45
    /**
46
     * @var array
47
     */
48
    protected $options;
49
50
    /**
51
     * @var \Symfony\Component\Console\Input\InputInterface
52
     */
53
    protected $input;
54
55
    /**
56
     * @var \Symfony\Component\Console\Output\OutputInterface
57
     */
58
    protected $output;
59
60
    /**
61
     * @var int
62
     */
63
    protected $currentVersion;
64
65
    /**
66
     * @var string
67
     */
68
    protected $schemaTableName = 'phinxlog';
69
70
    /**
71
     * @var \Phinx\Db\Adapter\AdapterInterface
72
     */
73
    protected $adapter;
74
75
    /**
76
     * Class Constructor.
77
     *
78
     * @param string $name Environment Name
79
     * @param array $options Options
80
     */
81
    public function __construct($name, $options)
82 404
    {
83
        $this->name = $name;
84 404
        $this->options = $options;
85 404
    }
86 404
87
    /**
88
     * Executes the specified migration on this environment.
89
     *
90
     * @param \Phinx\Migration\MigrationInterface $migration Migration
91
     * @param string $direction Direction
92
     * @param bool $fake flag that if true, we just record running the migration, but not actually do the migration
93
     * @return void
94
     */
95 10
    public function executeMigration(MigrationInterface $migration, $direction = MigrationInterface::UP, $fake = false)
96
    {
97 10
        $direction = ($direction === MigrationInterface::UP) ? MigrationInterface::UP : MigrationInterface::DOWN;
98 10
        $migration->setMigratingUp($direction === MigrationInterface::UP);
99
100 10
        $startTime = time();
101 10
        $migration->setAdapter($this->getAdapter());
102
103
        if (!$fake) {
104 10
            // begin the transaction if the adapter supports it
105 6
            if ($this->getAdapter()->hasTransactions()) {
106 6
                $this->getAdapter()->beginTransaction();
107
            }
108
109 10
            // Run the migration
110 5
            if (method_exists($migration, MigrationInterface::CHANGE)) {
111
                if ($direction === MigrationInterface::DOWN) {
112
                    // Create an instance of the ProxyAdapter so we can record all
113
                    // of the migration commands for reverse playback
114
115 4
                    /** @var \Phinx\Db\Adapter\ProxyAdapter $proxyAdapter */
116 4
                    $proxyAdapter = AdapterFactory::instance()
117 4
                        ->getWrapper('proxy', $this->getAdapter());
118
                    $migration->setAdapter($proxyAdapter);
119 4
                    /** @noinspection PhpUndefinedMethodInspection */
120 4
                    $migration->change();
0 ignored issues
show
Bug introduced by
The method change() does not seem to exist on object<Phinx\Migration\MigrationInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
121 4
                    $proxyAdapter->executeInvertedCommands();
122 4
                    $migration->setAdapter($this->getAdapter());
123
                } else {
124 4
                    /** @noinspection PhpUndefinedMethodInspection */
125
                    $migration->change();
0 ignored issues
show
Bug introduced by
The method change() does not seem to exist on object<Phinx\Migration\MigrationInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
126 5
                }
127 5
            } else {
128
                $migration->{$direction}();
129
            }
130
131 10
            // commit the transaction if the adapter supports it
132 6
            if ($this->getAdapter()->hasTransactions()) {
133 6
                $this->getAdapter()->commitTransaction();
134
            }
135
        }
136 10
137 10
        // Record it in the database
138
        $this->getAdapter()->migrated($migration, $direction, date('Y-m-d H:i:s', $startTime), date('Y-m-d H:i:s', time()));
139
    }
140
141
    /**
142
     * Executes the specified seeder on this environment.
143
     *
144
     * @param \Phinx\Seed\SeedInterface $seed
145
     * @return void
146
     */
147
    public function executeSeed(SeedInterface $seed)
148
    {
149
        $seed->setAdapter($this->getAdapter());
150
151
        // begin the transaction if the adapter supports it
152
        if ($this->getAdapter()->hasTransactions()) {
153
            $this->getAdapter()->beginTransaction();
154
        }
155
156
        // Run the seeder
157
        if (method_exists($seed, SeedInterface::RUN)) {
158
            $seed->run();
159
        }
160
161
        // commit the transaction if the adapter supports it
162
        if ($this->getAdapter()->hasTransactions()) {
163
            $this->getAdapter()->commitTransaction();
164
        }
165
    }
166
167
    /**
168
     * Sets the environment's name.
169
     *
170
     * @param string $name Environment Name
171 1
     * @return \Phinx\Migration\Manager\Environment
172
     */
173 1
    public function setName($name)
174 1
    {
175
        $this->name = $name;
176
177
        return $this;
178
    }
179
180
    /**
181
     * Gets the environment name.
182 3
     *
183
     * @return string
184 3
     */
185
    public function getName()
186
    {
187
        return $this->name;
188
    }
189
190
    /**
191
     * Sets the environment's options.
192
     *
193 6
     * @param array $options Environment Options
194
     * @return \Phinx\Migration\Manager\Environment
195 6
     */
196 6
    public function setOptions($options)
197
    {
198
        $this->options = $options;
199
200
        return $this;
201
    }
202
203
    /**
204 2
     * Gets the environment's options.
205
     *
206 2
     * @return array
207
     */
208
    public function getOptions()
209
    {
210
        return $this->parseAgnosticDsn($this->options);
211
    }
212
213
    /**
214
     * Parse a database-agnostic DSN into individual options.
215 7
     *
216
     * @param array $options Options
217 7
     * @return array
218 7
     */
219
    protected function parseAgnosticDsn(array $options)
220
    {
221
        if (isset($options['dsn']) && is_string($options['dsn'])) {
222
            $regex = '#^(?P<adapter>[^\\:]+)\\://(?:(?P<user>[^\\:@]+)(?:\\:(?P<pass>[^@]*))?@)?'
223
                   . '(?P<host>[^\\:@/]+)(?:\\:(?P<port>[1-9]\\d*))?/(?P<name>[^\?]+)(?:\?(?P<query>.*))?$#';
224
            if (preg_match($regex, trim($options['dsn']), $parsedOptions)) {
225
                $additionalOpts = [];
226 9
                if (isset($parsedOptions['query'])) {
227
                    parse_str($parsedOptions['query'], $additionalOpts);
228 9
                }
229
                $validOptions = ['adapter', 'user', 'pass', 'host', 'port', 'name'];
230
                $parsedOptions = array_filter(array_intersect_key($parsedOptions, array_flip($validOptions)));
231
                $options = array_merge($additionalOpts, $parsedOptions, $options);
232
                unset($options['dsn']);
233
            }
234
        }
235
236
        return $options;
237 6
    }
238
239 6
    /**
240 6
     * Sets the console input.
241
     *
242
     * @param \Symfony\Component\Console\Input\InputInterface $input
243
     * @return \Phinx\Migration\Manager\Environment
244
     */
245
    public function setInput(InputInterface $input)
246
    {
247
        $this->input = $input;
248 8
249
        return $this;
250 8
    }
251
252
    /**
253
     * Gets the console input.
254
     *
255
     * @return \Symfony\Component\Console\Input\InputInterface
256
     */
257
    public function getInput()
258 6
    {
259
        return $this->input;
260 6
    }
261
262
    /**
263
     * Sets the console output.
264
     *
265
     * @param \Symfony\Component\Console\Output\OutputInterface $output Output
266
     * @return \Phinx\Migration\Manager\Environment
267
     */
268
    public function setOutput(OutputInterface $output)
269 5
    {
270
        $this->output = $output;
271 5
272
        return $this;
273
    }
274
275
    /**
276
     * Gets the console output.
277
     *
278
     * @return \Symfony\Component\Console\Output\OutputInterface
279
     */
280 6
    public function getOutput()
281
    {
282 6
        return $this->output;
283 6
    }
284
285
    /**
286
     * Gets all migrated version numbers.
287
     *
288
     * @return array
289
     */
290
    public function getVersions()
291 6
    {
292
        return $this->getAdapter()->getVersions();
293
    }
294
295
    /**
296 6
     * Get all migration log entries, indexed by version creation time and sorted ascendingly by the configuration's
297 6
     * version_order option
298
     *
299 6
     * @return array
300 1
     */
301 1
    public function getVersionLog()
302
    {
303 6
        return $this->getAdapter()->getVersionLog();
304 6
    }
305
306
    /**
307
     * Sets the current version of the environment.
308
     *
309
     * @param int $version Environment Version
310
     * @return \Phinx\Migration\Manager\Environment
311
     */
312
    public function setCurrentVersion($version)
313 14
    {
314
        $this->currentVersion = $version;
315 14
316 14
        return $this;
317
    }
318
319
    /**
320
     * Gets the current version of the environment.
321
     *
322
     * @return int
323
     */
324 17
    public function getCurrentVersion()
325
    {
326 17
        // We don't cache this code as the current version is pretty volatile.
327 12
        // TODO - that means they're no point in a setter then?
328
        // maybe we should cache and call a reset() method everytime a migration is run
329 11
        $versions = $this->getVersions();
330 3
        $version = 0;
331 1
332
        if (!empty($versions)) {
333
            $version = end($versions);
334 2
        }
335 2
336 2
        $this->setCurrentVersion($version);
337 10
338 1
        return $this->currentVersion;
339
    }
340
341 9
    /**
342
     * Sets the database adapter.
343 9
     *
344
     * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter
345
     * @return \Phinx\Migration\Manager\Environment
346 8
     */
347
    public function setAdapter(AdapterInterface $adapter)
348 8
    {
349
        $this->adapter = $adapter;
350
351
        return $this;
352
    }
353 8
354 5
    /**
355 5
     * Gets the database adapter.
356
     *
357 8
     * @return \Phinx\Db\Adapter\AdapterInterface
358 5
     */
359 5
    public function getAdapter()
360
    {
361
        if (isset($this->adapter)) {
362 8
            return $this->adapter;
363 1
        }
364 1
        if (isset($this->options['connection'])) {
365 1
            if (!($this->options['connection'] instanceof \PDO)) {
366
                throw new \RuntimeException('The specified connection is not a PDO instance');
367 8
            }
368
369 8
            $this->options['connection']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
370
            $this->options['adapter'] = $this->options['connection']->getAttribute(\PDO::ATTR_DRIVER_NAME);
371
        }
372
        if (!isset($this->options['adapter'])) {
373
            throw new \RuntimeException('No adapter was specified for environment: ' . $this->getName());
374
        }
375
376
        $factory = AdapterFactory::instance();
377
        $adapter = $factory
378 1
            ->getAdapter($this->options['adapter'], $this->options);
379
380 1
        // Automatically time the executed commands
381 1
        $adapter = $factory->getWrapper('timed', $adapter);
382
383
        if (isset($this->options['wrapper'])) {
384
            $adapter = $factory
385
                ->getWrapper($this->options['wrapper'], $adapter);
386
        }
387
388
        if ($this->getInput()) {
389 1
            $adapter->setInput($this->getInput());
390
        }
391 1
392
        if ($this->getOutput()) {
393
            $adapter->setOutput($this->getOutput());
394
        }
395
396
        // Use the TablePrefixAdapter if table prefix/suffixes are in use
397
        if ($adapter->hasOption('table_prefix') || $adapter->hasOption('table_suffix')) {
398
            $adapter = AdapterFactory::instance()
399
                ->getWrapper('prefix', $adapter);
400
        }
401
402
        $this->setAdapter($adapter);
403
404
        return $adapter;
405
    }
406
407
    /**
408
     * Sets the schema table name.
409
     *
410
     * @param string $schemaTableName Schema Table Name
411
     * @return \Phinx\Migration\Manager\Environment
412
     */
413
    public function setSchemaTableName($schemaTableName)
414
    {
415
        $this->schemaTableName = $schemaTableName;
416
417
        return $this;
418
    }
419
420
    /**
421
     * Gets the schema table name.
422
     *
423
     * @return string
424
     */
425
    public function getSchemaTableName()
426
    {
427
        return $this->schemaTableName;
428
    }
429
}
430