Completed
Pull Request — master (#1331)
by mark
06:35
created

Manager::getSeeds()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 53
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 8.006

Importance

Changes 0
Metric Value
cc 8
eloc 28
nc 10
nop 0
dl 0
loc 53
ccs 21
cts 22
cp 0.9545
crap 8.006
rs 7.1199
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
28
 */
29
namespace Phinx\Migration;
30
31
use Phinx\Config\ConfigInterface;
32
use Phinx\Config\NamespaceAwareInterface;
33
use Phinx\Migration\Manager\Environment;
34
use Phinx\Seed\AbstractSeed;
35
use Phinx\Seed\SeedInterface;
36
use Phinx\Util\Util;
37
use Symfony\Component\Console\Input\InputInterface;
38
use Symfony\Component\Console\Output\OutputInterface;
39
40
class Manager
41
{
42
    /**
43
     * @var \Phinx\Config\ConfigInterface
44
     */
45
    protected $config;
46
47
    /**
48
     * @var \Symfony\Component\Console\Input\InputInterface
49
     */
50
    protected $input;
51
52
    /**
53
     * @var \Symfony\Component\Console\Output\OutputInterface
54
     */
55
    protected $output;
56
57
    /**
58
     * @var array
59
     */
60
    protected $environments;
61
62
    /**
63
     * @var array
64
     */
65
    protected $migrations;
66
67
    /**
68
     * @var array
69
     */
70
    protected $seeds;
71
72
    /**
73
     * @var integer
74
     */
75
    const EXIT_STATUS_DOWN = 1;
76
77
    /**
78
     * @var integer
79
     */
80
    const EXIT_STATUS_MISSING = 2;
81
82
    /**
83
     * Class Constructor.
84
     *
85
     * @param \Phinx\Config\ConfigInterface $config Configuration Object
86
     * @param \Symfony\Component\Console\Input\InputInterface $input Console Input
87
     * @param \Symfony\Component\Console\Output\OutputInterface $output Console Output
88
     */
89 432
    public function __construct(ConfigInterface $config, InputInterface $input, OutputInterface $output)
90
    {
91 432
        $this->setConfig($config);
92 432
        $this->setInput($input);
93 432
        $this->setOutput($output);
94 432
    }
95
96
    /**
97
     * Prints the specified environment's migration status.
98
     *
99
     * @param string $environment
100
     * @param null $format
101
     * @return int 0 if all migrations are up, or an error code
102
     */
103 22
    public function printStatus($environment, $format = null)
104
    {
105 22
        $output = $this->getOutput();
106 22
        $migrations = [];
0 ignored issues
show
Unused Code introduced by
$migrations is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
107 22
        $hasDownMigration = false;
108 22
        $hasMissingMigration = false;
109 22
        $migrations = $this->getMigrations($environment);
110 22
        if (count($migrations)) {
111
            // TODO - rewrite using Symfony Table Helper as we already have this library
112
            // included and it will fix formatting issues (e.g drawing the lines)
113 21
            $output->writeln('');
114
115 21
            switch ($this->getConfig()->getVersionOrder()) {
116 21
                case \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME:
117 19
                    $migrationIdAndStartedHeader = "<info>[Migration ID]</info>  Started            ";
118 19
                    break;
119 2
                case \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME:
120 1
                    $migrationIdAndStartedHeader = "Migration ID    <info>[Started          ]</info>";
121 1
                    break;
122 1
                default:
123 1
                    throw new \RuntimeException('Invalid version_order configuration option');
124 21
            }
125
126 20
            $output->writeln(" Status  $migrationIdAndStartedHeader  Finished             Migration Name ");
127 20
            $output->writeln('----------------------------------------------------------------------------------');
128
129 20
            $env = $this->getEnvironment($environment);
130 20
            $versions = $env->getVersionLog();
131
132
            $maxNameLength = $versions ? max(array_map(function ($version) {
133 17
                return strlen($version['migration_name']);
134 20
            }, $versions)) : 0;
135
136 20
            $missingVersions = array_diff_key($versions, $migrations);
137
138 20
            $hasMissingMigration = !empty($missingVersions);
139
140
            // get the migrations sorted in the same way as the versions
141 20
            $sortedMigrations = [];
142
143 20
            foreach ($versions as $versionCreationTime => $version) {
144 17
                if (isset($migrations[$versionCreationTime])) {
145 13
                    array_push($sortedMigrations, $migrations[$versionCreationTime]);
146 13
                    unset($migrations[$versionCreationTime]);
147 13
                }
148 20
            }
149
150 20
            if (empty($sortedMigrations) && !empty($missingVersions)) {
151
                // this means we have no up migrations, so we write all the missing versions already so they show up
152
                // before any possible down migration
153 4
                foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) {
154 4
                    $this->printMissingVersion($missingVersion, $maxNameLength);
155
156 4
                    unset($missingVersions[$missingVersionCreationTime]);
157 4
                }
158 4
            }
159
160
            // any migration left in the migrations (ie. not unset when sorting the migrations by the version order) is
161
            // a migration that is down, so we add them to the end of the sorted migrations list
162 20
            if (!empty($migrations)) {
163 13
                $sortedMigrations = array_merge($sortedMigrations, $migrations);
164 13
            }
165
166 20
            foreach ($sortedMigrations as $migration) {
167 20
                $version = array_key_exists($migration->getVersion(), $versions) ? $versions[$migration->getVersion()] : false;
168 20
                if ($version) {
169
                    // check if there are missing versions before this version
170 13
                    foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) {
171 6
                        if ($this->getConfig()->isVersionOrderCreationTime()) {
172 6
                            if ($missingVersion['version'] > $version['version']) {
173 4
                                break;
174
                            }
175 3
                        } else {
176
                            if ($missingVersion['start_time'] > $version['start_time']) {
177
                                break;
178
                            } elseif ($missingVersion['start_time'] == $version['start_time'] &&
179
                                $missingVersion['version'] > $version['version']) {
180
                                break;
181
                            }
182
                        }
183
184 3
                        $this->printMissingVersion($missingVersion, $maxNameLength);
185
186 3
                        unset($missingVersions[$missingVersionCreationTime]);
187 13
                    }
188
189 13
                    $status = '     <info>up</info> ';
190 13
                } else {
191 13
                    $hasDownMigration = true;
192 13
                    $status = '   <error>down</error> ';
193
                }
194 20
                $maxNameLength = max($maxNameLength, strlen($migration->getName()));
195
196 20
                $output->writeln(sprintf(
197 20
                    '%s %14.0f  %19s  %19s  <comment>%s</comment>',
198 20
                    $status,
199 20
                    $migration->getVersion(),
200 20
                    $version['start_time'],
201 20
                    $version['end_time'],
202 20
                    $migration->getName()
203 20
                ));
204
205 20
                if ($version && $version['breakpoint']) {
206 1
                    $output->writeln('         <error>BREAKPOINT SET</error>');
207 1
                }
208
209 20
                $migrations[] = ['migration_status' => trim(strip_tags($status)), 'migration_id' => sprintf('%14.0f', $migration->getVersion()), 'migration_name' => $migration->getName()];
210 20
                unset($versions[$migration->getVersion()]);
211 20
            }
212
213
            // and finally add any possibly-remaining missing migrations
214 20
            foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) {
215 4
                $this->printMissingVersion($missingVersion, $maxNameLength);
216
217 4
                unset($missingVersions[$missingVersionCreationTime]);
218 20
            }
219 20
        } else {
220
            // there are no migrations
221 1
            $output->writeln('');
222 1
            $output->writeln('There are no available migrations. Try creating one using the <info>create</info> command.');
223
        }
