Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
5.88% covered (danger)
5.88%
4 / 68
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
FileEnumerator
5.88% covered (danger)
5.88%
4 / 68
0.00% covered (danger)
0.00%
0 / 4
388.66
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 compileFileListForDependencies
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
182
 addFileWithDependency
17.39% covered (danger)
17.39%
4 / 23
0.00% covered (danger)
0.00%
0 / 1
19.09
 compileFileListForPaths
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Build a list of files from the composer autoloaders.
4 *
5 * Also record the `files` autoloaders.
6 */
7
8namespace BrianHenryIE\Strauss\Pipeline;
9
10use BrianHenryIE\Strauss\Composer\ComposerPackage;
11use BrianHenryIE\Strauss\Config\FileEnumeratorConfig;
12use BrianHenryIE\Strauss\Files\DiscoveredFiles;
13use BrianHenryIE\Strauss\Files\File;
14use BrianHenryIE\Strauss\Files\FileWithDependency;
15use BrianHenryIE\Strauss\Helpers\FileSystem;
16use League\Flysystem\FilesystemException;
17use Psr\Log\LoggerAwareTrait;
18use Psr\Log\LoggerInterface;
19use Psr\Log\NullLogger;
20
21class FileEnumerator
22{
23    use LoggerAwareTrait;
24
25    protected FileEnumeratorConfig $config;
26
27    protected Filesystem $filesystem;
28
29    protected DiscoveredFiles $discoveredFiles;
30
31    /**
32     * Record the files autoloaders for later use in building our own autoloader.
33     *
34     * Package-name: [ dir1, file1, file2, ... ].
35     *
36     * @var array<string, string[]>
37     */
38    protected array $filesAutoloaders = [];
39
40    /**
41     * Copier constructor.
42     */
43    public function __construct(
44        FileEnumeratorConfig $config,
45        FileSystem $filesystem,
46        ?LoggerInterface $logger = null
47    ) {
48        $this->discoveredFiles = new DiscoveredFiles();
49
50        $this->config = $config;
51
52        $this->filesystem = $filesystem;
53
54        $this->logger = $logger ?? new NullLogger();
55    }
56
57    /**
58     * Read the autoload keys of the dependencies and generate a list of the files referenced.
59     *
60     * Includes all files in the directories and subdirectories mentioned in the autoloaders.
61     *
62     * @param ComposerPackage[] $dependencies
63     *
64     * @throws FilesystemException
65     */
66    public function compileFileListForDependencies(array $dependencies): DiscoveredFiles
67    {
68        foreach ($dependencies as $dependency) {
69            $this->logger->info("Scanning for files for package {packageName}", ['packageName' => $dependency->getPackageName()]);
70
71            /**
72             * Where $dependency->autoload is ~
73             *
74             * [ "psr-4" => [ "BrianHenryIE\Strauss" => "src" ] ]
75             * Exclude "exclude-from-classmap"
76             * @see https://getcomposer.org/doc/04-schema.md#exclude-files-from-classmaps
77             */
78            $autoloaders = array_filter($dependency->getAutoload(), function ($type) {
79                return 'exclude-from-classmap' !== $type;
80            }, ARRAY_FILTER_USE_KEY);
81
82            $dependencyPackageAbsolutePath = $dependency->getPackageAbsolutePath();
83
84            foreach ($autoloaders as $type => $value) {
85                // Might have to switch/case here.
86
87                if ('files' === $type) {
88                    // TODO: This is not in use.
89                    $this->filesAutoloaders[$dependency->getRelativePath()] = $value;
90                }
91
92                foreach ($value as $namespace => $namespace_relative_paths) {
93                    if (!empty($namespace) && in_array($namespace, $this->config->getExcludeNamespacesFromCopy())) {
94                        $this->logger->info("Excluding namespace " . $namespace);
95                        continue;
96                    }
97
98                    $namespace_relative_paths = (array) $namespace_relative_paths;
99//                    if (! is_array($namespace_relative_paths)) {
100//                        $namespace_relative_paths = array( $namespace_relative_paths );
101//                    }
102
103                    foreach ($namespace_relative_paths as $namespaceRelativePath) {
104                        $sourceAbsoluteDirPath = in_array($namespaceRelativePath, ['.','./'])
105                            ? $dependencyPackageAbsolutePath
106                            : $dependencyPackageAbsolutePath . $namespaceRelativePath;
107
108                        if ($this->filesystem->directoryExists($sourceAbsoluteDirPath)) {
109                            $fileList = $this->filesystem->listContents($sourceAbsoluteDirPath, true);
110                            $actualFileList = $fileList->toArray();
111
112                            foreach ($actualFileList as $foundFile) {
113                                $sourceAbsoluteFilepath = '/'. $foundFile->path();
114                                // No need to record the directory itself.
115                                if (!$this->filesystem->fileExists($sourceAbsoluteFilepath)
116                                    ||
117                                    $this->filesystem->directoryExists($sourceAbsoluteFilepath)
118                                ) {
119                                    continue;
120                                }
121
122                                $this->addFileWithDependency(
123                                    $dependency,
124                                    $sourceAbsoluteFilepath,
125                                    $type
126                                );
127                            }
128                        } else {
129                            $this->addFileWithDependency($dependency, $sourceAbsoluteDirPath, $type);
130                        }
131                    }
132                }
133            }
134        }
135
136        $this->discoveredFiles->sort();
137        return $this->discoveredFiles;
138    }
139
140    /**
141     * @param ComposerPackage $dependency
142     * @param string $sourceAbsoluteFilepath
143     * @param string $autoloaderType
144     *
145     * @throws FilesystemException
146     * @uses DiscoveredFiles::add
147     *
148     */
149    protected function addFileWithDependency(
150        ComposerPackage $dependency,
151        string $sourceAbsoluteFilepath,
152        string $autoloaderType
153    ): void {
154
155        // Do not add a file if its source does not exist!
156        if (!$this->filesystem->fileExists($sourceAbsoluteFilepath)
157            && !$this->filesystem->directoryExists($sourceAbsoluteFilepath)) {
158            $this->logger->warning("File does not exist: {sourcePath}", ['sourcePath' => $sourceAbsoluteFilepath]);
159            return;
160        }
161
162        $vendorRelativePath = substr(
163            $sourceAbsoluteFilepath,
164            strpos($sourceAbsoluteFilepath, $dependency->getRelativePath() ?: 0)
165        );
166
167        if ($vendorRelativePath === $sourceAbsoluteFilepath) {
168            $vendorRelativePath = $dependency->getRelativePath() . str_replace($dependency->getPackageAbsolutePath(), '', $sourceAbsoluteFilepath);
169        }
170
171        $isOutsideProjectDir = 0 !== strpos($sourceAbsoluteFilepath, $this->config->getVendorDirectory());
172
173        /** @var FileWithDependency $f */
174        $f = $this->discoveredFiles->getFile($sourceAbsoluteFilepath)
175            ?? new FileWithDependency($dependency, $vendorRelativePath, $sourceAbsoluteFilepath);
176
177        $f->setAbsoluteTargetPath($this->config->getVendorDirectory() . $vendorRelativePath);
178
179        $f->addAutoloader($autoloaderType);
180        $f->setDoDelete($isOutsideProjectDir);
181
182        $this->discoveredFiles->add($f);
183
184        $relativeFilePath =
185            $this->filesystem->getRelativePath(
186                dirname($this->config->getVendorDirectory()),
187                $f->getAbsoluteTargetPath()
188            );
189        $this->logger->info("Found file " . $relativeFilePath);
190    }
191
192    /**
193     * @param string[] $paths
194     */
195    public function compileFileListForPaths(array $paths): DiscoveredFiles
196    {
197        $absoluteFilePaths = $this->filesystem->findAllFilesAbsolutePaths($paths);
198
199        foreach ($absoluteFilePaths as $sourceAbsolutePath) {
200            $f = $this->discoveredFiles->getFile($sourceAbsolutePath)
201                 ?? new File($sourceAbsolutePath);
202
203            $this->discoveredFiles->add($f);
204        }
205
206        $this->discoveredFiles->sort();
207        return $this->discoveredFiles;
208    }
209}