Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
73.61% covered (warning)
73.61%
53 / 72
65.00% covered (warning)
65.00%
13 / 20
CRAP
0.00% covered (danger)
0.00%
0 / 1
DiscoveredSymbols
73.61% covered (warning)
73.61%
53 / 72
65.00% covered (warning)
65.00%
13 / 20
46.54
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 add
78.57% covered (warning)
78.57%
11 / 14
0.00% covered (danger)
0.00%
0 / 1
8.63
 getSymbols
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getConstants
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNamespaces
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNamespace
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getGlobalClasses
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getGlobalClassChanges
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getAllClasses
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDiscoveredNamespaces
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 getDiscoveredNamespaceChanges
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getDiscoveredClasses
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getDiscoveredConstants
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 getDiscoveredFunctions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDiscoveredFunctionChanges
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getAll
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDiscoveredTraits
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDiscoveredInterfaces
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getClassmapSymbols
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getNamespaceSymbolByString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * @see \BrianHenryIE\Strauss\Pipeline\FileSymbolScanner
4 */
5
6namespace BrianHenryIE\Strauss\Types;
7
8use BrianHenryIE\Strauss\Files\File;
9use InvalidArgumentException;
10
11class DiscoveredSymbols
12{
13    private const CLASS_SYMBOL = 'CLASS';
14    private const CONST_SYMBOL = 'CONST';
15    private const NAMESPACE_SYMBOL = 'NAMESPACE';
16    private const FUNCTION_SYMBOL = 'FUNCTION';
17    private const TRAIT_SYMBOL = 'TRAIT';
18    private const INTERFACE_SYMBOL = 'INTERFACE';
19
20    /**
21     * All discovered symbols, grouped by type, indexed by original name.
22     *
23     * @var array{'NAMESPACE':array<string,NamespaceSymbol>, 'CONST':array<string,ConstantSymbol>, 'CLASS':array<string,ClassSymbol>, 'FUNCTION':array<string,FunctionSymbol>, 'TRAIT':array<string,TraitSymbol>, 'INTERFACE':array<string,InterfaceSymbol>}
24     */
25    protected array $types = [
26        self::CLASS_SYMBOL => [],
27        self::CONST_SYMBOL => [],
28        self::NAMESPACE_SYMBOL => [],
29        self::FUNCTION_SYMBOL => [],
30        self::TRAIT_SYMBOL => [],
31        self::INTERFACE_SYMBOL => [],
32    ];
33
34    public function __construct()
35    {
36        // TODO: Should this have the root package?
37        $this->types[self::NAMESPACE_SYMBOL]['\\'] = new NamespaceSymbol('\\', new File('', ''));
38    }
39
40    /**
41     * TODO: This should merge the symbols instead of overwriting them.
42     *
43     * @param DiscoveredSymbol $symbol
44     */
45    public function add(DiscoveredSymbol $symbol): void
46    {
47        switch (get_class($symbol)) {
48            case NamespaceSymbol::class:
49                $this->types[self::NAMESPACE_SYMBOL][$symbol->getOriginalSymbol()] = $symbol;
50                return;
51            case ConstantSymbol::class:
52                $this->types[self::CONST_SYMBOL][$symbol->getOriginalSymbol()] = $symbol;
53                return;
54            case ClassSymbol::class:
55                $this->types[self::CLASS_SYMBOL][$symbol->getOriginalSymbol()] = $symbol;
56                return;
57            case FunctionSymbol::class:
58                $this->types[self::FUNCTION_SYMBOL][$symbol->getOriginalSymbol()] = $symbol;
59                return;
60            case InterfaceSymbol::class:
61                $this->types[self::INTERFACE_SYMBOL][$symbol->getOriginalSymbol()] = $symbol;
62                return;
63            case TraitSymbol::class:
64                $this->types[self::TRAIT_SYMBOL][$symbol->getOriginalSymbol()] = $symbol;
65                return;
66            default:
67                throw new InvalidArgumentException('Unknown symbol type: ' . get_class($symbol));
68        }
69    }
70
71    /**
72     * @return DiscoveredSymbol[]
73     */
74    public function getSymbols(): array
75    {
76        return array_merge(
77            array_values($this->getNamespaces()),
78            array_values($this->getGlobalClasses()),
79            array_values($this->getConstants()),
80            array_values($this->getDiscoveredFunctions()),
81        );
82    }
83
84    /**
85     * @return array<string, ConstantSymbol>
86     */
87    public function getConstants(): array
88    {
89        return $this->types[self::CONST_SYMBOL];
90    }
91
92    /**
93     * @return array<string, NamespaceSymbol>
94     */
95    public function getNamespaces(): array
96    {
97        return $this->types[self::NAMESPACE_SYMBOL];
98    }
99
100    public function getNamespace(string $namespace): ?NamespaceSymbol
101    {
102        return $this->types[self::NAMESPACE_SYMBOL][$namespace] ?? null;
103    }
104
105    /**
106     * @return array<string, ClassSymbol>
107     */
108    public function getGlobalClasses(): array
109    {
110        return array_filter(
111            $this->types[self::CLASS_SYMBOL],
112            fn($classSymbol) => '\\' === $classSymbol->getNamespace()
113        );
114    }
115
116    /**
117     * @return array<string, ClassSymbol>
118     */
119    public function getGlobalClassChanges(): array
120    {
121        return array_filter(
122            $this->getGlobalClasses(),
123            fn($classSymbol) => $classSymbol->isDoRename()
124        );
125    }
126
127    /**
128     * @return array<string, ClassSymbol>
129     */
130    public function getAllClasses(): array
131    {
132        return $this->types[self::CLASS_SYMBOL];
133    }
134
135    /**
136     * TODO: Order by longest string first. (or instead, record classnames with their namespaces)
137     *
138     * @return array<string, NamespaceSymbol>
139     */
140    public function getDiscoveredNamespaces(?string $namespacePrefix = ''): array
141    {
142        $discoveredNamespaceReplacements = [];
143
144        // When running subsequent times, try to discover the original namespaces.
145        // This is naive: it will not work where namespace replacement patterns have been used.
146        foreach ($this->getNamespaces() as $namespaceSymbol) {
147            $discoveredNamespaceReplacements[ $namespaceSymbol->getOriginalSymbol() ] = $namespaceSymbol;
148        }
149
150        uksort($discoveredNamespaceReplacements, function ($a, $b) {
151            return strlen($a) <=> strlen($b);
152        });
153
154        unset($discoveredNamespaceReplacements['\\']);
155
156        return $discoveredNamespaceReplacements;
157    }
158
159    /**
160     * @return array<string, NamespaceSymbol>
161     */
162    public function getDiscoveredNamespaceChanges(?string $namespacePrefix = ''): array
163    {
164        return array_filter(
165            $this->getdiscoveredNamespaces($namespacePrefix),
166            fn($namespaceSymbol) => $namespaceSymbol->isDoRename()
167        );
168    }
169
170    /**
171     * @return string[]
172     */
173    public function getDiscoveredClasses(?string $classmapPrefix = ''): array
174    {
175        $discoveredClasses = $this->getGlobalClasses();
176
177        return array_filter(
178            array_keys($discoveredClasses),
179            function (string $replacement) use ($classmapPrefix) {
180                return empty($classmapPrefix) || ! str_starts_with($replacement, $classmapPrefix);
181            }
182        );
183    }
184
185    /**
186     * @return string[]
187     */
188    public function getDiscoveredConstants(?string $constantsPrefix = ''): array
189    {
190        return array_filter(
191            array_keys($this->getConstants()),
192            function (string $replacement) use ($constantsPrefix) {
193                return empty($constantsPrefix) || ! str_starts_with($replacement, $constantsPrefix);
194            }
195        );
196    }
197
198//  public function getDiscoveredConstantChanges(?string $functionsPrefix = ''): array
199//  {
200//      return array_filter(
201//          $this->getDiscoveredConstants($functionsPrefix),
202//          fn( $discoveredConstant ) => $discoveredConstant->isDoRename()
203//      );
204//  }
205
206    /**
207     * @return FunctionSymbol[]
208     */
209    public function getDiscoveredFunctions(): array
210    {
211        return $this->types[self::FUNCTION_SYMBOL];
212    }
213
214    /**
215     * @return FunctionSymbol[]
216     */
217    public function getDiscoveredFunctionChanges(): array
218    {
219        return array_filter(
220            $this->getDiscoveredFunctions(),
221            fn($discoveredFunction) => $discoveredFunction->isDoRename()
222        );
223    }
224
225    /**
226     * @return array<string,DiscoveredSymbol>
227     */
228    public function getAll(): array
229    {
230        return array_merge(...array_values($this->types));
231    }
232
233    /**
234     * @return array<string,TraitSymbol>
235     */
236    public function getDiscoveredTraits(): array
237    {
238        return (array) $this->types[self::TRAIT_SYMBOL];
239    }
240
241    /**
242     * @return array<string,InterfaceSymbol>
243     */
244    public function getDiscoveredInterfaces(): array
245    {
246        return (array) $this->types[self::INTERFACE_SYMBOL];
247    }
248
249    /**
250     * Get all discovered symbols that are classes, interfaces, or traits, i.e. only those that are autoloadable.
251     *
252     * @return array<DiscoveredSymbol>
253     */
254    public function getClassmapSymbols(): array
255    {
256        return array_merge(
257            $this->getGlobalClasses(),
258            $this->getDiscoveredInterfaces(),
259            $this->getDiscoveredTraits(),
260        );
261    }
262
263    public function getNamespaceSymbolByString(string $namespace): ?NamespaceSymbol
264    {
265        return $this->types[self::NAMESPACE_SYMBOL][$namespace] ?? null;
266    }
267}