Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.10% covered (success)
93.10%
81 / 87
70.00% covered (warning)
70.00%
7 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
CacheMetricsDetector
93.02% covered (success)
93.02%
80 / 86
70.00% covered (warning)
70.00%
7 / 10
47.75
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 detect
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
9
 checkOpcacheEngine
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
6
 checkOpcacheMemory
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
5.09
 checkOpcacheInterned
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
5.09
 checkOpcacheFiles
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 checkApcuEngine
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 checkApcuMemory
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
5.09
 checkFileCacheStorage
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
6
 parseBytes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Modules\About\Application\Service\Detector;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Modules\About\Domain\Model\RequirementCheckResult;
12use App\Modules\About\Domain\Model\SystemRequirement;
13
14/**
15 * Opcode, APCu and File Cache Diagnostics Detector.
16 *
17 * @package App\Modules\About\Application\Service\Detector
18 */
19final readonly class CacheMetricsDetector
20{
21    /**
22     * CacheMetricsDetector constructor.
23     *
24     * @param string $basePath Base application filesystem directory.
25     */
26    public function __construct(
27        private string $basePath
28    ) {
29    }
30
31    /**
32     * Detects cache metric based on requirement key.
33     *
34     * @param SystemRequirement $req System requirement definition.
35     * @return RequirementCheckResult Check result.
36     */
37    public function detect(SystemRequirement $req): RequirementCheckResult
38    {
39        return match ($req->requirementKey) {
40            'cache_opcache'          => $this->checkOpcacheEngine($req),
41            'cache_opcache_memory'   => $this->checkOpcacheMemory($req),
42            'cache_opcache_interned' => $this->checkOpcacheInterned($req),
43            'cache_opcache_files'    => $this->checkOpcacheFiles($req),
44            'cache_apcu'             => $this->checkApcuEngine($req),
45            'cache_apcu_memory'      => $this->checkApcuMemory($req),
46            'cache_file_storage'     => $this->checkFileCacheStorage($req),
47            default                  => new RequirementCheckResult($req, 'Active', 'pass', 'Cache check passed.'),
48        };
49    }
50
51    public function checkOpcacheEngine(SystemRequirement $req): RequirementCheckResult
52    {
53        $enabled = (function_exists('opcache_get_status') && is_array(opcache_get_status(false)))
54            || (bool) ini_get('opcache.enable');
55        $val = $enabled ? 'Enabled' : 'Disabled';
56        return new RequirementCheckResult(
57            $req,
58            $val,
59            $enabled ? 'pass' : 'fail',
60            $enabled ? 'Bytecode compilation and acceleration is active.' : 'Zend OPcache is disabled.'
61        );
62    }
63
64    public function checkOpcacheMemory(SystemRequirement $req): RequirementCheckResult
65    {
66        $mem = (int) (ini_get('opcache.memory_consumption') ?: 128);
67        $val = "{$mem} MB";
68        $status = 'fail';
69        if ($mem >= 128) {
70            $status = 'pass';
71        } elseif ($mem >= 64) {
72            $status = 'warning';
73        }
74        return new RequirementCheckResult(
75            $req,
76            $val,
77            $status,
78            $mem >= 128 ? 'OPcache memory buffer is optimal.' : 'Recommended to increase to at least 128M.'
79        );
80    }
81
82    public function checkOpcacheInterned(SystemRequirement $req): RequirementCheckResult
83    {
84        $mem = (int) (ini_get('opcache.interned_strings_buffer') ?: 16);
85        $val = "{$mem} MB";
86        $status = 'fail';
87        if ($mem >= 16) {
88            $status = 'pass';
89        } elseif ($mem >= 8) {
90            $status = 'warning';
91        }
92        return new RequirementCheckResult(
93            $req,
94            $val,
95            $status,
96            $mem >= 16 ? 'Interned strings memory pool is optimal.' : 'Recommended at least 16M interned strings.'
97        );
98    }
99
100    public function checkOpcacheFiles(SystemRequirement $req): RequirementCheckResult
101    {
102        $files = (int) (ini_get('opcache.max_accelerated_files') ?: 10000);
103        $val = "{$files}";
104        $status = $files >= 10000 ? 'pass' : 'warning';
105        return new RequirementCheckResult(
106            $req,
107            $val,
108            $status,
109            $files >= 10000 ? 'OPcache file limit is sufficient.' : 'Recommended at least 10000 files in opcache.'
110        );
111    }
112
113    public function checkApcuEngine(SystemRequirement $req): RequirementCheckResult
114    {
115        $enabled = extension_loaded('apcu') && (bool) ini_get('apc.enabled');
116        $val = $enabled ? 'Enabled' : 'Disabled';
117        return new RequirementCheckResult(
118            $req,
119            $val,
120            $enabled ? 'pass' : 'warning',
121            $enabled ? 'APCu cache memory is available.' : 'APCu extension is not active.'
122        );
123    }
124
125    public function checkApcuMemory(SystemRequirement $req): RequirementCheckResult
126    {
127        $shm = (string) (ini_get('apc.shm_size') ?: '64M');
128        $bytes = $this->parseBytes($shm);
129        $status = 'fail';
130        if ($bytes >= 33554432) {
131            $status = 'pass';
132        } elseif ($bytes >= 16777216) {
133            $status = 'warning';
134        }
135        return new RequirementCheckResult(
136            $req,
137            $shm,
138            $status,
139            $bytes >= 33554432 ? 'APCu memory allocation is optimal.' : 'Recommended SHM allocation min 32M.'
140        );
141    }
142
143    public function checkFileCacheStorage(SystemRequirement $req): RequirementCheckResult
144    {
145        $path = $this->basePath . '/storage/cache';
146        $writable = is_dir($path) && is_writable($path);
147        $perms = is_dir($path) ? substr(sprintf('%o', fileperms($path)), -4) : 'N/A';
148        return new RequirementCheckResult(
149            $req,
150            $writable ? "chmod {$perms}" : 'No permission',
151            $writable ? 'pass' : 'fail',
152            $writable ? 'Directory storage/cache is writable.' : 'Permission error writing to storage/cache.'
153        );
154    }
155
156    private function parseBytes(string $val): int
157    {
158        return \App\Shared\Infrastructure\Format\ByteUnitConverter::parseBytes($val);
159    }
160}