Completed
Push — master ( 84dee3...084275 )
by Michal
36:03
created

Manager::printStatus()   C

Complexity

Conditions 10
Paths 27

Size

Total Lines 72
Code Lines 50

Duplication

Lines 7
Ratio 9.72 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 7
loc 72
rs 5.9036
cc 10
eloc 50
nc 27
nop 2

How to fix   Long Method    Complexity   

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 Symfony\Component\Console\Output\OutputInterface;
32
use Phinx\Config\ConfigInterface;
33
use Phinx\Migration\Manager\Environment;
34
use Phinx\Seed\AbstractSeed;
35
use Phinx\Seed\SeedInterface;
36
use Phinx\Util\Util;
37
38
class Manager
39
{
40
    /**
41
     * @var ConfigInterface
42
     */
43
    protected $config;
44
45
    /**
46
     * @var OutputInterface
47
     */
48
    protected $output;
49
50
    /**
51
     * @var array
52
     */
53
    protected $environments;
54
55
    /**
56
     * @var array
57
     */
58
    protected $migrations;
59
60
    /**
61
     * @var array
62
     */
63
    protected $seeds;
64
65
    /**
66
     * @var integer
67
     */
68
    const EXIT_STATUS_DOWN = 1;
69
    
70
    /**
71
     * @var integer
72
     */
73
    const EXIT_STATUS_MISSING = 2;
74
    
75
    /**
76
     * Class Constructor.
77
     *
78
     * @param ConfigInterface $config Configuration Object
79
     * @param OutputInterface $output Console Output
80
     */
81
    public function __construct(ConfigInterface $config, OutputInterface $output)
82
    {
83
        $this->setConfig($config);
84
        $this->setOutput($output);
85
    }
86
87
    /**
88
     * Prints the specified environment's migration status.
89
     *
90
     * @param string $environment
91
     * @param null $format
92
     * @return integer 0 if all migrations are up, or an error code
93
     */
94
    public function printStatus($environment, $format = null)
95
    {
96
        $output = $this->getOutput();
97
        $migrations = array();
98
        $hasDownMigration = false;
99
        $hasMissingMigration = false;
100
        if (count($this->getMigrations())) {
101
            $output->writeln('');
102
            $output->writeln(' Status  Migration ID    Migration Name ');
103
            $output->writeln('-----------------------------------------');
104
105
            $env = $this->getEnvironment($environment);
106
            $versions = $env->getVersions();
107
108
            foreach ($this->getMigrations() as $migration) {
109 View Code Duplication
                if (in_array($migration->getVersion(), $versions)) {
110
                    $status = '     <info>up</info> ';
111
                    unset($versions[array_search($migration->getVersion(), $versions)]);
112
                } else {
113
                    $hasDownMigration = true;
114
                    $status = '   <error>down</error> ';
115
                }
116
117
                $output->writeln(
118
                    $status
119
                    . sprintf(' %14.0f ', $migration->getVersion())
120
                    . ' <comment>' . $migration->getName() . '</comment>'
121
                );
122
                $migrations[] = array('migration_status' => trim(strip_tags($status)), 'migration_id' => sprintf('%14.0f', $migration->getVersion()), 'migration_name' => $migration->getName());
123
            }
124
125
            if (count($versions)) {
126
                $hasMissingMigration = true;
127
                foreach ($versions as $missing) {
128
                    $output->writeln(
129
                        '     <error>up</error> '
130
                        . sprintf(' %14.0f ', $missing)
131
                        . ' <error>** MISSING **</error>'
132
                    );
133
                }
134
            }
135
        } else {
136
            // there are no migrations
137
            $output->writeln('');
138
            $output->writeln('There are no available migrations. Try creating one using the <info>create</info> command.');
139
        }
140
141
        // write an empty line
142
        $output->writeln('');
143
        if ($format !== null) {
144
            switch ($format) {
145
                case 'json':
146
                    $output->writeln(json_encode(
147
                        array(
148
                            'pending_count' => count($this->getMigrations()),
149
                            'migrations' => $migrations
150
                        )
151
                    ));
152
                    break;
153
                default:
154
                    $output->writeln('<info>Unsupported format: '.$format.'</info>');
155
            }
156
        }
157
158
        if ($hasMissingMigration) {
159
            return self::EXIT_STATUS_MISSING;
160
        } else if ($hasDownMigration) {
161
            return self::EXIT_STATUS_DOWN;
162
        } else {
163
            return 0;
164
        }
165
    }
166
167
    /**
168
     * Migrate to the version of the database on a given date.
169
     *
170
     * @param string    $environment Environment
171
     * @param \DateTime $dateTime    Date to migrate to
172
     *
173
     * @return void
174
     */
175 View Code Duplication
    public function migrateToDateTime($environment, \DateTime $dateTime)
176
    {
177
        $env            = $this->getEnvironment($environment);
0 ignored issues
show
Unused Code introduced by
$env 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...
178
        $versions       = array_keys($this->getMigrations());
179
        $dateString     = $dateTime->format('Ymdhis');
180
        $earlierVersion = null;
181
        foreach ($versions as $version) {
182
            if ($version > $dateString) {
183
                if (!is_null($earlierVersion)) {
184
                    $this->getOutput()->writeln(
185
                        'Migrating to version ' . $earlierVersion
186
                    );
187
                }
188
                $this->migrate($environment, $earlierVersion);
189
                return;
190
            }
191
            $earlierVersion = $version;
192
        }
193
        //If the date is greater than the latest version, migrate
194
        //to the latest version.
195
        $this->getOutput()->writeln(
196
            'Migrating to version ' . $earlierVersion
197
        );
198
        $this->migrate($environment, $earlierVersion);
199
    }
200
201
    /**
202
     * Roll back to the version of the database on a given date.
203
     *
204
     * @param string    $environment Environment
205
     * @param \DateTime $dateTime    Date to roll back to
206
     *
207
     * @return void
208
     */
209 View Code Duplication
    public function rollbackToDateTime($environment, \DateTime $dateTime)
210
    {
211
        $env        = $this->getEnvironment($environment);
212
        $versions   = $env->getVersions();
213
        $dateString = $dateTime->format('Ymdhis');
214
        sort($versions);
215
        $laterVersion = null;
216
        foreach (array_reverse($versions) as $version) {
217
            if ($version < $dateString) {
218
                if (!is_null($laterVersion)) {
219
                    $this->getOutput()->writeln('Rolling back to version '.$version);
220
                }
221
                $this->rollback($environment, $version);
222
                return;
223
            }
224
            $laterVersion = $version;
225
        }
226
        $this->getOutput()->writeln('Rolling back to version ' . $laterVersion);
227
        $this->rollback($environment, $laterVersion);
228
    }
229
230
    /**
231
     * Migrate an environment to the specified version.
232
     *
233
     * @param string $environment Environment
234
     * @param int $version
235
     * @return void
236
     */
237
    public function migrate($environment, $version = null)
238
    {
239
        $migrations = $this->getMigrations();
240
        $env = $this->getEnvironment($environment);
241
        $versions = $env->getVersions();
242
        $current = $env->getCurrentVersion();
243
244
        if (empty($versions) && empty($migrations)) {
245
            return;
246
        }
247
248
        if (null === $version) {
249
            $version = max(array_merge($versions, array_keys($migrations)));
250 View Code Duplication
        } else {
251
            if (0 != $version && !isset($migrations[$version])) {
252
                $this->output->writeln(sprintf(
253
                    '<comment>warning</comment> %s is not a valid version',
254
                    $version
255
                ));
256
                return;
257
            }
258
        }
259
260
        // are we migrating up or down?
261
        $direction = $version > $current ? MigrationInterface::UP : MigrationInterface::DOWN;
262
263
        if ($direction === MigrationInterface::DOWN) {
264
            // run downs first
265
            krsort($migrations);
266 View Code Duplication
            foreach ($migrations as $migration) {
267
                if ($migration->getVersion() <= $version) {
268
                    break;
269
                }
270
271
                if (in_array($migration->getVersion(), $versions)) {
272
                    $this->executeMigration($environment, $migration, MigrationInterface::DOWN);
273
                }
274
            }
275
        }
276
277
        ksort($migrations);
278 View Code Duplication
        foreach ($migrations as $migration) {
279
            if ($migration->getVersion() > $version) {
280
                break;
281
            }
282
283
            if (!in_array($migration->getVersion(), $versions)) {
284
                $this->executeMigration($environment, $migration, MigrationInterface::UP);
285
            }
286
        }
287
    }
288
289
    /**
290
     * Execute a migration against the specified environment.
291
     *
292
     * @param string $name Environment Name
293
     * @param MigrationInterface $migration Migration
294
     * @param string $direction Direction
295
     * @return void
296
     */
297
    public function executeMigration($name, MigrationInterface $migration, $direction = MigrationInterface::UP)
298
    {
299
        $this->getOutput()->writeln('');
300
        $this->getOutput()->writeln(
301
            ' =='
302
            . ' <info>' . $migration->getVersion() . ' ' . $migration->getName() . ':</info>'
303
            . ' <comment>' . ($direction === MigrationInterface::UP ? 'migrating' : 'reverting') . '</comment>'
304
        );
305
306
        // Execute the migration and log the time elapsed.
307
        $start = microtime(true);
308
        $this->getEnvironment($name)->executeMigration($migration, $direction);
309
        $end = microtime(true);
310
311
        $this->getOutput()->writeln(
312
            ' =='
313
            . ' <info>' . $migration->getVersion() . ' ' . $migration->getName() . ':</info>'
314
            . ' <comment>' . ($direction === MigrationInterface::UP ? 'migrated' : 'reverted')
315
            . ' ' . sprintf('%.4fs', $end - $start) . '</comment>'
316
        );
317
    }
318
319
    /**
320
     * Execute a seeder against the specified environment.
321
     *
322
     * @param string $name Environment Name
323
     * @param SeedInterface $seed Seed
324
     * @return void
325
     */
326
    public function executeSeed($name, SeedInterface $seed)
327
    {
328
        $this->getOutput()->writeln('');
329
        $this->getOutput()->writeln(
330
            ' =='
331
            . ' <info>' . $seed->getName() . ':</info>'
332
            . ' <comment>seeding</comment>'
333
        );
334
335
        // Execute the seeder and log the time elapsed.
336
        $start = microtime(true);
337
        $this->getEnvironment($name)->executeSeed($seed);
338
        $end = microtime(true);
339
340
        $this->getOutput()->writeln(
341
            ' =='
342
            . ' <info>' . $seed->getName() . ':</info>'
343
            . ' <comment>seeded'
344
            . ' ' . sprintf('%.4fs', $end - $start) . '</comment>'
345
        );
346
    }
347
348
    /**
349
     * Rollback an environment to the specified version.
350
     *
351
     * @param string $environment Environment
352
     * @param int $version
353
     * @return void
354
     */
355
    public function rollback($environment, $version = null)
356
    {
357
        $migrations = $this->getMigrations();
358
        $env = $this->getEnvironment($environment);
359
        $versions = $env->getVersions();
360
361
        ksort($migrations);
362
        sort($versions);
363
364
        // Check we have at least 1 migration to revert
365
        if (empty($versions) || $version == end($versions)) {
366
            $this->getOutput()->writeln('<error>No migrations to rollback</error>');
367
            return;
368
        }
369
370
        // If no target version was supplied, revert the last migration
371
        if (null === $version) {
372
            // Get the migration before the last run migration
373
            $prev = count($versions) - 2;
374
            $version = $prev >= 0 ? $versions[$prev] : 0;
375
        } else {
376
            // Get the first migration number
377
            $first = reset($versions);
378
379
            // If the target version is before the first migration, revert all migrations
380
            if ($version < $first) {
381
                $version = 0;
382
            }
383
        }
384
385
        // Check the target version exists
386 View Code Duplication
        if (0 !== $version && !isset($migrations[$version])) {
387
            $this->getOutput()->writeln("<error>Target version ($version) not found</error>");
388
            return;
389
        }
390
391
        // Revert the migration(s)
392
        krsort($migrations);
393 View Code Duplication
        foreach ($migrations as $migration) {
394
            if ($migration->getVersion() <= $version) {
395
                break;
396
            }
397
398
            if (in_array($migration->getVersion(), $versions)) {
399
                $this->executeMigration($environment, $migration, MigrationInterface::DOWN);
400
            }
401
        }
402
    }
403
404
    /**
405
     * Run database seeders against an environment.
406
     *
407
     * @param string $environment Environment
408
     * @param string $seed Seeder
409
     * @return void
410
     */
411
    public function seed($environment, $seed = null)
412
    {
413
        $seeds = $this->getSeeds();
414
        $env = $this->getEnvironment($environment);
0 ignored issues
show
Unused Code introduced by
$env 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...
415
416
        if (null === $seed) {
417
            // run all seeders
418
            foreach ($seeds as $seeder) {
419
                if (array_key_exists($seeder->getName(), $seeds)) {
420
                    $this->executeSeed($environment, $seeder);
421
                }
422
            }
423
        } else {
424
            // run only one seeder
425
            if (array_key_exists($seed, $seeds)) {
426
                $this->executeSeed($environment, $seeds[$seed]);
427
            } else {
428
                throw new \InvalidArgumentException(sprintf('The seed class "%s" does not exist', $seed));
429
            }
430
        }
431
    }
432
433
    /**
434
     * Sets the environments.
435
     *
436
     * @param array $environments Environments
437
     * @return Manager
438
     */
439
    public function setEnvironments($environments = array())
440
    {
441
        $this->environments = $environments;
442
        return $this;
443
    }
444
445
    /**
446
     * Gets the manager class for the given environment.
447
     *
448
     * @param string $name Environment Name
449
     * @throws \InvalidArgumentException
450
     * @return Environment
451
     */
452
    public function getEnvironment($name)
453
    {
454
        if (isset($this->environments[$name])) {
455
            return $this->environments[$name];
456
        }
457
458
        // check the environment exists
459
        if (!$this->getConfig()->hasEnvironment($name)) {
460
            throw new \InvalidArgumentException(sprintf(
461
                'The environment "%s" does not exist',
462
                $name
463
            ));
464
        }
465
466
        // create an environment instance and cache it
467
        $environment = new Environment($name, $this->getConfig()->getEnvironment($name));
0 ignored issues
show
Bug introduced by
It seems like $this->getConfig()->getEnvironment($name) targeting Phinx\Config\ConfigInterface::getEnvironment() can also be of type null; however, Phinx\Migration\Manager\Environment::__construct() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
468
        $this->environments[$name] = $environment;
469
        $environment->setOutput($this->getOutput());
470
471
        return $environment;
472
    }
473
474
    /**
475
     * Sets the console output.
476
     *
477
     * @param OutputInterface $output Output
478
     * @return Manager
479
     */
480
    public function setOutput(OutputInterface $output)
481
    {
482
        $this->output = $output;
483
        return $this;
484
    }
485
486
    /**
487
     * Gets the console output.
488
     *
489
     * @return OutputInterface
490
     */
491
    public function getOutput()
492
    {
493
        return $this->output;
494
    }
495
496
    /**
497
     * Sets the database migrations.
498
     *
499
     * @param array $migrations Migrations
500
     * @return Manager
501
     */
502
    public function setMigrations(array $migrations)
503
    {
504
        $this->migrations = $migrations;
505
        return $this;
506
    }
507
508
    /**
509
     * Gets an array of the database migrations.
510
     *
511
     * @throws \InvalidArgumentException
512
     * @return AbstractMigration[]
513
     */
514
    public function getMigrations()
515
    {
516
        if (null === $this->migrations) {
517
            $config = $this->getConfig();
518
            $phpFiles = glob($config->getMigrationPath() . DIRECTORY_SEPARATOR . '*.php');
519
520
            // filter the files to only get the ones that match our naming scheme
521
            $fileNames = array();
522
            /** @var AbstractMigration[] $versions */
523
            $versions = array();
524
525
            foreach ($phpFiles as $filePath) {
526
                if (Util::isValidMigrationFileName(basename($filePath))) {
527
                    $version = Util::getVersionFromFileName(basename($filePath));
528
529
                    if (isset($versions[$version])) {
530
                        throw new \InvalidArgumentException(sprintf('Duplicate migration - "%s" has the same version as "%s"', $filePath, $versions[$version]->getVersion()));
531
                    }
532
533
                    // convert the filename to a class name
534
                    $class = Util::mapFileNameToClassName(basename($filePath));
535
536
                    if (isset($fileNames[$class])) {
537
                        throw new \InvalidArgumentException(sprintf(
538
                            'Migration "%s" has the same name as "%s"',
539
                            basename($filePath),
540
                            $fileNames[$class]
541
                        ));
542
                    }
543
544
                    $fileNames[$class] = basename($filePath);
545
546
                    // load the migration file
547
                    /** @noinspection PhpIncludeInspection */
548
                    require_once $filePath;
549
                    if (!class_exists($class)) {
550
                        throw new \InvalidArgumentException(sprintf(
551
                            'Could not find class "%s" in file "%s"',
552
                            $class,
553
                            $filePath
554
                        ));
555
                    }
556
557
                    // instantiate it
558
                    $migration = new $class($version);
559
560
                    if (!($migration instanceof AbstractMigration)) {
561
                        throw new \InvalidArgumentException(sprintf(
562
                            'The class "%s" in file "%s" must extend \Phinx\Migration\AbstractMigration',
563
                            $class,
564
                            $filePath
565
                        ));
566
                    }
567
568
                    $migration->setOutput($this->getOutput());
569
                    $versions[$version] = $migration;
570
                }
571
            }
572
573
            ksort($versions);
574
            $this->setMigrations($versions);
575
        }
576
577
        return $this->migrations;
578
    }
579
580
    /**
581
     * Sets the database seeders.
582
     *
583
     * @param array $seeds Seeders
584
     * @return Manager
585
     */
586
    public function setSeeds(array $seeds)
587
    {
588
        $this->seeds = $seeds;
589
        return $this;
590
    }
591
592
    /**
593
     * Gets an array of database seeders.
594
     *
595
     * @throws \InvalidArgumentException
596
     * @return AbstractSeed[]
597
     */
598
    public function getSeeds()
599
    {
600
        if (null === $this->seeds) {
601
            $config = $this->getConfig();
602
            $phpFiles = glob($config->getSeedPath() . DIRECTORY_SEPARATOR . '*.php');
603
604
            // filter the files to only get the ones that match our naming scheme
605
            $fileNames = array();
606
            /** @var AbstractSeed[] $seeds */
607
            $seeds = array();
608
609
            foreach ($phpFiles as $filePath) {
610
                if (Util::isValidSeedFileName(basename($filePath))) {
611
                    // convert the filename to a class name
612
                    $class = pathinfo($filePath, PATHINFO_FILENAME);
613
                    $fileNames[$class] = basename($filePath);
614
615
                    // load the seed file
616
                    /** @noinspection PhpIncludeInspection */
617
                    require_once $filePath;
618
                    if (!class_exists($class)) {
619
                        throw new \InvalidArgumentException(sprintf(
620
                            'Could not find class "%s" in file "%s"',
621
                            $class,
622
                            $filePath
623
                        ));
624
                    }
625
626
                    // instantiate it
627
                    $seed = new $class();
628
629
                    if (!($seed instanceof AbstractSeed)) {
630
                        throw new \InvalidArgumentException(sprintf(
631
                            'The class "%s" in file "%s" must extend \Phinx\Seed\AbstractSeed',
632
                            $class,
633
                            $filePath
634
                        ));
635
                    }
636
637
                    $seed->setOutput($this->getOutput());
638
                    $seeds[$class] = $seed;
639
                }
640
            }
641
642
            ksort($seeds);
643
            $this->setSeeds($seeds);
644
        }
645
646
        return $this->seeds;
647
    }
648
649
    /**
650
     * Sets the config.
651
     *
652
     * @param  ConfigInterface $config Configuration Object
653
     * @return Manager
654
     */
655
    public function setConfig(ConfigInterface $config)
656
    {
657
        $this->config = $config;
658
        return $this;
659
    }
660
661
    /**
662
     * Gets the config.
663
     *
664
     * @return ConfigInterface
665
     */
666
    public function getConfig()
667
    {
668
        return $this->config;
669
    }
670
}
671