Passed
Pull Request — master (#7330)
by
unknown
08:51
created

InstallDbGuardSubscriber::onKernelRequest()   D

Complexity

Conditions 20
Paths 50

Size

Total Lines 85
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 20
eloc 51
c 1
b 0
f 0
nc 50
nop 1
dl 0
loc 85
rs 4.1666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\EventSubscriber;
8
9
use Doctrine\DBAL\Connection;
10
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
11
use Symfony\Component\HttpFoundation\RedirectResponse;
12
use Symfony\Component\HttpKernel\Event\RequestEvent;
13
use Symfony\Component\HttpKernel\KernelEvents;
14
15
final class InstallDbGuardSubscriber implements EventSubscriberInterface
16
{
17
    private const VERSION_KEY = 'chamilo_database_version';
18
19
    public function __construct(private readonly Connection $connection) {}
20
21
    public static function getSubscribedEvents(): array
22
    {
23
        return [KernelEvents::REQUEST => ['onKernelRequest', 2048]];
24
    }
25
26
    public function onKernelRequest(RequestEvent $event): void
27
    {
28
        if (!$event->isMainRequest() || PHP_SAPI === 'cli') {
29
            return;
30
        }
31
32
        $request = $event->getRequest();
33
        $path = $request->getPathInfo();
34
35
        if (
36
            str_starts_with($path, '/main/install/')
37
            || str_starts_with($path, '/_wdt')
38
            || str_starts_with($path, '/_profiler')
39
            || str_starts_with($path, '/build/')
40
            || str_starts_with($path, '/bundles/')
41
            || str_starts_with($path, '/favicon')
42
        ) {
43
            return;
44
        }
45
46
        $installedFlag = (string) (
47
            $request->server->get('APP_INSTALLED')
48
            ?? $_ENV['APP_INSTALLED']
49
            ?? getenv('APP_INSTALLED')
50
            ?? '0'
51
        );
52
        $installedFlag = trim($installedFlag, " \t\n\r\0\x0B'\"");
53
54
        if ($installedFlag !== '1') {
55
            $this->redirectToInstaller($event);
56
            return;
57
        }
58
59
        // Cache "healthy" per PHP-FPM worker
60
        static $isHealthy = false;
61
        if ($isHealthy) {
62
            return;
63
        }
64
65
        // Cache which settings table to use per PHP-FPM worker
66
        static $settingsTable = null;
67
68
        try {
69
            // If DB is down/unreachable, this will fail quickly
70
            $this->connection->executeQuery($this->connection->getDatabasePlatform()->getDummySelectSQL());
71
72
            if ($settingsTable === null) {
73
                $settingsTable = $this->detectSettingsTable();
74
                if ($settingsTable === null) {
75
                    $this->redirectToInstaller($event);
76
                    return;
77
                }
78
            }
79
80
            // Cheap proof: at least 1 row
81
            $hasRow = $this->connection->fetchOne("SELECT 1 FROM {$settingsTable} LIMIT 1");
82
            if ($hasRow === false || $hasRow === null) {
83
                $this->redirectToInstaller($event);
84
                return;
85
            }
86
87
            // Read DB version (try common column names; only runs until first success)
88
            $version = null;
89
            foreach (['selected_value', 'value', 'c_value'] as $col) {
90
                try {
91
                    $version = $this->connection->fetchOne(
92
                        "SELECT {$col} FROM {$settingsTable} WHERE variable = :var LIMIT 1",
93
                        ['var' => self::VERSION_KEY]
94
                    );
95
                    if (!empty($version)) {
96
                        break;
97
                    }
98
                } catch (\Throwable) {
99
                    // Try next column
100
                }
101
            }
102
103
            if (empty($version)) {
104
                $this->redirectToInstaller($event);
105
                return;
106
            }
107
108
            $isHealthy = true;
109
        } catch (\Throwable) {
110
            $this->redirectToInstaller($event);
111
        }
112
    }
113
114
    private function detectSettingsTable(): ?string
115
    {
116
        // Fallback to settings
117
        try {
118
            $this->connection->fetchOne('SELECT 1 FROM settings LIMIT 1');
119
            return 'settings';
120
        } catch (\Throwable) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
121
        }
122
123
        return null;
124
    }
125
126
    private function redirectToInstaller(RequestEvent $event): void
127
    {
128
        $event->setResponse(new RedirectResponse('/main/install/index.php', 302));
129
        $event->stopPropagation();
130
    }
131
}
132