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