Completed
Push — master ( ee300a...e46858 )
by Mike
10s
created

Configuration::resolveVersionAlias()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 23
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 8.0109

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 17
cts 18
cp 0.9444
rs 6.1403
c 0
b 0
f 0
cc 8
eloc 18
nc 8
nop 1
crap 8.0109
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Migrations\Configuration;
21
22
use Doctrine\DBAL\Connection;
23
use Doctrine\DBAL\Connections\MasterSlaveConnection;
24
use Doctrine\DBAL\Migrations\Finder\MigrationDeepFinderInterface;
25
use Doctrine\DBAL\Migrations\MigrationException;
26
use Doctrine\DBAL\Migrations\OutputWriter;
27
use Doctrine\DBAL\Migrations\Version;
28
use Doctrine\DBAL\Migrations\Finder\MigrationFinderInterface;
29
use Doctrine\DBAL\Migrations\Finder\RecursiveRegexFinder;
30
use Doctrine\DBAL\Schema\Column;
31
use Doctrine\DBAL\Schema\Table;
32
use Doctrine\DBAL\Types\Type;
33
34
/**
35
 * Default Migration Configuration object used for configuring an instance of
36
 * the Migration class. Set the connection, version table name, register migration
37
 * classes/versions, etc.
38
 *
39
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
40
 * @link        www.doctrine-project.org
41
 * @since       2.0
42
 * @author      Jonathan H. Wage <[email protected]>
43
 */
