Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
73.33% |
33 / 45 |
|
70.59% |
12 / 17 |
CRAP | |
0.00% |
0 / 1 |
ComposerPackage | |
73.33% |
33 / 45 |
|
70.59% |
12 / 17 |
38.82 | |
0.00% |
0 / 1 |
fromFile | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
fromComposerJsonArray | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
__construct | |
77.27% |
17 / 22 |
|
0.00% |
0 / 1 |
9.95 | |||
getPackageName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRelativePath | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getPackageAbsolutePath | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getAutoload | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getRequiresNames | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getLicense | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setCopy | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isCopy | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setDidCopy | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
didCopy | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setDelete | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isDoDelete | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setDidDelete | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
didDelete | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Object for getting typed values from composer.json. |
4 | * |
5 | * Use this for dependencies. Use ProjectComposerPackage for the primary composer.json. |
6 | */ |
7 | |
8 | namespace BrianHenryIE\Strauss\Composer; |
9 | |
10 | use Composer\Composer; |
11 | use Composer\Factory; |
12 | use Composer\IO\NullIO; |
13 | |
14 | /** |
15 | * @phpstan-type AutoloadKey array{files?:array<string>,classmap?:array<string>,"psr-4"?:array<string,string|array<string>>} |
16 | */ |
17 | class ComposerPackage |
18 | { |
19 | /** |
20 | * The composer.json file as parsed by Composer. |
21 | * |
22 | * @see \Composer\Factory::create |
23 | * |
24 | * @var \Composer\Composer |
25 | */ |
26 | protected \Composer\Composer $composer; |
27 | |
28 | /** |
29 | * The name of the project in composer.json. |
30 | * |
31 | * e.g. brianhenryie/my-project |
32 | * |
33 | * @var string |
34 | */ |
35 | protected string $packageName; |
36 | |
37 | /** |
38 | * Virtual packages and meta packages do not have a composer.json. |
39 | * Some packages are installed in a different directory name than their package name. |
40 | * |
41 | * @var ?string |
42 | */ |
43 | protected ?string $relativePath = null; |
44 | |
45 | /** |
46 | * Packages can be symlinked from outside the current project directory. |
47 | * |
48 | * @var ?string |
49 | */ |
50 | protected ?string $packageAbsolutePath = null; |
51 | |
52 | /** |
53 | * The discovered files, classmap, psr0 and psr4 autoload keys discovered (as parsed by Composer). |
54 | * |
55 | * @var AutoloadKey |
56 | */ |
57 | protected array $autoload = []; |
58 | |
59 | /** |
60 | * The names in the composer.json's "requires" field (without versions). |
61 | * |
62 | * @var string[] |
63 | */ |
64 | protected array $requiresNames = []; |
65 | |
66 | protected string $license; |
67 | |
68 | /** |
69 | * Should the package be copied to the vendor-prefixed/target directory? Default: true. |
70 | */ |
71 | protected bool $isCopy = true; |
72 | /** |
73 | * Has the package been copied to the vendor-prefixed/target directory? False until the package is copied. |
74 | */ |
75 | protected bool $didCopy = false; |
76 | /** |
77 | * Should the package be deleted from the vendor directory? Default: false. |
78 | */ |
79 | protected bool $isDelete = false; |
80 | /** |
81 | * Has the package been deleted from the vendor directory? False until the package is deleted. |
82 | */ |
83 | protected bool $didDelete = false; |
84 | |
85 | /** |
86 | * @param string $absolutePath The absolute path to composer.json |
87 | * @param ?array{files?:array<string>, classmap?:array<string>, psr?:array<string,string|array<string>>} $overrideAutoload Optional configuration to replace the package's own autoload definition with |
88 | * another which Strauss can use. |
89 | * @return ComposerPackage |
90 | */ |
91 | public static function fromFile(string $absolutePath, array $overrideAutoload = null): ComposerPackage |
92 | { |
93 | $composer = Factory::create(new NullIO(), $absolutePath, true); |
94 | |
95 | return new ComposerPackage($composer, $overrideAutoload); |
96 | } |
97 | |
98 | /** |
99 | * This is used for virtual packages, which don't have a composer.json. |
100 | * |
101 | * @param array{name?:string, license?:string, requires?:array<string,string>, autoload?:AutoloadKey} $jsonArray composer.json decoded to array |
102 | * @param ?AutoloadKey $overrideAutoload New autoload rules to replace the existing ones. |
103 | */ |
104 | public static function fromComposerJsonArray($jsonArray, array $overrideAutoload = null): ComposerPackage |
105 | { |
106 | $factory = new Factory(); |
107 | $io = new NullIO(); |
108 | $composer = $factory->createComposer($io, $jsonArray, true); |
109 | |
110 | return new ComposerPackage($composer, $overrideAutoload); |
111 | } |
112 | |
113 | /** |
114 | * Create a PHP object to represent a composer package. |
115 | * |
116 | * @param Composer $composer |
117 | * @param ?AutoloadKey $overrideAutoload Optional configuration to replace the package's own autoload definition with another which Strauss can use. |
118 | */ |
119 | public function __construct(Composer $composer, array $overrideAutoload = null) |
120 | { |
121 | $this->composer = $composer; |
122 | |
123 | $this->packageName = $composer->getPackage()->getName(); |
124 | |
125 | $composerJsonFileAbsolute = $composer->getConfig()->getConfigSource()->getName(); |
126 | |
127 | $absolutePath = realpath(dirname($composerJsonFileAbsolute)); |
128 | if (false !== $absolutePath) { |
129 | $this->packageAbsolutePath = $absolutePath . '/'; |
130 | } |
131 | |
132 | $vendorDirectory = $this->composer->getConfig()->get('vendor-dir'); |
133 | if (file_exists($vendorDirectory . '/' . $this->packageName)) { |
134 | $this->relativePath = $this->packageName; |
135 | $this->packageAbsolutePath = realpath($vendorDirectory . '/' . $this->packageName) . '/'; |
136 | // If the package is symlinked, the path will be outside the working directory. |
137 | } elseif (0 !== strpos($absolutePath, getcwd()) && 1 === preg_match('/.*[\/\\\\]([^\/\\\\]*[\/\\\\][^\/\\\\]*)[\/\\\\][^\/\\\\]*/', $vendorDirectory, $output_array)) { |
138 | $this->relativePath = $output_array[1]; |
139 | } elseif (1 === preg_match('/.*[\/\\\\]([^\/\\\\]+[\/\\\\][^\/\\\\]+)[\/\\\\]composer.json/', $composerJsonFileAbsolute, $output_array)) { |
140 | // Not every package gets installed to a folder matching its name (crewlabs/unsplash). |
141 | $this->relativePath = $output_array[1]; |
142 | } |
143 | |
144 | if (!is_null($overrideAutoload)) { |
145 | $composer->getPackage()->setAutoload($overrideAutoload); |
146 | } |
147 | |
148 | $this->autoload = $composer->getPackage()->getAutoload(); |
149 | |
150 | foreach ($composer->getPackage()->getRequires() as $_name => $packageLink) { |
151 | $this->requiresNames[] = $packageLink->getTarget(); |
152 | } |
153 | |
154 | // Try to get the license from the package's composer.json, assume proprietary (all rights reserved!). |
155 | $this->license = !empty($composer->getPackage()->getLicense()) |
156 | ? implode(',', $composer->getPackage()->getLicense()) |
157 | : 'proprietary?'; |
158 | } |
159 | |
160 | /** |
161 | * Composer package project name. |
162 | * |
163 | * vendor/project-name |
164 | * |
165 | * @return string |
166 | */ |
167 | public function getPackageName(): string |
168 | { |
169 | return $this->packageName; |
170 | } |
171 | |
172 | /** |
173 | * Is this relative to vendor? |
174 | */ |
175 | public function getRelativePath(): ?string |
176 | { |
177 | return $this->relativePath . '/'; |
178 | } |
179 | |
180 | public function getPackageAbsolutePath(): ?string |
181 | { |
182 | return $this->packageAbsolutePath; |
183 | } |
184 | |
185 | /** |
186 | * |
187 | * e.g. ['psr-4' => [ 'BrianHenryIE\Project' => 'src' ]] |
188 | * e.g. ['psr-4' => [ 'BrianHenryIE\Project' => ['src','lib] ]] |
189 | * e.g. ['classmap' => [ 'src', 'lib' ]] |
190 | * e.g. ['files' => [ 'lib', 'functions.php' ]] |
191 | * |
192 | * @return AutoloadKey |
193 | */ |
194 | public function getAutoload(): array |
195 | { |
196 | return $this->autoload; |
197 | } |
198 | |
199 | /** |
200 | * The names of the packages in the composer.json's "requires" field (without version). |
201 | * |
202 | * Excludes PHP, ext-*, since we won't be copying or prefixing them. |
203 | * |
204 | * @return string[] |
205 | */ |
206 | public function getRequiresNames(): array |
207 | { |
208 | // Unset PHP, ext-*. |
209 | $removePhpExt = function ($element) { |
210 | return !( 0 === strpos($element, 'ext') || 'php' === $element ); |
211 | }; |
212 | |
213 | return array_filter($this->requiresNames, $removePhpExt); |
214 | } |
215 | |
216 | public function getLicense():string |
217 | { |
218 | return $this->license; |
219 | } |
220 | |
221 | /** |
222 | * Should the file be copied? (defaults to yes) |
223 | */ |
224 | public function setCopy(bool $isCopy): void |
225 | { |
226 | $this->isCopy = $isCopy; |
227 | } |
228 | |
229 | /** |
230 | * Should the file be copied? (defaults to yes) |
231 | */ |
232 | public function isCopy(): bool |
233 | { |
234 | return $this->isCopy; |
235 | } |
236 | |
237 | /** |
238 | * Has the file been copied? (defaults to no) |
239 | */ |
240 | public function setDidCopy(bool $didCopy): void |
241 | { |
242 | $this->didCopy = $didCopy; |
243 | } |
244 | |
245 | /** |
246 | * Has the file been copied? (defaults to no) |
247 | */ |
248 | public function didCopy(): bool |
249 | { |
250 | return $this->didCopy; |
251 | } |
252 | |
253 | /** |
254 | * Should the file be deleted? (defaults to no) |
255 | */ |
256 | public function setDelete(bool $isDelete): void |
257 | { |
258 | $this->isDelete = $isDelete; |
259 | } |
260 | |
261 | /** |
262 | * Should the file be deleted? (defaults to no) |
263 | */ |
264 | public function isDoDelete(): bool |
265 | { |
266 | return $this->isDelete; |
267 | } |
268 | |
269 | /** |
270 | * Has the file been deleted? (defaults to no) |
271 | */ |
272 | public function setDidDelete(bool $didDelete): void |
273 | { |
274 | $this->didDelete = $didDelete; |
275 | } |
276 | |
277 | /** |
278 | * Has the file been deleted? (defaults to no) |
279 | */ |
280 | public function didDelete(): bool |
281 | { |
282 | return $this->didDelete; |
283 | } |
284 | } |