Completed
Pull Request — master (#255)
by Evgenij
17:18
created

V2MigrationsLoader   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 219
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 26
lcom 1
cbo 11
dl 0
loc 219
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A process() 0 12 3
F createMigrationConfigurationService() 0 77 11
A createTemporaryConfiguration() 0 39 3
A getConnection() 0 21 4
A getPredefinedMigrations() 0 27 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Liip\MonitorBundle\DependencyInjection\DoctrineMigrations;
6
7
use Doctrine\DBAL\Connection;
8
use Doctrine\DBAL\Driver\PDO\SQLite\Driver;
9
use Doctrine\DBAL\Driver\PDOSqlite\Driver as LegacyDriver;
10
use Doctrine\Migrations\Configuration\AbstractFileConfiguration;
11
use Doctrine\Migrations\Configuration\Configuration as DoctrineMigrationConfiguration;
12
use Liip\MonitorBundle\DoctrineMigrations\Configuration;
13
use Symfony\Component\DependencyInjection\ChildDefinition;
14
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15
use Symfony\Component\DependencyInjection\ContainerBuilder;
16
use Symfony\Component\DependencyInjection\Definition;
17
use Symfony\Component\DependencyInjection\Reference;
18
19
/**
20
 * Class V2MigrationsLoader
21
 */
22
final class V2MigrationsLoader extends AbstractDoctrineMigrationsLoader implements CompilerPassInterface
23
{
24
    /**
25
     * Connection object needed for correct migration loading.
26
     *
27
     * @var Connection
28
     */
29
    private $fakeConnection;
30
31
    /**
32
     * Tuple (migrationsConfiguration, tempConfiguration) for doctrine migrations check.
33
     *
34
     * @var array
35
     */
36
    private $migrationConfigurationsServices = [];
37
38
    /**
39
     * @inheritDoc
40
     */
41
    public function process(ContainerBuilder $container)
42
    {
43
        foreach ($this->migrationConfigurationsServices as $services) {
44
            [$configurationService, $configuration] = $services;
0 ignored issues
show
Bug introduced by
The variable $configurationService does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $configuration does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
45
            /** @var Definition $configurationService */
46
            /** @var DoctrineMigrationConfiguration $configuration */
47
            $versions = $this->getPredefinedMigrations($container, $configuration, $this->fakeConnection);
48
            if ($versions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $versions of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
49
                $configurationService->addMethodCall('registerMigrations', [$versions]);
50
            }
51
        }
52
    }
53
54
    /**
55
     * @inheritDoc
56
     */
57
    public function createMigrationConfigurationService(
58
        ContainerBuilder $container,
59
        string $connectionName,
60
        string $serviceId,
61
        string $filename = null
62
    ): void {
63
        if (!$container->has(Configuration::class)) {
64
            $container->register(Configuration::class)
65
                ->setAbstract(true)
66
                ->setPublic(true)
67
                ->setArguments([null])
68
                ->addMethodCall('setContainer', [new Reference('service_container')]);
69
        }
70
71
        $configuration = $this->createTemporaryConfiguration($container, $this->getConnection(), $filename);
72
73
        $serviceConfiguration = new ChildDefinition(Configuration::class);
74
75
        $this->migrationConfigurationsServices[] = [$serviceConfiguration, $configuration];
76
77
        $serviceConfiguration->replaceArgument(
78
            0,
79
            new Reference(sprintf('doctrine.dbal.%s_connection', $connectionName))
80
        );
81
82
        if ($configuration->getMigrationsNamespace()) {
0 ignored issues
show
Bug introduced by
The method getMigrationsNamespace() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
83
            $serviceConfiguration->addMethodCall(
84
                'setMigrationsNamespace',
85
                [$configuration->getMigrationsNamespace()]
0 ignored issues
show
Bug introduced by
The method getMigrationsNamespace() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
86
            );
87
        }
88
89
        if ($configuration->getMigrationsTableName()) {
0 ignored issues
show
Bug introduced by
The method getMigrationsTableName() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
90
            $serviceConfiguration->addMethodCall(
91
                'setMigrationsTableName',
92
                [$configuration->getMigrationsTableName()]
0 ignored issues
show
Bug introduced by
The method getMigrationsTableName() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
93
            );
94
        }
95
96
        if ($configuration->getMigrationsColumnName()) {
0 ignored issues
show
Bug introduced by
The method getMigrationsColumnName() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
97
            $serviceConfiguration->addMethodCall(
98
                'setMigrationsColumnName',
99
                [$configuration->getMigrationsColumnName()]
0 ignored issues
show
Bug introduced by
The method getMigrationsColumnName() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
100
            );
101
        }
102
103
        if ($configuration->getName()) {
0 ignored issues
show
Bug introduced by
The method getName() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
104
            $serviceConfiguration->addMethodCall('setName', [$configuration->getName()]);
0 ignored issues
show
Bug introduced by
The method getName() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
105
        }
106
107
        if ($configuration->getMigrationsDirectory()) {
0 ignored issues
show
Bug introduced by
The method getMigrationsDirectory() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
108
            $directory = $configuration->getMigrationsDirectory();
0 ignored issues
show
Bug introduced by
The method getMigrationsDirectory() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
109
            $pathPlaceholders = ['kernel.root_dir', 'kernel.cache_dir', 'kernel.logs_dir'];
110
            foreach ($pathPlaceholders as $parameter) {
111
                $kernelDir = realpath($container->getParameter($parameter));
112
                if (0 === strpos(realpath($directory), $kernelDir)) {
113
                    $directory = str_replace($kernelDir, "%{$parameter}%", $directory);
114
                    break;
115
                }
116
            }
117
118
            $serviceConfiguration->addMethodCall(
119
                'setMigrationsDirectory',
120
                [$directory]
121
            );
122
        }
123
124
        $serviceConfiguration->addMethodCall('configure', []);
125
126
        if ($configuration->areMigrationsOrganizedByYear()) {
127
            $serviceConfiguration->addMethodCall('setMigrationsAreOrganizedByYear', [true]);
128
        } elseif ($configuration->areMigrationsOrganizedByYearAndMonth()) {
129
            $serviceConfiguration->addMethodCall('setMigrationsAreOrganizedByYearAndMonth', [true]);
130
        }
131
132
        $container->setDefinition($serviceId, $serviceConfiguration);
133
    }
134
135
    /**
136
     * Creates in-memory migration configuration for setting up container service.
137
     *
138
     * @param ContainerBuilder $container  The container
139
     * @param Connection       $connection Fake connection
140
     * @param string           $filename   Migrations configuration file
141
     */
142
    private function createTemporaryConfiguration(
143
        ContainerBuilder $container,
144
        Connection $connection,
145
        string $filename = null
146
    ): DoctrineMigrationConfiguration {
147
        if (null === $filename) {
148
            // this is configured from migrations bundle
149
            return new DoctrineMigrationConfiguration($connection);
0 ignored issues
show
Unused Code introduced by
The call to Configuration::__construct() has too many arguments starting with $connection.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
150
        }
151
152
        // -------
153
        // This part must be in sync with Doctrine\Migrations\Tools\Console\Helper\ConfigurationHelper::loadConfig
154
        $map = [
155
            'xml' => '\XmlConfiguration',
156
            'yaml' => '\YamlConfiguration',
157
            'yml' => '\YamlConfiguration',
158
            'php' => '\ArrayConfiguration',
159
            'json' => '\JsonConfiguration',
160
        ];
161
        // --------
162
163
        $filename = $container->getParameterBag()->resolveValue($filename);
164
        $info = pathinfo($filename);
165
        // check we can support this file type
166
        if (empty($map[$info['extension']])) {
167
            throw new \InvalidArgumentException('Given config file type is not supported');
168
        }
169
170
        $class = 'Doctrine\Migrations\Configuration';
171
        $class .= $map[$info['extension']];
172
        // -------
173
174
        /** @var AbstractFileConfiguration $configuration */
175
        $configuration = new $class($connection);
176
        $configuration->load($filename);
177
        $configuration->validate();
178
179
        return $configuration;
180
    }
181
182
    private function getConnection(): Connection
183
    {
184
        if ($this->fakeConnection === null) {
185
            if (!class_exists(Connection::class)) {
186
                throw new \InvalidArgumentException(
187
                    sprintf(
188
                        'Can not configure doctrine migration checks, because of absence of "%s" class',
189
                        Connection::class
190
                    )
191
                );
192
            }
193
194
            $driver = class_exists(Driver::class)
195
                ? new Driver()
196
                : new LegacyDriver();
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\DBAL\Driver\PDOSqlite\Driver has been deprecated with message: Use {@link PDO\SQLite\Driver} instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
197
198
            $this->fakeConnection = new Connection([], $driver);
199
        }
200
201
        return $this->fakeConnection;
202
    }
203
204
    /**
205
     * Return key-value array with migration version as key and class as a value defined in config file.
206
     *
207
     * @param ContainerBuilder               $container  The container
208
     * @param DoctrineMigrationConfiguration $config     Current configuration
209
     * @param Connection                     $connection Fake connections
210
     *
211
     * @return string[]
212
     */
213
    private function getPredefinedMigrations(ContainerBuilder $container, DoctrineMigrationConfiguration $config, Connection $connection): array
214
    {
215
        $result = [];
216
217
        $diff = new Configuration($connection);
0 ignored issues
show
Unused Code introduced by
The call to Configuration::__construct() has too many arguments starting with $connection.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
218
219
        if ($namespace = $config->getMigrationsNamespace()) {
0 ignored issues
show
Bug introduced by
The method getMigrationsNamespace() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
Unused Code introduced by
$namespace is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
220
            $diff->setMigrationsNamespace($config->getMigrationsNamespace());
0 ignored issues
show
Bug introduced by
The method getMigrationsNamespace() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
Bug introduced by
The method setMigrationsNamespace() does not seem to exist on object<Liip\MonitorBundl...grations\Configuration>.

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...
221
        }
222
223
        if ($dir = $config->getMigrationsDirectory()) {
0 ignored issues
show
Bug introduced by
The method getMigrationsDirectory() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
224
            $diff->setMigrationsDirectory($dir);
0 ignored issues
show
Bug introduced by
The method setMigrationsDirectory() does not seem to exist on object<Liip\MonitorBundl...grations\Configuration>.

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...
225
        }
226
227
        $diff->setContainer($container);
228
        $diff->configure();
229
230
        foreach ($config->getMigrations() as $version) {
0 ignored issues
show
Bug introduced by
The method getMigrations() does not seem to exist on object<Doctrine\Migratio...guration\Configuration>.

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...
231
            $result[$version->getVersion()] = get_class($version->getMigration());
232
        }
233
234
        foreach ($diff->getAvailableVersions() as $version) {
0 ignored issues
show
Bug introduced by
The method getAvailableVersions() does not seem to exist on object<Liip\MonitorBundl...grations\Configuration>.

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...
235
            unset($result[$version]);
236
        }
237
238
        return $result;
239
    }
240
}
241