Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
18.87% covered (danger)
18.87%
10 / 53
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
ChangeEnumerator
18.87% covered (danger)
18.87%
10 / 53
0.00% covered (danger)
0.00%
0 / 2
171.34
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 determineReplacements
20.00% covered (danger)
20.00%
10 / 50
0.00% covered (danger)
0.00%
0 / 1
147.07
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
8namespace BrianHenryIE\Strauss\Pipeline;
9
10use BrianHenryIE\Strauss\Config\ChangeEnumeratorConfigInterface;
11use BrianHenryIE\Strauss\Files\FileWithDependency;
12use BrianHenryIE\Strauss\Types\ClassSymbol;
13use BrianHenryIE\Strauss\Types\DiscoveredSymbols;
14use BrianHenryIE\Strauss\Types\FunctionSymbol;
15use BrianHenryIE\Strauss\Types\NamespaceSymbol;
16use League\Flysystem\FilesystemReader;
17use Psr\Log\LoggerAwareTrait;
18use Psr\Log\LoggerInterface;
19use Psr\Log\NullLogger;
20
21class ChangeEnumerator
22{
23    use LoggerAwareTrait;
24
25    protected ChangeEnumeratorConfigInterface $config;
26    protected FilesystemReader $filesystem;
27
28    public function __construct(
29        ChangeEnumeratorConfigInterface $config,
30        FilesystemReader $filesystem,
31        ?LoggerInterface $logger = null
32    ) {
33        $this->config = $config;
34        $this->filesystem = $filesystem;
35        $this->setLogger($logger ?? new NullLogger());
36    }
37
38    public function determineReplacements(DiscoveredSymbols $discoveredSymbols): void
39    {
40        foreach ($discoveredSymbols->getSymbols() as $symbol) {
41            // TODO: this is a bit of a mess. Should be reconsidered. Previously there was 1-1 relationship between symbols and files.
42            $symbolSourceFiles = $symbol->getSourceFiles();
43            $symbolSourceFile = $symbolSourceFiles[array_key_first($symbolSourceFiles)];
44            if ($symbolSourceFile instanceof FileWithDependency) {
45                if (in_array(
46                    $symbolSourceFile->getDependency()->getPackageName(),
47                    $this->config->getExcludePackagesFromPrefixing(),
48                    true
49                )) {
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, $symbolSourceFile->getSourcePath())) {
57                        continue 2;
58                    }
59                }
60            }
61            
62            if ($symbol instanceof NamespaceSymbol) {
63                $namespaceReplacementPatterns = $this->config->getNamespaceReplacementPatterns();
64
65                // `namespace_prefix` is just a shorthand for a replacement pattern that applies to all namespaces.
66
67                // TODO: Maybe need to preg_quote and add regex delimiters to the patterns here.
68                foreach ($namespaceReplacementPatterns as $pattern => $replacement) {
69                    if (substr($pattern, 0, 1) !== substr($pattern, -1, 1)) {
70                        unset($namespaceReplacementPatterns[$pattern]);
71                        $pattern = '~'. preg_quote($pattern, '~') . '~';
72                        $namespaceReplacementPatterns[$pattern] = $replacement;
73                    }
74                    unset($pattern, $replacement);
75                }
76
77                if (!is_null($this->config->getNamespacePrefix())) {
78                    $stripPattern = '~^('.preg_quote($this->config->getNamespacePrefix(), '~') .'\\\\*)*(.*)~';
79                    $strippedSymbol = preg_replace(
80                        $stripPattern,
81                        '$2',
82                        $symbol->getOriginalSymbol()
83                    );
84                    $namespaceReplacementPatterns[ "~(" . preg_quote($this->config->getNamespacePrefix(), '~') . '\\\\*)*' . preg_quote($strippedSymbol, '~') . '~' ]
85                        = "{$this->config->getNamespacePrefix()}\\{$strippedSymbol}";
86                    unset($stripPattern, $strippedSymbol);
87                }
88
89                // `namespace_replacement_patterns` should be ordered by priority.
90                foreach ($namespaceReplacementPatterns as $namespaceReplacementPattern => $replacement) {
91                    $prefixed = preg_replace(
92                        $namespaceReplacementPattern,
93                        $replacement,
94                        $symbol->getOriginalSymbol()
95                    );
96
97                    if ($prefixed !== $symbol->getOriginalSymbol()) {
98                        $symbol->setReplacement($prefixed);
99                        continue 2;
100                    }
101                }
102                $this->logger->debug("Namespace {$symbol->getOriginalSymbol()} not changed.");
103            }
104
105            if ($symbol instanceof ClassSymbol) {
106                // Don't double-prefix classnames.
107                if (str_starts_with($symbol->getOriginalSymbol(), $this->config->getClassmapPrefix())) {
108                    continue;
109                }
110
111                $symbol->setReplacement($this->config->getClassmapPrefix() . $symbol->getOriginalSymbol());
112            }
113
114            if ($symbol instanceof FunctionSymbol) {
115                // TODO: Add its own config option.
116                $functionPrefix = strtolower($this->config->getClassmapPrefix());
117                if (str_starts_with($symbol->getOriginalSymbol(), $functionPrefix)) {
118                    continue;
119                }
120
121                $symbol->setReplacement($functionPrefix . $symbol->getOriginalSymbol());
122            }
123        }
124    }
125}