Completed
Push — master ( 05902f...9e6322 )
by mark
01:41 queued 11s
created

Rollback   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 4

Test Coverage

Coverage 93.85%

Importance

Changes 0
Metric Value
wmc 13
lcom 2
cbo 4
dl 0
loc 157
ccs 61
cts 65
cp 0.9385
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 32 1
B execute() 0 63 8
A getTargetFromDate() 0 30 4
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\Console\Command;
9
10
use DateTime;
11
use InvalidArgumentException;
12
use Symfony\Component\Console\Input\InputInterface;
13
use Symfony\Component\Console\Input\InputOption;
14
use Symfony\Component\Console\Output\OutputInterface;
15
16
class Rollback extends AbstractCommand
17
{
18
    /**
19
     * @var string
20
     */
21
    protected static $defaultName = 'rollback';
22
23
    /**
24
     * {@inheritDoc}
25
     *
26
     * @return void
27
     */
28
    protected function configure()
29
    {
30
        parent::configure();
31
32
        $this->addOption('--environment', '-e', InputOption::VALUE_REQUIRED, 'The target environment');
33
34
        $this->setDescription('Rollback the last or to a specific migration')
35
            ->addOption('--target', '-t', InputOption::VALUE_REQUIRED, 'The version number to rollback to')
36
            ->addOption('--date', '-d', InputOption::VALUE_REQUIRED, 'The date to rollback to')
37
            ->addOption('--force', '-f', InputOption::VALUE_NONE, 'Force rollback to ignore breakpoints')
38
            ->addOption('--dry-run', '-x', InputOption::VALUE_NONE, 'Dump query to standard output instead of executing it')
39
            ->addOption('--fake', null, InputOption::VALUE_NONE, "Mark any rollbacks selected as run, but don't actually execute them")
40
            ->setHelp(
41 44
                <<<EOT
42
The <info>rollback</info> command reverts the last migration, or optionally up to a specific version
43 44
44
<info>phinx rollback -e development</info>
45 44
<info>phinx rollback -e development -t 20111018185412</info>
46
<info>phinx rollback -e development -d 20111018</info>
47 44
<info>phinx rollback -e development -v</info>
48 44
<info>phinx rollback -e development -t 20111018185412 -f</info>
49 44
50 44
If you have a breakpoint set, then you can rollback to target 0 and the rollbacks will stop at the breakpoint.
51 44
<info>phinx rollback -e development -t 0 </info>
52 44
53 44
The <info>version_order</info> configuration option is used to determine the order of the migrations when rolling back.
54
This can be used to allow the rolling back of the last executed migration instead of the last created one, or combined
55
with the <info>-d|--date</info> option to rollback to a certain date using the migration start times to order them.
56
57
EOT
58
            );
59
    }
60
61
    /**
62
     * Rollback the migration.
63
     *
64
     * @param \Symfony\Component\Console\Input\InputInterface $input Input
65
     * @param \Symfony\Component\Console\Output\OutputInterface $output Output
66
     *
67
     * @return int integer 0 on success, or an error code.
68
     */
69
    protected function execute(InputInterface $input, OutputInterface $output)
70
    {
71 44
        $this->bootstrap($input, $output);
72 44
73
        $environment = $input->getOption('environment');
74
        $version = $input->getOption('target');
75
        $date = $input->getOption('date');
76
        $force = (bool)$input->getOption('force');
77
        $fake = (bool)$input->getOption('fake');
78
79
        $config = $this->getConfig();
80
81 6
        if ($environment === null) {
82
            $environment = $config->getDefaultEnvironment();
83 6
            $output->writeln('<comment>warning</comment> no environment specified, defaulting to: ' . $environment);
84
        } else {
85 6
            $output->writeln('<info>using environment</info> ' . $environment);
86 6
        }
87 6
88 6
        if (!$this->getConfig()->hasEnvironment($environment)) {
89
            $output->writeln(sprintf('<error>The environment "%s" does not exist</error>', $environment));
90 6
91
            return self::CODE_ERROR;
92 6
        }
93 5
94 5
        $envOptions = $config->getEnvironment($environment);
95 5
        if (isset($envOptions['adapter'])) {
96 1
            $output->writeln('<info>using adapter</info> ' . $envOptions['adapter']);
97
        }
98
99 6
        if (isset($envOptions['wrapper'])) {
100 6
            $output->writeln('<info>using wrapper</info> ' . $envOptions['wrapper']);
101 5
        }
102 5
103
        if (isset($envOptions['name'])) {
104 6
            $output->writeln('<info>using database</info> ' . $envOptions['name']);
105
        }
106
107
        $versionOrder = $this->getConfig()->getVersionOrder();
108 6
        $output->writeln('<info>ordering by</info> ' . $versionOrder . ' time');
109 5
110 5
        if ($fake) {
111
            $output->writeln('<comment>warning</comment> performing fake rollbacks');
112 6
        }
113 6
114
        // rollback the specified environment
115
        if ($date === null) {
116 6
            $targetMustMatchVersion = true;
117 4
            $target = $version;
118 4
        } else {
119 4
            $targetMustMatchVersion = false;
120 2
            $target = $this->getTargetFromDate($date);
121 2
        }
122
123
        $start = microtime(true);
124 6
        $this->getManager()->rollback($environment, $target, $force, $targetMustMatchVersion, $fake);
125 6
        $end = microtime(true);
126 6
127
        $output->writeln('');
128 6
        $output->writeln('<comment>All Done. Took ' . sprintf('%.4fs', $end - $start) . '</comment>');
129 6
130 6
        return self::CODE_SUCCESS;
131
    }
132
133
    /**
134
     * Get Target from Date
135
     *
136
     * @param string $date The date to convert to a target.
137
     *
138 10
     * @throws \InvalidArgumentException
139
     *
140 10
     * @return string The target
141 3
     */
142
    public function getTargetFromDate($date)
143
    {
144
        if (!preg_match('/^\d{4,14}$/', $date)) {
145
            throw new InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].');
146 7
        }
147 7
148 7
        // what we need to append to the date according to the possible date string lengths
149 7
        $dateStrlenToAppend = [
150 7
            14 => '',
151 7
            12 => '00',
152 7
            10 => '0000',
153
            8 => '000000',
154 7
            6 => '01000000',
155
            4 => '0101000000',
156
        ];
157
158 7
        if (!isset($dateStrlenToAppend[strlen($date)])) {
159
            throw new InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].');
160 7
        }
161
162 7
        $target = $date . $dateStrlenToAppend[strlen($date)];
163
164
        $dateTime = DateTime::createFromFormat('YmdHis', $target);
165
166 7
        if ($dateTime === false) {
167
            throw new InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].');
168
        }
169
170
        return $dateTime->format('YmdHis');
171
    }
172
}
173