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.
Completed
Pull Request — master (#126)
by joseph
28:55
created

NamespaceHelper   F

Complexity

Total Complexity 60

Size/Duplication

Total Lines 725
Duplicated Lines 0 %

Test Coverage

Coverage 81.63%

Importance

Changes 0
Metric Value
wmc 60
eloc 228
dl 0
loc 725
ccs 200
cts 245
cp 0.8163
rs 3.6
c 0
b 0
f 0

33 Methods

Rating   Name   Duplication   Size   Complexity  
A cropSuffix() 0 7 2
A swapSuffix() 0 3 1
A getObjectFqn() 0 3 1
A getObjectShortName() 0 3 1
A getClassShortName() 0 5 1
A tidy() 0 13 2
A basename() 0 8 2
A getFakerProviderFqnFromFieldTraitReflection() 0 13 1
A getOwningInterfaceFqn() 0 29 3
A getOwnedHasName() 0 23 2
A getSingularNamespacedName() 0 5 1
A getFactoryFqnFromEntityFqn() 0 8 1
A getHasPluralInterfaceFqnForEntity() 0 6 1
A getEntityFileSubPath() 0 4 1
A getOwningTraitFqn() 0 30 3
A getHasSingularInterfaceFqnForEntity() 0 13 2
A getNamespaceRootToDirectoryFromFqn() 0 11 2
A getEntityNamespaceRootFromEntityReflection() 0 7 1
A getOwningRelationsRootFqn() 0 11 2
B getProjectRootNamespaceFromComposerJson() 0 33 7
A getEntitySubNamespace() 0 11 1
A getNamespacedName() 0 6 1
A getEntitySubPath() 0 10 1
A stripPrefixFromHasType() 0 34 5
A getReciprocatedHasName() 0 14 1
A getEntityInterfaceFromEntityFqn() 0 7 1
B parseFullyQualifiedName() 0 40 7
A getInterfacesNamespaceForEntity() 0 9 1
A getProjectNamespaceRootFromEntityFqn() 0 9 1
A getPluralNamespacedName() 0 5 1
A getFixtureFqnFromEntityFqn() 0 7 1
A getTraitsNamespaceForEntity() 0 9 1
A root() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like NamespaceHelper 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 NamespaceHelper, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\CodeGeneration;
4
5
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Command\AbstractCommand;
6
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\AbstractGenerator;
7
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\RelationsGenerator;
8
use EdmondsCommerce\DoctrineStaticMeta\Config;
9
use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException;
10
use EdmondsCommerce\DoctrineStaticMeta\MappingHelper;
11
12
/**
13
 * Class NamespaceHelper
14
 *
15
 * Pure functions for working with namespaces and to calculate namespaces
16
 *
17
 * @package EdmondsCommerce\DoctrineStaticMeta\CodeGeneration
18
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
19
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
20
 */
21
class NamespaceHelper
22
{
23 1
    public function swapSuffix(string $fqn, string $currentSuffix, string $newSuffix): string
24
    {
25 1
        return $this->cropSuffix($fqn, $currentSuffix) . $newSuffix;
26
    }
27
28
    /**
29
     * Crop a suffix from an FQN if it is there.
30
     *
31
     * If it is not there, do nothing and return the FQN as is
32
     *
33
     * @param string $fqn
34
     * @param string $suffix
35
     *
36
     * @return string
37
     */
38 3
    public function cropSuffix(string $fqn, string $suffix): string
39
    {
40 3
        if ($suffix === \substr($fqn, -\strlen($suffix))) {
41 2
            return \substr($fqn, 0, -\strlen($suffix));
42
        }
43
44 1
        return $fqn;
45
    }
46
47
    /**
48
     * @param \ts\Reflection\ReflectionClass $fieldTraitReflection
49
     *
50
     * @return string
51
     */
52 1
    public function getFakerProviderFqnFromFieldTraitReflection(\ts\Reflection\ReflectionClass $fieldTraitReflection
53
    ): string
54
    {
55 1
        return \str_replace(
56
            [
57 1
                '\\Traits\\',
58
                'FieldTrait',
59
            ],
60
            [
61 1
                '\\FakerData\\',
62
                'FakerData',
63
            ],
64 1
            $fieldTraitReflection->getName()
65
        );
66
    }
67
68
    /**
69
     * @param mixed|object $object
70
     *
71
     * @return string
72
     */
73 1
    public function getObjectShortName($object): string
74
    {
75 1
        return $this->getClassShortName($this->getObjectFqn($object));
76
    }
77
78
    /**
79
     * @param string $className
80
     *
81
     * @return string
82
     */
83 2
    public function getClassShortName(string $className): string
84
    {
85 2
        $exp = explode('\\', $className);
86
87 2
        return end($exp);
88
    }
89
90
    /**
91
     * @param mixed|object $object
92
     *
93
     * @see https://gist.github.com/ludofleury/1708784
94
     * @return string
95
     */
96 2
    public function getObjectFqn($object): string
97
    {
98 2
        return \get_class($object);
99
    }
100
101
    /**
102
     * Get the basename of a namespace
103
     *
104
     * @param string $namespace
105
     *
106
     * @return string
107
     */
108
    public function basename(string $namespace): string
109
    {
110
        $strrpos = \strrpos($namespace, '\\');
111
        if (false === $strrpos) {
112
            return $namespace;
113
        }
114
115
        return $this->tidy(\substr($namespace, $strrpos + 1));
116
    }
117
118
    /**
119
     * Checks and tidies up a given namespace
120
     *
121
     * @param string $namespace
122
     *
123
     * @return string
124
     * @throws \RuntimeException
125
     */
126 19
    public function tidy(string $namespace): string
127
    {
128 19
        if (\ts\stringContains($namespace, '/')) {
129
            throw new \RuntimeException('Invalid namespace ' . $namespace);
130
        }
131
        #remove repeated separators
132 19
        $namespace = preg_replace(
133 19
            '#' . '\\\\' . '+#',
134 19
            '\\',
135 19
            $namespace
136
        );
137
138 19
        return $namespace;
139
    }
140
141
    /**
142
     * Get the fully qualified name of the Fixture class for a specified Entity fully qualified name
143
     *
144
     * @param string $entityFqn
145
     *
146
     * @return string
147
     */
148 1
    public function getFixtureFqnFromEntityFqn(string $entityFqn): string
149
    {
150 1
        return \str_replace(
151 1
            '\\Entities',
152 1
            '\\Assets\\EntityFixtures',
153 1
            $entityFqn
154 1
        ) . 'Fixture';
155
    }
156
157
    /**
158
     * Work out the entity namespace root from a single entity reflection object.
159
     *
160
     * @param \ts\Reflection\ReflectionClass $entityReflection
161
     *
162
     * @return string
163
     */
164 1
    public function getEntityNamespaceRootFromEntityReflection(
165
        \ts\Reflection\ReflectionClass $entityReflection
166
    ): string {
167 1
        return $this->tidy(
168 1
            $this->getNamespaceRootToDirectoryFromFqn(
169 1
                $entityReflection->getName(),
170 1
                AbstractGenerator::ENTITIES_FOLDER_NAME
171
            )
172
        );
173
    }
174
175
    /**
176
     * Get the namespace root up to and including a specified directory
177
     *
178
     * @param string $fqn
179
     * @param string $directory
180
     *
181
     * @return null|string
182
     */
183 1
    public function getNamespaceRootToDirectoryFromFqn(string $fqn, string $directory): ?string
184
    {
185 1
        $strPos = \strrpos(
186 1
            $fqn,
187 1
            $directory
188
        );
189 1
        if (false !== $strPos) {
190 1
            return $this->tidy(\substr($fqn, 0, $strPos + \strlen($directory)));
191
        }
192
193
        return null;
194
    }
195
196
    /**
197
     * Get the sub path for an Entity file, start from the Entities path - normally `/path/to/project/src/Entities`
198
     *
199
     * @param string $entityFqn
200
     *
201
     * @return string
202
     */
203 1
    public function getEntityFileSubPath(
204
        string $entityFqn
205
    ): string {
206 1
        return $this->getEntitySubPath($entityFqn) . '.php';
207
    }
208
209
    /**
210
     * Get the folder structure for an Entity, start from the Entities path - normally `/path/to/project/src/Entities`
211
     *
212
     * This is not the path to the file, but the sub path of directories for storing entity related items.
213
     *
214
     * @param string $entityFqn
215
     *
216
     * @return string
217
     */
218 2
    public function getEntitySubPath(
219
        string $entityFqn
220
    ): string {
221 2
        $entityPath = str_replace(
222 2
            '\\',
223 2
            '/',
224 2
            $this->getEntitySubNamespace($entityFqn)
225
        );
226
227 2
        return '/' . $entityPath;
228
    }
229
230
    /**
231
     * Get the Namespace for an Entity, start from the Entities Fully Qualified Name base - normally
232
     * `\My\Project\Entities\`
233
     *
234
     * @param string $entityFqn
235
     *
236
     * @return string
237
     */
238 7
    public function getEntitySubNamespace(
239
        string $entityFqn
240
    ): string {
241 7
        return $this->tidy(
242 7
            \substr(
243 7
                $entityFqn,
244 7
                \strrpos(
245 7
                    $entityFqn,
246 7
                    '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\'
247
                )
248 7
                + \strlen('\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\')
249
            )
250
        );
251
    }
252
253
    /**
254
     * Get the Fully Qualified Namespace root for Traits for the specified Entity
255
     *
256
     * @param string $entityFqn
257
     *
258
     * @return string
259
     */
260 1
    public function getTraitsNamespaceForEntity(
261
        string $entityFqn
262
    ): string {
263 1
        $traitsNamespace = $this->getProjectNamespaceRootFromEntityFqn($entityFqn)
264 1
                           . AbstractGenerator::ENTITY_RELATIONS_NAMESPACE
265 1
                           . '\\' . $this->getEntitySubNamespace($entityFqn)
266 1
                           . '\\Traits';
267
268 1
        return $traitsNamespace;
269
    }
270
271
    /**
272
     * Use the fully qualified name of two Entities to calculate the Project Namespace Root
273
     *
274
     * - note: this assumes a single namespace level for entities, eg `Entities`
275
     *
276
     * @param string $entityFqn
277
     *
278
     * @return string
279
     */
280 5
    public function getProjectNamespaceRootFromEntityFqn(string $entityFqn): string
281
    {
282 5
        return $this->tidy(
283 5
            \substr(
284 5
                $entityFqn,
285 5
                0,
286 5
                \strrpos(
287 5
                    $entityFqn,
288 5
                    '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\'
289
                )
290
            )
291
        );
292
    }
293
294
    /**
295
     * Get the Fully Qualified Namespace for the "HasEntities" interface for the specified Entity
296
     *
297
     * @param string $entityFqn
298
     *
299
     * @return string
300
     */
301 1
    public function getHasPluralInterfaceFqnForEntity(
302
        string $entityFqn
303
    ): string {
304 1
        $interfaceNamespace = $this->getInterfacesNamespaceForEntity($entityFqn);
305
306 1
        return $interfaceNamespace . '\\Has' . ucfirst($entityFqn::getDoctrineStaticMeta()->getPlural()) . 'Interface';
307
    }
308
309
    /**
310
     * Get the Fully Qualified Namespace root for Interfaces for the specified Entity
311
     *
312
     * @param string $entityFqn
313
     *
314
     * @return string
315
     */
316 3
    public function getInterfacesNamespaceForEntity(
317
        string $entityFqn
318
    ): string {
319 3
        $interfacesNamespace = $this->getProjectNamespaceRootFromEntityFqn($entityFqn)
320 3
                               . AbstractGenerator::ENTITY_RELATIONS_NAMESPACE
321 3
                               . '\\' . $this->getEntitySubNamespace($entityFqn)
322 3
                               . '\\Interfaces';
323
324 3
        return $this->tidy($interfacesNamespace);
325
    }
326
327
    /**
328
     * Get the Fully Qualified Namespace for the "HasEntity" interface for the specified Entity
329
     *
330
     * @param string $entityFqn
331
     *
332
     * @return string
333
     * @throws DoctrineStaticMetaException
334
     */
335 1
    public function getHasSingularInterfaceFqnForEntity(
336
        string $entityFqn
337
    ): string {
338
        try {
339 1
            $interfaceNamespace = $this->getInterfacesNamespaceForEntity($entityFqn);
340
341 1
            return $interfaceNamespace . '\\Has' . ucfirst($entityFqn::getDoctrineStaticMeta()->getSingular())
342 1
                   . 'Interface';
343
        } catch (\Exception $e) {
344
            throw new DoctrineStaticMetaException(
345
                'Exception in ' . __METHOD__ . ': ' . $e->getMessage(),
346
                $e->getCode(),
347
                $e
348
            );
349
        }
350
    }
351
352
    /**
353
     * Get the Fully Qualified Namespace for the Relation Trait for a specific Entity and hasType
354
     *
355
     * @param string      $hasType
356
     * @param string      $ownedEntityFqn
357
     * @param string|null $projectRootNamespace
358
     * @param string      $srcFolder
359
     *
360
     * @return string
361
     * @throws DoctrineStaticMetaException
362
     */
363 2
    public function getOwningTraitFqn(
364
        string $hasType,
365
        string $ownedEntityFqn,
366
        ?string $projectRootNamespace = null,
367
        string $srcFolder = AbstractCommand::DEFAULT_SRC_SUBFOLDER
368
    ): string {
369
        try {
370 2
            $ownedHasName = $this->getOwnedHasName($hasType, $ownedEntityFqn, $srcFolder, $projectRootNamespace);
0 ignored issues
show
Bug introduced by
It seems like $projectRootNamespace can also be of type null; however, parameter $projectRootNamespace of EdmondsCommerce\Doctrine...lper::getOwnedHasName() 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

370
            $ownedHasName = $this->getOwnedHasName($hasType, $ownedEntityFqn, $srcFolder, /** @scrutinizer ignore-type */ $projectRootNamespace);
Loading history...
371 2
            if (null === $projectRootNamespace) {
372
                $projectRootNamespace = $this->getProjectRootNamespaceFromComposerJson($srcFolder);
373
            }
374 2
            list($ownedClassName, , $ownedSubDirectories) = $this->parseFullyQualifiedName(
375 2
                $ownedEntityFqn,
376 2
                $srcFolder,
377 2
                $projectRootNamespace
378
            );
379 2
            $traitSubDirectories = \array_slice($ownedSubDirectories, 2);
380 2
            $owningTraitFqn      = $this->getOwningRelationsRootFqn(
381 2
                $projectRootNamespace,
382 2
                $traitSubDirectories
383
            );
384 2
            $owningTraitFqn      .= $ownedClassName . '\\Traits\\Has' . $ownedHasName
385 2
                                    . '\\Has' . $ownedHasName . $this->stripPrefixFromHasType($hasType);
386
387 2
            return $this->tidy($owningTraitFqn);
388
        } catch (\Exception $e) {
389
            throw new DoctrineStaticMetaException(
390
                'Exception in ' . __METHOD__ . ': ' . $e->getMessage(),
391
                $e->getCode(),
392
                $e
393
            );
394
        }
395
    }
396
397
    /**
398
     * Based on the $hasType, we calculate exactly what type of `Has` we have
399
     *
400
     * @param string $hasType
401
     * @param string $ownedEntityFqn
402
     * @param string $srcOrTestSubFolder
403
     *
404
     * @param string $projectRootNamespace
405
     *
406
     * @return string
407
     * @SuppressWarnings(PHPMD.StaticAccess)
408
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
409
     */
410 5
    public function getOwnedHasName(
411
        string $hasType,
412
        string $ownedEntityFqn,
413
        string $srcOrTestSubFolder,
414
        string $projectRootNamespace
415
    ): string {
416 5
        $parsedFqn = $this->parseFullyQualifiedName(
417 5
            $ownedEntityFqn,
418 5
            $srcOrTestSubFolder,
419 5
            $projectRootNamespace
420
        );
421
422 5
        $subDirectories = $parsedFqn[2];
423
424 5
        if (\in_array(
425 5
            $hasType,
426 5
            RelationsGenerator::HAS_TYPES_PLURAL,
427 5
            true
428
        )) {
429 5
            return $this->getPluralNamespacedName($ownedEntityFqn, $subDirectories);
430
        }
431
432 3
        return $this->getSingularNamespacedName($ownedEntityFqn, $subDirectories);
433
    }
434
435
    /**
436
     * From the fully qualified name, parse out:
437
     *  - class name,
438
     *  - namespace
439
     *  - the namespace parts not including the project root namespace
440
     *
441
     * @param string      $fqn
442
     *
443
     * @param string      $srcOrTestSubFolder eg 'src' or 'test'
444
     *
445
     * @param string|null $projectRootNamespace
446
     *
447
     * @return array [$className,$namespace,$subDirectories]
448
     * @throws DoctrineStaticMetaException
449
     */
450 6
    public function parseFullyQualifiedName(
451
        string $fqn,
452
        string $srcOrTestSubFolder = AbstractCommand::DEFAULT_SRC_SUBFOLDER,
453
        string $projectRootNamespace = null
454
    ): array {
455
        try {
456 6
            $fqn = $this->root($fqn);
457 6
            if (null === $projectRootNamespace) {
458
                $projectRootNamespace = $this->getProjectRootNamespaceFromComposerJson($srcOrTestSubFolder);
459
            }
460 6
            $projectRootNamespace = $this->root($projectRootNamespace);
461 6
            if (false === \ts\stringContains($fqn, $projectRootNamespace)) {
462
                throw new DoctrineStaticMetaException(
463
                    'The $fqn [' . $fqn . '] does not contain the project root namespace'
464
                    . ' [' . $projectRootNamespace . '] - are you sure it is the correct FQN?'
465
                );
466
            }
467 6
            $fqnParts       = explode('\\', $fqn);
468 6
            $className      = array_pop($fqnParts);
469 6
            $namespace      = implode('\\', $fqnParts);
470 6
            $rootParts      = explode('\\', $projectRootNamespace);
471 6
            $subDirectories = [];
472 6
            foreach ($fqnParts as $k => $fqnPart) {
473 6
                if (isset($rootParts[$k]) && $rootParts[$k] === $fqnPart) {
474 6
                    continue;
475
                }
476 6
                $subDirectories[] = $fqnPart;
477
            }
478 6
            array_unshift($subDirectories, $srcOrTestSubFolder);
479
480
            return [
481 6
                $className,
482 6
                $this->root($namespace),
483 6
                $subDirectories,
484
            ];
485
        } catch (\Exception $e) {
486
            throw new DoctrineStaticMetaException(
487
                'Exception in ' . __METHOD__ . ': ' . $e->getMessage(),
488
                $e->getCode(),
489
                $e
490
            );
491
        }
492
    }
493
494
    /**
495
     * Generate a tidy root namespace without a leading \
496
     *
497
     * @param string $namespace
498
     *
499
     * @return string
500
     */
501 7
    public function root(string $namespace): string
502
    {
503 7
        return $this->tidy(ltrim($namespace, '\\'));
504
    }
505
506
    /**
507
     * Read src autoloader from composer json
508
     *
509
     * @param string $dirForNamespace
510
     *
511
     * @return string
512
     * @throws DoctrineStaticMetaException
513
     * @SuppressWarnings(PHPMD.StaticAccess)
514
     */
515 2
    public function getProjectRootNamespaceFromComposerJson(
516
        string $dirForNamespace = 'src'
517
    ): string {
518
        try {
519 2
            $dirForNamespace = trim($dirForNamespace, '/');
520 2
            $jsonPath        = Config::getProjectRootDirectory() . '/composer.json';
521 2
            $json            = json_decode(\ts\file_get_contents($jsonPath), true);
522 2
            if (JSON_ERROR_NONE !== json_last_error()) {
523
                throw new \RuntimeException(
524
                    'Error decoding json from path ' . $jsonPath . ' , ' . json_last_error_msg()
525
                );
526
            }
527
            /**
528
             * @var string[][][][] $json
529
             */
530 2
            if (isset($json['autoload']['psr-4'])) {
531 2
                foreach ($json['autoload']['psr-4'] as $namespace => $dirs) {
532 2
                    foreach ($dirs as $dir) {
533 2
                        $dir = trim($dir, '/');
534 2
                        if ($dir === $dirForNamespace) {
535 2
                            return $this->tidy(rtrim($namespace, '\\'));
536
                        }
537
                    }
538
                }
539
            }
540
        } catch (\Exception $e) {
541
            throw new DoctrineStaticMetaException(
542
                'Exception in ' . __METHOD__ . ': ' . $e->getMessage(),
543
                $e->getCode(),
544
                $e
545
            );
546
        }
547
        throw new DoctrineStaticMetaException('Failed to find psr-4 namespace root');
548
    }
549
550
    /**
551
     * @param string $entityFqn
552
     * @param array  $subDirectories
553
     *
554
     * @return string
555
     * @SuppressWarnings(PHPMD.StaticAccess)
556
     */
557 5
    public function getPluralNamespacedName(string $entityFqn, array $subDirectories): string
558
    {
559 5
        $plural = \ucfirst(MappingHelper::getPluralForFqn($entityFqn));
560
561 5
        return $this->getNamespacedName($plural, $subDirectories);
562
    }
563
564
    /**
565
     * @param string $entityName
566
     * @param array  $subDirectories
567
     *
568
     * @return string
569
     */
570 5
    public function getNamespacedName(string $entityName, array $subDirectories): string
571
    {
572 5
        $noEntitiesDirectory = \array_slice($subDirectories, 2);
573 5
        $namespacedName      = \array_merge($noEntitiesDirectory, [$entityName]);
574
575 5
        return \ucfirst(\implode('', $namespacedName));
576
    }
577
578
    /**
579
     * @param string $entityFqn
580
     * @param array  $subDirectories
581
     *
582
     * @return string
583
     * @SuppressWarnings(PHPMD.StaticAccess)
584
     */
585 4
    public function getSingularNamespacedName(string $entityFqn, array $subDirectories): string
586
    {
587 4
        $singular = \ucfirst(MappingHelper::getSingularForFqn($entityFqn));
588
589 4
        return $this->getNamespacedName($singular, $subDirectories);
590
    }
591
592
    /**
593
     * Get the Namespace root for Entity Relations
594
     *
595
     * @param string $projectRootNamespace
596
     * @param array  $subDirectories
597
     *
598
     * @return string
599
     */
600 3
    public function getOwningRelationsRootFqn(
601
        string $projectRootNamespace,
602
        array $subDirectories
603
    ): string {
604
        $relationsRootFqn = $projectRootNamespace
605 3
                            . AbstractGenerator::ENTITY_RELATIONS_NAMESPACE . '\\';
606 3
        if (count($subDirectories) > 0) {
607 1
            $relationsRootFqn .= implode('\\', $subDirectories) . '\\';
608
        }
609
610 3
        return $this->tidy($relationsRootFqn);
611
    }
612
613
    /**
614
     * Normalise a has type, removing prefixes that are not required
615
     *
616
     * Inverse hasTypes use the standard template without the prefix
617
     * The exclusion ot this are the ManyToMany and OneToOne relations
618
     *
619
     * @param string $hasType
620
     *
621
     * @return string
622
     */
623 3
    public function stripPrefixFromHasType(
624
        string $hasType
625
    ): string {
626
        foreach ([
627 3
                     RelationsGenerator::INTERNAL_TYPE_MANY_TO_MANY,
628
                     RelationsGenerator::INTERNAL_TYPE_ONE_TO_ONE,
629
                 ] as $noStrip) {
630 3
            if (\ts\stringContains($hasType, $noStrip)) {
631 3
                return $hasType;
632
            }
633
        }
634
635
        foreach ([
636 2
                     RelationsGenerator::INTERNAL_TYPE_ONE_TO_MANY,
637
                     RelationsGenerator::INTERNAL_TYPE_MANY_TO_ONE,
638
                 ] as $stripAll) {
639 2
            if (\ts\stringContains($hasType, $stripAll)) {
640 2
                return str_replace(
641
                    [
642 2
                        RelationsGenerator::PREFIX_OWNING,
643
                        RelationsGenerator::PREFIX_INVERSE,
644
                    ],
645 2
                    '',
646 2
                    $hasType
647
                );
648
            }
649
        }
650
651
        return str_replace(
652
            [
653
                RelationsGenerator::PREFIX_INVERSE,
654
            ],
655
            '',
656
            $hasType
657
        );
658
    }
659
660 1
    public function getFactoryFqnFromEntityFqn(string $entityFqn): string
661
    {
662 1
        return $this->tidy(
663 1
            \str_replace(
664 1
                '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\',
665 1
                '\\' . AbstractGenerator::ENTITY_FACTORIES_NAMESPACE . '\\',
666 1
                $entityFqn
667 1
            ) . 'Factory'
668
        );
669
    }
670
671
    /**
672
     * @param string $ownedEntityFqn
673
     * @param string $srcOrTestSubFolder
674
     * @param string $projectRootNamespace
675
     *
676
     * @return string
677
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
678
     */
679 1
    public function getReciprocatedHasName(
680
        string $ownedEntityFqn,
681
        string $srcOrTestSubFolder,
682
        string $projectRootNamespace
683
    ): string {
684 1
        $parsedFqn = $this->parseFullyQualifiedName(
685 1
            $ownedEntityFqn,
686 1
            $srcOrTestSubFolder,
687 1
            $projectRootNamespace
688
        );
689
690 1
        $subDirectories = $parsedFqn[2];
691
692 1
        return $this->getSingularNamespacedName($ownedEntityFqn, $subDirectories);
693
    }
694
695
    /**
696
     * Get the Fully Qualified Namespace for the Relation Interface for a specific Entity and hasType
697
     *
698
     * @param string      $hasType
699
     * @param string      $ownedEntityFqn
700
     * @param string|null $projectRootNamespace
701
     * @param string      $srcFolder
702
     *
703
     * @return string
704
     * @throws DoctrineStaticMetaException
705
     */
706 2
    public function getOwningInterfaceFqn(
707
        string $hasType,
708
        string $ownedEntityFqn,
709
        string $projectRootNamespace = null,
710
        string $srcFolder = AbstractCommand::DEFAULT_SRC_SUBFOLDER
711
    ): string {
712
        try {
713 2
            $ownedHasName = $this->getOwnedHasName($hasType, $ownedEntityFqn, $srcFolder, $projectRootNamespace);
0 ignored issues
show
Bug introduced by
It seems like $projectRootNamespace can also be of type null; however, parameter $projectRootNamespace of EdmondsCommerce\Doctrine...lper::getOwnedHasName() 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

713
            $ownedHasName = $this->getOwnedHasName($hasType, $ownedEntityFqn, $srcFolder, /** @scrutinizer ignore-type */ $projectRootNamespace);
Loading history...
714 2
            if (null === $projectRootNamespace) {
715
                $projectRootNamespace = $this->getProjectRootNamespaceFromComposerJson($srcFolder);
716
            }
717 2
            list($ownedClassName, , $ownedSubDirectories) = $this->parseFullyQualifiedName(
718 2
                $ownedEntityFqn,
719 2
                $srcFolder,
720 2
                $projectRootNamespace
721
            );
722 2
            $interfaceSubDirectories = \array_slice($ownedSubDirectories, 2);
723 2
            $owningInterfaceFqn      = $this->getOwningRelationsRootFqn(
724 2
                $projectRootNamespace,
725 2
                $interfaceSubDirectories
726
            );
727 2
            $owningInterfaceFqn      .= '\\' . $ownedClassName . '\\Interfaces\\Has' . $ownedHasName . 'Interface';
728
729 2
            return $this->tidy($owningInterfaceFqn);
730
        } catch (\Exception $e) {
731
            throw new DoctrineStaticMetaException(
732
                'Exception in ' . __METHOD__ . ': ' . $e->getMessage(),
733
                $e->getCode(),
734
                $e
735
            );
736
        }
737
    }
738
739 1
    public function getEntityInterfaceFromEntityFqn(string $entityFqn): string
740
    {
741 1
        return \str_replace(
742 1
            '\\Entities\\',
743 1
            '\\Entity\\Interfaces\\',
744 1
            $entityFqn
745 1
        ) . 'Interface';
746
    }
747
}
748