Completed
Push — master ( 178c75...b82d68 )
by Luís
11s
created

AbstractCommand   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 140
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 97.83%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 11
dl 0
loc 140
ccs 45
cts 46
cp 0.9783
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 5 1
A outputHeader() 0 10 2
A setMigrationConfiguration() 0 4 1
A getMigrationConfiguration() 0 14 4
A getOutputWriter() 0 10 2
A getConnection() 0 22 3
A askConfirmation() 0 8 2
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Migrations\Tools\Console\Command;
21
22
use Doctrine\DBAL\Migrations\Configuration\Configuration;
23
use Doctrine\DBAL\Migrations\Configuration\Connection\Loader\ArrayConnectionConfigurationLoader;
24
use Doctrine\DBAL\Migrations\Configuration\Connection\Loader\ConnectionConfigurationLoader;
25
use Doctrine\DBAL\Migrations\Configuration\Connection\Loader\ConnectionHelperLoader;
26
use Doctrine\DBAL\Migrations\Configuration\Connection\Loader\ConnectionConfigurationChainLoader;
27
use Doctrine\DBAL\Migrations\OutputWriter;
28
use Doctrine\DBAL\Migrations\Tools\Console\Helper\ConfigurationHelper;
29
use Symfony\Component\Console\Command\Command;
30
use Symfony\Component\Console\Input\InputInterface;
31
use Symfony\Component\Console\Output\OutputInterface;
32
use Symfony\Component\Console\Input\InputOption;
33
use Symfony\Component\Console\Question\ConfirmationQuestion;
34
35
/**
36
 * CLI Command for adding and deleting migration versions from the version table.
37
 *
38
 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
39
 * @link    www.doctrine-project.org
40
 * @since   2.0
41
 * @author  Jonathan Wage <[email protected]>
42
 */
43
abstract class AbstractCommand extends Command
44
{
45
    /**
46
     * The configuration property only contains the configuration injected by the setter.
47
     *
48
     * @var Configuration
49
     */
50
    private $configuration;
51
52
    /**
53
     * The migrationConfiguration property contains the configuration
54
     * created taking into account the command line options.
55
     *
56
     * @var Configuration
57
     */
58
    private $migrationConfiguration;
59
60
    /**
61
     * @var OutputWriter
62
     */
63
    private $outputWriter;
64
65
    /**
66
     * @var \Doctrine\DBAL\Connection
67
     */
68
    private $connection;
69
70 50
    protected function configure()
71
    {
72 50
        $this->addOption('configuration', null, InputOption::VALUE_OPTIONAL, 'The path to a migrations configuration file.');
73 50
        $this->addOption('db-configuration', null, InputOption::VALUE_OPTIONAL, 'The path to a database connection configuration file.');
74 50
    }
75
76 11
    protected function outputHeader(Configuration $configuration, OutputInterface $output)
77
    {
78 11
        $name = $configuration->getName();
79 11
        $name = $name ? $name : 'Doctrine Database Migrations';
80 11
        $name = str_repeat(' ', 20) . $name . str_repeat(' ', 20);
81 11
        $output->writeln('<question>' . str_repeat(' ', strlen($name)) . '</question>');
82 11
        $output->writeln('<question>' . $name . '</question>');
83 11
        $output->writeln('<question>' . str_repeat(' ', strlen($name)) . '</question>');
84 11
        $output->writeln('');
85 11
    }
86
87 21
    public function setMigrationConfiguration(Configuration $config)
88
    {
89 21
        $this->configuration = $config;
90 21
    }
91
92
    /**
93
     * When any (config) command line option is passed to the migration the migrationConfiguration
94
     * property is set with the new generated configuration.
95
     * If no (config) option is passed the migrationConfiguration property is set to the value
96
     * of the configuration one (if any).
97
     * Else a new configuration is created and assigned to the migrationConfiguration property.
98
     *
99
     * @param InputInterface  $input
100
     * @param OutputInterface $output
101
     *
102
     * @return Configuration
103
     */
104 34
    protected function getMigrationConfiguration(InputInterface $input, OutputInterface $output)
105
    {
106 34
        if (!$this->migrationConfiguration) {
107 34
            if ($this->getHelperSet()->has('configuration')
108 34
                && $this->getHelperSet()->get('configuration') instanceof ConfigurationHelper) {
109 1
                $configHelper = $this->getHelperSet()->get('configuration');
110
            } else {
111 33
                $configHelper = new ConfigurationHelper($this->getConnection($input), $this->configuration);
112
            }
113 33
            $this->migrationConfiguration = $configHelper->getMigrationConfig($input, $this->getOutputWriter($output));
114
        }
115
116 33
        return $this->migrationConfiguration;
117
    }
118
119
    /**
120
     * This method ensure that we stay compatible with symfony console 2.3 by using the deprecated dialog helper
121
     * but use the ConfirmationQuestion when available.
122
     *
123
     * @param $question
124
     * @param InputInterface $input
125
     * @param OutputInterface $output
126
     * @return mixed
127
     */
128 6
    protected function askConfirmation($question, InputInterface $input, OutputInterface $output)
129
    {
130 6
        if (!$this->getHelperSet()->has('question')) {
131 1
            return $this->getHelper('dialog')->askConfirmation($output, '<question>' . $question . '</question>', false);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Helper\HelperInterface as the method askConfirmation() does only exist in the following implementations of said interface: Symfony\Component\Console\Helper\DialogHelper.

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...
132
        }
133
134 6
        return $this->getHelper('question')->ask($input, $output, new ConfirmationQuestion($question));
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Symfony\Component\Console\Helper\HelperInterface as the method ask() does only exist in the following implementations of said interface: Symfony\Component\Console\Helper\DialogHelper, Symfony\Component\Console\Helper\QuestionHelper, Symfony\Component\Consol...r\SymfonyQuestionHelper.

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...
135
    }
136
137
    /**
138
     * @param \Symfony\Component\Console\Output\OutputInterface $output
139
     *
140
     * @return \Doctrine\DBAL\Migrations\OutputWriter
141
     */
142 33
    private function getOutputWriter(OutputInterface $output)
143
    {
144 33
        if (!$this->outputWriter) {
145 33
            $this->outputWriter = new OutputWriter(function($message) use ($output) {
146 31
                return $output->writeln($message);
147 33
            });
148
        }
149
150 33
        return $this->outputWriter;
151
    }
152
153
    /**
154
     * @param \Symfony\Component\Console\Input\InputInterface $input
155
     *
156
     * @return \Doctrine\DBAL\Connection
157
     * @throws \Doctrine\DBAL\DBALException
158
     */
159 33
    private function getConnection(InputInterface $input)
160
    {
161 33
        if ($this->connection) {
162
            return $this->connection;
163
        }
164
165 33
        $chainLoader = new ConnectionConfigurationChainLoader(
166
            [
167 33
                new ArrayConnectionConfigurationLoader($input->getOption('db-configuration')),
168 33
                new ArrayConnectionConfigurationLoader('migrations-db.php'),
169 33
                new ConnectionHelperLoader($this->getHelperSet(), 'connection'),
170 33
                new ConnectionConfigurationLoader($this->configuration),
171
            ]
172
        );
173 33
        $connection = $chainLoader->chosen();
174
175 33
        if ($connection) {
176 32
            return $this->connection = $connection;
177
        }
178
179 1
        throw new \InvalidArgumentException('You have to specify a --db-configuration file or pass a Database Connection as a dependency to the Migrations.');
180
    }
181
182
}
183