Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.56% covered (success)
95.56%
43 / 45
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
MarkFilesExcludedFromChanges
95.56% covered (success)
95.56%
43 / 45
83.33% covered (warning)
83.33%
5 / 6
16
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 scanDiscoveredFiles
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 fileMatchesFilePattern
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 preparePattern
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 fileMatchesNamespace
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 fileMatchesPackage
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * Given the `exclude_files_from_update` config options:
4 * - `file_patterns`
5 * - `packages`
6 * - `namespaces`
7 * mark files matching those rules to be excluded from any changes.
8 *
9 * @package brianhenryie/strauss
10 */
11
12namespace BrianHenryIE\Strauss\Pipeline;
13
14use BrianHenryIE\Strauss\Config\MarkFilesExcludedFromChangesConfigInterface;
15use BrianHenryIE\Strauss\Files\DiscoveredFiles;
16use BrianHenryIE\Strauss\Files\File;
17use BrianHenryIE\Strauss\Files\FileBase;
18use BrianHenryIE\Strauss\Files\FileWithDependency;
19use Psr\Log\LoggerInterface;
20
21class MarkFilesExcludedFromChanges
22{
23    protected MarkFilesExcludedFromChangesConfigInterface $config;
24    protected LoggerInterface $logger;
25
26    public function __construct(
27        MarkFilesExcludedFromChangesConfigInterface $config,
28        LoggerInterface $logger
29    ) {
30        $this->config = $config;
31        $this->logger = $logger;
32    }
33
34    public function scanDiscoveredFiles(DiscoveredFiles $discoveredFiles): void
35    {
36        foreach ($discoveredFiles->getFiles() as $file) {
37            if ($this->fileMatchesFilePattern($file)
38                || $this->fileMatchesNamespace($file)
39                || $this->fileMatchesPackage($file)
40            ) {
41                $file->setDoUpdate(false);
42            }
43        }
44    }
45
46    protected function fileMatchesFilePattern(FileBase $file): bool
47    {
48        $vendorRelativePath = $file->getVendorRelativePath();
49        foreach ($this->config->getExcludeFilesFromUpdateFilePatterns() as $excludeFilePattern) {
50            if (1 === preg_match($this->preparePattern($excludeFilePattern), $vendorRelativePath)) {
51                $this->logger->info('Exclude from changes: {filePath} matches pattern {pattern}', [
52                    'filePath' => $file->getVendorRelativePath(),
53                    'pattern' => $excludeFilePattern
54                ]);
55
56                return true;
57            }
58        }
59        return false;
60    }
61
62    /**
63     * TODO: This should be moved into the class parsing the config.
64     */
65    private function preparePattern(string $pattern): string
66    {
67        $delimiter = '#';
68
69        if (substr($pattern, 0, 1) !== substr($pattern, - 1, 1)) {
70            $pattern = $delimiter . $pattern . $delimiter;
71        }
72
73        return $pattern;
74    }
75    /**
76     * If any excluded namespaces are in the file, do not make changes to the file.
77     *
78     * A PHP file can contain multiple namespaces, but that is generally discouraged.
79     */
80    protected function fileMatchesNamespace(FileBase $file): bool
81    {
82        $matchingNamespaces = array_intersect(
83            $file->getDiscoveredSymbols()->getNamespaces()->toArray(),
84            $this->config->getExcludeFileFromUpdateNamespaces()
85        );
86
87        if (empty($matchingNamespaces)) {
88            return false;
89        }
90
91        $this->logger->info('Exclude from changes: {filePath} matches namespace {namespaces}', [
92            'filePath' => $file->getTargetAbsolutePath(),
93            'namespaces' => implode(', ', $matchingNamespaces)
94        ]);
95
96        return true;
97    }
98
99    protected function fileMatchesPackage(FileBase $file): bool
100    {
101        if (!($file instanceof FileWithDependency)) {
102            return false;
103        }
104
105        $excludePackages = $this->config->getExcludeFilesFromUpdatePackages();
106        $matchingKey = array_search(
107            $file->getDependency()->getPackageName(),
108            $excludePackages
109        );
110
111        if ($matchingKey === false) {
112            return false;
113        }
114
115        $this->logger->info('Exclude from changes: {filePath} matches package {package}', [
116            'filePath' => $file->getTargetAbsolutePath(),
117            'package' => $excludePackages[$matchingKey]
118        ]);
119
120
121        return true;
122    }
123}