224
225
        // write an empty line
226 21
        $output->writeln('');
227 21
        if ($format !== null) {
228
            switch ($format) {
229
                case 'json':
230
                    $output->writeln(json_encode(
231
                        [
232
                            'pending_count' => count($this->getMigrations($environment)),
233
                            'migrations' => $migrations
234
                        ]
235
                    ));
236
                    break;
237
                default:
238
                    $output->writeln('<info>Unsupported format: ' . $format . '</info>');
239
            }
240
        }
241
242 21
        if ($hasMissingMigration) {
243 10
            return self::EXIT_STATUS_MISSING;
244 11
        } elseif ($hasDownMigration) {
245 6
            return self::EXIT_STATUS_DOWN;
246
        } else {
247 5
            return 0;
248
        }
249
    }
250
251
    /**
252
     * Print Missing Version
253
     *
254
     * @param array     $version        The missing version to print (in the format returned by Environment.getVersionLog).
255
     * @param int   $maxNameLength  The maximum migration name length.
256
     */
257 10
    private function printMissingVersion($version, $maxNameLength)
258
    {
259 10
        $this->getOutput()->writeln(sprintf(
260 10
            '     <error>up</error>  %14.0f  %19s  %19s  <comment>%s</comment>  <error>** MISSING **</error>',
261 10
            $version['version'],
262 10
            $version['start_time'],
263 10
            $version['end_time'],
264 10
            str_pad($version['migration_name'], $maxNameLength, ' ')
265 10
        ));
266
267 10
        if ($version && $version['breakpoint']) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $version of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
268 1
            $this->getOutput()->writeln('         <error>BREAKPOINT SET</error>');
269 1
        }
