Completed
Push — master ( 2f6667...a79c5c )
by Mike
11s
created

Configuration::resolveVersionAlias()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 23
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 8.0155

Importance

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