Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
82.61% covered (warning)
82.61%
38 / 46
68.75% covered (warning)
68.75%
11 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
DiscoveredSymbol
82.61% covered (warning)
82.61%
38 / 46
68.75% covered (warning)
68.75%
11 / 16
30.83
0.00% covered (danger)
0.00%
0 / 1
 __construct
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
5.03
 isGlobal
n/a
0 / 0
n/a
0 / 0
0
 getOriginalFqdnName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getReplacementFqdnName
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getSourceFiles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addSourceFile
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getLocalReplacement
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setLocalReplacement
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOriginalLocalName
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 setDoRename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDoRename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPackages
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 getPackageName
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getOriginalSymbolStripPrefix
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
3.14
 __toString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addDependency
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDependencies
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3declare(strict_types=1);
4
5/**
6 * A namespace, class, interface or trait discovered in the project.
7 */
8
9namespace BrianHenryIE\Strauss\Types;
10
11use BrianHenryIE\Strauss\Composer\ComposerPackage;
12use BrianHenryIE\Strauss\Composer\DependenciesCollection;
13use BrianHenryIE\Strauss\Files\FileBase;
14use BrianHenryIE\Strauss\Files\FileWithDependency;
15use BrianHenryIE\Strauss\Pipeline\ChangeEnumerator;
16use BrianHenryIE\Strauss\Pipeline\FileSymbolScanner;
17use Composer\Package\PackageInterface;
18
19abstract class DiscoveredSymbol
20{
21    /**
22     * The file(s) where this symbol was defined.
23     *
24     * @var array<FileBase> $sourceFiles
25     */
26    protected array $sourceFiles = [];
27
28    // E.g. for `My\Ns\Classname` this is just `Classname`.
29    protected string $localOriginalSymbol;
30
31    protected string $fqdnOriginalSymbol;
32
33    protected ?string $localReplacement = null;
34
35    protected bool $doRename = true;
36
37    // Possibly empty.
38    protected DependenciesCollection $dependencies;
39
40    /**
41     * @param string $fqdnSymbol The classname / namespace etc.
42     * @param ?FileBase $sourceFile The file it was discovered in. Unneeded for global namespace and some (Composer) predictable files.
43     */
44    public function __construct(
45        string $fqdnSymbol,
46        ?FileBase $sourceFile = null,
47        ?ComposerPackage $composerPackage = null
48    ) {
49        $this->dependencies = new DependenciesCollection([]);
50        if ($composerPackage) {
51            $this->dependencies->add($composerPackage);
52        }
53
54        $this->fqdnOriginalSymbol = $fqdnSymbol;
55
56        if (!str_contains($fqdnSymbol, '\\') || ($this instanceof NamespaceSymbol)) {
57            $this->localOriginalSymbol = $fqdnSymbol;
58        } else {
59            $this->localOriginalSymbol = array_reverse(explode('\\', $fqdnSymbol))[0];
60        }
61
62        if ($sourceFile) {
63            $this->addSourceFile($sourceFile);
64            $sourceFile->addDiscoveredSymbol($this);
65        }
66    }
67
68    abstract public function isGlobal(): bool;
69
70    /**
71     * TODO: Document does this contain or ltrim the leading slash.
72     */
73    public function getOriginalFqdnName(): string
74    {
75        return $this->fqdnOriginalSymbol;
76    }
77
78    /**
79     * Defaults to the original until otherwise set.
80     */
81    public function getReplacementFqdnName(): string
82    {
83        // TODO: Should this be here or should `::isDoRename()` always be called at the calling site.
84        return $this->isDoRename()
85            ? trim(($this->localReplacement ?? $this->fqdnOriginalSymbol), '\\')
86            : $this->fqdnOriginalSymbol;
87    }
88
89    /**
90     * @return FileBase[]
91     */
92    public function getSourceFiles(): array
93    {
94        return $this->sourceFiles;
95    }
96
97    /**
98     * @see FileSymbolScanner
99     */
100    public function addSourceFile(FileBase $sourceFile): void
101    {
102        $this->sourceFiles[$sourceFile->getVendorRelativePath()] = $sourceFile;
103
104        if ($sourceFile instanceof FileWithDependency) {
105            $this->addDependency($sourceFile->getDependency());
106        }
107    }
108
109    public function getLocalReplacement(): string
110    {
111        return $this->isDoRename()
112            ? ($this->localReplacement ?? $this->localOriginalSymbol)
113            : $this->localOriginalSymbol;
114    }
115
116    public function setLocalReplacement(string $localReplacement): void
117    {
118        $this->localReplacement = $localReplacement;
119    }
120
121    public function getOriginalLocalName(): string
122    {
123        $fqdnParts = explode('\\', $this->fqdnOriginalSymbol);
124        $localSymbol = array_pop($fqdnParts);
125        return $localSymbol;
126    }
127
128    public function setDoRename(bool $doRename): void
129    {
130        $this->doRename = $doRename;
131    }
132
133    public function isDoRename(): bool
134    {
135        return $this->doRename;
136    }
137
138    /**
139     * @return ComposerPackage[]
140     */
141    public function getPackages(): array
142    {
143        return array_values(array_unique(array_filter(array_map(
144            function (FileBase $file) {
145                return $file instanceof FileWithDependency
146                    ? $file->getDependency()
147                    : null;
148            },
149            $this->getSourceFiles()
150        ))));
151    }
152
153    public function getPackageName(): ?string
154    {
155        $packages = $this->getPackages();
156        if (0 === count($packages)) {
157            return null;
158        }
159        // TODO: `if count(packages)>1`, warning.
160        return $packages[0]->getPackageName();
161    }
162
163    /**
164     * @deprecated This is only being called in {@see Prefixer::replaceSingleClassnameInString()}, the actual determination should be made in {@see ChangeEnumerator}.
165     */
166    public function getOriginalSymbolStripPrefix(string $classPrefix): string
167    {
168        $symbolName = $this->fqdnOriginalSymbol;
169
170        while (str_starts_with($symbolName, $classPrefix) && trim($classPrefix, '_') !== trim($symbolName, '_')) {
171            $symbolName = preg_replace('/^'.preg_quote($classPrefix) . '/', '', $symbolName) ?? $symbolName;
172        }
173
174        return trim($symbolName, '_');
175    }
176
177    public function __toString(): string
178    {
179        return $this->getOriginalFqdnName();
180    }
181
182    public function addDependency(\BrianHenryIE\Strauss\Composer\ComposerPackage $package): void
183    {
184        $this->dependencies->add($package);
185    }
186
187    public function getDependencies(): DependenciesCollection
188    {
189        return $this->dependencies;
190    }
191}