1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace SolumDeSignum\ReComposer; |
||||
6 | |||||
7 | use App; |
||||
8 | use function array_first; |
||||
0 ignored issues
–
show
introduced
by
![]() |
|||||
9 | use function base_path; |
||||
10 | use ByteUnits\Binary; |
||||
11 | use function collect; |
||||
12 | use function config; |
||||
13 | use Illuminate\Contracts\Filesystem\FileNotFoundException; |
||||
14 | use Illuminate\Support\Facades\Cache; |
||||
15 | use Illuminate\Support\Facades\File; |
||||
16 | use Illuminate\Support\Facades\Request; |
||||
17 | use Illuminate\Support\Str; |
||||
18 | use const JSON_THROW_ON_ERROR; |
||||
19 | use JsonException; |
||||
20 | use function now; |
||||
21 | |||||
22 | class ReComposer |
||||
23 | { |
||||
24 | public string $packageName; |
||||
25 | |||||
26 | public array $laravelExtras = []; |
||||
27 | |||||
28 | public array $serverExtras = []; |
||||
29 | |||||
30 | public array $extraStats = []; |
||||
31 | |||||
32 | public array $composer = []; |
||||
33 | |||||
34 | public array $packages = []; |
||||
35 | |||||
36 | /** |
||||
37 | * ReComposer constructor. |
||||
38 | * |
||||
39 | * @throws FileNotFoundException |
||||
40 | * @throws JsonException |
||||
41 | */ |
||||
42 | public function __construct() |
||||
43 | { |
||||
44 | $this->composer = $this->composerJson(); |
||||
45 | $this->packages = $this->packagesWithDependencies(); |
||||
46 | $this->packageName = ReComposerServiceProvider::$namespaceSuffix.'/'.ReComposerServiceProvider::$alias; |
||||
47 | } |
||||
48 | |||||
49 | /** |
||||
50 | * Get the ReComposer system report as a PHP array. |
||||
51 | * |
||||
52 | * @throws FileNotFoundException |
||||
53 | * @throws JsonException |
||||
54 | * |
||||
55 | * @return array |
||||
56 | */ |
||||
57 | final public function report(): array |
||||
58 | { |
||||
59 | $reportResponse = []; |
||||
60 | $reportResponse['Server Environment'] = $this->serverEnvironment(); |
||||
61 | $reportResponse['Laravel Environment'] = $this->laravelEnvironment(); |
||||
62 | $reportResponse['Installed Packages'] = $this->installedPackages(); |
||||
63 | |||||
64 | if (!empty($this->extraStats())) { |
||||
65 | $reportResponse['Extra Stats'] = $this->extraStats(); |
||||
66 | } |
||||
67 | |||||
68 | return $reportResponse; |
||||
69 | } |
||||
70 | |||||
71 | /** |
||||
72 | * Add Extra stats by app or any other package dev. |
||||
73 | * |
||||
74 | * @param array $extraStats |
||||
75 | */ |
||||
76 | final public function addExtraStats(array $extraStats): void |
||||
77 | { |
||||
78 | $this->extraStats = \array_merge($this->extraStats, $extraStats); |
||||
79 | } |
||||
80 | |||||
81 | /** |
||||
82 | * Add Laravel specific stats by app or any other package dev. |
||||
83 | * |
||||
84 | * @param array $laravelStats |
||||
85 | */ |
||||
86 | final public function addLaravelStats(array $laravelStats): void |
||||
87 | { |
||||
88 | $this->laravelExtras = \array_merge($this->laravelExtras, $laravelStats); |
||||
89 | } |
||||
90 | |||||
91 | /** |
||||
92 | * Add Server specific stats by app or any other package dev. |
||||
93 | * |
||||
94 | * @param array $serverStats |
||||
95 | */ |
||||
96 | final public function addServerStats(array $serverStats): void |
||||
97 | { |
||||
98 | $this->serverExtras = \array_merge($this->serverExtras, $serverStats); |
||||
99 | } |
||||
100 | |||||
101 | /** |
||||
102 | * Get the extra stats added by the app or any other package dev. |
||||
103 | * |
||||
104 | * @return array |
||||
105 | */ |
||||
106 | final public function extraStats(): array |
||||
107 | { |
||||
108 | return $this->extraStats; |
||||
109 | } |
||||
110 | |||||
111 | /** |
||||
112 | * Get additional server info added by the app or any other package dev. |
||||
113 | * |
||||
114 | * @return array |
||||
115 | */ |
||||
116 | final public function serverExtras(): array |
||||
117 | { |
||||
118 | return $this->serverExtras; |
||||
119 | } |
||||
120 | |||||
121 | /** |
||||
122 | * Get additional laravel info added by the app or any other package dev. |
||||
123 | * |
||||
124 | * @return array |
||||
125 | */ |
||||
126 | final public function laravelExtras(): array |
||||
127 | { |
||||
128 | return $this->laravelExtras; |
||||
129 | } |
||||
130 | |||||
131 | /** |
||||
132 | * Get Laravel environment details. |
||||
133 | * |
||||
134 | * @return array |
||||
135 | */ |
||||
136 | final public function laravelEnvironment(): array |
||||
137 | { |
||||
138 | return \array_merge( |
||||
139 | [ |
||||
140 | 'version' => App::version(), |
||||
141 | 'timezone' => config('app.timezone'), |
||||
142 | 'debug_mode' => config('app.debug'), |
||||
143 | 'storage_dir_writable' => \is_writable(base_path('storage')), |
||||
144 | 'cache_dir_writable' => \is_writable(base_path('bootstrap/cache')), |
||||
145 | 'decomposer_version' => $this->packageVersion(), |
||||
146 | 'app_size' => Str::replaceFirst( |
||||
147 | config('recomposer.binary.search', 'MiB'), |
||||
148 | config('recomposer.binary.replace', 'mb'), |
||||
149 | (string) $this->appSize() |
||||
150 | ), |
||||
151 | ], |
||||
152 | $this->laravelExtras() |
||||
153 | ); |
||||
154 | } |
||||
155 | |||||
156 | /** |
||||
157 | * @return string |
||||
158 | */ |
||||
159 | final public function binaryFormat(): string |
||||
160 | { |
||||
161 | $binaryFormat = config('recomposer.binary.format'); |
||||
162 | |||||
163 | return Binary::$binaryFormat($this->directorySize())->format(); |
||||
164 | } |
||||
165 | |||||
166 | /** |
||||
167 | * @return string|null |
||||
168 | */ |
||||
169 | final public function cacheRemember(): ?string |
||||
170 | { |
||||
171 | return Cache::remember( |
||||
172 | 'recomposer.folderSize', |
||||
173 | now()->addHours(config('recomposer.cache.hours', 1)), |
||||
174 | function () { |
||||
175 | return $this->binaryFormat(); |
||||
176 | } |
||||
177 | ); |
||||
178 | } |
||||
179 | |||||
180 | /** |
||||
181 | * @return string|null |
||||
182 | */ |
||||
183 | final public function appSize(): ?string |
||||
184 | { |
||||
185 | return config('recomposer.cache.feature') ? |
||||
186 | $this->cacheRemember() : |
||||
187 | $this->binaryFormat(); |
||||
188 | } |
||||
189 | |||||
190 | /** |
||||
191 | * Get PHP/Server environment details. |
||||
192 | * |
||||
193 | * @return array |
||||
194 | */ |
||||
195 | public function serverEnvironment(): array |
||||
196 | { |
||||
197 | return \array_merge( |
||||
198 | [ |
||||
199 | 'version' => PHP_VERSION, |
||||
200 | 'server_software' => $_SERVER['SERVER_SOFTWARE'], |
||||
201 | 'server_os' => \php_uname(), |
||||
202 | 'database_connection_name' => config('database.default'), |
||||
203 | 'ssl_installed' => $this->isSecure(), |
||||
204 | 'cache_driver' => config('cache.default'), |
||||
205 | 'session_driver' => config('session.driver'), |
||||
206 | 'openssl' => \extension_loaded('openssl'), |
||||
207 | 'pdo' => \extension_loaded('pdo'), |
||||
208 | 'mbstring' => \extension_loaded('mbstring'), |
||||
209 | 'tokenizer' => \extension_loaded('tokenizer'), |
||||
210 | 'xml' => \extension_loaded('xml'), |
||||
211 | ], |
||||
212 | $this->serverExtras() |
||||
213 | ); |
||||
214 | } |
||||
215 | |||||
216 | /** |
||||
217 | * Get the Composer file contents as an array. |
||||
218 | * |
||||
219 | * @throws JsonException |
||||
220 | * |
||||
221 | * @return array |
||||
222 | */ |
||||
223 | private function composerJson(): array |
||||
224 | { |
||||
225 | $composerJson = \file_get_contents(base_path('composer.json')); |
||||
226 | |||||
227 | return \json_decode((string) $composerJson, true, 512, JSON_THROW_ON_ERROR); |
||||
228 | } |
||||
229 | |||||
230 | /** |
||||
231 | * @param string $key |
||||
232 | * @param array $responseDependencies |
||||
233 | * |
||||
234 | * @return mixed |
||||
235 | */ |
||||
236 | private function dependencies(string $key, array $responseDependencies) |
||||
237 | { |
||||
238 | return \array_key_exists( |
||||
239 | $key, |
||||
240 | $responseDependencies |
||||
241 | ) ? |
||||
242 | $responseDependencies[$key] : |
||||
243 | 'No dependencies'; |
||||
244 | } |
||||
245 | |||||
246 | /** |
||||
247 | * @return array |
||||
248 | */ |
||||
249 | private function excludeBlacklistPackages(): array |
||||
250 | { |
||||
251 | $extensions = collect(\get_loaded_extensions()) |
||||
252 | ->map( |
||||
253 | function (string $ext) { |
||||
254 | return 'ext-'.\mb_strtolower($ext); |
||||
255 | } |
||||
256 | ); |
||||
257 | |||||
258 | if (config('recomposer.exclude.packages.enabled')) { |
||||
259 | foreach (config('recomposer.exclude.packages.blacklist') as $package) { |
||||
260 | $extensions->add($package); |
||||
261 | } |
||||
262 | } |
||||
263 | |||||
264 | return $extensions->toArray(); |
||||
265 | } |
||||
266 | |||||
267 | /** |
||||
268 | * Get Installed packages & their Dependencies. |
||||
269 | * |
||||
270 | * @param string $requireType |
||||
271 | * |
||||
272 | * @throws FileNotFoundException |
||||
273 | * @throws JsonException |
||||
274 | * |
||||
275 | * @return array |
||||
276 | */ |
||||
277 | private function collectPackages(string $requireType): array |
||||
278 | { |
||||
279 | $responsePackages = []; |
||||
280 | foreach ($this->composer[$requireType] as $packageName => $version) { |
||||
281 | if (!\in_array($packageName, $this->excludeBlacklistPackages(), true)) { |
||||
282 | $packageComposerJson = base_path( |
||||
283 | "/vendor/{$packageName}/composer.json" |
||||
284 | ); |
||||
285 | |||||
286 | $packageComposerJson = File::get($packageComposerJson); |
||||
287 | $responseDependencies = \json_decode( |
||||
288 | $packageComposerJson, |
||||
289 | true, |
||||
290 | 512, |
||||
291 | JSON_THROW_ON_ERROR |
||||
292 | ); |
||||
293 | |||||
294 | $responsePackages[] = [ |
||||
295 | 'name' => $packageName, |
||||
296 | 'version' => $version, |
||||
297 | 'dependencies' => $this->dependencies( |
||||
298 | 'require', |
||||
299 | $responseDependencies |
||||
300 | ), |
||||
301 | 'dev-dependencies' => $this->dependencies( |
||||
302 | 'require-dev', |
||||
303 | $responseDependencies |
||||
304 | ), |
||||
305 | ]; |
||||
306 | } |
||||
307 | } |
||||
308 | |||||
309 | return $responsePackages; |
||||
310 | } |
||||
311 | |||||
312 | /** |
||||
313 | * @throws FileNotFoundException |
||||
314 | * @throws JsonException |
||||
315 | * |
||||
316 | * @return array |
||||
317 | */ |
||||
318 | private function packagesWithDependencies(): array |
||||
319 | { |
||||
320 | $responseRequirePackages = $this->collectPackages('require'); |
||||
321 | $responseRequireDevPackages = $this->collectPackages('require-dev'); |
||||
322 | |||||
323 | return \array_merge($responseRequirePackages, $responseRequireDevPackages); |
||||
324 | } |
||||
325 | |||||
326 | /** |
||||
327 | * Get Installed packages & their version numbers as an associative array. |
||||
328 | * |
||||
329 | * @throws JsonException |
||||
330 | * @throws FileNotFoundException |
||||
331 | * |
||||
332 | * @return array |
||||
333 | */ |
||||
334 | private function installedPackages(): array |
||||
335 | { |
||||
336 | $packagesWithDependencies = []; |
||||
337 | foreach ($this->packagesWithDependencies() as $packageWithDependencies) { |
||||
338 | $packages[$packageWithDependencies['name']] = $packageWithDependencies['version']; |
||||
339 | } |
||||
340 | |||||
341 | return $packagesWithDependencies; |
||||
342 | } |
||||
343 | |||||
344 | /** |
||||
345 | * Get current installed ReComposer version. |
||||
346 | * |
||||
347 | * @return string |
||||
348 | */ |
||||
349 | private function packageVersion(): string |
||||
350 | { |
||||
351 | $version = $this->composer['require-dev'][$this->packageName] ?? |
||||
352 | $this->composer['require'][$this->packageName] ?? |
||||
353 | 'unknown'; |
||||
354 | |||||
355 | foreach ($this->packages as $package) { |
||||
356 | if (isset($package['dependencies'][$this->packageName])) { |
||||
357 | $version = $package['dependencies'][$this->packageName]; |
||||
358 | } |
||||
359 | |||||
360 | if (isset($package['dev-dependencies'][$this->packageName])) { |
||||
361 | $version = $package['dev-dependencies'][$this->packageName]; |
||||
362 | } |
||||
363 | } |
||||
364 | |||||
365 | return $version; |
||||
366 | } |
||||
367 | |||||
368 | /** |
||||
369 | * Check if SSL is installed or not. |
||||
370 | * |
||||
371 | * @return bool |
||||
372 | */ |
||||
373 | private function isSecure(): bool |
||||
374 | { |
||||
375 | return Request::isSecure(); |
||||
376 | } |
||||
377 | |||||
378 | /** |
||||
379 | * @return int |
||||
380 | */ |
||||
381 | private function directorySize(): int |
||||
382 | { |
||||
383 | $basePath = config('recomposer.basePath'); |
||||
384 | $excludeDirectories = \implode( |
||||
385 | ' ', |
||||
386 | config('recomposer.exclude.folder.blacklist') |
||||
387 | ); |
||||
388 | $execResponse = \exec("du $basePath".' '.$excludeDirectories); |
||||
389 | $directorySize = \explode("\t", $execResponse); |
||||
390 | |||||
391 | /** @scrutinizer ignore-call */ |
||||
392 | return (int) array_first($directorySize); |
||||
0 ignored issues
–
show
The function
array_first was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
393 | } |
||||
394 | } |
||||
395 |