GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

AbstractTest   B
last analyzed

Complexity

Total Complexity 50

Size/Duplication

Total Lines 623
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 239
dl 0
loc 623
rs 8.4
c 1
b 0
f 0
wmc 50

78 Methods

Rating   Name   Duplication   Size   Complexity  
A hp$0 ➔ isTravis() 0 3 1
getCopiedFqn() 0 17 ?
A clearCache() 0 7 2
A tearDownAfterClass() 0 11 2
A hp$0 ➔ getFieldGenerator() 0 10 1
A getRealPath() 0 8 2
getEntityFactory() 0 6 ?
dump() 0 3 ?
assertFileContains() 0 9 ?
getNamespaceHelper() 0 3 ?
A hp$0 ➔ getUuidFactory() 0 3 1
getDataFillerFactory() 0 11 ?
A hp$0 ➔ getArchetypeEmbeddableGenerator() 0 10 1
isQuickTests() 0 10 ?
getTestCodeGenerator() 0 3 ?
A clearWorkDir() 0 14 3
A setUp() 0 33 5
isTravis() 0 3 ?
getCopiedNamespaceRoot() 0 6 ?
generateTestCode() 0 6 ?
A setUpBeforeClass() 0 4 1
A hp$0 ➔ loadClass() 0 12 4
A hp$0 ➔ isQuickTests() 0 10 3
A hp$0 ➔ __construct() 0 3 1
A hp$0 ➔ tearDown() 0 7 1
getCodeHelper() 0 3 ?
setupCopiedWorkDir() 0 38 ?
A getFileSystem() 0 7 2
getSchema() 0 3 ?
A setupContainer() 0 11 1
A hp$0 ➔ getFieldSetter() 0 7 1
extendAutoloader() 0 43 ?
A hp$0 ➔ getCodeHelper() 0 3 1
A hp$0 ➔ getEntityDtoFactory() 0 3 1
A hp$0 ➔ generateTestCode() 0 6 2
getUuidFactory() 0 3 ?
A hp$0 ➔ getSchema() 0 3 1
A hp$0 ➔ getNamespaceHelper() 0 3 1
A hp$0 ➔ getRelationsGenerator() 0 10 1
assertNoMissedReplacements() 0 11 ?
getEntityGenerator() 0 10 ?
A hp$0 ➔ assertNoMissedReplacements() 0 11 2
getPathHelper() 0 3 ?
getArchetypeEmbeddableGenerator() 0 10 ?
A hp$0 ➔ dump() 0 3 1
A hp$0 ➔ getEntityFactory() 0 6 1
getUnusedRelationsRemover() 0 3 ?
A hp$0 ➔ getTestCodeGenerator() 0 3 1
A hp$0 ➔ createEntity() 0 3 1
getFieldSetter() 0 7 ?
A hp$0 ➔ extendAutoloader() 0 43 4
A hp$0 ➔ setupCopiedWorkDir() 0 38 3
A hp$0 ➔ recreateDtos() 0 9 1
getEntityDtoFactory() 0 3 ?
getEntityEmbeddableSetter() 0 7 ?
A hp$0 ➔ getTestEntityGeneratorFactory() 0 3 1
getRelationsGenerator() 0 10 ?
A hp$0 ➔ getEntityGenerator() 0 10 1
A hp$0 ➔ qaGeneratedCode() 0 21 4
A emptyDirectory() 0 5 1
A hp$0 ➔ assertFileContains() 0 9 1
A hp$0 ➔ getRepositoryFactory() 0 3 1
A hp$0 ➔ getEntityEmbeddableSetter() 0 7 1
createEntity() 0 3 ?
getFieldGenerator() 0 10 ?
A hp$0 ➔ getUnusedRelationsRemover() 0 3 1
A hp$0 ➔ getPathHelper() 0 3 1
getRepositoryFactory() 0 3 ?
recreateDtos() 0 9 ?
A hp$0 ➔ getCopiedFqn() 0 17 1
A hp$0 ➔ getCopiedNamespaceRoot() 0 6 1
A getEntityManager() 0 3 1
getTestEntityGeneratorFactory() 0 3 ?
A hp$0 ➔ getDataFillerFactory() 0 11 1
loadAllEntityMetaData() 0 3 ?
tearDown() 0 7 ?
A hp$0 ➔ loadAllEntityMetaData() 0 3 1
qaGeneratedCode() 0 21 ?