270 10
    }
271
272
    /**
273
     * Migrate to the version of the database on a given date.
274
     *
275
     * @param string    $environment Environment
276
     * @param \DateTime $dateTime    Date to migrate to
277
     *
278
     * @return void
279
     */
280 4
    public function migrateToDateTime($environment, \DateTime $dateTime)
281
    {
282 4
        $versions = array_keys($this->getMigrations($environment));
283 4
        $dateString = $dateTime->format('YmdHis');
284
285
        $outstandingMigrations = array_filter($versions, function ($version) use ($dateString) {
286 4
            return $version <= $dateString;
287 4
        });
288
289 4
        if (count($outstandingMigrations) > 0) {
290 3
            $migration = max($outstandingMigrations);
291 3
            $this->getOutput()->writeln('Migrating to version ' . $migration);
292 3
            $this->migrate($environment, $migration);
293 3
        }
294 4
    }
295
296
    /**
297
     * Migrate an environment to the specified version.
298
     *
299
     * @param string $environment Environment
300
     * @param int $version
301
     * @return void
302
     */
303 8
    public function migrate($environment, $version = null)
304
    {
305 8
        $migrations = $this->getMigrations($environment);
306 8
        $env = $this->getEnvironment($environment);
307 8
        $versions = $env->getVersions();
308 8
        $current = $env->getCurrentVersion();
309
310 8
        if (empty($versions) && empty($migrations)) {
311
            return;
312
        }
313
314 8
        if ($version === null) {
315 5
            $version = max(array_merge($versions, array_keys($migrations)));
316 5 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
317 3
            if (0 != $version && !isset($migrations[$version])) {
318
                $this->output->writeln(sprintf(
319
                    '<comment>warning</comment> %s is not a valid version',
320
                    $version
321
                ));
322
323
                return;
324
            }
325
        }
326
327 8
        // are we migrating up or down?
328
        $direction = $version > $current ? MigrationInterface::UP : MigrationInterface::DOWN;
329 8
330
        if ($direction === MigrationInterface::DOWN) {
331
            // run downs first
332
            krsort($migrations);
333 View Code Duplication
            foreach ($migrations as $migration) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
334
                if ($migration->getVersion() <= $version) {
335
                    break;
336
                }
337
338
                if (in_array($migration->getVersion(), $versions)) {
339
                    $this->executeMigration($environment, $migration, MigrationInterface::DOWN);
340
                }
341
            }
342
        }
343 8
344 8
        ksort($migrations);
345 8 View Code Duplication
        foreach ($migrations as $migration) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
346 2
            if ($migration->getVersion() > $version) {
347
                break;
348
            }
349 8
350 5
            if (!in_array($migration->getVersion(), $versions)) {
351 5
                $this->executeMigration($environment, $migration, MigrationInterface::UP);
352 8
            }
353 8
        }
354
    }
355
356
    /**
357
     * Execute a migration against the specified environment.
358
     *
359
     * @param string $name Environment Name
360
     * @param \Phinx\Migration\MigrationInterface $migration Migration
361
     * @param string $direction Direction
362
     * @return void
363 119
     */
364
    public function executeMigration($name, MigrationInterface $migration, $direction = MigrationInterface::UP)
