Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
66.67% |
26 / 39 |
|
33.33% |
3 / 9 |
CRAP | |
0.00% |
0 / 1 |
ComposerPackage | |
66.67% |
26 / 39 |
|
33.33% |
3 / 9 |
32.37 | |
0.00% |
0 / 1 |
fromFile | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
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 |
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 | * @param string $absolutePath The absolute path to the vendor folder with the composer.json "name", |
70 | * i.e. the domain/package definition, which is the vendor subdir from where the package's |
71 | * composer.json should be read. |
72 | * @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 |
73 | * another which Strauss can use. |
74 | * @return ComposerPackage |
75 | */ |
76 | public static function fromFile(string $absolutePath, array $overrideAutoload = null): ComposerPackage |
77 | { |
78 | if (is_dir($absolutePath)) { |
79 | $absolutePath = rtrim($absolutePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'composer.json'; |
80 | } |
81 | |
82 | $composer = Factory::create(new NullIO(), $absolutePath, true); |
83 | |
84 | return new ComposerPackage($composer, $overrideAutoload); |
85 | } |
86 | |
87 | /** |
88 | * This is used for virtual packages, which don't have a composer.json. |
89 | * |
90 | * @param array{name?:string, license?:string, requires?:array<string,string>, autoload?:AutoloadKey} $jsonArray composer.json decoded to array |
91 | * @param ?AutoloadKey $overrideAutoload New autoload rules to replace the existing ones. |
92 | */ |
93 | public static function fromComposerJsonArray($jsonArray, array $overrideAutoload = null): ComposerPackage |
94 | { |
95 | $factory = new Factory(); |
96 | $io = new NullIO(); |
97 | $composer = $factory->createComposer($io, $jsonArray, true); |
98 | |
99 | return new ComposerPackage($composer, $overrideAutoload); |
100 | } |
101 | |
102 | /** |
103 | * Create a PHP object to represent a composer package. |
104 | * |
105 | * @param Composer $composer |
106 | * @param ?AutoloadKey $overrideAutoload Optional configuration to replace the package's own autoload definition with another which Strauss can use. |
107 | */ |
108 | public function __construct(Composer $composer, array $overrideAutoload = null) |
109 | { |
110 | $this->composer = $composer; |
111 | |
112 | $this->packageName = $composer->getPackage()->getName(); |
113 | |
114 | $composerJsonFileAbsolute = $composer->getConfig()->getConfigSource()->getName(); |
115 | |
116 | $absolutePath = realpath(dirname($composerJsonFileAbsolute)); |
117 | if (false !== $absolutePath) { |
118 | $this->packageAbsolutePath = $absolutePath . DIRECTORY_SEPARATOR; |
119 | } |
120 | |
121 | $vendorDirectory = $this->composer->getConfig()->get('vendor-dir'); |
122 | if (file_exists($vendorDirectory . DIRECTORY_SEPARATOR . $this->packageName)) { |
123 | $this->relativePath = $this->packageName; |
124 | $this->packageAbsolutePath = realpath($vendorDirectory . DIRECTORY_SEPARATOR . $this->packageName) . DIRECTORY_SEPARATOR; |
125 | // If the package is symlinked, the path will be outside the working directory. |
126 | } elseif (0 !== strpos($absolutePath, getcwd()) && 1 === preg_match('/.*[\/\\\\]([^\/\\\\]*[\/\\\\][^\/\\\\]*)[\/\\\\][^\/\\\\]*/', $vendorDirectory, $output_array)) { |
127 | $this->relativePath = $output_array[1]; |
128 | } elseif (1 === preg_match('/.*[\/\\\\]([^\/\\\\]+[\/\\\\][^\/\\\\]+)[\/\\\\]composer.json/', $composerJsonFileAbsolute, $output_array)) { |
129 | // Not every package gets installed to a folder matching its name (crewlabs/unsplash). |
130 | $this->relativePath = $output_array[1]; |
131 | } |
132 | |
133 | if (!is_null($overrideAutoload)) { |
134 | $composer->getPackage()->setAutoload($overrideAutoload); |
135 | } |
136 | |
137 | $this->autoload = $composer->getPackage()->getAutoload(); |
138 | |
139 | foreach ($composer->getPackage()->getRequires() as $_name => $packageLink) { |
140 | $this->requiresNames[] = $packageLink->getTarget(); |
141 | } |
142 | |
143 | // Try to get the license from the package's composer.json, assume proprietary (all rights reserved!). |
144 | $this->license = !empty($composer->getPackage()->getLicense()) |
145 | ? implode(',', $composer->getPackage()->getLicense()) |
146 | : 'proprietary?'; |
147 | } |
148 | |
149 | /** |
150 | * Composer package project name. |
151 | * |
152 | * vendor/project-name |
153 | * |
154 | * @return string |
155 | */ |
156 | public function getPackageName(): string |
157 | { |
158 | return $this->packageName; |
159 | } |
160 | |
161 | public function getRelativePath(): ?string |
162 | { |
163 | return $this->relativePath . DIRECTORY_SEPARATOR; |
164 | } |
165 | |
166 | public function getPackageAbsolutePath(): ?string |
167 | { |
168 | return $this->packageAbsolutePath; |
169 | } |
170 | |
171 | /** |
172 | * |
173 | * e.g. ['psr-4' => [ 'BrianHenryIE\Project' => 'src' ]] |
174 | * e.g. ['psr-4' => [ 'BrianHenryIE\Project' => ['src','lib] ]] |
175 | * e.g. ['classmap' => [ 'src', 'lib' ]] |
176 | * e.g. ['files' => [ 'lib', 'functions.php' ]] |
177 | * |
178 | * @return AutoloadKey |
179 | */ |
180 | public function getAutoload(): array |
181 | { |
182 | return $this->autoload; |
183 | } |
184 | |
185 | /** |
186 | * The names of the packages in the composer.json's "requires" field (without version). |
187 | * |
188 | * Excludes PHP, ext-*, since we won't be copying or prefixing them. |
189 | * |
190 | * @return string[] |
191 | */ |
192 | public function getRequiresNames(): array |
193 | { |
194 | // Unset PHP, ext-*. |
195 | $removePhpExt = function ($element) { |
196 | return !( 0 === strpos($element, 'ext') || 'php' === $element ); |
197 | }; |
198 | |
199 | return array_filter($this->requiresNames, $removePhpExt); |
200 | } |
201 | |
202 | public function getLicense():string |
203 | { |
204 | return $this->license; |
205 | } |
206 | } |