Completed
Push — php7.0 ( 4402d4 )
by André
37:16
created

Legacy   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 402
Duplicated Lines 9.7 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
dl 39
loc 402
rs 9.1199
c 0
b 0
f 0
wmc 41
lcom 1
cbo 18

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 4
A createTemporaryDirectory() 0 19 2
A getRepository() 0 15 3
A getConfigValue() 0 4 1
A getIdManager() 0 4 1
C insertData() 39 69 10
A getInitialVarDir() 0 4 1
A cleanupVarDir() 0 10 2
A clearInternalCaches() 0 20 3
A getPostInsertStatements() 0 10 2
A getInitialData() 0 9 2
A initializeSchema() 0 10 2
A applyStatements() 0 6 2
A getSchemaStatements() 0 6 1
A getDatabaseHandler() 0 4 1
A getServiceContainer() 0 39 2
A externalBuildContainer() 0 4 1
A getDB() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Legacy often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Legacy, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * File containing the Test Setup Factory base class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\API\Repository\Tests\SetupFactory;
10
11
use Doctrine\DBAL\DBALException;
12
use Doctrine\DBAL\Driver\PDOException;
13
use eZ\Publish\Core\Base\ServiceContainer;
14
use Symfony\Component\DependencyInjection\ContainerBuilder;
15
use eZ\Publish\API\Repository\Tests\SetupFactory;
16
use eZ\Publish\API\Repository\Tests\IdManager;
17
use eZ\Publish\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler as CachingContentTypeHandler;
18
use eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler as CachingLanguageHandler;
19
use Exception;
20
use eZ\Publish\Core\Repository\Values\User\UserReference;
21
use Symfony\Component\Filesystem\Filesystem;
22
use eZ\Publish\Core\Base\Container\Compiler;
23
24
/**
25
 * A Test Factory is used to setup the infrastructure for a tests, based on a
26
 * specific repository implementation to test.
27
 */