365 119
    {
366 119
        $this->getOutput()->writeln('');
367
        $this->getOutput()->writeln(
368 119
            ' ==' .
369 119
            ' <info>' . $migration->getVersion() . ' ' . $migration->getName() . ':</info>' .
370 119
            ' <comment>' . ($direction === MigrationInterface::UP ? 'migrating' : 'reverting') . '</comment>'
371
        );
372
373 119
        // Execute the migration and log the time elapsed.
374 119
        $start = microtime(true);
375 119
        $this->getEnvironment($name)->executeMigration($migration, $direction);
376
        $end = microtime(true);
377 119
378
        $this->getOutput()->writeln(
379 119
            ' ==' .
380 119
            ' <info>' . $migration->getVersion() . ' ' . $migration->getName() . ':</info>' .
381 119
            ' <comment>' . ($direction === MigrationInterface::UP ? 'migrated' : 'reverted') .
382 119
            ' ' . sprintf('%.4fs', $end - $start) . '</comment>'
383 119
        );
384
    }
385
386
    /**
387
     * Execute a seeder against the specified environment.
388
     *
389
     * @param string $name Environment Name
390
     * @param \Phinx\Seed\SeedInterface $seed Seed
391
     * @return void
392 6
     */
393
    public function executeSeed($name, SeedInterface $seed)
394 6
    {
395 6
        $this->getOutput()->writeln('');
396
        $this->getOutput()->writeln(
397 6
            ' ==' .
398 6
            ' <info>' . $seed->getName() . ':</info>' .
399 6
            ' <comment>seeding</comment>'
400
        );
401
402 6
        // Execute the seeder and log the time elapsed.
403 6
        $start = microtime(true);
404 6
        $this->getEnvironment($name)->executeSeed($seed);
405
        $end = microtime(true);
406 6
407
        $this->getOutput()->writeln(
408 6
            ' ==' .
409 6
            ' <info>' . $seed->getName() . ':</info>' .
410 6
            ' <comment>seeded' .
411 6
            ' ' . sprintf('%.4fs', $end - $start) . '</comment>'
412 6
        );
413
    }
414
415
    /**
416
     * Rollback an environment to the specified version.
417
     *
418
     * @param string $environment Environment
419
     * @param int|string $target
420
     * @param bool $force
421
     * @param bool $targetMustMatchVersion
422
     * @return void
423 349
     */
424
    public function rollback($environment, $target = null, $force = false, $targetMustMatchVersion = true)
425
    {
426 349
        // note that the migrations are indexed by name (aka creation time) in ascending order
427
        $migrations = $this->getMigrations($environment);
428
429 349
        // note that the version log are also indexed by name with the proper ascending order according to the version order
430
        $executedVersions = $this->getEnvironment($environment)->getVersionLog();
431
432 349
        // get a list of migrations sorted in the opposite way of the executed versions
433
        $sortedMigrations = [];
434 349
435
        foreach ($executedVersions as $versionCreationTime => &$executedVersion) {
436
            // if we have a date (ie. the target must not match a version) and we are sorting by execution time, we
437 349
            // convert the version start time so we can compare directly with the target date
438 48
            if (!$this->getConfig()->isVersionOrderCreationTime() && !$targetMustMatchVersion) {
439 48
                $dateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $executedVersion['start_time']);
440 48
                $executedVersion['start_time'] = $dateTime->format('YmdHis');
441
            }
442 349
443 349
            if (isset($migrations[$versionCreationTime])) {
444 349
                array_unshift($sortedMigrations, $migrations[$versionCreationTime]);
445
            } else {
446
                // this means the version is missing so we unset it so that we don't consider it when rolling back
447 47
                // migrations (or choosing the last up version as target)
448
                unset($executedVersions[$versionCreationTime]);
449 349
            }
450
        }
451 349
452 23
        if ($target === 'all' || $target === '0') {
453 349
            $target = 0;
454
        } elseif (!is_numeric($target) && !is_null($target)) { // try to find a target version based on name
455 20
            // search through the migrations using the name
456 20
            $migrationNames = array_map(function ($item) {
457 20
                return $item['migration_name'];
458 20
            }, $executedVersions);
459
            $found = array_search($target, $migrationNames);
460
461 20
            // check on was found
462 20
            if ($found !== false) {
463 20
                $target = (string)$found;
464
            } else {
465
                $this->getOutput()->writeln("<error>No migration found with name ($target)</error>");
466
467 20
                return;
468
            }
469
        }
470 349
471 349
        // Check we have at least 1 migration to revert
472 53
        $executedVersionCreationTimes = array_keys($executedVersions);
