Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
17.86% |
15 / 84 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
ChangeEnumerator | |
17.86% |
15 / 84 |
|
0.00% |
0 / 3 |
400.68 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
markFilesForExclusion | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
42 | |||
determineReplacements | |
21.74% |
15 / 69 |
|
0.00% |
0 / 1 |
192.04 |
1 | <?php |
2 | /** |
3 | * Determine the replacements to be made to the discovered symbols. |
4 | * |
5 | * Typically, this will just be a prefix, but more complex rules allow for replacements specific to individual symbols/namespaces. |
6 | */ |
7 | |
8 | namespace BrianHenryIE\Strauss\Pipeline; |
9 | |
10 | use BrianHenryIE\Strauss\Config\ChangeEnumeratorConfigInterface; |
11 | use BrianHenryIE\Strauss\Files\DiscoveredFiles; |
12 | use BrianHenryIE\Strauss\Files\FileWithDependency; |
13 | use BrianHenryIE\Strauss\Types\ClassSymbol; |
14 | use BrianHenryIE\Strauss\Types\DiscoveredSymbols; |
15 | use BrianHenryIE\Strauss\Types\FunctionSymbol; |
16 | use BrianHenryIE\Strauss\Types\NamespaceSymbol; |
17 | use League\Flysystem\FilesystemReader; |
18 | use Psr\Log\LoggerAwareTrait; |
19 | use Psr\Log\LoggerInterface; |
20 | use Psr\Log\NullLogger; |
21 | |
22 | class ChangeEnumerator |
23 | { |
24 | use LoggerAwareTrait; |
25 | |
26 | protected ChangeEnumeratorConfigInterface $config; |
27 | protected FilesystemReader $filesystem; |
28 | |
29 | public function __construct( |
30 | ChangeEnumeratorConfigInterface $config, |
31 | FilesystemReader $filesystem, |
32 | ?LoggerInterface $logger = null |
33 | ) { |
34 | $this->config = $config; |
35 | $this->filesystem = $filesystem; |
36 | $this->setLogger($logger ?? new NullLogger()); |
37 | } |
38 | |
39 | public function markFilesForExclusion(DiscoveredFiles $files) |
40 | { |
41 | |
42 | foreach ($files->getFiles() as $file) { |
43 | if ($file instanceof FileWithDependency) { |
44 | if (in_array( |
45 | $file->getDependency()->getPackageName(), |
46 | $this->config->getExcludePackagesFromPrefixing(), |
47 | true |
48 | )) { |
49 | $file->setDoPrefix(false); |
50 | continue; |
51 | } |
52 | |
53 | foreach ($this->config->getExcludeFilePatternsFromPrefixing() as $excludeFilePattern) { |
54 | // TODO: This source relative path should be from the vendor dir. |
55 | // TODO: Should the target path be used here? |
56 | if (1 === preg_match($excludeFilePattern, $file->getVendorRelativePath())) { |
57 | $file->setDoPrefix(false); |
58 | } |
59 | } |
60 | } |
61 | } |
62 | } |
63 | |
64 | public function determineReplacements(DiscoveredSymbols $discoveredSymbols): void |
65 | { |
66 | $discoveredNamespaces = $discoveredSymbols->getDiscoveredNamespaces(); |
67 | |
68 | foreach ($discoveredNamespaces as $symbol) { |
69 | if ($symbol instanceof NamespaceSymbol) { |
70 | $namespaceReplacementPatterns = $this->config->getNamespaceReplacementPatterns(); |
71 | |
72 | // `namespace_prefix` is just a shorthand for a replacement pattern that applies to all namespaces. |
73 | |
74 | // TODO: Maybe need to preg_quote and add regex delimiters to the patterns here. |
75 | foreach ($namespaceReplacementPatterns as $pattern => $replacement) { |
76 | if (substr($pattern, 0, 1) !== substr($pattern, - 1, 1)) { |
77 | unset($namespaceReplacementPatterns[ $pattern ]); |
78 | $pattern = '~' . preg_quote($pattern, '~') . '~'; |
79 | $namespaceReplacementPatterns[ $pattern ] = $replacement; |
80 | } |
81 | unset($pattern, $replacement); |
82 | } |
83 | |
84 | if (! is_null($this->config->getNamespacePrefix())) { |
85 | $stripPattern = '~^(' . preg_quote($this->config->getNamespacePrefix(), '~') . '\\\\*)*(.*)~'; |
86 | $strippedSymbol = preg_replace( |
87 | $stripPattern, |
88 | '$2', |
89 | $symbol->getOriginalSymbol() |
90 | ); |
91 | $namespaceReplacementPatterns[ "~(" . preg_quote($this->config->getNamespacePrefix(), '~') . '\\\\*)*' . preg_quote($strippedSymbol, '~') . '~' ] |
92 | = "{$this->config->getNamespacePrefix()}\\{$strippedSymbol}"; |
93 | unset($stripPattern, $strippedSymbol); |
94 | } |
95 | |
96 | // `namespace_replacement_patterns` should be ordered by priority. |
97 | foreach ($namespaceReplacementPatterns as $namespaceReplacementPattern => $replacement) { |
98 | $prefixed = preg_replace( |
99 | $namespaceReplacementPattern, |
100 | $replacement, |
101 | $symbol->getOriginalSymbol() |
102 | ); |
103 | |
104 | if ($prefixed !== $symbol->getOriginalSymbol()) { |
105 | $symbol->setReplacement($prefixed); |
106 | continue 2; |
107 | } |
108 | } |
109 | $this->logger->debug("Namespace {$symbol->getOriginalSymbol()} not changed."); |
110 | } |
111 | } |
112 | |
113 | $classmapPrefix = $this->config->getClassmapPrefix(); |
114 | |
115 | |
116 | $classesTraitsInterfaces = array_merge( |
117 | $discoveredSymbols->getDiscoveredTraits(), |
118 | $discoveredSymbols->getDiscoveredInterfaces(), |
119 | $discoveredSymbols->getAllClasses() |
120 | ); |
121 | |
122 | foreach ($classesTraitsInterfaces as $theclass) { |
123 | if (str_starts_with($theclass->getOriginalSymbol(), $classmapPrefix)) { |
124 | // Already prefixed / second scan. |
125 | continue; |
126 | } |
127 | |
128 | if ($theclass->getNamespace() === '\\') { |
129 | if ($symbol instanceof ClassSymbol) { |
130 | // Don't double-prefix classnames. |
131 | if (str_starts_with($symbol->getOriginalSymbol(), $this->config->getClassmapPrefix())) { |
132 | continue; |
133 | } |
134 | |
135 | $symbol->setReplacement($this->config->getClassmapPrefix() . $symbol->getOriginalSymbol()); |
136 | } |
137 | } |
138 | |
139 | if ($theclass->getNamespace() !== '\\') { |
140 | $newNamespace = $discoveredNamespaces[$theclass->getNamespace()]; |
141 | if ($newNamespace) { |
142 | // TODO: This should be in ChangeEnumerator. |
143 | // `str_replace` was replacing multiple. This stops after one. Maybe should be tied to start of string. |
144 | $determineReplacement = function ($originalNamespace, $newNamespace, $fqdnClassname) { |
145 | $search = '/'.preg_quote($originalNamespace, '/').'/'; |
146 | return preg_replace($search, $newNamespace, $fqdnClassname, 1); |
147 | }; |
148 | $theclass->setReplacement( |
149 | $determineReplacement( |
150 | $newNamespace->getOriginalSymbol(), |
151 | $newNamespace->getReplacement(), |
152 | $theclass->getOriginalSymbol() |
153 | ) |
154 | ); |
155 | unset($newNamespace); |
156 | } |
157 | continue; |
158 | } |
159 | $theclass->setReplacement($classmapPrefix . $theclass->getOriginalSymbol()); |
160 | } |
161 | |
162 | $functionsSymbols = $discoveredSymbols->getDiscoveredFunctions(); |
163 | |
164 | foreach ($functionsSymbols as $symbol) { |
165 | // Don't prefix functions in a namespace – that will be addressed by the namespace prefix. |
166 | if ($symbol->getNamespace() !== '\\') { |
167 | continue; |
168 | } |
169 | $functionPrefix = $this->config->getFunctionsPrefix(); |
170 | if (empty($functionPrefix) || str_starts_with($symbol->getOriginalSymbol(), $functionPrefix)) { |
171 | continue; |
172 | } |
173 | |
174 | $symbol->setReplacement($functionPrefix . $symbol->getOriginalSymbol()); |
175 | } |
176 | } |
177 | } |