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 Phinx\Config\ConfigInterface; |
32
|
|
|
use Phinx\Config\NamespaceAwareInterface; |
33
|
|
|
use Phinx\Migration\Manager\Environment; |
34
|
|
|
use Phinx\Seed\AbstractSeed; |
35
|
|
|
use Phinx\Seed\SeedInterface; |
36
|
|
|
use Phinx\Util\Util; |
37
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
38
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
39
|
|
|
|
40
|
|
|
class Manager |
41
|
|
|
{ |
42
|
|
|
const BREAKPOINT_TOGGLE = 1; |
43
|
|
|
const BREAKPOINT_SET = 2; |
44
|
|
|
const BREAKPOINT_UNSET = 3; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var \Phinx\Config\ConfigInterface |
48
|
|
|
*/ |
49
|
|
|
protected $config; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var \Symfony\Component\Console\Input\InputInterface |
53
|
|
|
*/ |
54
|
|
|
protected $input; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var \Symfony\Component\Console\Output\OutputInterface |
58
|
|
|
*/ |
59
|
|
|
protected $output; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var array |
63
|
|
|
*/ |
64
|
|
|
protected $environments; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var array |
68
|
|
|
*/ |
69
|
|
|
protected $migrations; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @var array |
73
|
|
|
*/ |
74
|
|
|
protected $seeds; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @var integer |
78
|
|
|
*/ |
79
|
|
|
const EXIT_STATUS_DOWN = 3; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @var integer |
83
|
|
|
*/ |
84
|
|
|
const EXIT_STATUS_MISSING = 2; |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Class Constructor. |
88
|
|
|
* |
89
|
432 |
|
* @param \Phinx\Config\ConfigInterface $config Configuration Object |
90
|
|
|
* @param \Symfony\Component\Console\Input\InputInterface $input Console Input |
91
|
432 |
|
* @param \Symfony\Component\Console\Output\OutputInterface $output Console Output |
92
|
432 |
|
*/ |
93
|
432 |
|
public function __construct(ConfigInterface $config, InputInterface $input, OutputInterface $output) |
94
|
432 |
|
{ |
95
|
|
|
$this->setConfig($config); |
96
|
|
|
$this->setInput($input); |
97
|
|
|
$this->setOutput($output); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Prints the specified environment's migration status. |
102
|
|
|
* |
103
|
22 |
|
* @param string $environment |
104
|
|
|
* @param null $format |
105
|
22 |
|
* @return int 0 if all migrations are up, or an error code |
106
|
22 |
|
*/ |
107
|
22 |
|
public function printStatus($environment, $format = null) |
108
|
22 |
|
{ |
109
|
22 |
|
$output = $this->getOutput(); |
110
|
22 |
|
$hasDownMigration = false; |
111
|
|
|
$hasMissingMigration = false; |
112
|
|
|
$migrations = $this->getMigrations($environment); |
113
|
21 |
|
$migrationCount = 0; |
114
|
|
|
$missingCount = 0; |
115
|
21 |
|
$pendingMigrationCount = 0; |
116
|
21 |
|
if (count($migrations)) { |
117
|
19 |
|
// TODO - rewrite using Symfony Table Helper as we already have this library |
118
|
19 |
|
// included and it will fix formatting issues (e.g drawing the lines) |
119
|
2 |
|
$output->writeln(''); |
120
|
1 |
|
|
121
|
1 |
|
switch ($this->getConfig()->getVersionOrder()) { |
122
|
1 |
|
case \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME: |
123
|
1 |
|
$migrationIdAndStartedHeader = "<info>[Migration ID]</info> Started "; |
124
|
21 |
|
break; |
125
|
|
|
case \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME: |
126
|
20 |
|
$migrationIdAndStartedHeader = "Migration ID <info>[Started ]</info>"; |
127
|
20 |
|
break; |
128
|
|
|
default: |
129
|
20 |
|
throw new \RuntimeException('Invalid version_order configuration option'); |
130
|
20 |
|
} |
131
|
|
|
|
132
|
|
|
$output->writeln(" Status $migrationIdAndStartedHeader Finished Migration Name "); |
133
|
17 |
|
$output->writeln('----------------------------------------------------------------------------------'); |
134
|
20 |
|
|
135
|
|
|
$env = $this->getEnvironment($environment); |
136
|
20 |
|
$versions = $env->getVersionLog(); |
137
|
|
|
|
138
|
20 |
|
$maxNameLength = $versions ? max(array_map(function ($version) { |
139
|
|
|
return strlen($version['migration_name']); |
140
|
|
|
}, $versions)) : 0; |
141
|
20 |
|
|
142
|
|
|
$missingVersions = array_diff_key($versions, $migrations); |
143
|
20 |
|
$missingCount = count($missingVersions); |
144
|
17 |
|
|
145
|
13 |
|
$hasMissingMigration = !empty($missingVersions); |
146
|
13 |
|
|
147
|
13 |
|
// get the migrations sorted in the same way as the versions |
148
|
20 |
|
$sortedMigrations = []; |
149
|
|
|
|
150
|
20 |
|
foreach ($versions as $versionCreationTime => $version) { |
151
|
|
|
if (isset($migrations[$versionCreationTime])) { |
152
|
|
|
array_push($sortedMigrations, $migrations[$versionCreationTime]); |
153
|
4 |
|
unset($migrations[$versionCreationTime]); |
154
|
4 |
|
} |
155
|
|
|
} |
156
|
4 |
|
|
157
|
4 |
|
if (empty($sortedMigrations) && !empty($missingVersions)) { |
158
|
4 |
|
// this means we have no up migrations, so we write all the missing versions already so they show up |
159
|
|
|
// before any possible down migration |
160
|
|
|
foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { |
161
|
|
|
$this->printMissingVersion($missingVersion, $maxNameLength); |
162
|
20 |
|
|
163
|
13 |
|
unset($missingVersions[$missingVersionCreationTime]); |
164
|
13 |
|
} |
165
|
|
|
} |
166
|
20 |
|
|
167
|
20 |
|
// any migration left in the migrations (ie. not unset when sorting the migrations by the version order) is |
168
|
20 |
|
// a migration that is down, so we add them to the end of the sorted migrations list |
169
|
|
|
if (!empty($migrations)) { |
170
|
13 |
|
$sortedMigrations = array_merge($sortedMigrations, $migrations); |
171
|
6 |
|
} |
172
|
6 |
|
|
173
|
4 |
|
$migrationCount = count($sortedMigrations); |
174
|
|
|
foreach ($sortedMigrations as $migration) { |
175
|
3 |
|
$version = array_key_exists($migration->getVersion(), $versions) ? $versions[$migration->getVersion()] : false; |
176
|
|
|
if ($version) { |
177
|
|
|
// check if there are missing versions before this version |
178
|
|
|
foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { |
179
|
|
|
if ($this->getConfig()->isVersionOrderCreationTime()) { |
180
|
|
|
if ($missingVersion['version'] > $version['version']) { |
181
|
|
|
break; |
182
|
|
|
} |
183
|
|
|
} else { |
184
|
3 |
|
if ($missingVersion['start_time'] > $version['start_time']) { |
185
|
|
|
break; |
186
|
3 |
|
} elseif ($missingVersion['start_time'] == $version['start_time'] && |
187
|
13 |
|
$missingVersion['version'] > $version['version']) { |
188
|
|
|
break; |
189
|
13 |
|
} |
190
|
13 |
|
} |
191
|
13 |
|
|
192
|
13 |
|
$this->printMissingVersion($missingVersion, $maxNameLength); |
193
|
|
|
|
194
|
20 |
|
unset($missingVersions[$missingVersionCreationTime]); |
195
|
|
|
} |
196
|
20 |
|
|
197
|
20 |
|
$status = ' <info>up</info> '; |
198
|
20 |
|
} else { |
199
|
20 |
|
$pendingMigrationCount++; |
200
|
20 |
|
$hasDownMigration = true; |
201
|
20 |
|
$status = ' <error>down</error> '; |
202
|
20 |
|
} |
203
|
20 |
|
$maxNameLength = max($maxNameLength, strlen($migration->getName())); |
204
|
|
|
|
205
|
20 |
|
$output->writeln(sprintf( |
206
|
1 |
|
'%s %14.0f %19s %19s <comment>%s</comment>', |
207
|
1 |
|
$status, |
208
|
|
|
$migration->getVersion(), |
209
|
20 |
|
$version['start_time'], |
210
|
20 |
|
$version['end_time'], |
211
|
20 |
|
$migration->getName() |
212
|
|
|
)); |
213
|
|
|
|
214
|
20 |
|
if ($version && $version['breakpoint']) { |
215
|
4 |
|
$output->writeln(' <error>BREAKPOINT SET</error>'); |
216
|
|
|
} |
217
|
4 |
|
|
218
|
20 |
|
$migrations[] = ['migration_status' => trim(strip_tags($status)), 'migration_id' => sprintf('%14.0f', $migration->getVersion()), 'migration_name' => $migration->getName()]; |
219
|
20 |
|
unset($versions[$migration->getVersion()]); |
220
|
|
|
} |
221
|
1 |
|
|
222
|
1 |
|
// and finally add any possibly-remaining missing migrations |
223
|
|
|
foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { |
224
|
|
|
$this->printMissingVersion($missingVersion, $maxNameLength); |
225
|
|
|
|
226
|
21 |
|
unset($missingVersions[$missingVersionCreationTime]); |
227
|
21 |
|
} |
228
|
|
|
} else { |
229
|
|
|
// there are no migrations |
230
|
|
|
$output->writeln(''); |
231
|
|
|
$output->writeln('There are no available migrations. Try creating one using the <info>create</info> command.'); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
// write an empty line |
235
|
|
|
$output->writeln(''); |
236
|
|
|
if ($format !== null) { |
237
|
|
|
switch ($format) { |
238
|
|
|
case 'json': |
239
|
|
|
$output->writeln(json_encode( |
240
|
|
|
[ |
241
|
|
|
'pending_count' => $pendingMigrationCount, |
242
|
21 |
|
'missing_count' => $missingCount, |
243
|
10 |
|
'total_count' => $migrationCount + $missingCount, |
244
|
11 |
|
'migrations' => $migrations |
245
|
6 |
|
] |
246
|
|
|
)); |
247
|
5 |
|
break; |
248
|
|
|
default: |
249
|
|
|
$output->writeln('<info>Unsupported format: ' . $format . '</info>'); |
250
|
|
|
} |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
if ($hasMissingMigration) { |
254
|
|
|
return self::EXIT_STATUS_MISSING; |
255
|
|
|
} elseif ($hasDownMigration) { |
256
|
|
|
return self::EXIT_STATUS_DOWN; |
257
|
10 |
|
} else { |
258
|
|
|
return 0; |
259
|
10 |
|
} |
260
|
10 |
|
} |
261
|
10 |
|
|
262
|
10 |
|
/** |
263
|
10 |
|
* Print Missing Version |
264
|
10 |
|
* |
265
|
10 |
|
* @param array $version The missing version to print (in the format returned by Environment.getVersionLog). |
266
|
|
|
* @param int $maxNameLength The maximum migration name length. |
267
|
10 |
|
*/ |
268
|
1 |
|
private function printMissingVersion($version, $maxNameLength) |
269
|
1 |
|
{ |
270
|
10 |
|
$this->getOutput()->writeln(sprintf( |
271
|
|
|
' <error>up</error> %14.0f %19s %19s <comment>%s</comment> <error>** MISSING **</error>', |
272
|
|
|
$version['version'], |
273
|
|
|
$version['start_time'], |
274
|
|
|
$version['end_time'], |
275
|
|
|
str_pad($version['migration_name'], $maxNameLength, ' ') |
276
|
|
|
)); |
277
|
|
|
|
278
|
|
|
if ($version && $version['breakpoint']) { |
|
|
|
|
279
|
|
|
$this->getOutput()->writeln(' <error>BREAKPOINT SET</error>'); |
280
|
4 |
|
} |
281
|
|
|
} |
282
|
4 |
|
|
283
|
4 |
|
/** |
284
|
|
|
* Migrate to the version of the database on a given date. |
285
|
|
|
* |
286
|
4 |
|
* @param string $environment Environment |
287
|
4 |
|
* @param \DateTime $dateTime Date to migrate to |
288
|
|
|
* @param bool $fake flag that if true, we just record running the migration, but not actually do the |
289
|
4 |
|
* migration |
290
|
3 |
|
* |
291
|
3 |
|
* @return void |
292
|
3 |
|
*/ |
293
|
3 |
|
public function migrateToDateTime($environment, \DateTime $dateTime, $fake = false) |
294
|
4 |
|
{ |
295
|
|
|
$versions = array_keys($this->getMigrations($environment)); |
296
|
|
|
$dateString = $dateTime->format('YmdHis'); |
297
|
|
|
|
298
|
|
|
$outstandingMigrations = array_filter($versions, function ($version) use ($dateString) { |
299
|
|
|
return $version <= $dateString; |
300
|
|
|
}); |
301
|
|
|
|
302
|
|
|
if (count($outstandingMigrations) > 0) { |
303
|
8 |
|
$migration = max($outstandingMigrations); |
304
|
|
|
$this->getOutput()->writeln('Migrating to version ' . $migration); |
305
|
8 |
|
$this->migrate($environment, $migration, $fake); |
306
|
8 |
|
} |
307
|
8 |
|
} |
308
|
8 |
|
|
309
|
|
|
/** |
310
|
8 |
|
* Migrate an environment to the specified version. |
311
|
|
|
* |
312
|
|
|
* @param string $environment Environment |
313
|
|
|
* @param int $version version to migrate to |
314
|
8 |
|
* @param bool $fake flag that if true, we just record running the migration, but not actually do the migration |
315
|
5 |
|
* @return void |
316
|
5 |
|
*/ |
317
|
3 |
|
public function migrate($environment, $version = null, $fake = false) |
318
|
|
|
{ |
319
|
|
|
$migrations = $this->getMigrations($environment); |
320
|
|
|
$env = $this->getEnvironment($environment); |
321
|
|
|
$versions = $env->getVersions(); |
322
|
|
|
$current = $env->getCurrentVersion(); |
323
|
|
|
|
324
|
|
|
if (empty($versions) && empty($migrations)) { |
325
|
|
|
return; |
326
|
|
|
} |
327
|
8 |
|
|
328
|
|
|
if ($version === null) { |
329
|
8 |
|
$version = max(array_merge($versions, array_keys($migrations))); |
330
|
|
View Code Duplication |
} else { |
|
|
|
|
331
|
|
|
if (0 != $version && !isset($migrations[$version])) { |
332
|
|
|
$this->output->writeln(sprintf( |
333
|
|
|
'<comment>warning</comment> %s is not a valid version', |
334
|
|
|
$version |
335
|
|
|
)); |
336
|
|
|
|
337
|
|
|
return; |
338
|
|
|
} |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
// are we migrating up or down? |
342
|
|
|
$direction = $version > $current ? MigrationInterface::UP : MigrationInterface::DOWN; |
343
|
8 |
|
|
344
|
8 |
|
if ($direction === MigrationInterface::DOWN) { |
345
|
8 |
|
// run downs first |
346
|
2 |
|
krsort($migrations); |
347
|
|
View Code Duplication |
foreach ($migrations as $migration) { |
|
|
|
|
348
|
|
|
if ($migration->getVersion() <= $version) { |
349
|
8 |
|
break; |
350
|
5 |
|
} |
351
|
5 |
|
|
352
|
8 |
|
if (in_array($migration->getVersion(), $versions)) { |
353
|
8 |
|
$this->executeMigration($environment, $migration, MigrationInterface::DOWN, $fake); |
354
|
|
|
} |
355
|
|
|
} |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
ksort($migrations); |
359
|
|
View Code Duplication |
foreach ($migrations as $migration) { |
|
|
|
|
360
|
|
|
if ($migration->getVersion() > $version) { |
361
|
|
|
break; |
362
|
|
|
} |
363
|
119 |
|
|
364
|
|
|
if (!in_array($migration->getVersion(), $versions)) { |
365
|
119 |
|
$this->executeMigration($environment, $migration, MigrationInterface::UP, $fake); |
366
|
119 |
|
} |
367
|
|
|
} |
368
|
119 |
|
} |
369
|
119 |
|
|
370
|
119 |
|
/** |
371
|
|
|
* Execute a migration against the specified environment. |
372
|
|
|
* |
373
|
119 |
|
* @param string $name Environment Name |
374
|
119 |
|
* @param \Phinx\Migration\MigrationInterface $migration Migration |
375
|
119 |
|
* @param string $direction Direction |
376
|
|
|
* @param bool $fake flag that if true, we just record running the migration, but not actually do the migration |
377
|
119 |
|
* @return void |
378
|
|
|
*/ |
379
|
119 |
|
public function executeMigration($name, MigrationInterface $migration, $direction = MigrationInterface::UP, $fake = false) |
380
|
119 |
|
{ |
381
|
119 |
|
$this->getOutput()->writeln(''); |
382
|
119 |
|
$this->getOutput()->writeln( |
383
|
119 |
|
' ==' . |
384
|
|
|
' <info>' . $migration->getVersion() . ' ' . $migration->getName() . ':</info>' . |
385
|
|
|
' <comment>' . ($direction === MigrationInterface::UP ? 'migrating' : 'reverting') . '</comment>' |
386
|
|
|
); |
387
|
|
|
$migration->preFlightCheck($direction); |
388
|
|
|
|
389
|
|
|
// Execute the migration and log the time elapsed. |
390
|
|
|
$start = microtime(true); |
391
|
|
|
$this->getEnvironment($name)->executeMigration($migration, $direction, $fake); |
392
|
6 |
|
$end = microtime(true); |
393
|
|
|
|
394
|
6 |
|
$migration->postFlightCheck($direction); |
395
|
6 |
|
$this->getOutput()->writeln( |
396
|
|
|
' ==' . |
397
|
6 |
|
' <info>' . $migration->getVersion() . ' ' . $migration->getName() . ':</info>' . |
398
|
6 |
|
' <comment>' . ($direction === MigrationInterface::UP ? 'migrated' : 'reverted') . |
399
|
6 |
|
' ' . sprintf('%.4fs', $end - $start) . '</comment>' |
400
|
|
|
); |
401
|
|
|
} |
402
|
6 |
|
|
403
|
6 |
|
/** |
404
|
6 |
|
* Execute a seeder against the specified environment. |
405
|
|
|
* |
406
|
6 |
|
* @param string $name Environment Name |
407
|
|
|
* @param \Phinx\Seed\SeedInterface $seed Seed |
408
|
6 |
|
* @return void |
409
|
6 |
|
*/ |
410
|
6 |
|
public function executeSeed($name, SeedInterface $seed) |
411
|
6 |
|
{ |
412
|
6 |
|
$this->getOutput()->writeln(''); |
413
|
|
|
$this->getOutput()->writeln( |
414
|
|
|
' ==' . |
415
|
|
|
' <info>' . $seed->getName() . ':</info>' . |
416
|
|
|
' <comment>seeding</comment>' |
417
|
|
|
); |
418
|
|
|
|
419
|
|
|
// Execute the seeder and log the time elapsed. |
420
|
|
|
$start = microtime(true); |
421
|
|
|
$this->getEnvironment($name)->executeSeed($seed); |
422
|
|
|
$end = microtime(true); |
423
|
349 |
|
|
424
|
|
|
$this->getOutput()->writeln( |
425
|
|
|
' ==' . |
426
|
349 |
|
' <info>' . $seed->getName() . ':</info>' . |
427
|
|
|
' <comment>seeded' . |
428
|
|
|
' ' . sprintf('%.4fs', $end - $start) . '</comment>' |
429
|
349 |
|
); |
430
|
|
|
} |
431
|
|
|
|
432
|
349 |
|
/** |
433
|
|
|
* Rollback an environment to the specified version. |
434
|
349 |
|
* |
435
|
|
|
* @param string $environment Environment |
436
|
|
|
* @param int|string $target |
437
|
349 |
|
* @param bool $force |
438
|
48 |
|
* @param bool $targetMustMatchVersion |
439
|
48 |
|
* @param bool $fake flag that if true, we just record running the migration, but not actually do the migration |
440
|
48 |
|
* @return void |
441
|
|
|
*/ |
442
|
349 |
|
public function rollback($environment, $target = null, $force = false, $targetMustMatchVersion = true, $fake = false) |
443
|
349 |
|
{ |
444
|
349 |
|
// note that the migrations are indexed by name (aka creation time) in ascending order |
445
|
|
|
$migrations = $this->getMigrations($environment); |
446
|
|
|
|
447
|
47 |
|
// note that the version log are also indexed by name with the proper ascending order according to the version order |
448
|
|
|
$executedVersions = $this->getEnvironment($environment)->getVersionLog(); |
449
|
349 |
|
|
450
|
|
|
// get a list of migrations sorted in the opposite way of the executed versions |
451
|
349 |
|
$sortedMigrations = []; |
452
|
23 |
|
|
453
|
349 |
|
foreach ($executedVersions as $versionCreationTime => &$executedVersion) { |
454
|
|
|
// if we have a date (ie. the target must not match a version) and we are sorting by execution time, we |
455
|
20 |
|
// convert the version start time so we can compare directly with the target date |
456
|
20 |
|
if (!$this->getConfig()->isVersionOrderCreationTime() && !$targetMustMatchVersion) { |
457
|
20 |
|
$dateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $executedVersion['start_time']); |
458
|
20 |
|
$executedVersion['start_time'] = $dateTime->format('YmdHis'); |
459
|
|
|
} |
460
|
|
|
|
461
|
20 |
|
if (isset($migrations[$versionCreationTime])) { |
462
|
20 |
|
array_unshift($sortedMigrations, $migrations[$versionCreationTime]); |
463
|
20 |
|
} else { |
464
|
|
|
// this means the version is missing so we unset it so that we don't consider it when rolling back |
465
|
|
|
// migrations (or choosing the last up version as target) |
466
|
|
|
unset($executedVersions[$versionCreationTime]); |
467
|
20 |
|
} |
468
|
|
|
} |
469
|
|
|
|
470
|
349 |
|
if ($target === 'all' || $target === '0') { |
471
|
349 |
|
$target = 0; |
472
|
53 |
|
} elseif (!is_numeric($target) && !is_null($target)) { // try to find a target version based on name |
473
|
53 |
|
// search through the migrations using the name |
474
|
|
|
$migrationNames = array_map(function ($item) { |
475
|
|
|
return $item['migration_name']; |
476
|
|
|
}, $executedVersions); |
477
|
296 |
|
$found = array_search($target, $migrationNames); |
478
|
|
|
|
479
|
94 |
|
// check on was found |
480
|
94 |
|
if ($found !== false) { |
481
|
94 |
|
$target = (string)$found; |
482
|
|
|
} else { |
483
|
|
|
$this->getOutput()->writeln("<error>No migration found with name ($target)</error>"); |
484
|
296 |
|
|
485
|
46 |
|
return; |
486
|
46 |
|
} |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
// Check we have at least 1 migration to revert |
490
|
250 |
|
$executedVersionCreationTimes = array_keys($executedVersions); |
491
|
|
|
if (empty($executedVersionCreationTimes) || $target == end($executedVersionCreationTimes)) { |
492
|
250 |
|
$this->getOutput()->writeln('<error>No migrations to rollback</error>'); |
493
|
250 |
|
|
494
|
70 |
|
return; |
495
|
|
|
} |
496
|
|
|
|
497
|
250 |
|
// If no target was supplied, revert the last migration |
498
|
250 |
|
if ($target === null) { |
499
|
|
|
// Get the migration before the last run migration |
500
|
250 |
|
$prev = count($executedVersionCreationTimes) - 2; |
501
|
96 |
|
$target = $prev >= 0 ? $executedVersionCreationTimes[$prev] : 0; |
502
|
96 |
|
} |
503
|
42 |
|
|
504
|
|
|
// If the target must match a version, check the target version exists |
505
|
68 |
|
if ($targetMustMatchVersion && 0 !== $target && !isset($migrations[$target])) { |
506
|
|
|
$this->getOutput()->writeln("<error>Target version ($target) not found</error>"); |
507
|
222 |
|
|
508
|
121 |
|
return; |
509
|
121 |
|
} |
510
|
|
|
|
511
|
117 |
|
// Rollback all versions until we find the wanted rollback target |
512
|
117 |
|
$rollbacked = false; |
513
|
117 |
|
|
514
|
250 |
|
foreach ($sortedMigrations as $migration) { |
515
|
|
|
if ($targetMustMatchVersion && $migration->getVersion() == $target) { |
516
|
250 |
|
break; |
517
|
133 |
|
} |
518
|
133 |
|
|
519
|
250 |
|
if (in_array($migration->getVersion(), $executedVersionCreationTimes)) { |
520
|
|
|
$executedVersion = $executedVersions[$migration->getVersion()]; |
521
|
|
|
|
522
|
|
|
if (!$targetMustMatchVersion) { |
523
|
|
|
if (($this->getConfig()->isVersionOrderCreationTime() && $executedVersion['version'] <= $target) || |
524
|
|
|
(!$this->getConfig()->isVersionOrderCreationTime() && $executedVersion['start_time'] <= $target)) { |
525
|
|
|
break; |
526
|
|
|
} |
527
|
|
|
} |
528
|
9 |
|
|
529
|
|
|
if (0 != $executedVersion['breakpoint'] && !$force) { |
530
|
9 |
|
$this->getOutput()->writeln('<error>Breakpoint reached. Further rollbacks inhibited.</error>'); |
531
|
|
|
break; |
532
|
9 |
|
} |
533
|
|
|
$this->executeMigration($environment, $migration, MigrationInterface::DOWN, $fake); |
534
|
3 |
|
$rollbacked = true; |
535
|
3 |
|
} |
536
|
3 |
|
} |
537
|
3 |
|
|
538
|
3 |
|
if (!$rollbacked) { |
539
|
3 |
|
$this->getOutput()->writeln('<error>No migrations to rollback</error>'); |
540
|
|
|
} |
541
|
6 |
|
} |
542
|
3 |
|
|
543
|
3 |
|
/** |
544
|
3 |
|
* Run database seeders against an environment. |
545
|
|
|
* |
546
|
|
|
* @param string $environment Environment |
547
|
6 |
|
* @param string $seed Seeder |
548
|
|
|
* @return void |
549
|
|
|
*/ |
550
|
|
|
public function seed($environment, $seed = null) |
551
|
|
|
{ |
552
|
|
|
$seeds = $this->getSeeds(); |
553
|
|
|
|
554
|
|
|
if ($seed === null) { |
555
|
381 |
|
// run all seeders |
556
|
|
|
foreach ($seeds as $seeder) { |
557
|
381 |
|
if (array_key_exists($seeder->getName(), $seeds)) { |
558
|
381 |
|
$this->executeSeed($environment, $seeder); |
559
|
|
|
} |
560
|
|
|
} |
561
|
|
|
} else { |
562
|
|
|
// run only one seeder |
563
|
|
|
if (array_key_exists($seed, $seeds)) { |
564
|
|
|
$this->executeSeed($environment, $seeds[$seed]); |
565
|
|
|
} else { |
566
|
|
|
throw new \InvalidArgumentException(sprintf('The seed class "%s" does not exist', $seed)); |
567
|
|
|
} |
568
|
382 |
|
} |
569
|
|
|
} |
570
|
382 |
|
|
571
|
380 |
|
/** |
572
|
|
|
* Sets the environments. |
573
|
|
|
* |
574
|
|
|
* @param array $environments Environments |
575
|
7 |
|
* @return \Phinx\Migration\Manager |
576
|
1 |
|
*/ |
577
|
1 |
|
public function setEnvironments($environments = []) |
578
|
|
|
{ |
579
|
1 |
|
$this->environments = $environments; |
580
|
|
|
|
581
|
|
|
return $this; |
582
|
|
|
} |
583
|
6 |
|
|
584
|
6 |
|
/** |
585
|
|
|
* Gets the manager class for the given environment. |
586
|
6 |
|
* |
587
|
6 |
|
* @param string $name Environment Name |
588
|
6 |
|
* @throws \InvalidArgumentException |
589
|
6 |
|
* @return \Phinx\Migration\Manager\Environment |
590
|
|
|
*/ |
591
|
6 |
|
public function getEnvironment($name) |
592
|
|
|
{ |
593
|
|
|
if (isset($this->environments[$name])) { |
594
|
|
|
return $this->environments[$name]; |
595
|
|
|
} |
596
|
|
|
|
597
|
|
|
// check the environment exists |
598
|
|
|
if (!$this->getConfig()->hasEnvironment($name)) { |
599
|
|
|
throw new \InvalidArgumentException(sprintf( |
600
|
400 |
|
'The environment "%s" does not exist', |
601
|
|
|
$name |
602
|
400 |
|
)); |
603
|
400 |
|
} |
604
|
|
|
|
605
|
|
|
// create an environment instance and cache it |
606
|
|
|
$envOptions = $this->getConfig()->getEnvironment($name); |
607
|
|
|
$envOptions['version_order'] = $this->getConfig()->getVersionOrder(); |
608
|
|
|
|
609
|
|
|
$environment = new Environment($name, $envOptions); |
610
|
|
|
$this->environments[$name] = $environment; |
611
|
393 |
|
$environment->setInput($this->getInput()); |
612
|
|
|
$environment->setOutput($this->getOutput()); |
613
|
393 |
|
|
614
|
|
|
return $environment; |
615
|
|
|
} |
616
|
|
|
|
617
|
|
|
/** |
618
|
|
|
* Sets the console input. |
619
|
|
|
* |
620
|
|
|
* @param \Symfony\Component\Console\Input\InputInterface $input Input |
621
|
|
|
* @return \Phinx\Migration\Manager |
622
|
400 |
|
*/ |
623
|
|
|
public function setInput(InputInterface $input) |
624
|
400 |
|
{ |
625
|
400 |
|
$this->input = $input; |
626
|
|
|
|
627
|
|
|
return $this; |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
/** |
631
|
|
|
* Gets the console input. |
632
|
|
|
* |
633
|
395 |
|
* @return \Symfony\Component\Console\Input\InputInterface |
634
|
|
|
*/ |
635
|
395 |
|
public function getInput() |
636
|
|
|
{ |
637
|
|
|
return $this->input; |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
/** |
641
|
|
|
* Sets the console output. |
642
|
|
|
* |
643
|
|
|
* @param \Symfony\Component\Console\Output\OutputInterface $output Output |
644
|
379 |
|
* @return \Phinx\Migration\Manager |
645
|
|
|
*/ |
646
|
379 |
|
public function setOutput(OutputInterface $output) |
647
|
379 |
|
{ |
648
|
|
|
$this->output = $output; |
649
|
|
|
|
650
|
|
|
return $this; |
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
/** |
654
|
|
|
* Gets the console output. |
655
|
|
|
* |
656
|
|
|
* @return \Symfony\Component\Console\Output\OutputInterface |
657
|
388 |
|
*/ |
658
|
|
|
public function getOutput() |
659
|
388 |
|
{ |
660
|
388 |
|
return $this->output; |
661
|
|
|
} |
662
|
|
|
|
663
|
388 |
|
/** |
664
|
|
|
* Sets the database migrations. |
665
|
388 |
|
* |
666
|
|
|
* @param array $migrations Migrations |
667
|
388 |
|
* @return \Phinx\Migration\Manager |
668
|
387 |
|
*/ |
669
|
387 |
|
public function setMigrations(array $migrations) |
670
|
|
|
{ |
671
|
387 |
|
$this->migrations = $migrations; |
672
|
3 |
|
|
673
|
|
|
return $this; |
674
|
|
|
} |
675
|
387 |
|
|
676
|
387 |
|
/** |
677
|
|
|
* Gets an array of the database migrations, indexed by migration name (aka creation time) and sorted in ascending |
678
|
|
|
* order |
679
|
387 |
|
* |
680
|
|
|
* @param string $environment Environment |
681
|
387 |
|
* @throws \InvalidArgumentException |
682
|
2 |
|
* @return \Phinx\Migration\AbstractMigration[] |
683
|
2 |
|
*/ |
684
|
2 |
|
public function getMigrations($environment) |
685
|
2 |
|
{ |
686
|
2 |
|
if ($this->migrations === null) { |
687
|
|
|
$phpFiles = $this->getMigrationFiles(); |
688
|
|
|
|
689
|
387 |
|
if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { |
690
|
|
|
$this->getOutput()->writeln('Migration file'); |
691
|
|
|
$this->getOutput()->writeln( |
692
|
|
|
array_map( |
|
|
|
|
693
|
387 |
|
function ($phpFile) { |
694
|
387 |
|
return " <info>{$phpFile}</info>"; |
695
|
2 |
|
}, |
696
|
2 |
|
$phpFiles |
697
|
2 |
|
) |
698
|
|
|
); |
699
|
2 |
|
} |
700
|
|
|
|
701
|
|
|
// filter the files to only get the ones that match our naming scheme |
702
|
|
|
$fileNames = []; |
703
|
385 |
|
/** @var \Phinx\Migration\AbstractMigration[] $versions */ |
704
|
|
|
$versions = []; |
705
|
385 |
|
|
706
|
2 |
|
foreach ($phpFiles as $filePath) { |
707
|
2 |
|
if (Util::isValidMigrationFileName(basename($filePath))) { |
708
|
2 |
|
if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { |
709
|
|
|
$this->getOutput()->writeln("Valid migration file <info>{$filePath}</info>."); |
710
|
2 |
|
} |
711
|
|
|
|
712
|
|
|
$version = Util::getVersionFromFileName(basename($filePath)); |
713
|
383 |
|
|
714
|
383 |
|
if (isset($versions[$version])) { |
715
|
384 |
|
throw new \InvalidArgumentException(sprintf('Duplicate migration - "%s" has the same version as "%s"', $filePath, $versions[$version]->getVersion())); |
716
|
|
|
} |
717
|
379 |
|
|
718
|
379 |
|
$config = $this->getConfig(); |
719
|
379 |
|
$namespace = $config instanceof NamespaceAwareInterface ? $config->getMigrationNamespaceByPath(dirname($filePath)) : null; |
720
|
|
|
|
721
|
379 |
|
// convert the filename to a class name |
722
|
|
|
$class = ($namespace === null ? '' : $namespace . '\\') . Util::mapFileNameToClassName(basename($filePath)); |
723
|
|
|
|
724
|
|
|
if (isset($fileNames[$class])) { |
725
|
|
|
throw new \InvalidArgumentException(sprintf( |
726
|
|
|
'Migration "%s" has the same name as "%s"', |
727
|
|
|
basename($filePath), |
728
|
|
|
$fileNames[$class] |
729
|
388 |
|
)); |
730
|
|
|
} |
731
|
388 |
|
|
732
|
388 |
|
$fileNames[$class] = basename($filePath); |
733
|
388 |
|
|
734
|
|
|
if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { |
735
|
388 |
|
$this->getOutput()->writeln("Loading class <info>$class</info> from <info>$filePath</info>."); |
736
|
388 |
|
} |
737
|
388 |
|
|
738
|
388 |
|
// load the migration file |
739
|
388 |
|
$orig_display_errors_setting = ini_get('display_errors'); |
740
|
388 |
|
ini_set('display_errors', 'On'); |
741
|
|
|
/** @noinspection PhpIncludeInspection */ |
742
|
388 |
|
require_once $filePath; |
743
|
|
|
ini_set('display_errors', $orig_display_errors_setting); |
744
|
|
|
if (!class_exists($class)) { |
745
|
|
|
throw new \InvalidArgumentException(sprintf( |
746
|
|
|
'Could not find class "%s" in file "%s"', |
747
|
|
|
$class, |
748
|
|
|
$filePath |
749
|
|
|
)); |
750
|
|
|
} |
751
|
11 |
|
|
752
|
|
|
if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { |
753
|
11 |
|
$this->getOutput()->writeln("Running <info>$class</info>."); |
754
|
11 |
|
} |
755
|
|
|
|
756
|
|
|
// instantiate it |
757
|
|
|
$migration = new $class($environment, $version, $this->getInput(), $this->getOutput()); |
758
|
|
|
|
759
|
|
|
if (!($migration instanceof AbstractMigration)) { |
760
|
|
|
throw new \InvalidArgumentException(sprintf( |
761
|
|
|
'The class "%s" in file "%s" must extend \Phinx\Migration\AbstractMigration', |
762
|
|
|
$class, |
763
|
11 |
|
$filePath |
764
|
|
|
)); |
765
|
11 |
|
} |
766
|
11 |
|
|
767
|
|
|
$versions[$version] = $migration; |
768
|
|
|
} else { |
769
|
11 |
|
if ($this->getOutput()->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { |
770
|
|
|
$this->getOutput()->writeln("Invalid migration file <error>{$filePath}</error>."); |
771
|
11 |
|
} |
772
|
|
|
} |
773
|
11 |
|
} |
774
|
11 |
|
|
775
|
11 |
|
ksort($versions); |
776
|
11 |
|
$this->setMigrations($versions); |
777
|
|
|
} |
778
|
|
|
|
779
|
11 |
|
return $this->migrations; |
780
|
11 |
|
} |
781
|
|
|
|
782
|
|
|
/** |
783
|
|
|
* Returns a list of migration files found in the provided migration paths. |
784
|
11 |
|
* |
785
|
11 |
|
* @return string[] |
786
|
|
|
*/ |
787
|
|
View Code Duplication |
protected function getMigrationFiles() |
|
|
|
|
788
|
|
|
{ |
789
|
|
|
$config = $this->getConfig(); |
790
|
|
|
$paths = $config->getMigrationPaths(); |
791
|
|
|
$files = []; |
792
|
|
|
|
793
|
|
|
foreach ($paths as $path) { |
794
|
11 |
|
$files = array_merge( |
795
|
|
|
$files, |
796
|
11 |
|
Util::glob($path . DIRECTORY_SEPARATOR . '*.php') |
797
|
|
|
); |
798
|
|
|
} |
799
|
|
|
// glob() can return the same file multiple times |
800
|
|
|
// This will cause the migration to fail with a |
801
|
|
|
// false assumption of duplicate migrations |
802
|
|
|
// http://php.net/manual/en/function.glob.php#110340 |
803
|
|
|
$files = array_unique($files); |
804
|
11 |
|
|
805
|
11 |
|
return $files; |
806
|
11 |
|
} |
807
|
|
|
|
808
|
11 |
|
/** |
809
|
11 |
|
* Sets the database seeders. |
810
|
11 |
|
* |
811
|
|
|
* @param array $seeds Seeders |
812
|
11 |
|
* @return \Phinx\Migration\Manager |
813
|
|
|
*/ |
814
|
|
|
public function setSeeds(array $seeds) |
815
|
|
|
{ |
816
|
|
|
$this->seeds = $seeds; |
817
|
|
|
|
818
|
|
|
return $this; |
819
|
|
|
} |
820
|
11 |
|
|
821
|
|
|
/** |
822
|
11 |
|
* Get seed dependencies instances from seed dependency array |
823
|
11 |
|
* |
824
|
11 |
|
* @param AbstractSeed $seed Seed |
825
|
|
|
* |
826
|
11 |
|
* @return AbstractSeed[] |
827
|
11 |
|
*/ |
828
|
11 |
|
private function getSeedDependenciesInstances(AbstractSeed $seed) |
829
|
11 |
|
{ |
830
|
11 |
|
$dependenciesInstances = []; |
831
|
11 |
|
$dependencies = $seed->getDependencies(); |
832
|
|
|
if (!empty($dependencies)) { |
833
|
11 |
|
foreach ($dependencies as $dependency) { |
834
|
|
|
foreach ($this->seeds as $seed) { |
835
|
|
|
if (get_class($seed) === $dependency) { |
836
|
|
|
$dependenciesInstances[get_class($seed)] = $seed; |
837
|
|
|
} |
838
|
|
|
} |
839
|
|
|
} |
840
|
|
|
} |
841
|
|
|
|
842
|
400 |
|
return $dependenciesInstances; |
843
|
|
|
} |
844
|
400 |
|
|
845
|
400 |
|
/** |
846
|
|
|
* Order seeds by dependencies |
847
|
|
|
* |
848
|
|
|
* @param AbstractSeed[] $seeds Seeds |
849
|
|
|
* |
850
|
|
|
* @return AbstractSeed[] |
851
|
|
|
*/ |
852
|
|
|
private function orderSeedsByDependencies(array $seeds) |
853
|
399 |
|
{ |
854
|
|
|
$orderedSeeds = []; |
855
|
399 |
|
foreach ($seeds as $seed) { |
856
|
|
|
$key = get_class($seed); |
857
|
|
|
$dependencies = $this->getSeedDependenciesInstances($seed); |
858
|
|
|
if (!empty($dependencies)) { |
859
|
|
|
$orderedSeeds[$key] = $seed; |
860
|
|
|
$orderedSeeds = array_merge($this->orderSeedsByDependencies($dependencies), $orderedSeeds); |
861
|
|
|
} else { |
862
|
|
|
$orderedSeeds[$key] = $seed; |
863
|
|
|
} |
864
|
|
|
} |
865
|
2 |
|
|
866
|
|
|
return $orderedSeeds; |
867
|
2 |
|
} |
868
|
2 |
|
|
869
|
2 |
|
/** |
870
|
2 |
|
* Gets an array of database seeders. |
871
|
|
|
* |
872
|
2 |
|
* @throws \InvalidArgumentException |
873
|
|
|
* @return \Phinx\Seed\AbstractSeed[] |
874
|
|
|
*/ |
875
|
|
|
public function getSeeds() |
876
|
2 |
|
{ |
877
|
1 |
|
if ($this->seeds === null) { |
878
|
1 |
|
$phpFiles = $this->getSeedFiles(); |
879
|
1 |
|
|
880
|
|
|
// filter the files to only get the ones that match our naming scheme |
881
|
2 |
|
$fileNames = []; |
882
|
1 |
|
/** @var \Phinx\Seed\AbstractSeed[] $seeds */ |
883
|
1 |
|
$seeds = []; |
884
|
|
|
|
885
|
1 |
|
foreach ($phpFiles as $filePath) { |
886
|
1 |
|
if (Util::isValidSeedFileName(basename($filePath))) { |
887
|
|
|
$config = $this->getConfig(); |
888
|
|
|
$namespace = $config instanceof NamespaceAwareInterface ? $config->getSeedNamespaceByPath(dirname($filePath)) : null; |
889
|
1 |
|
|
890
|
|
|
// convert the filename to a class name |
891
|
1 |
|
$class = ($namespace === null ? '' : $namespace . '\\') . pathinfo($filePath, PATHINFO_FILENAME); |
892
|
|
|
$fileNames[$class] = basename($filePath); |
893
|
1 |
|
|
894
|
1 |
|
// load the seed file |
895
|
1 |
|
/** @noinspection PhpIncludeInspection */ |
896
|
1 |
|
require_once $filePath; |
897
|
1 |
|
if (!class_exists($class)) { |
898
|
1 |
|
throw new \InvalidArgumentException(sprintf( |
899
|
|
|
'Could not find class "%s" in file "%s"', |
900
|
|
|
$class, |
901
|
|
|
$filePath |
902
|
|
|
)); |
903
|
|
|
} |
904
|
|
|
|
905
|
|
|
// instantiate it |
906
|
1 |
|
$seed = new $class($this->getInput(), $this->getOutput()); |
907
|
|
|
|
908
|
1 |
|
if (!($seed instanceof AbstractSeed)) { |
909
|
1 |
|
throw new \InvalidArgumentException(sprintf( |
910
|
1 |
|
'The class "%s" in file "%s" must extend \Phinx\Seed\AbstractSeed', |
911
|
1 |
|
$class, |
912
|
1 |
|
$filePath |
913
|
|
|
)); |
914
|
|
|
} |
915
|
|
|
|
916
|
|
|
$seeds[$class] = $seed; |
917
|
|
|
} |
918
|
|
|
} |
919
|
|
|
|
920
|
|
|
ksort($seeds); |
921
|
|
|
$this->setSeeds($seeds); |
922
|
|
|
} |
923
|
|
|
|
924
|
|
|
$this->seeds = $this->orderSeedsByDependencies($this->seeds); |
925
|
|
|
|
926
|
|
|
return $this->seeds; |
927
|
|
|
} |
928
|
|
|
|
929
|
|
|
/** |
930
|
|
|
* Returns a list of seed files found in the provided seed paths. |
931
|
|
|
* |
932
|
|
|
* @return string[] |
933
|
|
|
*/ |
934
|
|
View Code Duplication |
protected function getSeedFiles() |
|
|
|
|
935
|
|
|
{ |
936
|
|
|
$config = $this->getConfig(); |
937
|
|
|
$paths = $config->getSeedPaths(); |
938
|
|
|
$files = []; |
939
|
|
|
|
940
|
|
|
foreach ($paths as $path) { |
941
|
|
|
$files = array_merge( |
942
|
|
|
$files, |
943
|
|
|
Util::glob($path . DIRECTORY_SEPARATOR . '*.php') |
944
|
|
|
); |
945
|
|
|
} |
946
|
|
|
// glob() can return the same file multiple times |
947
|
|
|
// This will cause the migration to fail with a |
948
|
|
|
// false assumption of duplicate migrations |
949
|
|
|
// http://php.net/manual/en/function.glob.php#110340 |
950
|
|
|
$files = array_unique($files); |
951
|
|
|
|
952
|
|
|
return $files; |
953
|
|
|
} |
954
|
|
|
|
955
|
|
|
/** |
956
|
|
|
* Sets the config. |
957
|
|
|
* |
958
|
|
|
* @param \Phinx\Config\ConfigInterface $config Configuration Object |
959
|
|
|
* @return \Phinx\Migration\Manager |
960
|
|
|
*/ |
961
|
|
|
public function setConfig(ConfigInterface $config) |
962
|
|
|
{ |
963
|
|
|
$this->config = $config; |
964
|
|
|
|
965
|
|
|
return $this; |
966
|
|
|
} |
967
|
|
|
|
968
|
|
|
/** |
969
|
|
|
* Gets the config. |
970
|
|
|
* |
971
|
|
|
* @return \Phinx\Config\ConfigInterface |
972
|
|
|
*/ |
973
|
|
|
public function getConfig() |
974
|
|
|
{ |
975
|
|
|
return $this->config; |
976
|
|
|
} |
977
|
|
|
|
978
|
|
|
/** |
979
|
|
|
* Toggles the breakpoint for a specific version. |
980
|
|
|
* |
981
|
|
|
* @param string $environment |
982
|
|
|
* @param int|null $version |
983
|
|
|
* @return void |
984
|
|
|
*/ |
985
|
|
|
public function toggleBreakpoint($environment, $version) |
986
|
|
|
{ |
987
|
|
|
$this->markBreakpoint($environment, $version, self::BREAKPOINT_TOGGLE); |
988
|
|
|
} |
989
|
|
|
|
990
|
|
|
/** |
991
|
|
|
* Toggles the breakpoint for a specific version. |
992
|
|
|
* |
993
|
|
|
* @param string $environment The required environment |
994
|
|
|
* @param int|null $version The version of the target migration |
995
|
|
|
* @param int $mark The state of the breakpoint as defined by self::BREAKPOINT_xxxx constants. |
996
|
|
|
* |
997
|
|
|
* @return void |
998
|
|
|
*/ |
999
|
|
|
protected function markBreakpoint($environment, $version, $mark) |
1000
|
|
|
{ |
1001
|
|
|
$migrations = $this->getMigrations($environment); |
1002
|
|
|
$this->getMigrations($environment); |
1003
|
|
|
$env = $this->getEnvironment($environment); |
1004
|
|
|
$versions = $env->getVersionLog(); |
1005
|
|
|
|
1006
|
|
|
if (empty($versions) || empty($migrations)) { |
1007
|
|
|
return; |
1008
|
|
|
} |
1009
|
|
|
|
1010
|
|
|
if ($version === null) { |
1011
|
|
|
$lastVersion = end($versions); |
1012
|
|
|
$version = $lastVersion['version']; |
1013
|
|
|
} |
1014
|
|
|
|
1015
|
|
View Code Duplication |
if (0 != $version && !isset($migrations[$version])) { |
|
|
|
|
1016
|
|
|
$this->output->writeln(sprintf( |
1017
|
|
|
'<comment>warning</comment> %s is not a valid version', |
1018
|
|
|
$version |
1019
|
|
|
)); |
1020
|
|
|
|
1021
|
|
|
return; |
1022
|
|
|
} |
1023
|
|
|
|
1024
|
|
|
switch ($mark) { |
1025
|
|
|
case self::BREAKPOINT_TOGGLE: |
1026
|
|
|
$env->getAdapter()->toggleBreakpoint($migrations[$version]); |
1027
|
|
|
break; |
1028
|
|
View Code Duplication |
case self::BREAKPOINT_SET: |
|
|
|
|
1029
|
|
|
if ($versions[$version]['breakpoint'] == 0) { |
1030
|
|
|
$env->getAdapter()->setBreakpoint($migrations[$version]); |
1031
|
|
|
} |
1032
|
|
|
break; |
1033
|
|
View Code Duplication |
case self::BREAKPOINT_UNSET: |
|
|
|
|
1034
|
|
|
if ($versions[$version]['breakpoint'] == 1) { |
1035
|
|
|
$env->getAdapter()->unsetBreakpoint($migrations[$version]); |
1036
|
|
|
} |
1037
|
|
|
break; |
1038
|
|
|
} |
1039
|
|
|
|
1040
|
|
|
$versions = $env->getVersionLog(); |
1041
|
|
|
|
1042
|
|
|
$this->getOutput()->writeln( |
1043
|
|
|
' Breakpoint ' . ($versions[$version]['breakpoint'] ? 'set' : 'cleared') . |
1044
|
|
|
' for <info>' . $version . '</info>' . |
1045
|
|
|
' <comment>' . $migrations[$version]->getName() . '</comment>' |
1046
|
|
|
); |
1047
|
|
|
} |
1048
|
|
|
|
1049
|
|
|
/** |
1050
|
|
|
* Remove all breakpoints |
1051
|
|
|
* |
1052
|
|
|
* @param string $environment The required environment |
1053
|
|
|
* @return void |
1054
|
|
|
*/ |
1055
|
|
|
public function removeBreakpoints($environment) |
1056
|
|
|
{ |
1057
|
|
|
$this->getOutput()->writeln(sprintf( |
1058
|
|
|
' %d breakpoints cleared.', |
1059
|
|
|
$this->getEnvironment($environment)->getAdapter()->resetAllBreakpoints() |
1060
|
|
|
)); |
1061
|
|
|
} |
1062
|
|
|
|
1063
|
|
|
/** |
1064
|
|
|
* Set the breakpoint for a specific version. |
1065
|
|
|
* |
1066
|
|
|
* @param string $environment The required environment |
1067
|
|
|
* @param int|null $version The version of the target migration |
1068
|
|
|
* |
1069
|
|
|
* @return void |
1070
|
|
|
*/ |
1071
|
|
|
public function setBreakpoint($environment, $version) |
1072
|
|
|
{ |
1073
|
|
|
$this->markBreakpoint($environment, $version, self::BREAKPOINT_SET); |
1074
|
|
|
} |
1075
|
|
|
|
1076
|
|
|
/** |
1077
|
|
|
* Unset the breakpoint for a specific version. |
1078
|
|
|
* |
1079
|
|
|
* @param string $environment The required environment |
1080
|
|
|
* @param int|null $version The version of the target migration |
1081
|
|
|
* |
1082
|
|
|
* @return void |
1083
|
|
|
*/ |
1084
|
|
|
public function unsetBreakpoint($environment, $version) |
1085
|
|
|
{ |
1086
|
|
|
$this->markBreakpoint($environment, $version, self::BREAKPOINT_UNSET); |
1087
|
|
|
} |
1088
|
|
|
} |
1089
|
|
|
|
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.