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) { |
|
|
|
|
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) { |
|
|
|
|
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); |
|
|
|
|
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
|
|
|
|
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.