Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
AutoloadedFilesEnumerator
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 3
342
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
 scanForAutoloadedFiles
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 scanPackage
0.00% covered (danger)
0.00%
0 / 84
0.00% covered (danger)
0.00%
0 / 1
240
1<?php
2/**
3 * Use each package's autoload key to determine which files in the package are to be prefixed, apply exclusion rules.
4 */
5
6namespace BrianHenryIE\Strauss\Pipeline;
7
8use BrianHenryIE\Strauss\Composer\ComposerPackage;
9use BrianHenryIE\Strauss\Config\AutoloadFilesEnumeratorConfigInterface;
10use BrianHenryIE\Strauss\Helpers\FileSystem;
11use Composer\ClassMapGenerator\ClassMapGenerator;
12use League\Flysystem\FilesystemException;
13use Psr\Log\LoggerAwareTrait;
14use Psr\Log\LoggerInterface;
15
16class AutoloadedFilesEnumerator
17{
18    use LoggerAwareTrait;
19
20    protected AutoloadFilesEnumeratorConfigInterface $config;
21    protected FileSystem $filesystem;
22
23    public function __construct(
24        AutoloadFilesEnumeratorConfigInterface $config,
25        FileSystem $filesystem,
26        LoggerInterface $logger
27    ) {
28        $this->config = $config;
29        $this->filesystem = $filesystem;
30        $this->setLogger($logger);
31    }
32
33    /**
34     * @param ComposerPackage[] $dependencies
35     */
36    public function scanForAutoloadedFiles(array $dependencies): void
37    {
38        foreach ($dependencies as $dependency) {
39            $this->scanPackage($dependency);
40        }
41    }
42
43    /**
44     * Read the autoload keys of the dependencies and marks the appropriate files to be prefixed
45     * @throws FilesystemException
46     */
47    protected function scanPackage(ComposerPackage $dependency): void
48    {
49        $this->logger->debug('AutoloadFileEnumerator::scanPackage() {packageName}', [ 'packageName' => $dependency->getPackageName() ]);
50
51        $this->logger->info("Scanning for autoloaded files in package {packageName}", ['packageName' => $dependency->getPackageName()]);
52
53        $dependencyAutoloadKey = $dependency->getAutoload();
54        $excludeFromClassmap = isset($dependencyAutoloadKey['exclude_from_classmap']) ? $dependencyAutoloadKey['exclude_from_classmap'] : [];
55
56        /**
57         * Where $dependency->autoload is ~
58         *
59         * [ "psr-4" => [ "BrianHenryIE\Strauss" => "src" ] ]
60         * Exclude "exclude-from-classmap"
61         * @see https://getcomposer.org/doc/04-schema.md#exclude-files-from-classmaps
62         */
63        $autoloaders = array_filter($dependencyAutoloadKey, function ($type) {
64            return 'exclude-from-classmap' !== $type;
65        }, ARRAY_FILTER_USE_KEY);
66
67        $dependencyPackageAbsolutePath = $dependency->getPackageAbsolutePath();
68
69        $classMapGenerator = new ClassMapGenerator();
70
71        $excluded = null;
72        $autoloadType = 'classmap';
73
74        $excludedDirs = array_map(
75            fn(string $path) => $dependencyPackageAbsolutePath . '/' . $path,
76            $excludeFromClassmap
77        );
78
79        foreach ($autoloaders as $type => $value) {
80            // Might have to switch/case here.
81
82            /** @var ?string $namespace */
83            $namespace = null;
84
85            switch ($type) {
86                case 'files':
87                    $filesAbsolutePaths = array_map(
88                        fn(string $path) => $dependencyPackageAbsolutePath . '/' . $path,
89                        (array)$value
90                    );
91                    $filesAutoloaderFiles = $this->filesystem->findAllFilesAbsolutePaths($filesAbsolutePaths, true);
92                    foreach ($filesAutoloaderFiles as $filePackageAbsolutePath) {
93                        $filePackageRelativePath = $this->filesystem->getRelativePath(
94                            $dependencyPackageAbsolutePath,
95                            $filePackageAbsolutePath
96                        );
97                        $file = $dependency->getFile($filePackageRelativePath);
98                        if (!$file) {
99                            $this->logger->warning("Expected discovered file at {relativePath} not found in package {packageName}", [
100                                'relativePath' => $filePackageRelativePath,
101                                'packageName' => $dependency->getPackageName(),
102                            ]);
103                        } else {
104                            $file->setIsAutoloaded(true);
105                            $file->setDoPrefix(true);
106                        }
107                    }
108                    break;
109                case 'classmap':
110                    $autoloadKeyPaths = array_map(
111                        fn(string $path) =>
112                            '/'. $this->filesystem->normalize(
113                                $dependencyPackageAbsolutePath . $path
114                            ),
115                        (array)$value
116                    );
117                    foreach ($autoloadKeyPaths as $autoloadKeyPath) {
118                        $classMapGenerator->scanPaths(
119                            $autoloadKeyPath,
120                            $excluded,
121                            $autoloadType,
122                            $namespace,
123                            $excludedDirs,
124                        );
125                    }
126
127                    break;
128                case 'psr-0':
129                case 'psr-4':
130                    foreach ((array)$value as $namespace => $namespaceRelativePaths) {
131                        $psrPaths = array_map(
132                            fn(string $path) => $dependencyPackageAbsolutePath . '/' . $path,
133                            (array)$namespaceRelativePaths
134                        );
135
136                        foreach ($psrPaths as $autoloadKeyPath) {
137                            $classMapGenerator->scanPaths(
138                                $autoloadKeyPath,
139                                $excluded,
140                                $autoloadType,
141                                $namespace,
142                                $excludedDirs,
143                            );
144                        }
145                    }
146                    break;
147                default:
148                    $this->logger->info('Unexpected autoloader type');
149                    // TODO: include everything;
150                    break;
151            }
152        }
153
154        $classMap = $classMapGenerator->getClassMap();
155        $classMapPaths = $classMap->getMap();
156        foreach ($classMapPaths as $fileAbsolutePath) {
157            $relativePath = $this->filesystem->getRelativePath($dependency->getPackageAbsolutePath(), $fileAbsolutePath);
158            $file = $dependency->getFile($relativePath);
159            if (!$file) {
160                $this->logger->warning("Expected discovered file at {relativePath} not found in package {packageName}", [
161                    'relativePath' => $relativePath,
162                    'packageName' => $dependency->getPackageName(),
163                ]);
164            } else {
165                $file->setIsAutoloaded(true);
166                $file->setDoPrefix(true);
167            }
168        }
169    }
170}