Lines 100.00% 33 / 33
Methods 100.00% 7 / 7
Classes 100.00% 1 / 1
Covered by tests of size
Name Lines Methods CRAP
 factory 100.00% 9 / 9 100.00% 1 / 1 4
 getInstance 100.00% 3 / 3 100.00% 1 / 1 3
 getType 100.00% 1 / 1 100.00% 1 / 1 1
 checkOnEmptyTable 100.00% 8 / 8 100.00% 1 / 1 1
 errorPage 100.00% 10 / 10 100.00% 1 / 1 2
 setTablePrefix 100.00% 1 / 1 100.00% 1 / 1 1
 getTablePrefix 100.00% 1 / 1 100.00% 1 / 1 1
30class Database
31{
32    private static ?DatabaseDriver $databaseDriver = null;
33
34    /** @var string Database type. */
35    private static string $dbType = '';
36
37    /** @var string Table prefix */
38    private static string $tablePrefix = '';
39
40    /**
41     * Database factory.
42     *
43     * @param string $type Database management system type
44     * @throws Exception
45     */
46    public static function factory(string $type): DatabaseDriver
47    {
48        self::$dbType = $type;
49
50        $class = 'phpMyFAQ\Database\\' . ucfirst($type);
51        if (str_starts_with($type, 'pdo_')) {
52            $class = 'phpMyFAQ\Database\Pdo' . ucfirst(substr($type, offset: 4));
53        }
54
55        if (class_exists($class) && is_subclass_of($class, DatabaseDriver::class)) {
56            $databaseDriver = new $class();
57            self::$databaseDriver = $databaseDriver;
58            return $databaseDriver;
59        }
60
61        throw new Exception('Invalid Database Type: ' . $type);
62    }
63
64    /**
65     * Returns the single instance.
66     */
67    public static function getInstance(): DatabaseDriver
68    {
69        if (null === self::$databaseDriver) {
70            self::$databaseDriver = self::factory(self::$dbType !== '' ? self::$dbType : 'sqlite3');
71        }
72
73        return self::$databaseDriver;
74    }
75
76    /**
77     * Returns the database type as string
78     */
79    public static function getType(): string
80    {
81        return self::$dbType;
82    }
83
84    /**
85     * Check if a table is filled with data.
86     *
87     * @param string $tableName Table name
88     * @return bool true, if the table is empty, otherwise false
89     */
90    public static function checkOnEmptyTable(string $tableName): bool
91    {
92        $databaseDriver = self::getInstance();
93
94        return (
95            $databaseDriver->numRows($databaseDriver->query(sprintf(
96                'SELECT * FROM %s%s',
97                self::getTablePrefix(),
98                $tableName,
99            ))) < 1
100        );
101    }
102
103    /**
104     * Error page, if the database connection is not possible.
105     */
106    public static function errorPage(string $method): void
107    {
108        if (!headers_sent()) {
109            header('HTTP/1.1 503 Service Unavailable', replace: true, response_code: 503);
110            header('Retry-After: 60');
111        }
112        echo
113            '<!DOCTYPE html>
114            <html lang="en" class="no-js">
115            <head>
116                <meta charset="utf-8">
117                <title>Fatal phpMyFAQ Error</title>
118                <link href="assets/public/styles.css" rel="stylesheet">
119                <script src="assets/public/frontend.js"></script>
120            </head>
121            <body>
122                <div class="container">
123                <h1 class="pt-5">Fatal phpMyFAQ Error</h1>
124                <p class="alert alert-danger mt-5">The connection to the database server could not be established.</p>
125                <p class="alert alert-info p-2">The error message of the database server: '
126                . $method
127                . '</p>
128                </div>
129            </body>
130            </html>'
131        ;
132    }
133
134    /**
135     * Sets the table prefix.
136     */
137    public static function setTablePrefix(string $tablePrefix): void
138    {
139        self::$tablePrefix = $tablePrefix;
140    }
141
142    /**
143     * Returns the table prefix.
144     */
145    public static function getTablePrefix(): string
146    {
147        return self::$tablePrefix;
148    }
149}