Lines 58.82% 10 / 17
Methods 60.00% 3 / 5
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 detectCpuCount 100.00% 3 / 3 100.00% 1 / 1 2
 readCgroupQuota 100.00% 2 / 2 100.00% 1 / 1 3
 countFromNproc 66.66% 4 / 6 0.00% 0 / 1 4.59
 countFromProcCpuInfo 0.00% 0 / 5 0.00% 0 / 1 20
10final readonly class CpuCountDetector
11{
12    private const string CPU_INFO = '/proc/cpuinfo';
13
14    private const string CGROUP_CPU_MAX = '/sys/fs/cgroup/cpu.max';
15
16    public function __construct(private string $cgroupCpuMaxFile = self::CGROUP_CPU_MAX) {}
17
18    public function detectCpuCount(): int
19    {
20        $hostCount = $this->countFromNproc() ?? $this->countFromProcCpuInfo() ?? 1;
21        $quota = $this->readCgroupQuota();
22
23        return $quota === null ? $hostCount : min($hostCount, $quota->cpus);
24    }
25
26    private function readCgroupQuota(): ?CgroupCpuQuota
27    {
28        $cpuMax = is_readable($this->cgroupCpuMaxFile) ? file_get_contents($this->cgroupCpuMaxFile) : false;
29
30        return $cpuMax === false ? null : CgroupCpuQuota::parseCpuMax($cpuMax);
31    }
32
33    private function countFromNproc(): ?int
34    {
35        $process = new Process(['nproc']);
36
37        try {
38            $process->run();
39        } catch (ProcessException) {
40            return null;
41        }
42
43        $output = trim($process->getOutput());
44
45        return $process->isSuccessful() && ctype_digit($output) ? max(1, (int) $output) : null;
46    }
47
48    private function countFromProcCpuInfo(): ?int
49    {
50        $cpuInfo = is_readable(self::CPU_INFO) ? file_get_contents(self::CPU_INFO) : false;
51
52        if ($cpuInfo === false) {
53            return null;
54        }
55
56        $processors = preg_match_all('/^processor\s*:/m', $cpuInfo);
57
58        return $processors > 0 ? $processors : null;
59    }
60}