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 (#93)
by joseph
17:38 queued 30s
created

NamespaceHelper::getInterfacesNamespaceForEntity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 9
rs 10
c 0
b 0
f 0
ccs 6
cts 6
cp 1
cc 1
nc 1
nop 1
crap 1
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 8
    public function swapSuffix(string $fqn, string $currentSuffix, string $newSuffix): string
24
    {
25 8
        return $this->cropSuffix($fqn, $currentSuffix) . $newSuffix;
26
    }
27
28
    /**
29
     * @param \ts\Reflection\ReflectionClass $fieldTraitReflection
30
     *
31
     * @return string
32
     */
33 77
    public function getFakerProviderFqnFromFieldTraitReflection(\ts\Reflection\ReflectionClass $fieldTraitReflection
34
    ): string
35
    {
36 77
        return \str_replace(
37
            [
38 77
                '\\Traits\\',
39
                'FieldTrait',
40
            ],
41
            [
42 77
                '\\FakerData\\',
43
                'FakerData',
44
            ],
45 77
            $fieldTraitReflection->getName()
46
        );
47
    }
48
49
    /**
50
     * Crop a suffix from an FQN if it is there.
51
     *
52
     * If it is not there, do nothing and return the FQN as is
53
     *
54
     * @param string $fqn
55
     * @param string $suffix
56
     *
57
     * @return string
58
     */
59 28
    public function cropSuffix(string $fqn, string $suffix): string
60
    {
61 28
        if ($suffix === \substr($fqn, -\strlen($suffix))) {
62 27
            return \substr($fqn, 0, -\strlen($suffix));
63
        }
64
65 8
        return $fqn;
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 9
    public function getClassShortName(string $className): string
84
    {
85 9
        $exp = explode('\\', $className);
86
87 9
        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 6
    public function getObjectFqn($object): string
97
    {
98 6
        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 39
    public function basename(string $namespace): string
109
    {
110 39
        $strrpos = \strrpos($namespace, '\\');
111 39
        if (false === $strrpos) {
112
            return $namespace;
113
        }
114
115 39
        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 143
    public function tidy(string $namespace): string
127
    {
128 143
        if (\ts\stringContains($namespace, '/')) {
129
            throw new \RuntimeException('Invalid namespace ' . $namespace);
130
        }
131
        #remove repeated separators
132 143
        $namespace = preg_replace(
133 143
            '#' . '\\\\' . '+#',
134 143
            '\\',
135 143
            $namespace
136
        );
137
138 143
        return $namespace;
139
    }
140
141
    /**
142
     * Work out the entity namespace root from a single entity reflection object.
143
     *
144
     * @param \ts\Reflection\ReflectionClass $entityReflection
145
     *
146
     * @return string
147
     */
148 1
    public function getEntityNamespaceRootFromEntityReflection(
149
        \ts\Reflection\ReflectionClass $entityReflection
150
    ): string {
151 1
        return $this->tidy(
152 1
            $this->getNamespaceRootToDirectoryFromFqn(
153 1
                $entityReflection->getName(),
154 1
                AbstractGenerator::ENTITIES_FOLDER_NAME
155
            )
156
        );
157
    }
158
159
    /**
160
     * Get the namespace root up to and including a specified directory
161
     *
162
     * @param string $fqn
163
     * @param string $directory
164
     *
165
     * @return null|string
166
     */
167 1
    public function getNamespaceRootToDirectoryFromFqn(string $fqn, string $directory): ?string
168
    {
169 1
        $strPos = \strrpos(
170 1
            $fqn,
171 1
            $directory
172
        );
173 1
        if (false !== $strPos) {
174 1
            return $this->tidy(\substr($fqn, 0, $strPos + \strlen($directory)));
175
        }
176
177
        return null;
178
    }
179
180
    /**
181
     * Get the sub path for an Entity file, start from the Entities path - normally `/path/to/project/src/Entities`
182
     *
183
     * @param string $entityFqn
184
     *
185
     * @return string
186
     */
187 3
    public function getEntityFileSubPath(
188
        string $entityFqn
189
    ): string {
190 3
        return $this->getEntitySubPath($entityFqn) . '.php';
191
    }
192
193
    /**
194
     * Get the folder structure for an Entity, start from the Entities path - normally `/path/to/project/src/Entities`
195
     *
196
     * This is not the path to the file, but the sub path of directories for storing entity related items.
197
     *
198
     * @param string $entityFqn
199
     *
200
     * @return string
201
     */
202 5
    public function getEntitySubPath(
203
        string $entityFqn
204
    ): string {
205 5
        $entityPath = str_replace(
206 5
            '\\',
207 5
            '/',
208 5
            $this->getEntitySubNamespace($entityFqn)
209
        );
210
211 5
        return '/' . $entityPath;
212
    }
213
214
    /**
215
     * Get the Namespace for an Entity, start from the Entities Fully Qualified Name base - normally
216
     * `\My\Project\Entities\`
217
     *
218
     * @param string $entityFqn
219
     *
220
     * @return string
221
     */
222 68
    public function getEntitySubNamespace(
223
        string $entityFqn
224
    ): string {
225 68
        return $this->tidy(
226 68
            \substr(
227 68
                $entityFqn,
228 68
                \strrpos(
229 68
                    $entityFqn,
230 68
                    '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\'
231
                )
232 68
                + \strlen('\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\')
233
            )
234
        );
235
    }
236
237
    /**
238
     * Get the Fully Qualified Namespace root for Traits for the specified Entity
239
     *
240
     * @param string $entityFqn
241
     *
242
     * @return string
243
     */
244 1
    public function getTraitsNamespaceForEntity(
245
        string $entityFqn
246
    ): string {
247 1
        $traitsNamespace = $this->getProjectNamespaceRootFromEntityFqn($entityFqn)
248 1
                           . AbstractGenerator::ENTITY_RELATIONS_NAMESPACE
249 1
                           . '\\' . $this->getEntitySubNamespace($entityFqn)
250 1
                           . '\\Traits';
251
252 1
        return $traitsNamespace;
253
    }
254
255
    /**
256
     * Use the fully qualified name of two Entities to calculate the Project Namespace Root
257
     *
258
     * - note: this assumes a single namespace level for entities, eg `Entities`
259
     *
260
     * @param string $entityFqn
261
     *
262
     * @return string
263
     */
264 6
    public function getProjectNamespaceRootFromEntityFqn(string $entityFqn): string
265
    {
266 6
        return $this->tidy(
267 6
            \substr(
268 6
                $entityFqn,
269 6
                0,
270 6
                \strrpos(
271 6
                    $entityFqn,
272 6
                    '\\' . AbstractGenerator::ENTITIES_FOLDER_NAME . '\\'
273
                )
274
            )
275
        );
276
    }
277
278
    /**
279
     * Get the Fully Qualified Namespace for the "HasEntities" interface for the specified Entity
280
     *
281
     * @param string $entityFqn
282
     *
283
     * @return string
284
     */
285 2
    public function getHasPluralInterfaceFqnForEntity(
286
        string $entityFqn
287
    ): string {
288 2
        $interfaceNamespace = $this->getInterfacesNamespaceForEntity($entityFqn);
289
290 2
        return $interfaceNamespace . '\\Has' . ucfirst($entityFqn::getPlural()) . 'Interface';
291
    }
292
293
    /**
294
     * Get the Fully Qualified Namespace root for Interfaces for the specified Entity
295
     *
296
     * @param string $entityFqn
297
     *
298
     * @return string
299
     */
300 4
    public function getInterfacesNamespaceForEntity(
301
        string $entityFqn
302
    ): string {
303 4
        $interfacesNamespace = $this->getProjectNamespaceRootFromEntityFqn($entityFqn)
304 4
                               . AbstractGenerator::ENTITY_RELATIONS_NAMESPACE
305 4
                               . '\\' . $this->getEntitySubNamespace($entityFqn)
306 4
                               . '\\Interfaces';
307
308 4
        return $this->tidy($interfacesNamespace);
309
    }
310
311
    /**
312
     * Get the Fully Qualified Namespace for the "HasEntity" interface for the specified Entity
313
     *
314
     * @param string $entityFqn
315
     *
316
     * @return string
317
     * @throws DoctrineStaticMetaException
318
     */
319 1
    public function getHasSingularInterfaceFqnForEntity(
320
        string $entityFqn
321
    ): string {
322
        try {
323 1
            $interfaceNamespace = $this->getInterfacesNamespaceForEntity($entityFqn);
324
325 1
            return $interfaceNamespace . '\\Has' . ucfirst($entityFqn::getSingular()) . 'Interface';
326
        } catch (\Exception $e) {
327
            throw new DoctrineStaticMetaException(
328
                'Exception in ' . __METHOD__ . ': ' . $e->getMessage(),
329
                $e->getCode(),
330
                $e
331
            );
332
        }
333
    }
334
335
    /**
336
     * Get the Fully Qualified Namespace for the Relation Trait for a specific Entity and hasType
337
     *
338
     * @param string      $hasType
339
     * @param string      $ownedEntityFqn
340
     * @param string|null $projectRootNamespace
341
     * @param string      $srcFolder
342
     *
343
     * @return string
344
     * @throws DoctrineStaticMetaException
345
     */
346 24
    public function getOwningTraitFqn(
347
        string $hasType,
348
        string $ownedEntityFqn,
349
        ?string $projectRootNamespace = null,
350
        string $srcFolder = AbstractCommand::DEFAULT_SRC_SUBFOLDER
351
    ): string {
352
        try {
353 24
            $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

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

682
            $ownedHasName = $this->getOwnedHasName($hasType, $ownedEntityFqn, $srcFolder, /** @scrutinizer ignore-type */ $projectRootNamespace);
Loading history...
683 24
            if (null === $projectRootNamespace) {
684
                $projectRootNamespace = $this->getProjectRootNamespaceFromComposerJson($srcFolder);
685
            }
686 24
            list($ownedClassName, , $ownedSubDirectories) = $this->parseFullyQualifiedName(
687 24
                $ownedEntityFqn,
688 24
                $srcFolder,
689 24
                $projectRootNamespace
690
            );
691 24
            $interfaceSubDirectories = \array_slice($ownedSubDirectories, 2);
692 24
            $owningInterfaceFqn      = $this->getOwningRelationsRootFqn(
693 24
                $projectRootNamespace,
694 24
                $interfaceSubDirectories
695
            );
696 24
            $owningInterfaceFqn      .= '\\' . $ownedClassName . '\\Interfaces\\Has' . $ownedHasName . 'Interface';
697
698 24
            return $this->tidy($owningInterfaceFqn);
699
        } catch (\Exception $e) {
700
            throw new DoctrineStaticMetaException(
701
                'Exception in ' . __METHOD__ . ': ' . $e->getMessage(),
702
                $e->getCode(),
703
                $e
704
            );
705
        }
706
    }
707
708 97
    public function getEntityInterfaceFromEntityFqn(string $entityFqn): string
709
    {
710 97
        return \str_replace(
711 97
            '\\Entities\\',
712 97
            '\\Entity\\Interfaces\\',
713 97
            $entityFqn
714 97
        ) . 'Interface';
715
    }
716
}
717