Completed
Push — ezp-31420-merge-up ( ec14fb...141a64 )
by
unknown
40:13 queued 27:42
created

Legacy::insertData()   B

Complexity

Conditions 9
Paths 42

Size

Total Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
nc 42
nop 0
dl 0
loc 60
rs 7.3171
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\API\Repository\Tests\SetupFactory;
8
9
use Doctrine\DBAL\Connection;
10
use Doctrine\DBAL\DBALException;
11
use Doctrine\DBAL\Driver\PDOException;
12
use Doctrine\DBAL\Schema\Schema;
13
use eZ\Publish\API\Repository\Tests\LegacySchemaImporter;
14
use eZ\Publish\Core\Base\ServiceContainer;
15
use Symfony\Component\DependencyInjection\ContainerBuilder;
16
use eZ\Publish\API\Repository\Tests\SetupFactory;
17
use eZ\Publish\API\Repository\Tests\IdManager;
18
use eZ\Publish\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler as CachingContentTypeHandler;
19
use eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler as CachingLanguageHandler;
20
use Exception;
21
use eZ\Publish\Core\Repository\Values\User\UserReference;
22
use Symfony\Component\Filesystem\Filesystem;
23
use eZ\Publish\Core\Base\Container\Compiler;
24
25
/**
26
 * A Test Factory is used to setup the infrastructure for a tests, based on a
27
 * specific repository implementation to test.
28
 */
