Failed Conditions
Pull Request — master (#941)
by Vincent
02:26
created

DiffCommand::execute()   C

Complexity

Conditions 12
Paths 27

Size

Total Lines 84
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 53
c 1
b 0
f 0
nc 27
nop 2
dl 0
loc 84
ccs 0
cts 53
cp 0
crap 156
rs 6.9666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Migrations\Tools\Console\Command;
6
7
use Doctrine\Migrations\Generator\Exception\NoChangesDetected;
8
use Doctrine\Migrations\Metadata\AvailableMigrationsList;
9
use Doctrine\Migrations\Metadata\ExecutedMigrationsSet;
10
use Doctrine\Migrations\Provider\OrmSchemaProvider;
0 ignored issues
show
introduced by
Type Doctrine\Migrations\Provider\OrmSchemaProvider is not used in this file.
Loading history...
11
use Doctrine\Migrations\Provider\SchemaProviderInterface;
0 ignored issues
show
Bug introduced by
The type Doctrine\Migrations\Prov...SchemaProviderInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
introduced by
Type Doctrine\Migrations\Provider\SchemaProviderInterface is not used in this file.
Loading history...
12
use Doctrine\Migrations\Tools\Console\Exception\InvalidOptionUsage;
13
use OutOfBoundsException;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use function addslashes;
18
use function assert;
19
use function class_exists;
20
use function filter_var;
21
use function is_string;
22
use function key;
23
use function sprintf;
24
use const FILTER_VALIDATE_BOOLEAN;
25
26
/**
27
 * The DiffCommand class is responsible for generating a migration by comparing your current database schema to
28
 * your mapping information.
29
 */
30
class DiffCommand extends DoctrineCommand
31
{
32
    /** @var string */
33
    protected static $defaultName = 'migrations:diff';
34
35
    protected function configure() : void
36
    {
37
        parent::configure();
38
39
        $this
40
            ->setAliases(['diff'])
41
            ->setDescription('Generate a migration by comparing your current database to your mapping information.')
42
            ->setHelp(<<<EOT
43
The <info>%command.name%</info> command generates a migration by comparing your current database to your mapping information:
44
45
    <info>%command.full_name%</info>
46
47
You can optionally specify a <comment>--editor-cmd</comment> option to open the generated file in your favorite editor:
48
49
    <info>%command.full_name% --editor-cmd=mate</info>
50
EOT
51
            )
52
            ->addOption(
53
                'namespace',
54
                null,
55
                InputOption::VALUE_REQUIRED,
56
                'The namespace to use for the migration (must be in the list of configured namespaces)'
57
            )
58
            ->addOption(
59
                'editor-cmd',
60
                null,
61
                InputOption::VALUE_REQUIRED,
62
                'Open file with this command upon creation.'
63
            )
64
            ->addOption(
65
                'filter-expression',
66
                null,
67
                InputOption::VALUE_REQUIRED,
68
                'Tables which are filtered by Regular Expression.'
69
            )
70
            ->addOption(
71
                'formatted',
72
                null,
73
                InputOption::VALUE_NONE,
74
                'Format the generated SQL.'
75
            )
76
            ->addOption(
77
                'line-length',
78
                null,
79
                InputOption::VALUE_REQUIRED,
80
                'Max line length of unformatted lines.',
81
                120
82
            )
83
            ->addOption(
84
                'check-database-platform',
85
                null,
86
                InputOption::VALUE_OPTIONAL,
87
                'Check Database Platform to the generated code.',
88
                false
89
            )
90
            ->addOption(
91
                'allow-empty-diff',
92
                null,
93
                InputOption::VALUE_NONE,
94
                'Do not throw an exception when no changes are detected.'
95
            );
96
    }
97
98
    /**
99
     * @throws InvalidOptionUsage
100
     */
101
    public function execute(
102
        InputInterface $input,
103
        OutputInterface $output
104
    ) : ?int {
105
        $filterExpression = (string) $input->getOption('filter-expression') ?: null;
106
        $formatted        = filter_var($input->getOption('formatted'), FILTER_VALIDATE_BOOLEAN);
107
        $lineLength       = (int) $input->getOption('line-length');
108
        $allowEmptyDiff   = $input->getOption('allow-empty-diff');
109
        $checkDbPlatform  = filter_var($input->getOption('check-database-platform'), FILTER_VALIDATE_BOOLEAN);
110
        $namespace        = $input->getOption('namespace') ?: null;
111
        if ($formatted) {
112
            if (! class_exists('SqlFormatter')) {
113
                throw InvalidOptionUsage::new(
114
                    'The "--formatted" option can only be used if the sql formatter is installed. Please run "composer require jdorn/sql-formatter".'
115
                );
116
            }
117
        }
118
119
        $configuration = $this->getDependencyFactory()->getConfiguration();
120
121
        $dirs = $configuration->getMigrationDirectories();
122
        if ($namespace === null) {
123
            $namespace = key($dirs);
124
        } elseif (! isset($dirs[$namespace])) {
125
            throw new OutOfBoundsException(sprintf('Path not defined for the namespace %s', $namespace));
126
        }
127
128
        assert(is_string($namespace));
129
130
        $planCalculator                = $this->getDependencyFactory()->getMigrationPlanCalculator();
131
        $executedUnavailableMigrations = $planCalculator->getExecutedUnavailableMigrations();
0 ignored issues
show
Bug introduced by
The method getExecutedUnavailableMigrations() does not exist on Doctrine\Migrations\Vers...MigrationPlanCalculator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

131
        /** @scrutinizer ignore-call */ 
132
        $executedUnavailableMigrations = $planCalculator->getExecutedUnavailableMigrations();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
132
        $newMigrations                 = $planCalculator->getNewMigrations();
0 ignored issues
show
Bug introduced by
The method getNewMigrations() does not exist on Doctrine\Migrations\Vers...MigrationPlanCalculator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

132
        /** @scrutinizer ignore-call */ 
133
        $newMigrations                 = $planCalculator->getNewMigrations();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
133
134
        if ($this->checkNewMigrations($newMigrations, $input, $output) === false) {
0 ignored issues
show
introduced by
Expected 1 lines after "if", found 0.
Loading history...
135
            return 3;
136
        }
137
        if ($this->checkExecutedUnavailableMigrations($executedUnavailableMigrations, $input, $output) === false) {
138
            return 3;
139
        }
140
141
        $fqcn = $this->getDependencyFactory()->getClassNameGenerator()->generateClassName($namespace);
142
143
        $diffGenerator = $this->getDependencyFactory()->getDiffGenerator();
144
145
        try {
146
            $path = $diffGenerator->generate(
147
                $fqcn,
148
                $filterExpression,
149
                $formatted,
150
                $lineLength,
151
                $checkDbPlatform
152
            );
153
        } catch (NoChangesDetected $exception) {
154
            if ($allowEmptyDiff) {
155
                $output->writeln($exception->getMessage());
156
157
                return 0;
158
            }
159
160
            throw $exception;
161
        }
162
163
        $editorCommand = $input->getOption('editor-cmd');
164
165
        if ($editorCommand !== null) {
166
            assert(is_string($editorCommand));
167
            $this->procOpen($editorCommand, $path);
168
        }
169
170
        $output->writeln([
171
            sprintf('Generated new migration class to "<info>%s</info>"', $path),
172
            '',
173
            sprintf(
174
                'To run just this migration for testing purposes, you can use <info>migrations:execute --up \'%s\'</info>',
175
                addslashes($fqcn)
176
            ),
177
            '',
178
            sprintf(
179
                'To revert the migration you can use <info>migrations:execute --down \'%s\'</info>',
180
                addslashes($fqcn)
181
            ),
182
        ]);
183
184
        return 0;
185
    }
186
187
    private function checkNewMigrations(
188
        AvailableMigrationsList $newMigrations,
189
        InputInterface $input,
190
        OutputInterface $output
191
    ) : bool {
192
        if (count($newMigrations) !== 0) {
0 ignored issues
show
introduced by
Function count() should not be referenced via a fallback global name, but via a use statement.
Loading history...
193
            $output->writeln(sprintf(
194
                '<error>WARNING! You have %s available migrations to execute.</error>',
195
                count($newMigrations)
0 ignored issues
show
introduced by
Function count() should not be referenced via a fallback global name, but via a use statement.
Loading history...
196
            ));
197
198
            $question = 'Are you sure you wish to continue? (y/n)';
199
200
            if (! $this->canExecute($question, $input, $output)) {
201
                $output->writeln('<error>Migration cancelled!</error>');
202
203
                return false;
204
            }
205
        }
206
207
        return true;
208
    }
209
210
    private function checkExecutedUnavailableMigrations(
211
        ExecutedMigrationsSet $executedUnavailableMigrations,
212
        InputInterface $input,
213
        OutputInterface $output
214
    ) : bool {
215
        if (count($executedUnavailableMigrations) !== 0) {
0 ignored issues
show
introduced by
Function count() should not be referenced via a fallback global name, but via a use statement.
Loading history...
216
            $output->writeln(sprintf(
217
                '<error>WARNING! You have %s previously executed migrations in the database that are not registered migrations.</error>',
218
                count($executedUnavailableMigrations)
0 ignored issues
show
introduced by
Function count() should not be referenced via a fallback global name, but via a use statement.
Loading history...
219
            ));
220
221
            foreach ($executedUnavailableMigrations->getItems() as $executedUnavailableMigration) {
222
                $output->writeln(sprintf(
223
                    '    <comment>>></comment> %s (<comment>%s</comment>)',
224
                    $executedUnavailableMigration->getExecutedAt() !== null
225
                        ? $executedUnavailableMigration->getExecutedAt()->format('Y-m-d H:i:s')
226
                        : null,
227
                    $executedUnavailableMigration->getVersion()
228
                ));
229
            }
230
231
            $question = 'Are you sure you wish to continue? (y/n)';
232
233
            if (! $this->canExecute($question, $input, $output)) {
234
                $output->writeln('<error>Migration cancelled!</error>');
235
236
                return false;
237
            }
238
        }
239
240
        return true;
241
    }
242
}
243