28
class Legacy extends SetupFactory
29
{
30
    /**
31
     * Data source name.
32
     *
33
     * @var string
34
     */
35
    protected static $dsn;
36
37
    /**
38
     * Root dir for IO operations.
39
     *
40
     * @var string
41
     */
42
    protected static $ioRootDir;
43
44
    /**
45
     * Database type (sqlite, mysql, ...).
46
     *
47
     * @var string
48
     */
49
    protected static $db;
50
51
    /**
52
     * Service container.
53
     *
54
     * @var \eZ\Publish\Core\Base\ServiceContainer
55
     */
56
    protected static $serviceContainer;
57
58
    /**
59
     * If the DB schema has already been initialized.
60
     *
61
     * @var bool
62
     */
63
    protected static $schemaInitialized = false;
64
65
    /**
66
     * Initial database data.
67
     *
68
     * @var array
69
     */
70
    protected static $initialData;
71
72
    protected $repositoryReference = 'ezpublish.api.repository';
73
74
    /**
75
     * Creates a new setup factory.
76
     */
77
    public function __construct()
78
    {
79
        self::$dsn = getenv('DATABASE');
80
        if (!self::$dsn) {
81
            self::$dsn = 'sqlite://:memory:';
82
        }
83
84
        if ($repositoryReference = getenv('REPOSITORY_SERVICE_ID')) {
85
            $this->repositoryReference = $repositoryReference;
86
        }
87
88
        self::$db = preg_replace('(^([a-z]+).*)', '\\1', self::$dsn);
89
90
        if (!isset(self::$ioRootDir)) {
91
            self::$ioRootDir = $this->createTemporaryDirectory();
92
        }
93
    }
94
95
    /**
96
     * Creates a temporary directory and returns it.
97
     *
98
     * @return string
99
     * @throw \RuntimeException If the root directory can't be created
100
     */
101
    private function createTemporaryDirectory()
102
    {
103
        $tmpFile = tempnam(
104
            sys_get_temp_dir(),
105
            'ez_legacy_tests_' . time()
106
        );
107
        unlink($tmpFile);
108
109
        $fs = new Filesystem();
110
        $fs->mkdir($tmpFile);
111
112
        $varDir = $tmpFile . '/var';
113
        if ($fs->exists($varDir)) {
114
            $fs->remove($varDir);
115
        }
116
        $fs->mkdir($varDir);
117
118
        return $tmpFile;
119
    }
120
121
    /**
122
     * Returns a configured repository for testing.
123
     *
124
     * @param bool $initializeFromScratch if the back end should be initialized
125
     *                                    from scratch or re-used
126
     *
127
     * @return \eZ\Publish\API\Repository\Repository
128
     */
129
    public function getRepository($initializeFromScratch = true)
130
    {
131
        if ($initializeFromScratch || !self::$schemaInitialized) {
132
            $this->initializeSchema();
133
            $this->insertData();
134
        }
135
136
        $this->clearInternalCaches();
137
        $repository = $this->getServiceContainer()->get($this->repositoryReference);
138
139
        // Set admin user as current user by default
140
        $repository->setCurrentUser(new UserReference(14));
141
142
        return $repository;
143
    }
144
145
    /**
146
     * Returns a config value for $configKey.
147
     *
148
     * @param string $configKey
149
     *
150
     * @throws Exception if $configKey could not be found.
151
     *
152
     * @return mixed
153
     */
154
    public function getConfigValue($configKey)
155
    {
156
        return $this->getServiceContainer()->getParameter($configKey);
157
    }
158
159
    /**
160
     * Returns a repository specific ID manager.
161
     *
162
     * @return \eZ\Publish\API\Repository\Tests\IdManager
163
     */
164
    public function getIdManager()
165
    {
166
        return new IdManager\Php();
167
    }
168
169
    /**
170
     * Insert the database data.
171
     */
172
    public function insertData()
173
    {
174
        $data = $this->getInitialData();
175
        $handler = $this->getDatabaseHandler();
176
        $connection = $handler->getConnection();
177
        $dbPlatform = $connection->getDatabasePlatform();
178
        $this->cleanupVarDir($this->getInitialVarDir());
179
180
        // @todo FIXME: Needs to be in fixture
181
        $data['ezcontentobject_trash'] = array();
182
        $data['ezurlwildcard'] = array();
183
        $data['ezmedia'] = array();
184
        $data['ezkeyword'] = array();
185
186
        foreach (array_reverse(array_keys($data)) as $table) {
187
            try {
188
                // Cleanup before inserting (using TRUNCATE for speed, however not possible to rollback)
189
                $connection->executeUpdate($dbPlatform->getTruncateTableSql($handler->quoteIdentifier($table)));
190
            } catch (DBALException $e) {
191
                // Fallback to DELETE if TRUNCATE failed (because of FKs for instance)
192
                $connection->createQueryBuilder()->delete($table)->execute();
193
            } catch (PDOException $e) {
194
                // Fallback to DELETE if TRUNCATE failed (because of FKs for instance)
195
                $connection->createQueryBuilder()->delete($table)->execute();
196
            }
197
        }
198
199 View Code Duplication
        foreach ($data as $table => $rows) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
200
            // Check that at least one row exists
201
            if (!isset($rows[0])) {
202
                continue;
203
            }
204
205
            $q = $handler->createInsertQuery();
206
            $q->insertInto($handler->quoteIdentifier($table));
207
208
            // Contains the bound parameters
209
            $values = array();
210
211
            // Binding the parameters
212
            foreach ($rows[0] as $col => $val) {
213
                $q->set(
214
                    $handler->quoteIdentifier($col),
215
                    $q->bindParam($values[$col])
216
                );
217
            }
218
219
            $stmt = $q->prepare();
220
221
            foreach ($rows as $row) {
222
                try {
223
                    // This CANNOT be replaced by:
224
                    // $values = $row
225
                    // each $values[$col] is a PHP reference which should be
226
                    // kept for parameters binding to work
227
                    foreach ($row as $col => $val) {
228
                        $values[$col] = $val;
229
                    }
230
231
                    $stmt->execute();
232
                } catch (Exception $e) {
233
                    echo "$table ( ", implode(', ', $row), " )\n";
234
                    throw $e;
235
                }
236
            }
237
        }
238
239
        $this->applyStatements($this->getPostInsertStatements());
240
    }
241
242
    protected function getInitialVarDir()
243
    {
244
        return __DIR__ . '/../../../../../../var';
245
    }
246
247
    protected function cleanupVarDir($sourceDir)
248
    {
249
        $fs = new Filesystem();
250
        $varDir = self::$ioRootDir . '/var';
251
        if ($fs->exists($varDir)) {
252
            $fs->remove($varDir);
253
        }
254
        $fs->mkdir($varDir);
255
        $fs->mirror($sourceDir, $varDir);
256
    }
257
258
    /**
259
     * CLears internal in memory caches after inserting data circumventing the
260
     * API.
261
     */
262
    protected function clearInternalCaches()
263
    {
264
        /** @var $handler \eZ\Publish\Core\Persistence\Legacy\Handler */
265
        $handler = $this->getServiceContainer()->get('ezpublish.spi.persistence.legacy');
266
267
        $contentLanguageHandler = $handler->contentLanguageHandler();
268
        if ($contentLanguageHandler instanceof CachingLanguageHandler) {
269
            $contentLanguageHandler->clearCache();
270
        }
271
272
        $contentTypeHandler = $handler->contentTypeHandler();
273
        if ($contentTypeHandler instanceof CachingContentTypeHandler) {
274
            $contentTypeHandler->clearCache();
275
        }
276
277
        /** @var $cachePool \Psr\Cache\CacheItemPoolInterface */
278
        $cachePool = $this->getServiceContainer()->get('ezpublish.cache_pool');
279
280
        $cachePool->clear();
281
    }
