Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
61 / 61
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
HealthCheckService
100.00% covered (success)
100.00%
61 / 61
100.00% covered (success)
100.00%
5 / 5
16
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 performCheck
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
6
 checkDatabase
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
2
 checkGit
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
4
 checkSystem
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace App\Shared\Diagnostics;
6
7use Yiisoft\Db\Connection\ConnectionInterface;
8
9 class HealthCheckService
10{
11    public function __construct(
12        private ConnectionInterface $db,
13        private string $projectRoot
14    ) {}
15
16    /**
17     * @return array{status: string, timestamp: int, environment: string, database: array<string, mixed>, git: array<string, mixed>, system: array<string, mixed>}
18     */
19    public function performCheck(): array
20    {
21        $dbStatus = $this->checkDatabase();
22        $gitStatus = $this->checkGit();
23        $systemStatus = $this->checkSystem();
24
25        $integrity = $dbStatus['integrity'] ?? [];
26        $integrityStatus = is_array($integrity) ? ($integrity['status'] ?? '') : '';
27
28        $isHealthy = $dbStatus['status'] === 'CONNECTED'
29            && $integrityStatus === 'OK'
30            && ($gitStatus['is_clean_working_tree'] ?? true) === true;
31
32        return [
33            'status' => $isHealthy ? 'HEALTHY' : 'DEGRADED',
34            'timestamp' => time(),
35            'environment' => (string) (getenv('YII_ENV') ?: 'production'),
36            'database' => $dbStatus,
37            'git' => $gitStatus,
38            'system' => $systemStatus,
39        ];
40    }
41
42    /**
43     * @return array<string, mixed>
44     */
45    private function checkDatabase(): array
46    {
47        try {
48            $this->db->open();
49            $driver = $this->db->getDriverName();
50
51            $checker = new DatabaseIntegrityChecker(
52                $this->db,
53                $this->projectRoot . '/migrations'
54            );
55
56            return [
57                'status' => 'CONNECTED',
58                'driver' => $driver,
59                'integrity' => $checker->check(),
60            ];
61        } catch (\Throwable $e) {
62            return [
63                'status' => 'DISCONNECTED',
64                'error' => $e->getMessage(),
65                'integrity' => [
66                    'status' => 'ERROR',
67                    'pending_migrations' => [],
68                    'applied_migrations_count' => 0,
69                    'schema_fingerprint' => 'UNKNOWN',
70                ],
71            ];
72        }
73    }
74
75    /**
76     * @return array<string, mixed>
77     */
78    private function checkGit(): array
79    {
80        $headFile = $this->projectRoot . '/.git/HEAD';
81        $commit = 'UNKNOWN';
82        $isClean = true;
83
84        if (file_exists($headFile)) {
85            $headContent = trim((string) file_get_contents($headFile));
86            if (str_starts_with($headContent, 'ref: ')) {
87                $refPath = $this->projectRoot . '/.git/' . substr($headContent, 5);
88                if (file_exists($refPath)) {
89                    $commit = trim((string) file_get_contents($refPath));
90                }
91            } else {
92                $commit = $headContent;
93            }
94        }
95
96        return [
97            'commit' => substr($commit, 0, 7),
98            'is_clean_working_tree' => $isClean,
99        ];
100    }
101
102    /**
103     * @return array<string, mixed>
104     */
105    private function checkSystem(): array
106    {
107        $runtimeDir = $this->projectRoot . '/runtime';
108        if (!is_dir($runtimeDir)) {
109            @mkdir($runtimeDir, 0o777, true);
110        }
111        $isWritable = is_dir($runtimeDir) && is_writable($runtimeDir);
112
113        return [
114            'php_version' => PHP_VERSION,
115            'runtime_writable' => $isWritable,
116        ];
117    }
118}