Completed
Push — master ( e15c58...b150a8 )
by Changwan
07:08
created

MigrateManager::getAllMigrationFiles()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 7
nc 3
nop 0
dl 0
loc 10
ccs 0
cts 10
cp 0
crap 30
rs 8.8571
c 0
b 0
f 0
1
<?php
2
namespace Wandu\Database\Migrator;
3
4
use RuntimeException;
5
use DirectoryIterator;
6
use Wandu\DI\ContainerInterface;
7
8
class MigrateManager
9
{
10
    /** @var \Wandu\Database\Migrator\MigrateAdapterInterface */
11
    protected $adapter;
12
    
13
    /** @var \Wandu\Database\Migrator\Configuration */
14
    protected $config;
15
16
    /** @var \Wandu\DI\ContainerInterface */
17
    protected $container;
18
    
19
    /**
20
     * @param \Wandu\Database\Migrator\MigrateAdapterInterface $adapter
21
     * @param \Wandu\Database\Migrator\Configuration $config
22
     * @param \Wandu\DI\ContainerInterface $container
23
     */
24
    public function __construct(MigrateAdapterInterface $adapter, Configuration $config, ContainerInterface $container)
25
    {
26
        $this->adapter = $adapter;
27
        $this->config = $config;
28
        $this->container = $container;
29
    }
30
31
    /**
32
     * @return array|\Wandu\Database\Migrator\MigrationContainer[]
33
     */
34
    public function getMigrations()
35
    {
36
        $migrations = [];
37
        foreach ($this->getAllMigrationFiles() as $file) {
38
            $migrations[] = new MigrationContainer($file, $this->adapter, $this->container);
39
        }
40
        return $migrations;
41
    }
42
43
    /**
44
     * @param string $migrationId
45
     * @return \Wandu\Database\Migrator\MigrationContainer
46
     */
47
    public function getMigration($migrationId)
48
    {
49
        foreach ($this->getAllMigrationFiles() as $file) {
50
            if (strpos($file, $migrationId . '_') === 0) {
51
                return new MigrationContainer($file, $this->adapter, $this->container);
52
            }
53
        }
54
        throw new RuntimeException("there is no migration id \"{$migrationId}\".");
55
    }
56
57
    /**
58
     * @param string $migrationId
59
     */
60
    public function up($migrationId)
61
    {
62
        if (!preg_match('/^\d{6}_\d{6}$/', $migrationId)) {
63
            throw new RuntimeException("invalid migration id. it must be like 000000_000000.");
64
        }
65
        $version = $this->adapter->version($migrationId);
66
        if ($version) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $version of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
67
            throw new RuntimeException("this {$migrationId} is already applied.");
68
        }
69
        
70
        $this->getMigration($migrationId)->up();
71
    }
72
73
    /**
74
     * @param string $migrationId
75
     */
76
    public function down($migrationId)
77
    {
78
        if (!preg_match('/^\d{6}_\d{6}$/', $migrationId)) {
79
            throw new RuntimeException("invalid migration id. it must be like 000000_000000.");
80
        }
81
        $version = $this->adapter->version($migrationId);
82
        if (!$version) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $version of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
83
            throw new RuntimeException("this {$migrationId} is not already applied.");
84
        }
85
86
        $migrationName = $this->getMigrationClassFromSource($version['source']);
87
        $this->container->create($migrationName)->down();
88
        $this->adapter->down($migrationId);
89
    }
90
91
    /**
92
     * @return array|\Wandu\Database\Migrator\MigrationContainer[]
93
     */
94
    public function migrate()
95
    {
96
        $migratedMigrations = [];
97
        $migrations = $this->getMigrations();
98
        foreach ($migrations as $migration) {
99
            if (!$this->adapter->version($migration->getId())) {
100
                $migration->up();
101
                $migratedMigrations[] = $migration;
102
            }
103
        }
104
        return $migratedMigrations;
105
    }
106
    
107
    /**
108
     * @return array
109
     */
110
    protected function getAllMigrationFiles()
111
    {
112
        $files = [];
113
        foreach (new DirectoryIterator($this->config->getPath()) as $file) {
114
            if ($file->isDot() || $file->isDir() || $file->getFilename()[0] === '.') continue;
115
            $files[] = $file->getFileInfo();
116
        }
117
        sort($files);
118
        return $files;
119
    }
120
121
    /**
122
     * @param string $source
123
     * @return string
124
     */
125
    protected function getMigrationClassFromSource($source)
126
    {
127
        $definedClasses = get_declared_classes();
128
        eval('?>' . $source);
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
129
        $lastMigrationClass = null;
130
        foreach (array_diff(get_declared_classes(), $definedClasses) as $declaredClass) {
131
            if ((new \ReflectionClass($declaredClass))->isSubclassOf(MigrationInterface::class)) {
132
                $lastMigrationClass = $declaredClass; // last migration class is migration class. (maybe)
133
            }
134
        }
135
        return $lastMigrationClass;
136
    }
137
}
138