Completed
Push — master ( 94018a...6a6ebc )
by AD
13s
created

Manager::setOutput()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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