Failed Conditions
Push — master ( c24352...310518 )
by Jonathan
16s
created

Executor::execute()   B

Complexity

Conditions 3
Paths 9

Size

Total Lines 44
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 27
nc 9
nop 4
dl 0
loc 44
ccs 27
cts 27
cp 1
crap 3
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Migrations\Version;
6
7
use Doctrine\DBAL\Connection;
8
use Doctrine\Migrations\AbstractMigration;
9
use Doctrine\Migrations\Configuration\Configuration;
10
use Doctrine\Migrations\Events;
11
use Doctrine\Migrations\Exception\SkipMigration;
12
use Doctrine\Migrations\MigratorConfiguration;
13
use Doctrine\Migrations\OutputWriter;
14
use Doctrine\Migrations\ParameterFormatterInterface;
15
use Doctrine\Migrations\Provider\SchemaDiffProviderInterface;
16
use Doctrine\Migrations\Stopwatch;
17
use Doctrine\Migrations\Tools\BytesFormatter;
18
use Throwable;
19
use function count;
20
use function rtrim;
21
use function sprintf;
22
use function ucfirst;
23
24
/**
25
 * @internal
26
 */
27
final class Executor implements ExecutorInterface
28
{
29
    /** @var Configuration */
30
    private $configuration;
31
32
    /** @var Connection */
33
    private $connection;
34
35
    /** @var SchemaDiffProviderInterface */
36
    private $schemaProvider;
37
38
    /** @var OutputWriter */
39
    private $outputWriter;
40
41
    /** @var ParameterFormatterInterface */
42
    private $parameterFormatter;
43
44
    /** @var Stopwatch */
45
    private $stopwatch;
46
47
    /** @var string[] */
48
    private $sql = [];
49
50
    /** @var mixed[] */
51
    private $params = [];
52
53
    /** @var mixed[] */
54
    private $types = [];
55
56 182
    public function __construct(
57
        Configuration $configuration,
58
        Connection $connection,
59
        SchemaDiffProviderInterface $schemaProvider,
60
        OutputWriter $outputWriter,
61
        ParameterFormatterInterface $parameterFormatter,
62
        Stopwatch $stopwatch
63
    ) {
64 182
        $this->configuration      = $configuration;
65 182
        $this->connection         = $connection;
66 182
        $this->schemaProvider     = $schemaProvider;
67 182
        $this->outputWriter       = $outputWriter;
68 182
        $this->parameterFormatter = $parameterFormatter;
69 182
        $this->stopwatch          = $stopwatch;
70 182
    }
71
72
    /**
73
     * @return string[]
74
     */
75 1
    public function getSql() : array
76
    {
77 1
        return $this->sql;
78
    }
79
80
    /**
81
     * @return mixed[]
82
     */
83 1
    public function getParams() : array
84
    {
85 1
        return $this->params;
86
    }
87
88
    /**
89
     * @return mixed[]
90
     */
91 1
    public function getTypes() : array
92
    {
93 1
        return $this->types;
94
    }
95
96
    /**
97
     * @param mixed[] $params
98
     * @param mixed[] $types
99
     */
100 42
    public function addSql(string $sql, array $params = [], array $types = []) : void
101
    {
102 42
        $this->sql[] = $sql;
103
104 42
        if (count($params) === 0) {
105 28
            return;
106
        }
107
108 23
        $this->addQueryParams($params, $types);
109 23
    }
110
111 55
    public function execute(
112
        Version $version,
113
        AbstractMigration $migration,
114
        string $direction,
115
        ?MigratorConfiguration $migratorConfiguration = null
116
    ) : ExecutionResult {
117 55
        $migratorConfiguration = $migratorConfiguration ?? new MigratorConfiguration();
118
119 55
        $versionExecutionResult = new ExecutionResult();
120
121 55
        $this->startMigration($version, $migration, $direction, $migratorConfiguration);
122
123
        try {
124 55
            $this->executeMigration(
125 55
                $version,
126 55
                $migration,
127 55
                $versionExecutionResult,
128 55
                $direction,
129 55
                $migratorConfiguration
130
            );
131
132 51
            $versionExecutionResult->setSql($this->sql);
133 51
            $versionExecutionResult->setParams($this->params);
134 51
            $versionExecutionResult->setTypes($this->types);
135 8
        } catch (SkipMigration $e) {
136 6
            $this->skipMigration(
137 6
                $e,
138 6
                $version,
139 6
                $migration,
140 6
                $direction,
141 6
                $migratorConfiguration
142
            );
143
144 6
            $versionExecutionResult->setSkipped(true);
145 2
        } catch (Throwable $e) {
146 2
            $this->migrationError($e, $version, $migration);
147
148 2
            $versionExecutionResult->setError(true);
149 2
            $versionExecutionResult->setException($e);
150
151 2
            throw $e;
152
        }
153
154 53
        return $versionExecutionResult;
155
    }
156
157 55
    private function startMigration(
158
        Version $version,
159
        AbstractMigration $migration,
160
        string $direction,
161
        MigratorConfiguration $migratorConfiguration
162
    ) : void {
163 55
        $this->sql    = [];
164 55
        $this->params = [];
165 55
        $this->types  = [];
166
167 55
        $this->configuration->dispatchVersionEvent(
168 55
            $version,
169 55
            Events::onMigrationsVersionExecuting,
170 55
            $direction,
171 55
            $migratorConfiguration->isDryRun()
172
        );
173
174 55
        if (! $migration->isTransactional()) {
175
            return;
176
        }
177
178
        // only start transaction if in transactional mode
179 55
        $this->connection->beginTransaction();
180 55
    }
181
182 55
    private function executeMigration(
183
        Version $version,
184
        AbstractMigration $migration,
185
        ExecutionResult $versionExecutionResult,
186
        string $direction,
187
        MigratorConfiguration $migratorConfiguration
188
    ) : ExecutionResult {
189 55
        $stopwatchEvent = $this->stopwatch->start('execute');
190
191 55
        $version->setState(State::PRE);
192
193 55
        $fromSchema = $this->schemaProvider->createFromSchema();
194
195 55
        $migration->{'pre' . ucfirst($direction)}($fromSchema);
196
197 53
        if ($direction === Direction::UP) {
198 50
            $this->outputWriter->write("\n" . sprintf('  <info>++</info> migrating <comment>%s</comment>', $version) . "\n");
199
        } else {
200 9
            $this->outputWriter->write("\n" . sprintf('  <info>--</info> reverting <comment>%s</comment>', $version) . "\n");
201
        }
202
203 53
        $version->setState(State::EXEC);
204
205 53
        $toSchema = $this->schemaProvider->createToSchema($fromSchema);
206
207 53
        $migration->$direction($toSchema);
208
209 51
        foreach ($this->schemaProvider->getSqlDiffToMigrate($fromSchema, $toSchema) as $sql) {
210 14
            $this->addSql($sql);
211
        }
212
213 51
        if (count($this->sql) !== 0) {
214 40
            if (! $migratorConfiguration->isDryRun()) {
215 23
                $this->executeVersionExecutionResult($version, $migratorConfiguration);
216
            } else {
217 17
                foreach ($this->sql as $idx => $query) {
218 40
                    $this->outputSqlQuery($idx, $query);
219
                }
220
            }
221
        } else {
222 11
            $this->outputWriter->write(sprintf(
223 11
                '<error>Migration %s was executed but did not result in any SQL statements.</error>',
224 11
                $version
225
            ));
226
        }
227
228 51
        $version->setState(State::POST);
229
230 51
        $migration->{'post' . ucfirst($direction)}($toSchema);
231
232 51
        if (! $migratorConfiguration->isDryRun()) {
233 34
            $version->markVersion($direction);
234
        }
235
236 51
        $stopwatchEvent->stop();
237
238 51
        $versionExecutionResult->setTime($stopwatchEvent->getDuration());
239 51
        $versionExecutionResult->setMemory($stopwatchEvent->getMemory());
240
241 51
        if ($direction === Direction::UP) {
242 48
            $this->outputWriter->write(sprintf(
243 48
                "\n  <info>++</info> migrated (took %sms, used %s memory)",
244 48
                $stopwatchEvent->getDuration(),
245 48
                BytesFormatter::formatBytes($stopwatchEvent->getMemory())
246
            ));
247
        } else {
248 9
            $this->outputWriter->write(sprintf(
249 9
                "\n  <info>--</info> reverted (took %sms, used %s memory)",
250 9
                $stopwatchEvent->getDuration(),
251 9
                BytesFormatter::formatBytes($stopwatchEvent->getMemory())
252
            ));
253
        }
254
255 51
        if ($migration->isTransactional()) {
256
            //commit only if running in transactional mode
257 51
            $this->connection->commit();
258
        }
259
260 51
        $version->setState(State::NONE);
261
262 51
        $this->configuration->dispatchVersionEvent(
263 51
            $version,
264 51
            Events::onMigrationsVersionExecuted,
265 51
            $direction,
266 51
            $migratorConfiguration->isDryRun()
267
        );
268
269 51
        return $versionExecutionResult;
270
    }
271
272 6
    private function skipMigration(
273
        SkipMigration $e,
274
        Version $version,
275
        AbstractMigration $migration,
276
        string $direction,
277
        MigratorConfiguration $migratorConfiguration
278
    ) : void {
279 6
        if ($migration->isTransactional()) {
280
            //only rollback transaction if in transactional mode
281 6
            $this->connection->rollBack();
282
        }
283
284 6
        if (! $migratorConfiguration->isDryRun()) {
285 5
            $version->markVersion($direction);
286
        }
287
288 6
        $this->outputWriter->write(sprintf("\n  <info>SS</info> skipped (Reason: %s)", $e->getMessage()));
289
290 6
        $version->setState(State::NONE);
291
292 6
        $this->configuration->dispatchVersionEvent(
293 6
            $version,
294 6
            Events::onMigrationsVersionSkipped,
295 6
            $direction,
296 6
            $migratorConfiguration->isDryRun()
297
        );
298 6
    }
299
300
    /**
301
     * @throws Throwable
302
     */
303 2
    private function migrationError(Throwable $e, Version $version, AbstractMigration $migration) : void
304
    {
305 2
        $this->outputWriter->write(sprintf(
306 2
            '<error>Migration %s failed during %s. Error %s</error>',
307 2
            $version,
308 2
            $version->getExecutionState(),
309 2
            $e->getMessage()
310
        ));
311
312 2
        if ($migration->isTransactional()) {
313
            //only rollback transaction if in transactional mode
314 2
            $this->connection->rollBack();
315
        }
316
317 2
        $version->setState(State::NONE);
318 2
    }
319
320 23
    private function executeVersionExecutionResult(
321
        Version $version,
322
        MigratorConfiguration $migratorConfiguration
323
    ) : void {
324 23
        foreach ($this->sql as $key => $query) {
325 23
            $stopwatchEvent = $this->stopwatch->start('query');
326
327 23
            $this->outputSqlQuery($key, $query);
328
329 23
            if (! isset($this->params[$key])) {
330 20
                $this->connection->executeQuery($query);
331
            } else {
332 10
                $this->connection->executeQuery($query, $this->params[$key], $this->types[$key]);
333
            }
334
335 23
            $stopwatchEvent->stop();
336
337 23
            if (! $migratorConfiguration->getTimeAllQueries()) {
338 21
                continue;
339
            }
340
341 2
            $this->outputWriter->write(sprintf('  <info>%sms</info>', $stopwatchEvent->getDuration()));
342
        }
343 23
    }
344
345
    /**
346
     * @param mixed[]|int $params
347
     * @param mixed[]|int $types
348
     */
349 23
    private function addQueryParams($params, $types) : void
350
    {
351 23
        $index                = count($this->sql) - 1;
352 23
        $this->params[$index] = $params;
353 23
        $this->types[$index]  = $types;
354 23
    }
355
356 40
    private function outputSqlQuery(int $idx, string $query) : void
357
    {
358 40
        $params = $this->parameterFormatter->formatParameters(
359 40
            $this->params[$idx] ?? [],
360 40
            $this->types[$idx] ?? []
361
        );
362
363 40
        $this->outputWriter->write(rtrim(sprintf(
364 40
            '     <comment>-></comment> %s %s',
365 40
            $query,
366 40
            $params
367
        )));
368 40
    }
369
}
370