282
283
    /**
284
     * Returns statements to be executed after data insert.
285
     *
286
     * @return string[]
287
     */
288
    protected function getPostInsertStatements()
289
    {
290
        if (self::$db === 'pgsql') {
291
            $setvalPath = __DIR__ . '/../../../../Core/Persistence/Legacy/Tests/_fixtures/setval.pgsql.sql';
292
293
            return array_filter(preg_split('(;\\s*$)m', file_get_contents($setvalPath)));
294
        }
295
296
        return array();
297
    }
298
299
    /**
300
     * Returns the initial database data.
301
     *
302
     * @return array
303
     */
304
    protected function getInitialData()
305
    {
306
        if (!isset(self::$initialData)) {
307
            self::$initialData = include __DIR__ . '/../../../../Core/Repository/Tests/Service/Integration/Legacy/_fixtures/clean_ezdemo_47_dump.php';
308
            // self::$initialData = include __DIR__ . '/../../../../Core/Repository/Tests/Service/Legacy/_fixtures/full_dump.php';
309
        }
310
311
        return self::$initialData;
312
    }
313
314
    /**
315
     * Initializes the database schema.
316
     */
317
    protected function initializeSchema()
318
    {
319
        if (!self::$schemaInitialized) {
320
            $statements = $this->getSchemaStatements();
321
322
            $this->applyStatements($statements);
323
324
            self::$schemaInitialized = true;
325
        }
326
    }
327
328
    /**
329
     * Applies the given SQL $statements to the database in use.
330
     *
331
     * @param array $statements
332
     */
333
    protected function applyStatements(array $statements)
334
    {
335
        foreach ($statements as $statement) {
336
            $this->getDatabaseHandler()->exec($statement);
337
        }
338
    }
339
340
    // ************* Setup copied and refactored from common.php ************
341
342
    /**
343
     * Returns the database schema as an array of SQL statements.
344
     *
345
     * @return string[]
346
     */
347
    protected function getSchemaStatements()
348
    {
349
        $schemaPath = __DIR__ . '/../../../../Core/Persistence/Legacy/Tests/_fixtures/schema.' . self::$db . '.sql';
350
351
        return array_filter(preg_split('(;\\s*$)m', file_get_contents($schemaPath)));
352
    }
353
354
    /**
355
     * Returns the database handler from the service container.
356
     *
357
     * @return \eZ\Publish\Core\Persistence\Doctrine\ConnectionHandler
358
     */
359
    protected function getDatabaseHandler()
360
    {
361
        return $this->getServiceContainer()->get('ezpublish.api.storage_engine.legacy.dbhandler');
362
    }
363
364
    /**
365
     * Returns the service container used for initialization of the repository.
366
     *
367
     * @return \eZ\Publish\Core\Base\ServiceContainer
368
     */
369
    public function getServiceContainer()
370
    {
371
        if (!isset(self::$serviceContainer)) {
372
            $config = include __DIR__ . '/../../../../../../config.php';
373
            $installDir = $config['install_dir'];
374
375
            /** @var \Symfony\Component\DependencyInjection\ContainerBuilder $containerBuilder */
376
            $containerBuilder = include $config['container_builder_path'];
377
378
            /* @var \Symfony\Component\DependencyInjection\Loader\YamlFileLoader $loader */
379
            $loader->load('search_engines/legacy.yml');
0 ignored issues
show
Bug introduced by
The variable $loader 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...
380
            $loader->load('tests/integration_legacy.yml');
381
382
            $this->externalBuildContainer($containerBuilder);
383
384
            $containerBuilder->setParameter(
385
                'legacy_dsn',
386
                self::$dsn
387
            );
388
389
            $containerBuilder->setParameter(
390
                'io_root_dir',
391
                self::$ioRootDir . '/' . $containerBuilder->getParameter('storage_dir')
392
            );
393
394
            $containerBuilder->addCompilerPass(new Compiler\Search\SearchEngineSignalSlotPass('legacy'));
395
            $containerBuilder->addCompilerPass(new Compiler\Search\FieldRegistryPass());
396
397
            self::$serviceContainer = new ServiceContainer(
398
                $containerBuilder,
399
                $installDir,
400
                $config['cache_dir'],
401
                true,
402
                true
403
            );
404
        }
405
406
        return self::$serviceContainer;
407
    }
408
409
    /**
410
     * This is intended to be used from external repository in order to
411
     * enable container customization.
412
     *
413
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $containerBuilder
414
     */
415
    protected function externalBuildContainer(ContainerBuilder $containerBuilder)
0 ignored issues
show
Unused Code introduced by
The parameter $containerBuilder is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
416
    {
417
        // Does nothing by default
418
    }
419
420
    /**
421
     * Get the Database name.
422
     *
423
     * @return string
424
     */
425
    public function getDB()
426
    {
427
        return self::$db;
428
    }
429
}
430