29
class Legacy extends SetupFactory
30
{
31
    /**
32
     * Data source name.
33
     *
34
     * @var string
35
     */
36
    protected static $dsn;
37
38
    /**
39
     * Root dir for IO operations.
40
     *
41
     * @var string
42
     */
43
    protected static $ioRootDir;
44
45
    /**
46
     * Database type (sqlite, mysql, ...).
47
     *
48
     * @var string
49
     */
50
    protected static $db;
51
52
    /**
53
     * Service container.
54
     *
55
     * @var \eZ\Publish\Core\Base\ServiceContainer
56
     */
57
    protected static $serviceContainer;
58
59
    /**
60
     * If the DB schema has already been initialized.
61
     *
62
     * @var bool
63
     */
64
    protected static $schemaInitialized = false;
65
66
    /**
67
     * Initial database data.
68
     *
69
     * @var array
70
     */
71
    protected static $initialData;
72
73
    protected $repositoryReference = 'ezpublish.api.repository';
74
75
    /**
76
     * Creates a new setup factory.
77
     */
78
    public function __construct()
79
    {
80
        self::$dsn = getenv('DATABASE');
81
        if (!self::$dsn) {
82
            // use sqlite in-memory by default (does not need special handling for paratest as it's per process)
83
            self::$dsn = 'sqlite://:memory:';
84
        } elseif (getenv('TEST_TOKEN') !== false) {
85
            // Using paratest, assuming dsn ends with db name here...
86
            self::$dsn .= '_' . getenv('TEST_TOKEN');
87
        }
88
89
        if ($repositoryReference = getenv('REPOSITORY_SERVICE_ID')) {
90
            $this->repositoryReference = $repositoryReference;
91
        }
92
93
        self::$db = preg_replace('(^([a-z]+).*)', '\\1', self::$dsn);
94
95
        if (!isset(self::$ioRootDir)) {
96
            self::$ioRootDir = $this->createTemporaryDirectory();
97
        }
98
    }
99
100
    /**
101
     * Creates a temporary directory and returns it.
102
     *
103
     * @return string
104
     * @throw \RuntimeException If the root directory can't be created
105
     */
106
    private function createTemporaryDirectory()
107
    {
108
        $tmpFile = tempnam(
109
            sys_get_temp_dir(),
110
            'ez_legacy_tests_' . time()
111
        );
112
        unlink($tmpFile);
113
114
        $fs = new Filesystem();
115
        $fs->mkdir($tmpFile);
116
117
        $varDir = $tmpFile . '/var';
118
        if ($fs->exists($varDir)) {
119
            $fs->remove($varDir);
120
        }
121
        $fs->mkdir($varDir);
122
123
        return $tmpFile;
124
    }
125
126
    /**
127
     * Returns a configured repository for testing.
128
     *
129
     * @param bool $initializeFromScratch if the back end should be initialized
130
     *                                    from scratch or re-used
131
     *
132
     * @return \eZ\Publish\API\Repository\Repository
133
     */
134
    public function getRepository($initializeFromScratch = true)
135
    {
136
        if ($initializeFromScratch || !self::$schemaInitialized) {
137
            $this->initializeSchema();
138
            $this->insertData();
139
        }
140
141
        $this->clearInternalCaches();
142
        /** @var \eZ\Publish\API\Repository\Repository $repository */
143
        $repository = $this->getServiceContainer()->get($this->repositoryReference);
144
145
        // Set admin user as current user by default
146
        $repository->getPermissionResolver()->setCurrentUserReference(
147
            new UserReference(14)
148
        );
149
150
        return $repository;
151
    }
152
153
    /**
154
     * Returns a config value for $configKey.
155
     *
156
     * @param string $configKey
157
     *
158
     * @throws Exception if $configKey could not be found.
159
     *
160
     * @return mixed
161
     */
162
    public function getConfigValue($configKey)
163
    {
164
        return $this->getServiceContainer()->getParameter($configKey);
165
    }
166
167
    /**
168
     * Returns a repository specific ID manager.
169
     *
170
     * @return \eZ\Publish\API\Repository\Tests\IdManager
171
     */
172
    public function getIdManager()
173
    {
174
        return new IdManager\Php();
175
    }
176
177
    /**
178
     * Insert the database data.
179
     */
180
    public function insertData()
181
    {
182
        $data = $this->getInitialData();
183
        $handler = $this->getDatabaseHandler();
184
        $connection = $handler->getConnection();
185
        $dbPlatform = $connection->getDatabasePlatform();
186
        $this->cleanupVarDir($this->getInitialVarDir());
187
188
        foreach (array_reverse(array_keys($data)) as $table) {
189
            try {
190
                // Cleanup before inserting (using TRUNCATE for speed, however not possible to rollback)
191
                $connection->executeUpdate($dbPlatform->getTruncateTableSql($handler->quoteIdentifier($table)));
192
            } catch (DBALException | PDOException $e) {
193
                // Fallback to DELETE if TRUNCATE failed (because of FKs for instance)
194
                $connection->createQueryBuilder()->delete($table)->execute();
195
            }
196
        }
197
198
        foreach ($data as $table => $rows) {
199
            // Check that at least one row exists
200
            if (!isset($rows[0])) {
201
                continue;
202
            }
203
204
            $q = $handler->createInsertQuery();
205
            $q->insertInto($handler->quoteIdentifier($table));
206
207
            // Contains the bound parameters
208
            $values = [];
209
210
            // Binding the parameters
211
            foreach ($rows[0] as $col => $val) {
212
                $q->set(
213
                    $handler->quoteIdentifier($col),
214
                    $q->bindParam($values[$col])
215
                );
216
            }
217
218
            $stmt = $q->prepare();
219
220
            foreach ($rows as $row) {
221
                try {
222
                    // This CANNOT be replaced by:
223
                    // $values = $row
224
                    // each $values[$col] is a PHP reference which should be
225
                    // kept for parameters binding to work
226
                    foreach ($row as $col => $val) {
227
                        $values[$col] = $val;
228
                    }
229
230
                    $stmt->execute();
231
                } catch (Exception $e) {
232
                    echo "$table ( ", implode(', ', $row), " )\n";
233
                    throw $e;
234
                }
235
            }
236
        }
237
238
        $this->applyStatements($this->getPostInsertStatements());
239
    }
240
241
    protected function getInitialVarDir()
242
    {
243
        return __DIR__ . '/../../../../../../var';
244
    }
245
246
    protected function cleanupVarDir($sourceDir)
247
    {
248
        $fs = new Filesystem();
249
        $varDir = self::$ioRootDir . '/var';
250
        if ($fs->exists($varDir)) {
251
            $fs->remove($varDir);
252
        }
253
        $fs->mkdir($varDir);
254
        $fs->mirror($sourceDir, $varDir);
255
    }
256
257
    /**
258
     * CLears internal in memory caches after inserting data circumventing the
259
     * API.
260
     */
261
    protected function clearInternalCaches()
262
    {
263
        /** @var $handler \eZ\Publish\Core\Persistence\Legacy\Handler */
264
        $handler = $this->getServiceContainer()->get('ezpublish.spi.persistence.legacy');
265
266
        $contentLanguageHandler = $handler->contentLanguageHandler();
267
        if ($contentLanguageHandler instanceof CachingLanguageHandler) {
268
            $contentLanguageHandler->clearCache();
269
        }
270
271
        $contentTypeHandler = $handler->contentTypeHandler();
272
        if ($contentTypeHandler instanceof CachingContentTypeHandler) {
273
            $contentTypeHandler->clearCache();
274
        }
275
276
        /** @var $cachePool \Psr\Cache\CacheItemPoolInterface */
277
        $cachePool = $this->getServiceContainer()->get('ezpublish.cache_pool');
278
279
        $cachePool->clear();
280
    }
281
282
    /**
283
     * Returns statements to be executed after data insert.
284
     *
285
     * @return string[]
286
     */
287
    protected function getPostInsertStatements()
288
    {
289
        if (self::$db === 'pgsql') {
290
            $setvalPath = __DIR__ . '/../../../../Core/Persistence/Legacy/Tests/_fixtures/setval.pgsql.sql';
291
292
            return array_filter(preg_split('(;\\s*$)m', file_get_contents($setvalPath)));
293
        }
294
295
        return [];
296
    }
297
298
    /**
299
     * Returns the initial database data.
300
     *
301
     * @return array
302
     */
303
    protected function getInitialData()
304
    {
305
        if (!isset(self::$initialData)) {
306
            self::$initialData = include __DIR__ . '/../../../../Core/Repository/Tests/Service/Integration/Legacy/_fixtures/test_data.php';
307
        }
308
309
        return self::$initialData;
310
    }
311
312
    /**
313
     * Initializes the database schema.
314
     *
315
     * @throws \Doctrine\DBAL\ConnectionException
316
     */
317
    protected function initializeSchema(): void
318
    {
319
        if (!self::$schemaInitialized) {
320
            $schemaImporter = new LegacySchemaImporter($this->getDatabaseConnection());
321
            $schemaImporter->importSchema(
322
                dirname(__DIR__, 5) .
323
                '/Bundle/EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml'
324
            );
325
326
            self::$schemaInitialized = true;
327
        }
328
    }
329
330
    /**
331
     * Applies the given SQL $statements to the database in use.
332
     *
333
     * @param array $statements
334
     */
335
    protected function applyStatements(array $statements)
336
    {
337
        foreach ($statements as $statement) {
338
            $this->getDatabaseHandler()->exec($statement);
339
        }
340
    }
341
342
    // ************* Setup copied and refactored from common.php ************
343
344
    /**
345
     * Returns the database handler from the service container.
346
     *
347
     * @return \eZ\Publish\Core\Persistence\Doctrine\ConnectionHandler
348
     */
349
    protected function getDatabaseHandler()
350
    {
351
        return $this->getServiceContainer()->get('ezpublish.api.storage_engine.legacy.dbhandler');
352
    }
353
354
    /**
355
     * Returns the raw database connection from the service container.
356
     *
357
     * @return \Doctrine\DBAL\Connection
358
     */
359
    private function getDatabaseConnection(): Connection
360
    {
361
        return $this->getServiceContainer()->get('ezpublish.persistence.connection');
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\FieldRegistryPass());
395
396
            // load overrides just before creating test Container
397
            $loader->load('tests/override.yml');
398
399
            self::$serviceContainer = new ServiceContainer(
400
                $containerBuilder,
401
                $installDir,
402
                $config['cache_dir'],
403
                true,
404
                true
405
            );
406
        }
407
408
        return self::$serviceContainer;
409
    }
410
411
    /**
412
     * This is intended to be used from external repository in order to
413
     * enable container customization.
414
     *
415
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $containerBuilder
416
     */
417
    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...
418
    {
419
        // Does nothing by default
420
    }
421
422
    /**
423
     * Get the Database name.
424
     *
425
     * @return string
426
     */
427
    public function getDB()
428
    {
429
        return self::$db;
430
    }
431
}
432