Completed
Push — master ( e3d872...a24c50 )
by Kamil
42:39
created

Provider/DatabaseSetupCommandsProvider.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sylius\Bundle\CoreBundle\Installer\Provider;
15
16
use Doctrine\Bundle\DoctrineBundle\Registry;
17
use Doctrine\DBAL\Schema\AbstractSchemaManager;
18
use Symfony\Component\Console\Helper\QuestionHelper;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Output\OutputInterface;
21
use Symfony\Component\Console\Question\ConfirmationQuestion;
22
use Symfony\Component\Console\Style\SymfonyStyle;
23
24
final class DatabaseSetupCommandsProvider implements DatabaseSetupCommandsProviderInterface
25
{
26
    /**
27
     * @var Registry
28
     */
29
    private $doctrineRegistry;
30
31
    /**
32
     * @param Registry $doctrineRegistry
33
     */
34
    public function __construct(Registry $doctrineRegistry)
35
    {
36
        $this->doctrineRegistry = $doctrineRegistry;
37
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function getCommands(InputInterface $input, OutputInterface $output, QuestionHelper $questionHelper): array
43
    {
44
        if (!$this->isDatabasePresent()) {
45
            return [
46
                'doctrine:database:create',
47
                'doctrine:migrations:migrate' => ['--no-interaction' => true],
48
                'cache:clear',
49
            ];
50
        }
51
52
        return array_merge($this->getRequiredCommands($input, $output, $questionHelper), [
53
            'cache:clear',
54
            'doctrine:migrations:version' => [
55
                '--add' => true,
56
                '--all' => true,
57
                '--no-interaction' => true,
58
            ],
59
        ]);
60
    }
61
62
    /**
63
     * @return bool
64
     *
65
     * @throws \Exception
66
     */
67
    private function isDatabasePresent(): bool
68
    {
69
        $databaseName = $this->getDatabaseName();
70
71
        try {
72
            $schemaManager = $this->getSchemaManager();
73
74
            return in_array($databaseName, $schemaManager->listDatabases());
75
        } catch (\Exception $exception) {
76
            $message = $exception->getMessage();
77
78
            $mysqlDatabaseError = false !== strpos($message, sprintf("Unknown database '%s'", $databaseName));
79
            $postgresDatabaseError = false !== strpos($message, sprintf('database "%s" does not exist', $databaseName));
80
81
            if ($mysqlDatabaseError || $postgresDatabaseError) {
82
                return false;
83
            }
84
85
            throw $exception;
86
        }
87
    }
88
89
    /**
90
     * @param InputInterface $input
91
     * @param OutputInterface $output
92
     * @param QuestionHelper $questionHelper
93
     *
94
     * @return array
95
     */
96
    private function getRequiredCommands(InputInterface $input, OutputInterface $output, QuestionHelper $questionHelper): array
97
    {
98
        if ($input->getOption('no-interaction')) {
99
            $commands['doctrine:migrations:migrate'] = ['--no-interaction' => true];
100
        }
101
102
        return $this->setupDatabase($input, $output, $questionHelper);
103
    }
104
105
    /**
106
     * @param InputInterface $input
107
     * @param OutputInterface $output
108
     * @param QuestionHelper $questionHelper
109
     *
110
     * @return array
111
     */
112
    private function setupDatabase(InputInterface $input, OutputInterface $output, QuestionHelper $questionHelper): array
113
    {
114
        $outputStyle = new SymfonyStyle($input, $output);
115
        $outputStyle->writeln('It appears that your database already exists.');
116
        $outputStyle->writeln('<error>Warning! This action will erase your database.</error>');
117
118
        $question = new ConfirmationQuestion('Would you like to reset it? (y/N) ', false);
119
        if ($questionHelper->ask($input, $output, $question)) {
120
            return [
121
                'doctrine:database:drop' => ['--force' => true],
122
                'doctrine:database:create',
123
                'doctrine:migrations:migrate' => ['--no-interaction' => true],
124
            ];
125
        }
126
127
        if (!$this->isSchemaPresent()) {
128
            return ['doctrine:migrations:migrate' => ['--no-interaction' => true]];
129
        }
130
131
        $outputStyle->writeln('Seems like your database contains schema.');
132
        $outputStyle->writeln('<error>Warning! This action will erase your database.</error>');
133
        $question = new ConfirmationQuestion('Do you want to reset it? (y/N) ', false);
134
        if ($questionHelper->ask($input, $output, $question)) {
135
            return [
136
                'doctrine:schema:drop' => ['--force' => true],
137
                'doctrine:migrations:migrate' => ['--no-interaction' => true],
138
            ];
139
        }
140
141
        return [];
142
    }
143
144
    /**
145
     * @return bool
146
     */
147
    private function isSchemaPresent(): bool
148
    {
149
        return 0 !== count($this->getSchemaManager()->listTableNames());
150
    }
151
152
    /**
153
     * @return string
154
     */
155
    private function getDatabaseName(): string
156
    {
157
        return $this->doctrineRegistry->getManager()->getConnection()->getDatabase();
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Common\Persistence\ObjectManager as the method getConnection() does only exist in the following implementations of said interface: Doctrine\ORM\Decorator\EntityManagerDecorator, Doctrine\ORM\EntityManager.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
158
    }
159
160
    /**
161
     * @return AbstractSchemaManager
162
     */
163
    private function getSchemaManager(): AbstractSchemaManager
164
    {
165
        return $this->doctrineRegistry->getManager()->getConnection()->getSchemaManager();
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Common\Persistence\ObjectManager as the method getConnection() does only exist in the following implementations of said interface: Doctrine\ORM\Decorator\EntityManagerDecorator, Doctrine\ORM\EntityManager.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
166
    }
167
}
168