Passed
Push — master ( 68d4bb...43fc94 )
by Darko
14:36 queued 06:51
created

UpdatePerformanceHelper::getSystemMemoryInfo()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
c 1
b 0
f 0
dl 0
loc 27
rs 9.3888
cc 5
nc 4
nop 0
1
<?php
2
3
namespace App\Support;
4
5
use Illuminate\Support\Facades\Cache;
6
use Illuminate\Support\Facades\File;
7
use Illuminate\Support\Facades\Process;
8
9
class UpdatePerformanceHelper
10
{
11
    /**
12
     * Check if a file has changed since last update
13
     */
14
    public static function hasFileChanged(string $filePath, ?string $cacheKey = null): bool
15
    {
16
        if (! File::exists($filePath)) {
17
            return false;
18
        }
19
20
        $cacheKey = $cacheKey ?? 'file_hash_'.md5($filePath);
21
        $currentHash = md5_file($filePath);
22
        $lastHash = Cache::get($cacheKey);
23
24
        if ($currentHash !== $lastHash) {
25
            Cache::put($cacheKey, $currentHash, now()->addDays(7));
26
27
            return true;
28
        }
29
30
        return false;
31
    }
32
33
    /**
34
     * Execute commands in parallel where possible
35
     */
36
    public static function executeParallel(array $commands, int $timeout = 300): array
37
    {
38
        $processes = [];
39
        $results = [];
40
41
        // Start all processes
42
        foreach ($commands as $key => $command) {
43
            $processes[$key] = Process::timeout($timeout)->start($command);
44
        }
45
46
        // Wait for all processes to complete
47
        foreach ($processes as $key => $process) {
48
            $process->wait();
49
            $results[$key] = [
50
                'successful' => $process->successful(),
51
                'output' => $process->output(),
52
                'errorOutput' => $process->errorOutput(),
53
                'exitCode' => $process->exitCode(),
54
            ];
55
        }
56
57
        return $results;
58
    }
59
60
    /**
61
     * Clear various application caches efficiently
62
     */
63
    public static function clearAllCaches(): array
64
    {
65
        $cacheOperations = [
66
            'config' => fn () => \Artisan::call('config:clear'),
67
            'route' => fn () => \Artisan::call('route:clear'),
68
            'view' => fn () => \Artisan::call('view:clear'),
69
            'cache' => fn () => \Artisan::call('cache:clear'),
70
            'opcache' => fn () => function_exists('opcache_reset') ? opcache_reset() : true,
71
        ];
72
73
        $results = [];
74
        foreach ($cacheOperations as $type => $operation) {
75
            try {
76
                $operation();
77
                $results[$type] = 'success';
78
            } catch (\Exception $e) {
79
                $results[$type] = 'failed: '.$e->getMessage();
80
            }
81
        }
82
83
        return $results;
84
    }
85
86
    /**
87
     * Optimize file permissions for better performance
88
     */
89
    public static function optimizePermissions(): void
90
    {
91
        $paths = [
92
            base_path('bootstrap/cache'),
93
            base_path('storage'),
94
            base_path('storage/logs'),
95
            base_path('storage/framework/cache'),
96
            base_path('storage/framework/sessions'),
97
            base_path('storage/framework/views'),
98
        ];
99
100
        foreach ($paths as $path) {
101
            if (File::exists($path)) {
102
                chmod($path, 0755);
103
            }
104
        }
105
    }
106
107
    /**
108
     * Check system resources and recommend optimizations
109
     */
110
    public static function checkSystemResources(): array
111
    {
112
        $recommendations = [];
113
114
        // Check system memory usage
115
        $memInfo = self::getSystemMemoryInfo();
116
        if ($memInfo && $memInfo['usage_percent'] > 80) {
117
            $recommendations[] = sprintf(
118
                'High system memory usage: %.1f%% (%.1fGB of %.1fGB used)',
119
                $memInfo['usage_percent'],
120
                $memInfo['used'] / 1024 / 1024 / 1024,
121
                $memInfo['total'] / 1024 / 1024 / 1024
122
            );
123
        }
124
125
        // Still check PHP memory separately
126
        $phpMemoryUsage = memory_get_usage(true);
127
        $phpMemoryLimit = self::parseMemoryLimit(ini_get('memory_limit'));
128
        if ($phpMemoryLimit > 0 && $phpMemoryUsage > ($phpMemoryLimit * 0.8)) {
129
            $recommendations[] = sprintf(
130
                'PHP memory usage high: %.1fMB of %s',
131
                $phpMemoryUsage / 1024 / 1024,
132
                ini_get('memory_limit')
133
            );
134
        }
135
136
        // Check disk space
137
        $freeSpace = disk_free_space(base_path());
138
        $totalSpace = disk_total_space(base_path());
139
140
        if ($freeSpace < ($totalSpace * 0.1)) {
141
            $recommendations[] = 'Low disk space detected';
142
        }
143
144
        // Check PHP extensions
145
        $requiredExtensions = ['pdo', 'mbstring', 'openssl', 'json', 'curl'];
146
        foreach ($requiredExtensions as $ext) {
147
            if (! extension_loaded($ext)) {
148
                $recommendations[] = "Missing PHP extension: $ext";
149
            }
150
        }
151
152
        return $recommendations;
153
    }
154
155
    /**
156
     * Get system memory information
157
     */
158
    private static function getSystemMemoryInfo(): ?array
159
    {
160
        if (PHP_OS_FAMILY === 'Windows') {
161
            return null; // Windows memory check would require different approach
162
        }
163
164
        $memInfo = @file_get_contents('/proc/meminfo');
165
        if (! $memInfo) {
166
            return null;
167
        }
168
169
        preg_match('/MemTotal:\s+(\d+)/', $memInfo, $totalMatch);
170
        preg_match('/MemAvailable:\s+(\d+)/', $memInfo, $availableMatch);
171
172
        if (! isset($totalMatch[1]) || ! isset($availableMatch[1])) {
173
            return null;
174
        }
175
176
        $total = (int) $totalMatch[1] * 1024; // Convert from KB to bytes
177
        $available = (int) $availableMatch[1] * 1024;
178
        $used = $total - $available;
179
180
        return [
181
            'total' => $total,
182
            'available' => $available,
183
            'used' => $used,
184
            'usage_percent' => ($used / $total) * 100,
185
        ];
186
    }
187
188
    /**
189
     * Parse memory limit string to bytes
190
     */
191
    private static function parseMemoryLimit(string $limit): int
192
    {
193
        $limit = trim($limit);
194
        if ($limit === '-1') {
195
            return -1;
196
        }
197
198
        $last = strtolower($limit[strlen($limit) - 1]);
199
        $value = (int) $limit;
200
201
        switch ($last) {
202
            case 'g':
203
                $value *= 1024;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
204
            case 'm':
205
                $value *= 1024;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
206
            case 'k':
207
                $value *= 1024;
208
        }
209
210
        return $value;
211
    }
212
213
    /**
214
     * Create a system performance snapshot
215
     */
216
    public static function createPerformanceSnapshot(): array
217
    {
218
        return [
219
            'timestamp' => now()->toISOString(),
220
            'memory_usage' => memory_get_usage(true),
221
            'memory_peak' => memory_get_peak_usage(true),
222
            'execution_time' => microtime(true) - LARAVEL_START,
223
            'php_version' => PHP_VERSION,
224
            'laravel_version' => app()->version(),
0 ignored issues
show
introduced by
The method version() does not exist on Illuminate\Container\Container. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

224
            'laravel_version' => app()->/** @scrutinizer ignore-call */ version(),
Loading history...
225
            'disk_free' => disk_free_space(base_path()),
226
            'load_average' => function_exists('sys_getloadavg') ? sys_getloadavg() : null,
227
        ];
228
    }
229
}
230