Completed
Push — master ( c3efa2...a812ec )
by Guilherme
13s
created

DeployCommand::checkDatabase()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 5
nop 1
dl 0
loc 20
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace LoginCidadao\CoreBundle\Command;
4
5
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
6
use Symfony\Component\Console\Output\OutputInterface;
7
use Symfony\Component\Console\Output\BufferedOutput;
8
use Symfony\Component\Console\Input\InputInterface;
9
use Symfony\Component\Console\Input\InputOption;
10
use Symfony\Component\Console\Input\ArrayInput;
11
use Symfony\Component\Console\Style\SymfonyStyle;
12
13
class DeployCommand extends ContainerAwareCommand
14
{
15
    private $updateDb = false;
16
17
    protected function configure()
18
    {
19
        $this
20
            ->setName('lc:deploy')
21
            ->addOption('--update-db', null, InputOption::VALUE_NONE,
22
                'Update database schema without prompting')
23
            ->setDescription('Perform basic deploy commands.');
24
    }
25
26
    protected function execute(InputInterface $input, OutputInterface $output)
27
    {
28
        $this->updateDb = $input->getOption('update-db');
29
30
        $io = new SymfonyStyle($input, $output);
31
        $io->title("Running deploy tasks...");
32
33
        $this->clearMetadata($io);
34
        $this->clearCache($io, 'prod');
35
        $this->checkDatabase($io);
36
        $this->installAssets($io);
37
    }
38
39
    private function clearMetadata(SymfonyStyle $io)
40
    {
41
        $io->section("Clearing Doctrine Metadata...");
42
        $command = $this->getApplication()->find('doctrine:cache:clear-metadata');
43
44
        $envs = $this->getEnvsInput();
45
        $io->progressStart(count($envs));
46
        foreach ($envs as $env => $input) {
0 ignored issues
show
Bug introduced by
The expression $envs of type array<string,object<Symf...nsole\Input\ArrayInput> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
47
            $cmdOutput = new BufferedOutput();
48
            $returnCode = $command->run($input, $cmdOutput);
49
50
            if ($returnCode !== 0) {
51
                $io->newLine(2);
52
                $io->error("Couldn't clear metadata cache on $env");
53
54
                return;
55
            }
56
            $io->progressAdvance();
57
        }
58
        $io->progressFinish();
59
    }
60
61
    private function installAssets(SymfonyStyle $io)
62
    {
63
        $io->section("Installing assets...");
64
        $input = $this->getEnvsInput('prod');
65
        $commands = ['assets:install', 'assetic:dump'];
66
        $io->progressStart(count($commands));
67
        foreach ($commands as $command) {
68
            $cmdOutput = new BufferedOutput();
69
            $returnCode = $this->getApplication()
70
                ->find($command)->run($input, $cmdOutput);
71
72
            if ($returnCode !== 0) {
73
                $io->newLine(2);
74
                $io->error("$command failed. Run it separately to find out why.");
75
76
                return;
77
            }
78
            $io->progressAdvance();
79
        }
80
        $io->progressFinish();
81
    }
82
83
    private function clearCache(SymfonyStyle $io, $env)
84
    {
85
        $io->section("Clearing cache ($env)...");
86
        $io->progressStart(1);
87
        $input = $this->getEnvsInput($env);
88
        $command = $this->getApplication()->find('cache:clear');
89
90
        $cmdOutput = new BufferedOutput();
91
        $returnCode = $command->run($input, $cmdOutput);
92
93
        if ($returnCode !== 0) {
94
            $io->error("cache:clear command failed. You may need to manually delete the cache folders.");
95
96
            return;
97
        }
98
99
        $io->progressFinish();
100
    }
101
102
    private function getEnvsInput($env = null)
103
    {
104
        $envs = [
105
            'prod' => new ArrayInput(['--env' => 'prod']),
106
            'dev' => new ArrayInput(['--env' => 'dev']),
107
        ];
108
109
        if ($env === null) {
110
            return $envs;
111
        } else {
112
            return $envs[$env];
113
        }
114
    }
115
116
    private function checkDatabase(SymfonyStyle $io)
117
    {
118
        $io->section("Checking database schema...");
119
120
        $defaultEm = $this->checkSchemaNeedsUpdate('default');
121
        $logsEm = $this->checkSchemaNeedsUpdate('logs');
122
123
        if (!$defaultEm && !$logsEm) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $defaultEm of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $logsEm of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
124
            $io->success(trim($defaultEm));
125
126
            return;
127
        }
128
129
        if ($defaultEm) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $defaultEm of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
130
            $this->updateSchema($io, explode("\n", trim($defaultEm)), 'default');
131
        }
132
        if ($logsEm) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $logsEm of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
133
            $this->updateSchema($io, explode("\n", trim($logsEm)), 'logs');
134
        }
135
    }
136
137
    private function checkSchemaNeedsUpdate($entityManager)
138
    {
139
        $cmdOutput = new BufferedOutput();
140
        $command = $this->getApplication()->find('doctrine:schema:update');
141
        $input = new ArrayInput([
142
            '--env' => 'dev',
143
            '--dump-sql' => true,
144
            '--em' => $entityManager,
145
        ]);
146
147
        $command->run($input, $cmdOutput);
148
149
        $output = $cmdOutput->fetch();
150
        if (strstr($output, 'Nothing to update') !== false) {
151
            return false;
152
        }
153
154
        return $output;
155
    }
156
157
    private function updateSchema(SymfonyStyle $io, $queries, $entityManager)
158
    {
159
        $io->caution("Your database schema needs to be updated. The following queries will be run:");
160
        $io->listing($queries);
161
        if ($this->updateDb === false &&
162
            $io->confirm("Should we run this queries now?", false) === false) {
163
            return;
164
        }
165
166
        $cmdOutput = new BufferedOutput();
167
        $command = $this->getApplication()->find('doctrine:schema:update');
168
        $force = new ArrayInput([
169
            '--env' => 'dev',
170
            '--dump-sql' => true,
171
            '--force' => true,
172
            '--em' => $entityManager,
173
        ]);
174
        $command->run($force, $cmdOutput);
175
176
        $result = $cmdOutput->fetch();
177
        if (strstr($result, 'Database schema updated successfully!') === false) {
178
            $io->error("Couldn't update the schema. Run 'doctrine:schema:update' separately to find out why");
179
        }
180
        $io->success("Database schema updated successfully!");
181
182
        $this->clearMetadata($io);
183
    }
184
}
185