Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
Database
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
7 / 7
13
100.00% covered (success)
100.00%
1 / 1
 factory
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 getInstance
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 getType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkOnEmptyTable
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 errorPage
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 setTablePrefix
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTablePrefix
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * The database abstraction factory.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2003-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2003-02-24
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ;
21
22use phpMyFAQ\Core\Exception;
23use phpMyFAQ\Database\DatabaseDriver;
24
25/**
26 * Class Database
27 *
28 * @package phpMyFAQ
29 */
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}