Completed
Push — master ( 7072a5...f0864d )
by Kurita
16s
created

Manager::getSeedDependenciesInstances()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 8.125

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 4
cts 8
cp 0.5
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 9
nc 2
nop 1
crap 8.125
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();
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()),
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());
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();
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();
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
     * @throws \InvalidArgumentException
663 388
     * @return \Phinx\Migration\AbstractMigration[]
664
     */
665 388
    public function getMigrations()
666
    {
667 388
        if ($this->migrations === null) {
668 387
            $phpFiles = $this->getMigrationFiles();
669 387
670
            // filter the files to only get the ones that match our naming scheme
671 387
            $fileNames = [];
672 3
            /** @var \Phinx\Migration\AbstractMigration[] $versions */
673
            $versions = [];
674
675 387
            foreach ($phpFiles as $filePath) {
676 387
                if (Util::isValidMigrationFileName(basename($filePath))) {
677
                    $version = Util::getVersionFromFileName(basename($filePath));
678
679 387
                    if (isset($versions[$version])) {
680
                        throw new \InvalidArgumentException(sprintf('Duplicate migration - "%s" has the same version as "%s"', $filePath, $versions[$version]->getVersion()));
681 387
                    }
682 2
683 2
                    $config = $this->getConfig();
684 2
                    $namespace = $config instanceof NamespaceAwareInterface ? $config->getMigrationNamespaceByPath(dirname($filePath)) : null;
685 2
686 2
                    // convert the filename to a class name
687
                    $class = ($namespace === null ? '' : $namespace . '\\') . Util::mapFileNameToClassName(basename($filePath));
688
689 387
                    if (isset($fileNames[$class])) {
690
                        throw new \InvalidArgumentException(sprintf(
691
                            'Migration "%s" has the same name as "%s"',
692
                            basename($filePath),
693 387
                            $fileNames[$class]
694 387
                        ));
695 2
                    }
696 2
697 2
                    $fileNames[$class] = basename($filePath);
698
699 2
                    // load the migration file
700
                    /** @noinspection PhpIncludeInspection */
701
                    require_once $filePath;
702
                    if (!class_exists($class)) {
703 385
                        throw new \InvalidArgumentException(sprintf(
704
                            'Could not find class "%s" in file "%s"',
705 385
                            $class,
706 2
                            $filePath
707 2
                        ));
708 2
                    }
709
710 2
                    // instantiate it
711
                    $migration = new $class($version, $this->getInput(), $this->getOutput());
712
713 383
                    if (!($migration instanceof AbstractMigration)) {
714 383
                        throw new \InvalidArgumentException(sprintf(
715 384
                            'The class "%s" in file "%s" must extend \Phinx\Migration\AbstractMigration',
716
                            $class,
717 379
                            $filePath
718 379
                        ));
719 379
                    }
720
721 379
                    $versions[$version] = $migration;
722
                }
723
            }
724
725
            ksort($versions);
726
            $this->setMigrations($versions);
727
        }
728
729 388
        return $this->migrations;
730
    }
731 388
732 388
    /**
733 388
     * Returns a list of migration files found in the provided migration paths.
734
     *
735 388
     * @return string[]
736 388
     */
737 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...
738 388
    {
739 388
        $config = $this->getConfig();
740 388
        $paths = $config->getMigrationPaths();
741
        $files = [];
742 388
743
        foreach ($paths as $path) {
744
            $files = array_merge(
745
                $files,
746
                Util::glob($path . DIRECTORY_SEPARATOR . '*.php')
747
            );
748
        }
749
        // glob() can return the same file multiple times
750
        // This will cause the migration to fail with a
751 11
        // false assumption of duplicate migrations
752
        // http://php.net/manual/en/function.glob.php#110340
753 11
        $files = array_unique($files);
754 11
755
        return $files;
756
    }
757
758
    /**
759
     * Sets the database seeders.
760
     *
761
     * @param array $seeds Seeders
762
     * @return \Phinx\Migration\Manager
763 11
     */
764
    public function setSeeds(array $seeds)
765 11
    {
766 11
        $this->seeds = $seeds;
767
768
        return $this;
769 11
    }
770
771 11
    /**
772
     * Get seed dependencies instances from seed dependency array
773 11
     *
774 11
     * @param AbstractSeed $seed Seed
775 11
     *
776 11
     * @return AbstractSeed[]
777
     */
778
    private function getSeedDependenciesInstances(AbstractSeed $seed)
779 11
    {
780 11
        $dependenciesInstances = [];
781
        $dependencies = $seed->getDependencies();
782
        if (!empty($dependencies)) {
783
            foreach ($dependencies as $dependency) {
784 11
                foreach ($this->seeds as $seed) {
785 11
                    if (get_class($seed) === $dependency) {
786
                        $dependenciesInstances[get_class($seed)] = $seed;
787
                    }
788
                }
789
            }
790
        }
791
792
        return $dependenciesInstances;
793
    }
794 11
795
    /**
796 11
     * Order seeds by dependencies
797
     *
798
     * @param AbstractSeed[] $seeds Seeds
799
     *
800
     * @return AbstractSeed[]
801
     */
802
    private function orderSeedsByDependencies(array $seeds)
803
    {
804 11
        $orderedSeeds = [];
805 11
        foreach ($seeds as $seed) {
806 11
            $key = get_class($seed);
807
            $dependencies = $this->getSeedDependenciesInstances($seed);
808 11
            if (!empty($dependencies)) {
809 11
                $orderedSeeds[$key] = $seed;
810 11
                $orderedSeeds = array_merge($this->orderSeedsByDependencies($dependencies), $orderedSeeds);
811
            } else {
812 11
                $orderedSeeds[$key] = $seed;
813
            }
814
        }
815
816
        return $orderedSeeds;
817
    }