How to fix   Complexity   

Complex Class

Complex classes like AbstractTest 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.

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 AbstractTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace EdmondsCommerce\DoctrineStaticMeta\Tests\Assets;
6
7
use Closure;
8
use Composer\Autoload\ClassLoader;
9
use Doctrine\Common\Cache\CacheProvider;
10
use Doctrine\ORM\EntityManagerInterface;
11
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Action\CreateDtosForAllEntitiesAction;
12
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\CodeHelper;
13
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Command\AbstractCommand;
14
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\AbstractGenerator;
15
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Embeddable\ArchetypeEmbeddableGenerator;
16
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Embeddable\EntityEmbeddableSetter;
17
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\EntityGenerator;
18
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field\EntityFieldSetter;
19
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field\FieldGenerator;
20
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\FindAndReplaceHelper;
21
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\RelationsGenerator;
22
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
23
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\PathHelper;
24
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\UnusedRelationsRemover;
25
use EdmondsCommerce\DoctrineStaticMeta\Config;
26
use EdmondsCommerce\DoctrineStaticMeta\ConfigInterface;
27
use EdmondsCommerce\DoctrineStaticMeta\Container;
28
use EdmondsCommerce\DoctrineStaticMeta\Entity\DataTransferObjects\DtoFactory;
29
use EdmondsCommerce\DoctrineStaticMeta\Entity\Factory\EntityFactory;
30
use EdmondsCommerce\DoctrineStaticMeta\Entity\Factory\EntityFactoryInterface;
31
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Factories\UuidFactory;
32
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\DataTransferObjectInterface;
33
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface;
34
use EdmondsCommerce\DoctrineStaticMeta\Entity\Repositories\RepositoryFactory;
35
use EdmondsCommerce\DoctrineStaticMeta\Entity\Testing\EntityDebugDumper;
36
use EdmondsCommerce\DoctrineStaticMeta\Entity\Testing\EntityGenerator\FakerDataFillerFactory;
37
use EdmondsCommerce\DoctrineStaticMeta\Entity\Testing\EntityGenerator\TestEntityGeneratorFactory;
38
use EdmondsCommerce\DoctrineStaticMeta\Exception\ConfigException;
39
use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException;
40
use EdmondsCommerce\DoctrineStaticMeta\Schema\Schema;
41
use EdmondsCommerce\DoctrineStaticMeta\SimpleEnv;
42
use EdmondsCommerce\PHPQA\Constants;
43
use PHPUnit\Framework\TestCase;
44
use ReflectionException;
45
use RuntimeException;
46
use Symfony\Component\Filesystem\Filesystem;
47
use ts\Reflection\ReflectionClass;
48
49
use function get_class;
50
use function spl_autoload_functions;
51
use function spl_autoload_unregister;
52
use function str_replace;
53
54
/**
55
 * Class AbstractTest
56
 *
57
 * @package EdmondsCommerce\DoctrineStaticMeta
58
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
59
 * @SuppressWarnings(PHPMD.NumberOfChildren)
60
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
61
 */