473 53
        if (empty($executedVersionCreationTimes) || $target == end($executedVersionCreationTimes)) {
474
            $this->getOutput()->writeln('<error>No migrations to rollback</error>');
475
476
            return;
477 296
        }
478
479 94
        // If no target was supplied, revert the last migration
480 94
        if ($target === null) {
481 94
            // Get the migration before the last run migration
482
            $prev = count($executedVersionCreationTimes) - 2;
483
            $target = $prev >= 0 ? $executedVersionCreationTimes[$prev] : 0;
484 296
        }
485 46
486 46
        // If the target must match a version, check the target version exists
487
        if ($targetMustMatchVersion && 0 !== $target && !isset($migrations[$target])) {
488
            $this->getOutput()->writeln("<error>Target version ($target) not found</error>");
489
490 250
            return;
491
        }
492 250
493 250
        // Rollback all versions until we find the wanted rollback target
494 70
        $rollbacked = false;
495
496
        foreach ($sortedMigrations as $migration) {
497 250
            if ($targetMustMatchVersion && $migration->getVersion() == $target) {
498 250
                break;
499
            }
500 250
501 96
            if (in_array($migration->getVersion(), $executedVersionCreationTimes)) {
502 96
                $executedVersion = $executedVersions[$migration->getVersion()];
503 42
504
                if (!$targetMustMatchVersion) {
505 68
                    if (($this->getConfig()->isVersionOrderCreationTime() && $executedVersion['version'] <= $target) ||
506
                        (!$this->getConfig()->isVersionOrderCreationTime() && $executedVersion['start_time'] <= $target)) {
507 222
                        break;
508 121
                    }
509 121
                }
510
511 117
                if (0 != $executedVersion['breakpoint'] && !$force) {
512 117
                    $this->getOutput()->writeln('<error>Breakpoint reached. Further rollbacks inhibited.</error>');
513 117
                    break;
514 250
                }
515
                $this->executeMigration($environment, $migration, MigrationInterface::DOWN);
516 250
                $rollbacked = true;
517 133
            }
518 133
        }
519 250
520
        if (!$rollbacked) {
521
            $this->getOutput()->writeln('<error>No migrations to rollback</error>');
522
        }
523
    }
524
525
    /**
526
     * Run database seeders against an environment.
527
     *
528 9
     * @param string $environment Environment
529
     * @param string $seed Seeder
530 9
     * @return void
531
     */
532 9
    public function seed($environment, $seed = null)
533
    {
534 3
        $seeds = $this->getSeeds();
535 3
536 3
        if ($seed === null) {
537 3
            // run all seeders
538 3
            foreach ($seeds as $seeder) {
539 3
                if (array_key_exists($seeder->getName(), $seeds)) {
540
                    $this->executeSeed($environment, $seeder);
541 6
                }
542 3
            }
543 3
        } else {
544 3
            // run only one seeder
545
            if (array_key_exists($seed, $seeds)) {
546
                $this->executeSeed($environment, $seeds[$seed]);
547 6
            } else {
548
                throw new \InvalidArgumentException(sprintf('The seed class "%s" does not exist', $seed));
549
            }
550
        }
551
    }
552
553
    /**
554
     * Sets the environments.
555 381
     *
556
     * @param array $environments Environments
557 381
     * @return \Phinx\Migration\Manager
558 381
     */
559
    public function setEnvironments($environments = [])
560
    {
561
        $this->environments = $environments;
562
563
        return $this;
564
    }
565
566
    /**
567
     * Gets the manager class for the given environment.
568 382
     *
569
     * @param string $name Environment Name
570 382
     * @throws \InvalidArgumentException
571 380
     * @return \Phinx\Migration\Manager\Environment
572
     */
573
    public function getEnvironment($name)
574
    {
575 7
        if (isset($this->environments[$name])) {
576 1
            return $this->environments[$name];
577 1
        }
578
579 1
        // check the environment exists
580
        if (!$this->getConfig()->hasEnvironment($name)) {
581
            throw new \InvalidArgumentException(sprintf(
582
                'The environment "%s" does not exist',
583 6
                $name
584 6
            ));
585
        }
586 6
587 6
        // create an environment instance and cache it
588 6
        $envOptions = $this->getConfig()->getEnvironment($name);
589 6
        $envOptions['version_order'] = $this->getConfig()->getVersionOrder();
590
591 6
        $environment = new Environment($name, $envOptions);
592
        $this->environments[$name] = $environment;
593
        $environment->setInput($this->getInput());
594
        $environment->setOutput($this->getOutput());
595
596
        return $environment;
597
    }
