Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 41 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
DependenciesEnumerator | |
0.00% |
0 / 41 |
|
0.00% |
0 / 5 |
272 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getAllDependencies | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
recursiveGetAllDependencies | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
72 | |||
getAllFilesAutoloaders | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
removeVirtualPackagesFilter | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | /** |
3 | * Build a list of ComposerPackage objects for all dependencies. |
4 | */ |
5 | |
6 | namespace BrianHenryIE\Strauss; |
7 | |
8 | use BrianHenryIE\Strauss\Composer\ComposerPackage; |
9 | use BrianHenryIE\Strauss\Composer\Extra\StraussConfig; |
10 | use Exception; |
11 | use League\Flysystem\Filesystem; |
12 | use League\Flysystem\Local\LocalFilesystemAdapter; |
13 | |
14 | class DependenciesEnumerator |
15 | { |
16 | /** |
17 | * The only path variable with a leading slash. |
18 | * All directories in project end with a slash. |
19 | * |
20 | * @var string |
21 | */ |
22 | protected string $workingDir; |
23 | |
24 | /** @var string */ |
25 | protected string $vendorDir; |
26 | |
27 | /** |
28 | * @var string[] |
29 | */ |
30 | protected array $requiredPackageNames; |
31 | |
32 | /** @var Filesystem */ |
33 | protected Filesystem $filesystem; |
34 | |
35 | /** @var string[] */ |
36 | protected array $virtualPackages = array( |
37 | 'php-http/client-implementation' |
38 | ); |
39 | |
40 | /** @var array<string, ComposerPackage> */ |
41 | protected array $flatDependencyTree = array(); |
42 | |
43 | /** |
44 | * Record the files autoloaders for later use in building our own autoloader. |
45 | * |
46 | * Package-name: [ dir1, file1, file2, ... ]. |
47 | * |
48 | * @var array<string, string[]> |
49 | */ |
50 | protected array $filesAutoloaders = []; |
51 | |
52 | /** |
53 | * @var array{}|array<string, array{files?:array<string>,classmap?:array<string>,"psr-4":array<string|array<string>>}> $overrideAutoload |
54 | */ |
55 | protected array $overrideAutoload = array(); |
56 | |
57 | /** |
58 | * Constructor. |
59 | * |
60 | * @param string $workingDir |
61 | * @param StraussConfig $config |
62 | */ |
63 | public function __construct( |
64 | string $workingDir, |
65 | StraussConfig $config |
66 | ) { |
67 | $this->workingDir = $workingDir; |
68 | $this->vendorDir = $config->getVendorDirectory(); |
69 | $this->overrideAutoload = $config->getOverrideAutoload(); |
70 | $this->requiredPackageNames = $config->getPackages(); |
71 | |
72 | $this->filesystem = new Filesystem(new LocalFilesystemAdapter($this->workingDir)); |
73 | } |
74 | |
75 | /** |
76 | * @return array<string, ComposerPackage> Packages indexed by package name. |
77 | * @throws Exception |
78 | */ |
79 | public function getAllDependencies(): array |
80 | { |
81 | $this->recursiveGetAllDependencies($this->requiredPackageNames); |
82 | return $this->flatDependencyTree; |
83 | } |
84 | |
85 | /** |
86 | * @param string[] $requiredPackageNames |
87 | */ |
88 | protected function recursiveGetAllDependencies(array $requiredPackageNames): void |
89 | { |
90 | $requiredPackageNames = array_filter($requiredPackageNames, array( $this, 'removeVirtualPackagesFilter' )); |
91 | |
92 | foreach ($requiredPackageNames as $requiredPackageName) { |
93 | // Avoid infinite recursion. |
94 | if (isset($this->flatDependencyTree[$requiredPackageName])) { |
95 | continue; |
96 | } |
97 | |
98 | $packageComposerFile = $this->workingDir . $this->vendorDir |
99 | . $requiredPackageName . DIRECTORY_SEPARATOR . 'composer.json'; |
100 | |
101 | $overrideAutoload = $this->overrideAutoload[ $requiredPackageName ] ?? null; |
102 | |
103 | if (file_exists($packageComposerFile)) { |
104 | $requiredComposerPackage = ComposerPackage::fromFile($packageComposerFile, $overrideAutoload); |
105 | } else { |
106 | $fileContents = file_get_contents($this->workingDir . 'composer.lock'); |
107 | if (false === $fileContents) { |
108 | throw new Exception('Failed to read contents of ' . $this->workingDir . 'composer.lock'); |
109 | } |
110 | $composerLock = json_decode($fileContents, true); |
111 | $requiredPackageComposerJson = null; |
112 | foreach ($composerLock['packages'] as $packageJson) { |
113 | if ($requiredPackageName === $packageJson['name']) { |
114 | $requiredPackageComposerJson = $packageJson; |
115 | break; |
116 | } |
117 | } |
118 | if (is_null($requiredPackageComposerJson)) { |
119 | // e.g. composer-plugin-api. |
120 | continue; |
121 | } |
122 | |
123 | $requiredComposerPackage = ComposerPackage::fromComposerJsonArray($requiredPackageComposerJson, $overrideAutoload); |
124 | } |
125 | |
126 | $this->flatDependencyTree[$requiredComposerPackage->getPackageName()] = $requiredComposerPackage; |
127 | $nextRequiredPackageNames = $requiredComposerPackage->getRequiresNames(); |
128 | |
129 | $this->recursiveGetAllDependencies($nextRequiredPackageNames); |
130 | } |
131 | } |
132 | |
133 | /** |
134 | * Get the recorded files autoloaders. |
135 | * |
136 | * @return array<string, array<string>> |
137 | */ |
138 | public function getAllFilesAutoloaders(): array |
139 | { |
140 | $filesAutoloaders = array(); |
141 | foreach ($this->flatDependencyTree as $packageName => $composerPackage) { |
142 | if (isset($composerPackage->getAutoload()['files'])) { |
143 | $filesAutoloaders[$packageName] = $composerPackage->getAutoload()['files']; |
144 | } |
145 | } |
146 | return $filesAutoloaders; |
147 | } |
148 | |
149 | /** |
150 | * Unset PHP, ext-*, ... |
151 | * |
152 | * @param string $requiredPackageName |
153 | */ |
154 | protected function removeVirtualPackagesFilter(string $requiredPackageName): bool |
155 | { |
156 | return ! ( |
157 | 0 === strpos($requiredPackageName, 'ext') |
158 | || 'php' === $requiredPackageName |
159 | || in_array($requiredPackageName, $this->virtualPackages) |
160 | ); |
161 | } |
162 | } |