44
class Configuration
45
{
46
    /**
47
     * Configure versions to be organized by year.
48
     */
49
    const VERSIONS_ORGANIZATION_BY_YEAR = 'year';
50
51
    /**
52
     * Configure versions to be organized by year and month.
53
     *
54
     * @var string
55
     */
56
    const VERSIONS_ORGANIZATION_BY_YEAR_AND_MONTH = 'year_and_month';
57
58
    /**
59
     * The date format for new version numbers
60
     */
61
    const VERSION_FORMAT = 'YmdHis';
62
63
    /**
64
     * Name of this set of migrations
65
     *
66
     * @var string
67
     */
68
    private $name;
69
70
    /**
71
     * Flag for whether or not the migration table has been created
72
     *
73
     * @var boolean
74
     */
75
    private $migrationTableCreated = false;
76
77
    /**
78
     * Connection instance to use for migrations
79
     *
80
     * @var Connection
81
     */
82
    private $connection;
83
84
    /**
85
     * OutputWriter instance for writing output during migrations
86
     *
87
     * @var OutputWriter
88
     */
89
    private $outputWriter;
90
91
    /**
92
     * The migration finder implementation -- used to load migrations from a
93
     * directory.
94
     *
95
     * @var MigrationFinderInterface
96
     */
97
    private $migrationFinder;
98
99
    /**
100
     * The migration table name to track versions in
101
     *
102
     * @var string
103
     */
104
    private $migrationsTableName = 'doctrine_migration_versions';
105
106
    /**
107
     * The migration column name to track versions in
108
     *
109
     * @var string
110
     */
111
    private $migrationsColumnName = 'version';
112
113
    /**
114
     * The path to a directory where new migration classes will be written
115
     *
116
     * @var string
117
     */
118
    private $migrationsDirectory;
119
120
    /**
121
     * Namespace the migration classes live in
122
     *
123
     * @var string
124
     */
125
    private $migrationsNamespace;
126
127
    /**
128
     * Array of the registered migrations
129
     *
130
     * @var Version[]
131
     */
132
    private $migrations = [];
133
134
    /**
135
     * Versions are organized by year.
136
     *
137
     * @var boolean
138
     */
139
    private $migrationsAreOrganizedByYear = false;
140
141
    /**
142
     * Versions are organized by year and month.
143
     *
144
     * @var boolean
145
     */
146
    private $migrationsAreOrganizedByYearAndMonth = false;
147
148
    /**
149
     * Construct a migration configuration object.
150
     *
151
     * @param Connection               $connection   A Connection instance
152
     * @param OutputWriter             $outputWriter A OutputWriter instance
153
     * @param MigrationFinderInterface $finder       Migration files finder
154
     */
155 265
    public function __construct(Connection $connection, OutputWriter $outputWriter = null, MigrationFinderInterface $finder = null)
156
    {
157 265
        $this->connection = $connection;
158 265
        if ($outputWriter === null) {
159 227
            $outputWriter = new OutputWriter();
160 227
        }
161 265
        $this->outputWriter = $outputWriter;
162 265
        if ($finder === null) {
163 261
            $finder = new RecursiveRegexFinder();
164 261
        }
165 265
        $this->migrationFinder = $finder;
166 265
    }
167
168
    /**
169
     * @return bool
170
     */
171 22
    public function areMigrationsOrganizedByYear()
172
    {
173 22
        return $this->migrationsAreOrganizedByYear;
174 9
    }
175
176
    /**
177
     * @return bool
178
     */
179 22
    public function areMigrationsOrganizedByYearAndMonth()
180
    {
181 22
        return $this->migrationsAreOrganizedByYearAndMonth;
182
    }
183
184
    /**
185
     * Validation that this instance has all the required properties configured
186
     *
187
     * @throws MigrationException
188
     */
189 142
    public function validate()
190
    {
191 142
        if (!$this->migrationsNamespace) {
192 10
            throw MigrationException::migrationsNamespaceRequired();
193
        }
194 132
        if (!$this->migrationsDirectory) {
195 10
            throw MigrationException::migrationsDirectoryRequired();
196
        }
197 122
    }
198
199
    /**
200
     * Set the name of this set of migrations
201
     *
202
     * @param string $name The name of this set of migrations
203
     */
204 63
    public function setName($name)
205
    {
206 63
        $this->name = $name;
207 63
    }
208
209
    /**
210
     * Returns the name of this set of migrations
211
     *
212
     * @return string $name The name of this set of migrations
213
     */
214 15
    public function getName()
215
    {
216 15
        return $this->name;
217
    }
218
219
    /**
220
     * Sets the output writer.
221
     *
222
     * @param OutputWriter $outputWriter
223
     */
224 3
    public function setOutputWriter(OutputWriter $outputWriter)
225
    {
226 3
        $this->outputWriter = $outputWriter;
227 3
    }
228
229
    /**
230
     * Returns the OutputWriter instance
231
     *
232
     * @return OutputWriter $outputWriter  The OutputWriter instance
233
     */
234 112
    public function getOutputWriter()
235
    {
236 112
        return $this->outputWriter;
237
    }
238
239
    /**
240
     * Returns a timestamp version as a formatted date
241
     *
242
     * @param string $version
243
     *
244
     * @return string The formatted version
245
     * @deprecated
246
     */
247
    public function formatVersion($version)
248
    {
249
        return $this->getDateTime($version);
250
    }
251
252
    /**
253
     * Returns the datetime of a migration
254
     *
255
     * @param $version
256
     * @return string
257
     */
258 21
    public function getDateTime($version)
259
    {
260 21
        $datetime = str_replace('Version', '', $version);
261 21
        $datetime = \DateTime::createFromFormat('YmdHis', $datetime);
262
263 21
        if ($datetime === false) {
264 7
            return '';
265
        }
266
267 15
        return $datetime->format('Y-m-d H:i:s');
268
    }
269
270
    /**
271
     * Returns the Connection instance
272
     *
273
     * @return Connection $connection  The Connection instance
274
     */
275 112
    public function getConnection()
276
    {
277 112
        return $this->connection;
278
    }
279
280
    /**
281
     * Set the migration table name
282
     *
283
     * @param string $tableName The migration table name
284
     */
285 82
    public function setMigrationsTableName($tableName)
286
    {
287 82
        $this->migrationsTableName = $tableName;
288 82
    }
289
290
    /**
291
     * Returns the migration table name
292
     *
293
     * @return string $migrationsTableName The migration table name
294
     */
295 69
    public function getMigrationsTableName()
296
    {
297 69
        return $this->migrationsTableName;
298
    }
299
300
    /**
301
     * Set the migration column name
302
     *
303
     * @param string $columnName The migration column name
304
     */
305 53
    public function setMigrationsColumnName($columnName)
306
    {
307 53
        $this->migrationsColumnName = $columnName;
308 53
    }
309
310
    /**
311
     * Returns the migration column name
312
     *
313
     * @return string $migrationsColumnName The migration column name
314
     */
315 63
    public function getMigrationsColumnName()
316
    {
317 63
        return $this->migrationsColumnName;
318
    }
319
320
    /**
321
     * Set the new migrations directory where new migration classes are generated
322
     *
323
     * @param string $migrationsDirectory The new migrations directory
324
     */
325 178
    public function setMigrationsDirectory($migrationsDirectory)
326
    {
327 178
        $this->migrationsDirectory = $migrationsDirectory;
328 178
    }
329
330
    /**
331
     * Returns the new migrations directory where new migration classes are generated
332
     *
333
     * @return string $migrationsDirectory The new migrations directory
334
     */
335 64
    public function getMigrationsDirectory()
336
    {
337 64
        return $this->migrationsDirectory;
338
    }
339
340
    /**
341
     * Set the migrations namespace
342
     *
343
     * @param string $migrationsNamespace The migrations namespace
344
     */
345 190
    public function setMigrationsNamespace($migrationsNamespace)
346
    {
347 190
        $this->migrationsNamespace = $migrationsNamespace;
348 190
    }
349
350
    /**
351
     * Returns the migrations namespace
352
     *
353
     * @return string $migrationsNamespace The migrations namespace
354
     */
355 91
    public function getMigrationsNamespace()
356
    {
357 91
        return $this->migrationsNamespace;
358
    }
359
360
    /**
361
     * Set the implementation of the migration finder.
362
     *
363
     * @param MigrationFinderInterface $finder The new migration finder
364
     * @throws MigrationException
365
     */
366 11
    public function setMigrationsFinder(MigrationFinderInterface $finder)
367
    {
368 8
        if (($this->migrationsAreOrganizedByYear || $this->migrationsAreOrganizedByYearAndMonth) &&
369 8
            !($finder instanceof MigrationDeepFinderInterface)) {
370
371 11
            throw MigrationException::configurationIncompatibleWithFinder(
372 4
                'organize-migrations',
373
                $finder
374 4
            );
375
        }
376
377 4
        $this->migrationFinder = $finder;
378 4
    }
379
380
    /**
381
     * Register migrations from a given directory. Recursively finds all files
382
     * with the pattern VersionYYYYMMDDHHMMSS.php as the filename and registers
383
     * them as migrations.
384
     *
385
     * @param string $path The root directory to where some migration classes live.
386
     *
387
     * @return Version[] The array of migrations registered.
388
     */
389 103
    public function registerMigrationsFromDirectory($path)
390
    {
391 103
        $this->validate();
392
393 87
        return $this->registerMigrations($this->findMigrations($path));
394
    }
395
396
    /**
397
     * Register a single migration version to be executed by a AbstractMigration
398
     * class.
399
     *
400
     * @param string $version The version of the migration in the format YYYYMMDDHHMMSS.
401
     * @param string $class   The migration class to execute for the version.
402
     *
403
     * @return Version
404
     *
405
     * @throws MigrationException
406
     */
407 65
    public function registerMigration($version, $class)
408
    {
409 65
        $this->ensureMigrationClassExists($class);
410
411 64
        $version = (string) $version;
412 64
        $class = (string) $class;
413 64
        if (isset($this->migrations[$version])) {
414 1
            throw MigrationException::duplicateMigrationVersion($version, get_class($this->migrations[$version]));
415
        }
416 64
        $version = new Version($this, $version, $class);
417 64
        $this->migrations[$version->getVersion()] = $version;
418 64
        ksort($this->migrations, SORT_STRING);
419
420 64
        return $version;
421
    }
422
423
    /**
424
     * Register an array of migrations. Each key of the array is the version and
425
     * the value is the migration class name.
426
     *
427
     *
428
     * @param array $migrations
429
     *
430
     * @return Version[]
431
     */
432 91
    public function registerMigrations(array $migrations)
433
    {
434 91
        $versions = [];
435 91
        foreach ($migrations as $version => $class) {
436 34
            $versions[] = $this->registerMigration($version, $class);
437 90
        }
438
439 90
        return $versions;
440
    }
441
442
    /**
443
     * Get the array of registered migration versions.
444
     *
445
     * @return Version[] $migrations
446
     */
447 31
    public function getMigrations()
448
    {
449 31
        return $this->migrations;
450
    }
451
452
    /**
453
     * Returns the Version instance for a given version in the format YYYYMMDDHHMMSS.
454
     *
455
     * @param string $version The version string in the format YYYYMMDDHHMMSS.
456
     *
457
     * @return Version
458
     *
459
     * @throws MigrationException Throws exception if migration version does not exist.
460
     */
461 19
    public function getVersion($version)
462
    {
463 19
        if (empty($this->migrations)) {
464 7
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
465 7
        }
466
467 19
        if (!isset($this->migrations[$version])) {
468 2
            throw MigrationException::unknownMigrationVersion($version);
469
        }
470
471 17
        return $this->migrations[$version];
472
    }
473
474
    /**
475
     * Check if a version exists.
476
     *
477
     * @param string $version
478
     *
479
     * @return boolean
480
     */
481 21
    public function hasVersion($version)
482
    {
483 21
        if (empty($this->migrations)) {
484 4
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
485 2
        }
486
487 19
        return isset($this->migrations[$version]);
488
    }
489
490
    /**
491
     * Check if a version has been migrated or not yet
492
     *
493
     * @param Version $version
494
     *
495
     * @return boolean
496
     */
497 21
    public function hasVersionMigrated(Version $version)
498
    {
499 21
        $this->connect();
500 21
        $this->createMigrationTable();
501
502 21
        $version = $this->connection->fetchColumn(
503 21
            "SELECT " . $this->migrationsColumnName . " FROM " . $this->migrationsTableName . " WHERE " . $this->migrationsColumnName . " = ?",
504 21
            [$version->getVersion()]
505 21
        );
506
507 21
        return $version !== false;
508
    }
509
510
    /**
511
     * Returns all migrated versions from the versions table, in an array.
512
     *
513
     * @return Version[]
514
     */
515 37 View Code Duplication
    public function getMigratedVersions()
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...
516
    {
517 37
        $this->connect();
518 37
        $this->createMigrationTable();
519
520 37
        $ret = $this->connection->fetchAll("SELECT " . $this->migrationsColumnName . " FROM " . $this->migrationsTableName);
521
522 37
        return array_map('current', $ret);
523
    }
524
525
    /**
526
     * Returns an array of available migration version numbers.
527
     *
528
     * @return array
529
     */
530 15
    public function getAvailableVersions()
531
    {
532 15
        $availableVersions = [];
533
534 15
        if (empty($this->migrations)) {
535 3
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
536 1
        }
537
538 13
        foreach ($this->migrations as $migration) {
539 13
            $availableVersions[] = $migration->getVersion();
540 13
        }
541
542 13
        return $availableVersions;
543
    }
544
545
    /**
546
     * Returns the current migrated version from the versions table.
547
     *
548
     * @return string
549
     */
550 39
    public function getCurrentVersion()
551
    {
552 39
        $this->connect();
553 39
        $this->createMigrationTable();
554
555 37
        if (empty($this->migrations)) {
556 15
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
557 15
        }
558
559 37
        $where = null;
560 37
        if (!empty($this->migrations)) {
561 33
            $migratedVersions = [];
562 33
            foreach ($this->migrations as $migration) {
563 33
                $migratedVersions[] = sprintf("'%s'", $migration->getVersion());
564 33
            }
565 33
            $where = " WHERE " . $this->migrationsColumnName . " IN (" . implode(', ', $migratedVersions) . ")";
566 33
        }
567
568 37
        $sql = sprintf("SELECT %s FROM %s%s ORDER BY %s DESC",
569 37
            $this->migrationsColumnName, $this->migrationsTableName, $where, $this->migrationsColumnName
570 37
        );
571
572 37
        $sql = $this->connection->getDatabasePlatform()->modifyLimitQuery($sql, 1);
573 37
        $result = $this->connection->fetchColumn($sql);
574
575 37
        return $result !== false ? (string) $result : '0';
576
    }
577
578
    /**
579
     * Returns the version prior to the current version.
580
     *
581
     * @return string|null A version string, or null if the current version is
582
     *                     the first.
583
     */
584 9
    public function getPrevVersion()
585
    {
586 9
        return $this->getRelativeVersion($this->getCurrentVersion(), -1);
587
    }
588
589
    /**
590
     * Returns the version following the current version.
591
     *
592
     * @return string|null A version string, or null if the current version is
593
     *                     the latest.
594
     */
595 9
    public function getNextVersion()
596
    {
597 9
        return $this->getRelativeVersion($this->getCurrentVersion(), 1);
598
    }
599
600
    /**
601
     * Returns the version with the specified offset to the specified version.
602
     *
603
     * @return string|null A version string, or null if the specified version
604
     *                     is unknown or the specified delta is not within the
605
     *                     list of available versions.
606
     */
607 15
    public function getRelativeVersion($version, $delta)
608
    {
609 15
        if (empty($this->migrations)) {
610 5
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
611 3
        }
612
613 13
        $versions = array_map('strval', array_keys($this->migrations));
614 13
        array_unshift($versions, '0');
615 13
        $offset = array_search((string)$version, $versions);
616 13
        if ($offset === false || !isset($versions[$offset + $delta])) {
617
            // Unknown version or delta out of bounds.
618 11
            return null;
619
        }
620
621 11
        return $versions[$offset + $delta];
622
    }
623
624
    /**
625
     * Returns the version with the specified to the current version.
626
     *
627
     * @return string|null A version string, or null if the specified delta is
628
     *                     not within the list of available versions.
629
     */
630 1
    public function getDeltaVersion($delta) {
631 1
        $symbol = substr($delta, 0, 1);
632 1
        $number = (int) substr($delta, 1);
633
634 1
        if ($number <= 0) {
635
            return null;
636
        }
637
638 1
        if ($symbol == "+" || $symbol == "-") {
639 1
            return $this->getRelativeVersion($this->getCurrentVersion(), (int) $delta);
640
        }
641
642
        return null;
643
    }
644
645
    /**
646
     * Returns the version number from an alias.
647
     *
648
     * Supported aliases are:
649
     * - first: The very first version before any migrations have been run.
650
     * - current: The current version.
651
     * - prev: The version prior to the current version.
652
     * - next: The version following the current version.
653
     * - latest: The latest available version.
654
     *
655
     * If an existing version number is specified, it is returned verbatimly.
656
     *
657
     * @return string|null A version number, or null if the specified alias
658
     *                     does not map to an existing version, e.g. if "next"
659
     *                     is passed but the current version is already the
660
     *                     latest.
661
     */
662 8
    public function resolveVersionAlias($alias)
663
    {
664 8
        if ($this->hasVersion($alias)) {
665 1
            return $alias;
666
        }
667
        switch ($alias) {
668 8
            case 'first':
669 1
                return '0';
670 8
            case 'current':
671 8
                return $this->getCurrentVersion();
672 8
            case 'prev':
673 8
                return $this->getPrevVersion();
674 8
            case 'next':
675 8
                return $this->getNextVersion();
676 8
            case 'latest':
677 8
                return $this->getLatestVersion();
678 1
            default:
679 1
                if (substr($alias, 0, 7) == 'current') {
680
                    return $this->getDeltaVersion(substr($alias, 7));
681
                }
682 1
                return null;
683 1
        }
684
    }
685
686
    /**
687
     * Returns the total number of executed migration versions
688
     *
689
     * @return integer
690
     */
691 1 View Code Duplication
    public function getNumberOfExecutedMigrations()
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...
692
    {
693 1
        $this->connect();
694 1
        $this->createMigrationTable();
695
696 1
        $result = $this->connection->fetchColumn("SELECT COUNT(" . $this->migrationsColumnName . ") FROM " . $this->migrationsTableName);
697
698 1
        return $result !== false ? $result : 0;
699
    }
700
701
    /**
702
     * Returns the total number of available migration versions
703
     *
704
     * @return integer
705
     */
706 5
    public function getNumberOfAvailableMigrations()
707
    {
708 5
        if (empty($this->migrations)) {
709 4
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
710 2
        }
711
712 3
        return count($this->migrations);
713
    }
714
715
    /**
716
     * Returns the latest available migration version.
717
     *
718
     * @return string The version string in the format YYYYMMDDHHMMSS.
719
     */
720 24
    public function getLatestVersion()
721
    {
722 24
        if (empty($this->migrations)) {
723 7
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
724 5
        }
725
726 22
        $versions = array_keys($this->migrations);
727 22
        $latest = end($versions);
728
729 22
        return $latest !== false ? (string) $latest : '0';
730
    }
731
732
    /**
733
     * Create the migration table to track migrations with.
734
     *
735
     * @return boolean Whether or not the table was created.
736
     */
737 60
    public function createMigrationTable()
738
    {
739 60
        $this->validate();
740
741 58
        if ($this->migrationTableCreated) {
742 47
            return false;
743
        }
744
745 58
        $this->connect();
746 58
        if ($this->connection->getSchemaManager()->tablesExist([$this->migrationsTableName])) {
747 4
            $this->migrationTableCreated = true;
748
749 4
            return false;
750
        }
751
752
        $columns = [
753 57
            $this->migrationsColumnName => new Column($this->migrationsColumnName, Type::getType('string'), ['length' => 255]),
754 57
        ];
755 57
        $table = new Table($this->migrationsTableName, $columns);
756 57
        $table->setPrimaryKey([$this->migrationsColumnName]);
757 57
        $this->connection->getSchemaManager()->createTable($table);
758
759 57
        $this->migrationTableCreated = true;
760
761 57
        return true;
762
    }
763
764
    /**
765
     * Returns the array of migrations to executed based on the given direction
766
     * and target version number.
767
     *
768
     * @param string $direction The direction we are migrating.
769
     * @param string $to        The version to migrate to.
770
     *
771
     * @return Version[] $migrations   The array of migrations we can execute.
772
     */
773 36
    public function getMigrationsToExecute($direction, $to)
774
    {
775 36
        if (empty($this->migrations)) {
776 11
            $this->registerMigrationsFromDirectory($this->getMigrationsDirectory());
777 5
        }
778
779 30
        if ($direction === Version::DIRECTION_DOWN) {
780 7
            if (count($this->migrations)) {
781 7
                $allVersions = array_reverse(array_keys($this->migrations));
782 7
                $classes = array_reverse(array_values($this->migrations));
783 7
                $allVersions = array_combine($allVersions, $classes);
784 7
            } else {
785
                $allVersions = [];
786
            }
787 7
        } else {
788 28
            $allVersions = $this->migrations;
789
        }
790 30
        $versions = [];
791 30
        $migrated = $this->getMigratedVersions();
792 30
        foreach ($allVersions as $version) {
793 28
            if ($this->shouldExecuteMigration($direction, $version, $to, $migrated)) {
794 25
                $versions[$version->getVersion()] = $version;
795 25
            }
796 30
        }
797
798 30
        return $versions;
799
    }
800
801
    /**
802
     * Find all the migrations in a given directory.
803
     *
804
     * @param   string $path the directory to search.
805
     * @return  array
806
     */
807 87
    protected function findMigrations($path)
808
    {
809 87
        return $this->migrationFinder->findMigrations($path, $this->getMigrationsNamespace());
810
    }
811
812
    /**
813
     * @param bool $migrationsAreOrganizedByYear
814
     * @throws MigrationException
815
     */
816 9
    public function setMigrationsAreOrganizedByYear($migrationsAreOrganizedByYear = true)
817
    {
818 9
        $this->ensureOrganizeMigrationsIsCompatibleWithFinder();
819
820 5
        $this->migrationsAreOrganizedByYear = $migrationsAreOrganizedByYear;
821 5
    }
822
823
    /**
824
     * @param bool $migrationsAreOrganizedByYearAndMonth
825
     * @throws MigrationException
826
     */
827 10
    public function setMigrationsAreOrganizedByYearAndMonth($migrationsAreOrganizedByYearAndMonth = true)
828
    {
829 10
        $this->ensureOrganizeMigrationsIsCompatibleWithFinder();
830
831 10
        $this->migrationsAreOrganizedByYear = $migrationsAreOrganizedByYearAndMonth;
832 10
        $this->migrationsAreOrganizedByYearAndMonth = $migrationsAreOrganizedByYearAndMonth;
833 10
    }
834
835
    /**
836
     * Generate a new migration version. A version is (usually) a datetime string.
837
     *
838
     * @param DateTimeInterface|null $now Defaults to the current time in UTC
839
     * @return string The newly generated version
840
     */
841 9
    public function generateVersionNumber(\DateTimeInterface $now=null)
842
    {
843 9
        $now = $now ?: new \DateTime('now', new \DateTimeZone('UTC'));
844
845 9
        return $now->format(self::VERSION_FORMAT);
846
    }
847
848
    /**
849
     * Explicitely opens the database connection. This is done to play nice
850
     * with DBAL's MasterSlaveConnection. Which, in some cases, connects to a
851
     * follower when fetching the executed migrations. If a follower is lagging
852
     * significantly behind that means the migrations system may see unexecuted
853
     * migrations that were actually executed earlier.
854
     *
855
     * @return bool The same value returned from the `connect` method
856
     */
857 60
    protected function connect()
858
    {
859 60
        if ($this->connection instanceof MasterSlaveConnection) {
860 1
            return $this->connection->connect('master');
861
        }
862
863 59
        return $this->connection->connect();
864
    }
865
866
    /**
867
     * @throws MigrationException
868
     */
869 19
    private function ensureOrganizeMigrationsIsCompatibleWithFinder()
870
    {
871 19
        if (!($this->migrationFinder instanceof MigrationDeepFinderInterface)) {
872 4
            throw MigrationException::configurationIncompatibleWithFinder(
873 4
                'organize-migrations',
874 4
                $this->migrationFinder
875 4
            );
876
        }
877 15
    }
878
879
    /**
880
     * Check if we should execute a migration for a given direction and target
881
     * migration version.
882
     *
883
     * @param string  $direction The direction we are migrating.
884
     * @param Version $version   The Version instance to check.
885
     * @param string  $to        The version we are migrating to.
886
     * @param array   $migrated  Migrated versions array.
887
     *
888
     * @return boolean
889
     */
890 28
    private function shouldExecuteMigration($direction, Version $version, $to, $migrated)
891
    {
892 28 View Code Duplication
        if ($direction === Version::DIRECTION_DOWN) {
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...
893 7
            if (!in_array($version->getVersion(), $migrated)) {
894 4
                return false;
895
            }
896
897 5
            return $version->getVersion() > $to;
898
        }
899
900 26 View Code Duplication
        if ($direction === Version::DIRECTION_UP) {
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...
901 26
            if (in_array($version->getVersion(), $migrated)) {
902 8
                return false;
903
            }
904
905 25
            return $version->getVersion() <= $to;
906
        }
907
    }
908
909
    /**
910
     * @param string $class
911
     */
912 65
    private function ensureMigrationClassExists($class)
913
    {
914 65
        if ( ! class_exists($class)) {
915 1
            throw MigrationException::migrationClassNotFound($class, $this->getMigrationsNamespace());
916
        }
917 64
    }
918
}
919