598
599
    /**
600 400
     * Sets the console input.
601
     *
602 400
     * @param \Symfony\Component\Console\Input\InputInterface $input Input
603 400
     * @return \Phinx\Migration\Manager
604
     */
605
    public function setInput(InputInterface $input)
606
    {
607
        $this->input = $input;
608
609
        return $this;
610
    }
611 393
612
    /**
613 393
     * Gets the console input.
614
     *
615
     * @return \Symfony\Component\Console\Input\InputInterface
616
     */
617
    public function getInput()
618
    {
619
        return $this->input;
620
    }
621
622 400
    /**
623
     * Sets the console output.
624 400
     *
625 400
     * @param \Symfony\Component\Console\Output\OutputInterface $output Output
626
     * @return \Phinx\Migration\Manager
627
     */
628
    public function setOutput(OutputInterface $output)
629
    {
630
        $this->output = $output;
631
632
        return $this;
633 395
    }
634
635 395
    /**
636
     * Gets the console output.
637
     *
638
     * @return \Symfony\Component\Console\Output\OutputInterface
639
     */
640
    public function getOutput()
641
    {
642
        return $this->output;
643
    }
644 379
645
    /**
646 379
     * Sets the database migrations.
647 379
     *
648
     * @param array $migrations Migrations
649
     * @return \Phinx\Migration\Manager
650
     */
651
    public function setMigrations(array $migrations)
652
    {
653
        $this->migrations = $migrations;
654
655
        return $this;
656
    }
657 388
658
    /**
659 388
     * Gets an array of the database migrations, indexed by migration name (aka creation time) and sorted in ascending
660 388
     * order
661
     *
662
     * @param string $environment Environment
663 388
     * @throws \InvalidArgumentException
664
     * @return \Phinx\Migration\AbstractMigration[]
665 388
     */
666
    public function getMigrations($environment)
667 388
    {
668 387
        if ($this->migrations === null) {
669 387
            $phpFiles = $this->getMigrationFiles();
670
671 387
            // filter the files to only get the ones that match our naming scheme
672 3
            $fileNames = [];
673
            /** @var \Phinx\Migration\AbstractMigration[] $versions */
674
            $versions = [];
675 387
676 387
            foreach ($phpFiles as $filePath) {
677
                if (Util::isValidMigrationFileName(basename($filePath))) {
678
                    $version = Util::getVersionFromFileName(basename($filePath));
679 387
680
                    if (isset($versions[$version])) {
681 387
                        throw new \InvalidArgumentException(sprintf('Duplicate migration - "%s" has the same version as "%s"', $filePath, $versions[$version]->getVersion()));
682 2
                    }
683 2
684 2
                    $config = $this->getConfig();
685 2
                    $namespace = $config instanceof NamespaceAwareInterface ? $config->getMigrationNamespaceByPath(dirname($filePath)) : null;
686 2
687
                    // convert the filename to a class name
688
                    $class = ($namespace === null ? '' : $namespace . '\\') . Util::mapFileNameToClassName(basename($filePath));
689 387
690
                    if (isset($fileNames[$class])) {
691
                        throw new \InvalidArgumentException(sprintf(
692
                            'Migration "%s" has the same name as "%s"',
693 387
                            basename($filePath),
694 387
                            $fileNames[$class]
695 2
                        ));
696 2
                    }
697 2
698
                    $fileNames[$class] = basename($filePath);
699 2
700
                    // load the migration file
701
                    /** @noinspection PhpIncludeInspection */
702
                    require_once $filePath;
703 385
                    if (!class_exists($class)) {
704
                        throw new \InvalidArgumentException(sprintf(
705 385
                            'Could not find class "%s" in file "%s"',
706 2
                            $class,
707 2
                            $filePath
708 2
                        ));
709
                    }
710 2
711
                    // instantiate it
712
                    $migration = new $class($environment, $version, $this->getInput(), $this->getOutput());
713 383
714 383
                    if (!($migration instanceof AbstractMigration)) {
715 384
                        throw new \InvalidArgumentException(sprintf(
716
                            'The class "%s" in file "%s" must extend \Phinx\Migration\AbstractMigration',
717 379
                            $class,
718 379
                            $filePath
719 379
                        ));
720
                    }
721 379
722
                    $versions[$version] = $migration;
723
                }
724
            }
725
726
            ksort($versions);
727
            $this->setMigrations($versions);
728
        }
729 388
730
        return $this->migrations;
731 388
    }
