Issues (2)

src/ReComposer.php (2 issues)

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
The function array_first was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
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 ignore-call  annotation

392
        return (int) /** @scrutinizer ignore-call */ array_first($directorySize);
Loading history...
393
    }
394
}
395