Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
18.52% covered (danger)
18.52%
10 / 54
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
ChangeEnumerator
18.52% covered (danger)
18.52%
10 / 54
0.00% covered (danger)
0.00%
0 / 2
173.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
19.61% covered (danger)
19.61%
10 / 51
0.00% covered (danger)
0.00%
0 / 1
149.01
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
41        foreach ($discoveredSymbols->getSymbols() as $symbol) {
42            // TODO: this is a bit of a mess. Should be reconsidered. Previously there was 1-1 relationship between symbols and files.
43            $symbolSourceFiles = $symbol->getSourceFiles();
44            $symbolSourceFile = $symbolSourceFiles[array_key_first($symbolSourceFiles)];
45            if ($symbolSourceFile instanceof FileWithDependency) {
46                if (in_array(
47                    $symbolSourceFile->getDependency()->getPackageName(),
48                    $this->config->getExcludePackagesFromPrefixing(),
49                    true
50                )) {
51                    $symbolSourceFile->setDoPrefix(false);
52                    continue;
53                }
54
55                foreach ($this->config->getExcludeFilePatternsFromPrefixing() as $excludeFilePattern) {
56                    // TODO: This source relative path should be from the vendor dir.
57                    // TODO: Should the target path be used here?
58                    if (1 === preg_match($excludeFilePattern, $symbolSourceFile->getSourcePath())) {
59                        continue 2;
60                    }
61                }
62            }
63            
64            if ($symbol instanceof NamespaceSymbol) {
65                $namespaceReplacementPatterns = $this->config->getNamespaceReplacementPatterns();
66
67                // `namespace_prefix` is just a shorthand for a replacement pattern that applies to all namespaces.
68
69                // TODO: Maybe need to preg_quote and add regex delimiters to the patterns here.
70                foreach ($namespaceReplacementPatterns as $pattern => $replacement) {
71                    if (substr($pattern, 0, 1) !== substr($pattern, -1, 1)) {
72                        unset($namespaceReplacementPatterns[$pattern]);
73                        $pattern = '~'. preg_quote($pattern, '~') . '~';
74                        $namespaceReplacementPatterns[$pattern] = $replacement;
75                    }
76                    unset($pattern, $replacement);
77                }
78
79                if (!is_null($this->config->getNamespacePrefix())) {
80                    $stripPattern = '~^('.preg_quote($this->config->getNamespacePrefix(), '~') .'\\\\*)*(.*)~';
81                    $strippedSymbol = preg_replace(
82                        $stripPattern,
83                        '$2',
84                        $symbol->getOriginalSymbol()
85                    );
86                    $namespaceReplacementPatterns[ "~(" . preg_quote($this->config->getNamespacePrefix(), '~') . '\\\\*)*' . preg_quote($strippedSymbol, '~') . '~' ]
87                        = "{$this->config->getNamespacePrefix()}\\{$strippedSymbol}";
88                    unset($stripPattern, $strippedSymbol);
89                }
90
91                // `namespace_replacement_patterns` should be ordered by priority.
92                foreach ($namespaceReplacementPatterns as $namespaceReplacementPattern => $replacement) {
93                    $prefixed = preg_replace(
94                        $namespaceReplacementPattern,
95                        $replacement,
96                        $symbol->getOriginalSymbol()
97                    );
98
99                    if ($prefixed !== $symbol->getOriginalSymbol()) {
100                        $symbol->setReplacement($prefixed);
101                        continue 2;
102                    }
103                }
104                $this->logger->debug("Namespace {$symbol->getOriginalSymbol()} not changed.");
105            }
106
107            if ($symbol instanceof ClassSymbol) {
108                // Don't double-prefix classnames.
109                if (str_starts_with($symbol->getOriginalSymbol(), $this->config->getClassmapPrefix())) {
110                    continue;
111                }
112
113                $symbol->setReplacement($this->config->getClassmapPrefix() . $symbol->getOriginalSymbol());
114            }
115
116            if ($symbol instanceof FunctionSymbol) {
117                // TODO: Add its own config option.
118                $functionPrefix = strtolower($this->config->getClassmapPrefix());
119                if (str_starts_with($symbol->getOriginalSymbol(), $functionPrefix)) {
120                    continue;
121                }
122
123                $symbol->setReplacement($functionPrefix . $symbol->getOriginalSymbol());
124            }
125        }
126    }
127}