62
abstract class AbstractTest extends TestCase
63
{
64
    public const TEST_TYPE_SMALL              = 'Small';
65
    public const TEST_TYPE_MEDIUM             = 'Medium';
66
    public const TEST_TYPE_LARGE              = 'Large';
67
    public const VAR_PATH                     = __DIR__ . '/../../var/testOutput/';
68
    public const WORK_DIR                     = 'override me';
69
    public const TEST_PROJECT_ROOT_NAMESPACE  = 'My\\Test\\Project';
70
    public const TEST_ENTITIES_ROOT_NAMESPACE = self::TEST_PROJECT_ROOT_NAMESPACE . '\\' .
71
                                                AbstractGenerator::ENTITIES_FOLDER_NAME;
72
    protected static $buildOnce = false;
73
    protected static $built     = false;
74
    /**
75
     * @var Container
76
     */
77
    protected static $containerStaticRef;
78
    /**
79
     * The absolute path to the Entities folder, eg:
80
     * /var/www/vhosts/doctrine-static-meta/var/{testWorkDir}/Entities
81
     *
82
     * @var string
83
     */
84
    protected $entitiesPath = '';
85
    /**
86
     * The absolute path to the EntityRelations folder, eg:
87
     * /var/www/vhosts/doctrine-static-meta/var/{testWorkDir}/Entity/Relations
88
     *
89
     * @var string
90
     */
91
    protected $entityRelationsPath = '';
92
    /**
93
     * @var Container
94
     */
95
    protected $container;
96
    /**
97
     * @var Filesystem
98
     */
99
    protected $filesystem;
100
    /**
101
     * @var string|null
102
     */
103
    protected $copiedWorkDir;
104
    /**
105
     * @var string|null
106
     */
107
    protected $copiedRootNamespace;
108
109
    /**
110
     * Ensure built steps are set to false when test class is instantiated
111
     */
112
    public static function setUpBeforeClass()
113
    {
114
        self::$built   = false;
115
        static::$built = false;
116
    }
117
118
    public static function tearDownAfterClass()
119
    {
120
        if (!(self::$containerStaticRef instanceof Container)) {
0 ignored issues
show
introduced by
self::containerStaticRef is always a sub-type of EdmondsCommerce\DoctrineStaticMeta\Container.
Loading history...
121
            return;
122
        }
123
        $entityManager = static::$containerStaticRef->get(EntityManagerInterface::class);
124
        $connection    = $entityManager->getConnection();
125
126
        $entityManager->close();
127
        $connection->close();
128
        static::$containerStaticRef = null;
129
    }
130
131
132
    /**
133
     * Prepare working directory, ensure its empty, create entities folder and set up env variables
134
     *
135
     * The order of these actions is critical
136
     */
137
    public function setUp()
138
    {
139
        if (false !== stripos(static::WORK_DIR, self::WORK_DIR)) {
140
            throw new RuntimeException(
141
                "You must set a `public const WORK_DIR=AbstractTest::VAR_PATH.'/'"
142
                . ".self::TEST_TYPE.'/folderName/';` in your test class"
143
            );
144
        }
145
        if (
146
            false === strpos(static::WORK_DIR, '/' . static::TEST_TYPE_SMALL)
147
            && false === strpos(static::WORK_DIR, '/' . static::TEST_TYPE_MEDIUM)
148
            && false === strpos(static::WORK_DIR, '/' . static::TEST_TYPE_LARGE)
149
        ) {
150
            throw new RuntimeException(
151
                'Your WORK_DIR is missing the test type, should look like: '
152
                . "`public const WORK_DIR=AbstractTest::VAR_PATH.'/'"
153
                . ".self::TEST_TYPE_(SMALL|MEDIUM|LARGE).'/folderName/';` in your test class"
154
            );
155
        }
156
        $this->copiedWorkDir       = null;
157
        $this->copiedRootNamespace = null;
158
        $this->entitiesPath        = static::WORK_DIR
159
                                     . '/' . AbstractCommand::DEFAULT_SRC_SUBFOLDER
160
                                     . '/' . AbstractGenerator::ENTITIES_FOLDER_NAME;
161
        $this->entityRelationsPath = static::WORK_DIR
162
                                     . '/' . AbstractCommand::DEFAULT_SRC_SUBFOLDER
163
                                     . '/' . AbstractGenerator::ENTITY_RELATIONS_FOLDER_NAME;
164
        $this->clearWorkDir();
165
        $this->setupContainer($this->entitiesPath);
166
        $this->clearCache();
167
        $this->extendAutoloader(
168
            static::TEST_PROJECT_ROOT_NAMESPACE . '\\',
169
            static::WORK_DIR
170
        );
171
    }
172
173
    protected function clearWorkDir(): void
174
    {
175
        if (true === static::$buildOnce && true === static::$built) {
176
            $this->entitiesPath = $this->getRealPath($this->entitiesPath);
177
178
            return;
179
        }
180
        $this->getFileSystem()->mkdir(static::WORK_DIR);
181
        $this->emptyDirectory(static::WORK_DIR);
182
        $this->getFileSystem()->mkdir($this->entitiesPath);
183
        $this->entitiesPath = $this->getRealPath($this->entitiesPath);
184
185
        $this->getFileSystem()->mkdir($this->entityRelationsPath);
186
        $this->entityRelationsPath = realpath($this->entityRelationsPath);
187
    }
188
189
    protected function getRealPath(string $path)
190
    {
191
        $realpath = realpath($path);
192
        if (false === $realpath) {
193
            throw new RuntimeException('Failed getting realpath for path: ' . $path);
194
        }
195
196
        return $realpath;
197
    }
198
199
    protected function getFileSystem(): Filesystem
200
    {
201
        if (null === $this->filesystem) {
202
            $this->filesystem = new Filesystem();
203
        }
204
205
        return $this->filesystem;
206
    }
207
208
    protected function emptyDirectory(string $path): void
209
    {
210
        $fileSystem = $this->getFileSystem();
211
        $fileSystem->remove($path);
212
        $fileSystem->mkdir($path);
213
    }
214
215
    /**
216
     * @param string $entitiesPath
217
     *
218
     * @throws ConfigException
219
     * @throws DoctrineStaticMetaException
220
     * @SuppressWarnings(PHPMD.Superglobals)
221
     * @SuppressWarnings(PHPMD.StaticAccess)
222
     */
223
    protected function setupContainer(string $entitiesPath): void
224
    {
225
        SimpleEnv::setEnv(Config::getProjectRootDirectory() . '/.env');
226
        $testConfig                                               = $_SERVER;
227
        $testConfig[ConfigInterface::PARAM_ENTITIES_PATH]         = $entitiesPath;
228
        $testConfig[ConfigInterface::PARAM_DB_NAME]               .= '_test';
229
        $testConfig[ConfigInterface::PARAM_DEVMODE]               = true;
230
        $testConfig[ConfigInterface::PARAM_FILESYSTEM_CACHE_PATH] = static::WORK_DIR . '/cache/dsm';
231
        $this->container                                          = new Container();
232
        $this->container->buildSymfonyContainer($testConfig);
233
        static::$containerStaticRef = $this->container;
234
    }
235
236
    /**
237
     * Clear the Doctrine Cache
238
     *
239
     * @throws DoctrineStaticMetaException
240
     */
241
    protected function clearCache(): void
242
    {
243
        $cache = $this->getEntityManager()
244
                      ->getConfiguration()
245
                      ->getMetadataCacheImpl();
246
        if ($cache instanceof CacheProvider) {
247
            $cache->deleteAll();
248
        }
249
    }
250
251
    /**
252
     * @return EntityManagerInterface
253
     * @throws DoctrineStaticMetaException
254
     */
255
    protected function getEntityManager(): EntityManagerInterface
256
    {
257
        return $this->container->get(EntityManagerInterface::class);
258
    }
259
260
    /**
261
     * Accesses the standard Composer autoloader
262
     *
263
     * Extends the class and also providing an entry point for xdebugging
264
     *
265
     * Extends this with a PSR4 root for our WORK_DIR
266
     *
267
     * @param string $namespace
268
     * @param string $path
269
     *
270
     * @throws ReflectionException
271
     */
272
    protected function extendAutoloader(string $namespace, string $path): void
273
    {
274
        //Unregister any previously set extension first
275
        $registered = spl_autoload_functions();
276
        foreach ($registered as $loader) {
277
            if ($loader instanceof Closure) {
278
                continue;
279
            }
280
            if ((new  ReflectionClass(get_class($loader[0])))->isAnonymous()) {
281
                spl_autoload_unregister($loader);
282
            }
283
        }
284
        //Then build a new extension and register it
285
        $namespace  = rtrim($namespace, '\\') . '\\';
286
        $testLoader = new class ($namespace) extends ClassLoader
287
        {
288
            /**
289
             * @var string
290
             */
291
            protected $namespace;
292
293
            public function __construct(string $namespace)
294
            {
295
                $this->namespace = $namespace;
296
            }
297
298
            public function loadClass($class)
299
            {
300
                if (false === strpos($class, $this->namespace)) {
301
                    return false;
302
                }
303
                $found = parent::loadClass($class);
304
                if (false === $found || null === $found) {
0 ignored issues
show
introduced by
The condition null === $found is always false.
Loading history...
305
                    //good point to set a breakpoint
306
                    return $found;
307
                }
308
309
                return $found;
310
            }
311
        };
312
        $testLoader->addPsr4($namespace, $path . '/src', true);
313
        $testLoader->addPsr4($namespace, $path . '/tests', true);
314
        $testLoader->register();
315
    }
316
317
    /**
318
     * Run QA tools against the generated code
319
     *
320
     * Can specify a custom namespace root if required
321
     *
322
     * Will run:
323
     *
324
     * - PHP linting
325
     * - PHPStan
326
     *
327
     * @SuppressWarnings(PHPMD.Superglobals)
328
     * @param null|string $namespaceRoot
329
     *
330
     * @return bool
331
     */
332
    public function qaGeneratedCode(?string $namespaceRoot = null): bool
333
    {
334
        if ($this->isQuickTests()) {
335
            self::assertTrue(true);
336
337
            return true;
338
        }
339
        $workDir       = static::WORK_DIR;
340
        $namespaceRoot = trim($namespaceRoot ?? static::TEST_PROJECT_ROOT_NAMESPACE, '\\');
341
        if (null !== $this->copiedRootNamespace) {
342
            $workDir       = $this->copiedWorkDir;
343
            $namespaceRoot = trim($this->copiedRootNamespace, '\\');
344
        }
345
        static $codeValidator;
346
        if (null === $codeValidator) {
347
            $codeValidator = new CodeValidator();
348
        }
349
        $errors = $codeValidator($workDir, $namespaceRoot);
350
        self::assertNull($errors);
351
352
        return true;
353
    }
354
355
    /**
356
     * @return bool
357
     * @SuppressWarnings(PHPMD.Superglobals)
358
     */
359
    protected function isQuickTests(): bool
360
    {
361
        if (
362
            isset($_SERVER[Constants::QA_QUICK_TESTS_KEY])
363
            && (int)$_SERVER[Constants::QA_QUICK_TESTS_KEY] === Constants::QA_QUICK_TESTS_ENABLED
364
        ) {
365
            return true;
366
        }
367
368
        return false;
369
    }
370
371
    public function getDataFillerFactory(): FakerDataFillerFactory
372
    {
373
        /**
374
         * @var FakerDataFillerFactory $factory
375
         */
376
        $factory         = $this->container->get(FakerDataFillerFactory::class);
377
        $abstractTestFqn =
378
            ($this->copiedRootNamespace ?? self::TEST_PROJECT_ROOT_NAMESPACE) . '\\Entities\\AbstractEntityTest';
379
        $factory->setFakerDataProviders($abstractTestFqn::FAKER_DATA_PROVIDERS);
380
381
        return $factory;
382
    }
383
384
    protected function getUuidFactory(): UuidFactory
385
    {
386
        return $this->container->get(UuidFactory::class);
387
    }
388
389
    protected function generateTestCode(): void
390
    {
391
        if (false === static::$built) {
392
            $this->getTestCodeGenerator()
393
                 ->copyTo(static::WORK_DIR);
394
            static::$built = true;
395
        }
396
    }
397
398
    protected function getTestCodeGenerator(): TestCodeGenerator
399
    {
400
        return $this->container->get(TestCodeGenerator::class);
401
    }
402
403
    protected function recreateDtos(): void
404
    {
405
        /**
406
         * @var CreateDtosForAllEntitiesAction $dtoAction
407
         */
408
        $dtoAction = $this->container->get(CreateDtosForAllEntitiesAction::class);
409
        $dtoAction->setProjectRootNamespace($this->copiedRootNamespace)
0 ignored issues
show
Bug introduced by
It seems like $this->copiedRootNamespace can also be of type null; however, parameter $projectRootNamespace of EdmondsCommerce\Doctrine...tProjectRootNamespace() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

409
        $dtoAction->setProjectRootNamespace(/** @scrutinizer ignore-type */ $this->copiedRootNamespace)
Loading history...
410
                  ->setProjectRootDirectory($this->copiedWorkDir)
0 ignored issues
show
Bug introduced by
It seems like $this->copiedWorkDir can also be of type null; however, parameter $projectRootDirectory of EdmondsCommerce\Doctrine...tProjectRootDirectory() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

410
                  ->setProjectRootDirectory(/** @scrutinizer ignore-type */ $this->copiedWorkDir)
Loading history...
411
                  ->run();
412
    }
413
414
    protected function tearDown()
415
    {
416
        $entityManager = $this->getEntityManager();
417
        $connection    = $entityManager->getConnection();
418
419
        $entityManager->close();
420
        $connection->close();
421
    }
422
423
    protected function getRepositoryFactory(): RepositoryFactory
424
    {
425
        return $this->container->get(RepositoryFactory::class);
426
    }
427
428
    protected function getNamespaceHelper(): NamespaceHelper
429
    {
430
        return $this->container->get(NamespaceHelper::class);
431
    }
432
433
    protected function dump(EntityInterface $entity): string
434
    {
435
        return (new EntityDebugDumper())->dump($entity, $this->getEntityManager());
436
    }
437
438
    /**
439
     * If PHP loads any files whilst generating, then subsequent changes to those files will not have any effect
440
     *
441
     * To resolve this, we need to clone the copied code into a new namespace before running it
442
     *
443
     * We only allow copying to a new work dir once per test run, different extras must be used
444
     *
445
     * @return string $copiedWorkDir
446
     * @throws DoctrineStaticMetaException
447
     * @throws ReflectionException
448
     */
449
    protected function setupCopiedWorkDir(): string
450
    {
451
        $copiedNamespaceRoot       = $this->getCopiedNamespaceRoot();
452
        $this->copiedWorkDir       = rtrim(static::WORK_DIR, '/') . 'Copies/' . $copiedNamespaceRoot . '/';
453
        $this->entitiesPath        = $this->copiedWorkDir . '/src/Entities/';
454
        $this->copiedRootNamespace = $copiedNamespaceRoot;
455
        if (is_dir($this->copiedWorkDir)) {
456
            throw new RuntimeException(
457
                'The Copied WorkDir ' . $this->copiedWorkDir . ' Already Exists'
458
            );
459
        }
460
        if (is_dir($this->copiedWorkDir)) {
461
            $this->getFileSystem()->remove($this->copiedWorkDir);
462
        }
463
        $codeCopier = new CodeCopier(
464
            $this->getFileSystem(),
465
            $this->container->get(FindAndReplaceHelper::class)
466
        );
467
        $codeCopier->copy(
468
            static::WORK_DIR,
469
            $this->copiedWorkDir,
470
            self::TEST_PROJECT_ROOT_NAMESPACE,
471
            $copiedNamespaceRoot
472
        );
473
        $this->extendAutoloader(
474
            $this->copiedRootNamespace . '\\',
475
            $this->copiedWorkDir
476
        );
477
        $this->clearCache();
478
        $this->setupContainer(
479
            $this->copiedWorkDir
480
            . '/' . AbstractCommand::DEFAULT_SRC_SUBFOLDER
481
            . '/' . AbstractGenerator::ENTITIES_FOLDER_NAME
482
        );
483
484
        $this->loadAllEntityMetaData();
485
486
        return $this->copiedWorkDir;
487
    }
488
489
    /**
490
     * Get the namespace root to use in a copied work dir
491
     *
492
     * @return string
493
     * @throws ReflectionException
494
     */
495
    protected function getCopiedNamespaceRoot(): string
496
    {
497
        $name          = ucwords($this->getName());
498
        $namespaceName = preg_replace('%[^a-z0-9]+%i', '_', $name);
499
500
        return (new  ReflectionClass(static::class))->getShortName() . '_' . $namespaceName . '_';
501
    }
502
503
    private function loadAllEntityMetaData(): void
504
    {
505
        $this->getEntityManager()->getMetadataFactory()->getAllMetadata();
506
    }
507
508
    /**
509
     * When working with a copied work dir, use this function to translate the FQN of any Entities etc
510
     *
511
     * This will replace both the raw TestCodeGenerator root namespace and the test level root namespace
512
     *
513
     * @param string $fqn
514
     *
515
     * @return string
516
     * @throws DoctrineStaticMetaException
517
     * @throws ReflectionException
518
     */
519
    protected function getCopiedFqn(string $fqn): string
520
    {
521
        $copiedNamespaceRoot = $this->getCopiedNamespaceRoot();
522
523
        $currentRootRemoved = str_replace(
524
            static::TEST_PROJECT_ROOT_NAMESPACE,
525
            '',
526
            $fqn
527
        );
528
        $currentRootRemoved = ltrim(
529
            $currentRootRemoved,
530
            '\\'
531
        );
532
533
        return $this->container
534
            ->get(NamespaceHelper::class)
535
            ->tidy('\\' . $copiedNamespaceRoot . '\\' . $currentRootRemoved);
536
    }
537
538
    /**
539
     * @return bool
540
     * @SuppressWarnings(PHPMD.Superglobals)
541
     */
542
    protected function isTravis(): bool
543
    {
544
        return isset($_SERVER['TRAVIS']);
545
    }
546
547
    protected function getEntityEmbeddableSetter(): EntityEmbeddableSetter
548
    {
549
        $setter = $this->container->get(EntityEmbeddableSetter::class);
550
        $setter->setPathToProjectRoot($this->copiedWorkDir ?? static::WORK_DIR);
551
        $setter->setProjectRootNamespace($this->copiedRootNamespace ?? self::TEST_PROJECT_ROOT_NAMESPACE);
552
553
        return $setter;
554
    }
555
556
    /**
557
     * @return ArchetypeEmbeddableGenerator
558
     * @throws DoctrineStaticMetaException
559
     */
560
    protected function getArchetypeEmbeddableGenerator(): ArchetypeEmbeddableGenerator
561
    {
562
        /**
563
         * @var ArchetypeEmbeddableGenerator $generator
564
         */
565
        $generator = $this->container->get(ArchetypeEmbeddableGenerator::class);
566
        $generator->setProjectRootNamespace(static::TEST_PROJECT_ROOT_NAMESPACE)
567
                  ->setPathToProjectRoot(static::WORK_DIR);
568
569
        return $generator;
570
    }
571
572
    protected function assertNoMissedReplacements(string $createdFile, array $checkFor = []): void
573
    {
574
        $createdFile = $this->getPathHelper()->resolvePath($createdFile);
575
        self::assertFileExists($createdFile);
576
        $contents   = file_get_contents($createdFile);
577
        $checkFor[] = 'template';
578
        foreach ($checkFor as $check) {
579
            self::assertNotRegExp(
580
                '%[^a-z]' . $check . '[^a-z]%i',
581
                $contents,
582
                'Found the word "' . $check . '" (case insensitive) in the created file ' . $createdFile
583
            );
584
        }
585
    }
586
587
    protected function getPathHelper(): PathHelper
588
    {
589
        return $this->container->get(PathHelper::class);
590
    }
591
592
    protected function getTestEntityGeneratorFactory(): TestEntityGeneratorFactory
593
    {
594
        return $this->container->get(TestEntityGeneratorFactory::class);
595
    }
596
597
    protected function assertFileContains(string $createdFile, string $needle): void
598
    {
599
        $createdFile = $this->getPathHelper()->resolvePath($createdFile);
600
        self::assertFileExists($createdFile);
601
        $contents = file_get_contents($createdFile);
602
        self::assertContains(
603
            $needle,
604
            $contents,
605
            "Missing '$needle' in file '$createdFile'"
606
        );
607
    }
608
609
    protected function getEntityGenerator(): EntityGenerator
610
    {
611
        /**
612
         * @var EntityGenerator $entityGenerator
613
         */
614
        $entityGenerator = $this->container->get(EntityGenerator::class);
615
        $entityGenerator->setPathToProjectRoot($this->copiedWorkDir ?? static::WORK_DIR)
616
                        ->setProjectRootNamespace($this->copiedRootNamespace ?? static::TEST_PROJECT_ROOT_NAMESPACE);
617
618
        return $entityGenerator;
619
    }
620
621
    protected function getRelationsGenerator(): RelationsGenerator
622
    {
623
        /**
624
         * @var RelationsGenerator $relationsGenerator
625
         */
626
        $relationsGenerator = $this->container->get(RelationsGenerator::class);
627
        $relationsGenerator->setPathToProjectRoot($this->copiedWorkDir ?? static::WORK_DIR)
628
                           ->setProjectRootNamespace($this->copiedRootNamespace ?? static::TEST_PROJECT_ROOT_NAMESPACE);
629
630
        return $relationsGenerator;
631
    }
632
633
    protected function getFieldGenerator(): FieldGenerator
634
    {
635
        /**
636
         * @var FieldGenerator $fieldGenerator
637
         */
638
        $fieldGenerator = $this->container->get(FieldGenerator::class);
639
        $fieldGenerator->setPathToProjectRoot($this->copiedWorkDir ?? static::WORK_DIR)
640
                       ->setProjectRootNamespace($this->copiedRootNamespace ?? static::TEST_PROJECT_ROOT_NAMESPACE);
641
642
        return $fieldGenerator;
643
    }
644
645
    protected function getFieldSetter(): EntityFieldSetter
646
    {
647
        $fieldSetter = $this->container->get(EntityFieldSetter::class);
648
        $fieldSetter->setPathToProjectRoot($this->copiedWorkDir ?? static::WORK_DIR)
649
                    ->setProjectRootNamespace($this->copiedRootNamespace ?? static::TEST_PROJECT_ROOT_NAMESPACE);
650
651
        return $fieldSetter;
652
    }
653
654
    protected function getSchema(): Schema
655
    {
656
        return $this->container->get(Schema::class);
657
    }
658
659
    protected function getCodeHelper(): CodeHelper
660
    {
661
        return $this->container->get(CodeHelper::class);
662
    }
663
664
    protected function getUnusedRelationsRemover(): UnusedRelationsRemover
665
    {
666
        return $this->container->get(UnusedRelationsRemover::class);
667
    }
668
669
    protected function createEntity(string $entityFqn, DataTransferObjectInterface $dto = null): EntityInterface
670
    {
671
        return $this->getEntityFactory()->create($entityFqn, $dto);
672
    }
673
674
    protected function getEntityFactory(): EntityFactoryInterface
675
    {
676
        $factory = $this->container->get(EntityFactory::class);
677
        $factory->setEntityManager($this->getEntityManager());
678
679
        return $factory;
680
    }
681
682
    protected function getEntityDtoFactory(): DtoFactory
683
    {
684
        return $this->container->get(DtoFactory::class);
685
    }
686
}
687