818
819
    /**
820 11
     * Gets an array of database seeders.
821
     *
822 11
     * @throws \InvalidArgumentException
823 11
     * @return \Phinx\Seed\AbstractSeed[]
824 11
     */
825
    public function getSeeds()
826 11
    {
827 11
        if ($this->seeds === null) {
828 11
            $phpFiles = $this->getSeedFiles();
829 11
830 11
            // filter the files to only get the ones that match our naming scheme
831 11
            $fileNames = [];
832
            /** @var \Phinx\Seed\AbstractSeed[] $seeds */
833 11
            $seeds = [];
834
835
            foreach ($phpFiles as $filePath) {
836
                if (Util::isValidSeedFileName(basename($filePath))) {
837
                    $config = $this->getConfig();
838
                    $namespace = $config instanceof NamespaceAwareInterface ? $config->getSeedNamespaceByPath(dirname($filePath)) : null;
839
840
                    // convert the filename to a class name
841
                    $class = ($namespace === null ? '' : $namespace . '\\') . pathinfo($filePath, PATHINFO_FILENAME);
842 400
                    $fileNames[$class] = basename($filePath);
843
844 400
                    // load the seed file
845 400
                    /** @noinspection PhpIncludeInspection */
846
                    require_once $filePath;
847
                    if (!class_exists($class)) {
848
                        throw new \InvalidArgumentException(sprintf(
849
                            'Could not find class "%s" in file "%s"',
850
                            $class,
851
                            $filePath
852
                        ));
853 399
                    }
854
855 399
                    // instantiate it
856
                    $seed = new $class($this->getInput(), $this->getOutput());
857
858
                    if (!($seed instanceof AbstractSeed)) {
859
                        throw new \InvalidArgumentException(sprintf(
860
                            'The class "%s" in file "%s" must extend \Phinx\Seed\AbstractSeed',
861
                            $class,
862
                            $filePath
863
                        ));
864
                    }
865 2
866
                    $seeds[$class] = $seed;
867 2
                }
868 2
            }
869 2
870 2
            ksort($seeds);
871
            $this->setSeeds($seeds);
872 2
        }
873
874
        $this->seeds = $this->orderSeedsByDependencies($this->seeds);
875
        return $this->seeds;
876 2
    }
877 1
878 1
    /**
879 1
     * Returns a list of seed files found in the provided seed paths.
880
     *
881 2
     * @return string[]
882 1
     */
883 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...
884
    {
885 1
        $config = $this->getConfig();
886 1
        $paths = $config->getSeedPaths();
887
        $files = [];
888
889 1
        foreach ($paths as $path) {
890
            $files = array_merge(
891 1
                $files,
892
                Util::glob($path . DIRECTORY_SEPARATOR . '*.php')
893 1
            );
894 1
        }
895 1
        // glob() can return the same file multiple times
896 1
        // This will cause the migration to fail with a
897 1
        // false assumption of duplicate migrations
898 1
        // http://php.net/manual/en/function.glob.php#110340
899
        $files = array_unique($files);
900
901
        return $files;
902
    }
903
904
    /**
905
     * Sets the config.
906 1
     *
907
     * @param  \Phinx\Config\ConfigInterface $config Configuration Object
908 1
     * @return \Phinx\Migration\Manager
909 1
     */
910 1
    public function setConfig(ConfigInterface $config)
911 1
    {
912 1
        $this->config = $config;
913
914
        return $this;
915
    }
916
917
    /**
918
     * Gets the config.
919
     *
920
     * @return \Phinx\Config\ConfigInterface
921
     */
922
    public function getConfig()
923
    {
924
        return $this->config;
925
    }
926
927
    /**
928
     * Toggles the breakpoint for a specific version.
929
     *
930
     * @param string $environment
931
     * @param int $version
932
     * @return void
933
     */
934
    public function toggleBreakpoint($environment, $version)
935
    {
936
        $migrations = $this->getMigrations();
937
        $this->getMigrations();
938
        $env = $this->getEnvironment($environment);
939
        $versions = $env->getVersionLog();
940
941
        if (empty($versions) || empty($migrations)) {
942
            return;
943
        }
944
945
        if ($version === null) {
946
            $lastVersion = end($versions);
947
            $version = $lastVersion['version'];
948
        }
949
950 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...
951
            $this->output->writeln(sprintf(
952
                '<comment>warning</comment> %s is not a valid version',
953
                $version
954
            ));
955
956
            return;
957
        }
958
959
        $env->getAdapter()->toggleBreakpoint($migrations[$version]);
960
961
        $versions = $env->getVersionLog();
962
963
        $this->getOutput()->writeln(
964
            ' Breakpoint ' . ($versions[$version]['breakpoint'] ? 'set' : 'cleared') .
965
            ' for <info>' . $version . '</info>' .
966
            ' <comment>' . $migrations[$version]->getName() . '</comment>'
967
        );
968
    }
969
970
    /**
971
     * Remove all breakpoints
972
     *
973
     * @param string $environment
974
     * @return void
975
     */
976
    public function removeBreakpoints($environment)
977
    {
978
        $this->getOutput()->writeln(sprintf(
979
            ' %d breakpoints cleared.',
980
            $this->getEnvironment($environment)->getAdapter()->resetAllBreakpoints()
981
        ));
982
    }
983
}
984