732 388
733 388
    /**
734
     * Returns a list of migration files found in the provided migration paths.
735 388
     *
736 388
     * @return string[]
737 388
     */
738 388 View Code Duplication
    protected function getMigrationFiles()
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...
739 388
    {
740 388
        $config = $this->getConfig();
741
        $paths = $config->getMigrationPaths();
742 388
        $files = [];
743
744
        foreach ($paths as $path) {
745
            $files = array_merge(
746
                $files,
747
                Util::glob($path . DIRECTORY_SEPARATOR . '*.php')
748
            );
749
        }
750
        // glob() can return the same file multiple times
751 11
        // This will cause the migration to fail with a
752
        // false assumption of duplicate migrations
753 11
        // http://php.net/manual/en/function.glob.php#110340
754 11
        $files = array_unique($files);
755
756
        return $files;
757
    }
758
759
    /**
760
     * Sets the database seeders.
761
     *
762
     * @param array $seeds Seeders
763 11
     * @return \Phinx\Migration\Manager
764
     */
765 11
    public function setSeeds(array $seeds)
766 11
    {
767
        $this->seeds = $seeds;
768
769 11
        return $this;
770
    }
771 11
772
    /**
773 11
     * Get seed dependencies instances from seed dependency array
774 11
     *
775 11
     * @param AbstractSeed $seed Seed
776 11
     *
777
     * @return AbstractSeed[]
778
     */
779 11
    private function getSeedDependenciesInstances(AbstractSeed $seed)
780 11
    {
781
        $dependenciesInstances = [];
782
        $dependencies = $seed->getDependencies();
783
        if (!empty($dependencies)) {
784 11
            foreach ($dependencies as $dependency) {
785 11
                foreach ($this->seeds as $seed) {
786
                    if (get_class($seed) === $dependency) {
787
                        $dependenciesInstances[get_class($seed)] = $seed;
788
                    }
789
                }
790
            }
791
        }
792
793
        return $dependenciesInstances;
794 11
    }
795
796 11
    /**
797
     * Order seeds by dependencies
798
     *
799
     * @param AbstractSeed[] $seeds Seeds
800
     *
801
     * @return AbstractSeed[]
802
     */
803
    private function orderSeedsByDependencies(array $seeds)
804 11
    {
805 11
        $orderedSeeds = [];
806 11
        foreach ($seeds as $seed) {
807
            $key = get_class($seed);
808 11
            $dependencies = $this->getSeedDependenciesInstances($seed);
809 11
            if (!empty($dependencies)) {
810 11
                $orderedSeeds[$key] = $seed;
811
                $orderedSeeds = array_merge($this->orderSeedsByDependencies($dependencies), $orderedSeeds);
812 11
            } else {
813
                $orderedSeeds[$key] = $seed;
814
            }
815
        }
816
817
        return $orderedSeeds;
818
    }
819
820 11
    /**
821
     * Gets an array of database seeders.
822 11
     *
823 11
     * @throws \InvalidArgumentException
824 11
     * @return \Phinx\Seed\AbstractSeed[]
825
     */
826 11
    public function getSeeds()
827 11
    {
828 11
        if ($this->seeds === null) {
829 11
            $phpFiles = $this->getSeedFiles();
830 11
831 11
            // filter the files to only get the ones that match our naming scheme
832
            $fileNames = [];
833 11
            /** @var \Phinx\Seed\AbstractSeed[] $seeds */
834
            $seeds = [];
835
836
            foreach ($phpFiles as $filePath) {
837
                if (Util::isValidSeedFileName(basename($filePath))) {
838
                    $config = $this->getConfig();
839
                    $namespace = $config instanceof NamespaceAwareInterface ? $config->getSeedNamespaceByPath(dirname($filePath)) : null;
840
841
                    // convert the filename to a class name
842 400
                    $class = ($namespace === null ? '' : $namespace . '\\') . pathinfo($filePath, PATHINFO_FILENAME);
843
                    $fileNames[$class] = basename($filePath);
844 400
845 400
                    // load the seed file
846
                    /** @noinspection PhpIncludeInspection */
847
                    require_once $filePath;
848
                    if (!class_exists($class)) {
849
                        throw new \InvalidArgumentException(sprintf(
850
                            'Could not find class "%s" in file "%s"',
851
                            $class,
852
                            $filePath
853 399
                        ));
854
                    }
855 399
856
                    // instantiate it
857
                    $seed = new $class($this->getInput(), $this->getOutput());
858
859
                    if (!($seed instanceof AbstractSeed)) {
860
                        throw new \InvalidArgumentException(sprintf(
861
                            'The class "%s" in file "%s" must extend \Phinx\Seed\AbstractSeed',
862
                            $class,
863
                            $filePath
864
                        ));
865 2
                    }
866
867 2
                    $seeds[$class] = $seed;
868 2
                }
869 2
            }
870 2
871
            ksort($seeds);
872 2
            $this->setSeeds($seeds);
873
        }
874
875
        $this->seeds = $this->orderSeedsByDependencies($this->seeds);
876 2
877 1
        return $this->seeds;
878 1
    }
879 1
880
    /**
881 2
     * Returns a list of seed files found in the provided seed paths.
882 1
     *
883 1
     * @return string[]
884
     */
885 1 View Code Duplication
    protected function getSeedFiles()
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...
886 1
    {
887
        $config = $this->getConfig();
888
        $paths = $config->getSeedPaths();
889 1
        $files = [];
890
891 1
        foreach ($paths as $path) {
892
            $files = array_merge(
893 1
                $files,
894 1
                Util::glob($path . DIRECTORY_SEPARATOR . '*.php')
895 1
            );
896 1
        }
897 1
        // glob() can return the same file multiple times
898 1
        // This will cause the migration to fail with a
899
        // false assumption of duplicate migrations
900
        // http://php.net/manual/en/function.glob.php#110340
901
        $files = array_unique($files);
902
903
        return $files;
904
    }
905
906 1
    /**
907
     * Sets the config.
908 1
     *
909 1
     * @param  \Phinx\Config\ConfigInterface $config Configuration Object
910 1
     * @return \Phinx\Migration\Manager
911 1
     */
912 1
    public function setConfig(ConfigInterface $config)
913
    {
914
        $this->config = $config;
915
916
        return $this;
917
    }
918
919
    /**
920
     * Gets the config.
921
     *
922
     * @return \Phinx\Config\ConfigInterface
923
     */
924
    public function getConfig()
925
    {
926
        return $this->config;
927
    }
928
929
    /**
930
     * Toggles the breakpoint for a specific version.
931
     *
932
     * @param string $environment
933
     * @param int $version
934
     * @return void
935
     */
936
    public function toggleBreakpoint($environment, $version)
937
    {
938
        $migrations = $this->getMigrations($environment);
939
        $this->getMigrations($environment);
940
        $env = $this->getEnvironment($environment);
941
        $versions = $env->getVersionLog();
942
943
        if (empty($versions) || empty($migrations)) {
944
            return;
945
        }
946
947
        if ($version === null) {
948
            $lastVersion = end($versions);
949
            $version = $lastVersion['version'];
950
        }
951
952 View Code Duplication
        if (0 != $version && !isset($migrations[$version])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
953
            $this->output->writeln(sprintf(
954
                '<comment>warning</comment> %s is not a valid version',
955
                $version
956
            ));
957
958
            return;
959
        }
960
961
        $env->getAdapter()->toggleBreakpoint($migrations[$version]);
962
963
        $versions = $env->getVersionLog();
964
965
        $this->getOutput()->writeln(
966
            ' Breakpoint ' . ($versions[$version]['breakpoint'] ? 'set' : 'cleared') .
967
            ' for <info>' . $version . '</info>' .
968
            ' <comment>' . $migrations[$version]->getName() . '</comment>'
969
        );
970
    }
971
972
    /**
973
     * Remove all breakpoints
974
     *
975
     * @param string $environment
976
     * @return void
977
     */
978
    public function removeBreakpoints($environment)
979
    {
980
        $this->getOutput()->writeln(sprintf(
981
            ' %d breakpoints cleared.',
982
            $this->getEnvironment($environment)->getAdapter()->resetAllBreakpoints()
983
        ));
984
    }
985
}
986