Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.10% covered (success)
96.10%
74 / 77
87.50% covered (success)
87.50%
7 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
DatabaseConfigurationStore
96.10% covered (success)
96.10%
74 / 77
87.50% covered (success)
87.50%
7 / 8
16
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 updateConfigValue
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 fetchAll
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 insert
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 delete
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 renameKey
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 fetchValue
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 fetchValues
85.00% covered (success)
85.00%
17 / 20
0.00% covered (danger)
0.00%
0 / 1
6.12
1<?php
2
3/**
4 * Database-backed configuration storage
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 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     2026-02-23
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Configuration\Storage;
21
22use phpMyFAQ\Database;
23use phpMyFAQ\Database\DatabaseDriver;
24
25readonly class DatabaseConfigurationStore implements ConfigurationStoreInterface
26{
27    public function __construct(
28        private DatabaseDriver $databaseDriver,
29        private string $tableName = 'faqconfig',
30    ) {
31    }
32
33    public function updateConfigValue(string $key, string $value): bool
34    {
35        $sql = <<<'SQL'
36                UPDATE %s%s SET config_value = '%s' WHERE config_name = '%s'
37            SQL;
38        $query = sprintf(
39            $sql,
40            Database::getTablePrefix(),
41            $this->tableName,
42            $this->databaseDriver->escape(trim($value)),
43            $this->databaseDriver->escape(trim($key)),
44        );
45
46        return (bool) $this->databaseDriver->query($query);
47    }
48
49    /**
50     * @return array<int, \stdClass>
51     */
52    public function fetchAll(): array
53    {
54        $sql = <<<'SQL'
55                SELECT config_name, config_value FROM %s%s
56            SQL;
57        $query = sprintf($sql, Database::getTablePrefix(), $this->tableName);
58
59        $result = $this->databaseDriver->query($query);
60        $rows = $this->databaseDriver->fetchAll($result);
61        return is_array($rows) ? $rows : [];
62    }
63
64    public function insert(string $name, string $value): bool
65    {
66        $sql = <<<'SQL'
67                INSERT INTO %s%s (config_name, config_value) VALUES ('%s', '%s')
68            SQL;
69        $query = sprintf(
70            $sql,
71            Database::getTablePrefix(),
72            $this->tableName,
73            $this->databaseDriver->escape(trim($name)),
74            $this->databaseDriver->escape(trim($value)),
75        );
76
77        return (bool) $this->databaseDriver->query($query);
78    }
79
80    public function delete(string $name): bool
81    {
82        $sql = <<<'SQL'
83                DELETE FROM %s%s WHERE config_name = '%s'
84            SQL;
85        $query = sprintf(
86            $sql,
87            Database::getTablePrefix(),
88            $this->tableName,
89            $this->databaseDriver->escape(trim($name)),
90        );
91
92        return (bool) $this->databaseDriver->query($query);
93    }
94
95    public function renameKey(string $currentKey, string $newKey): bool
96    {
97        $sql = <<<'SQL'
98                UPDATE %s%s SET config_name = '%s' WHERE config_name = '%s'
99            SQL;
100        $query = sprintf(
101            $sql,
102            Database::getTablePrefix(),
103            $this->tableName,
104            $this->databaseDriver->escape(trim($newKey)),
105            $this->databaseDriver->escape(trim($currentKey)),
106        );
107
108        return (bool) $this->databaseDriver->query($query);
109    }
110
111    public function fetchValue(string $name): ?string
112    {
113        $sql = <<<'SQL'
114                SELECT config_value FROM %s%s WHERE config_name = '%s'
115            SQL;
116        $query = sprintf(
117            $sql,
118            Database::getTablePrefix(),
119            $this->tableName,
120            $this->databaseDriver->escape(trim($name)),
121        );
122
123        $result = $this->databaseDriver->query($query);
124        $row = $this->databaseDriver->fetchObject($result);
125
126        return is_object($row) && property_exists($row, 'config_value') ? (string) $row->config_value : null;
127    }
128
129    /**
130     * @param array<int, string> $names
131     * @return array<string, ?string>
132     */
133    public function fetchValues(array $names): array
134    {
135        if ($names === []) {
136            return [];
137        }
138
139        $trimmedNames = array_values(array_unique(array_map(static fn(string $name): string => trim($name), $names)));
140        $quotedNames = array_map(
141            fn(string $name): string => "'" . $this->databaseDriver->escape($name) . "'",
142            $trimmedNames,
143        );
144
145        $sql = <<<'SQL'
146                SELECT config_name, config_value FROM %s%s WHERE config_name IN (%s)
147            SQL;
148        $query = sprintf($sql, Database::getTablePrefix(), $this->tableName, implode(', ', $quotedNames));
149
150        $result = $this->databaseDriver->query($query);
151        $rows = $this->databaseDriver->fetchAll($result);
152
153        $values = array_fill_keys($trimmedNames, value: null);
154        if (!is_array($rows)) {
155            return $values;
156        }
157
158        foreach ($rows as $row) {
159            if (!property_exists($row, 'config_name') || !property_exists($row, 'config_value')) {
160                continue;
161            }
162
163            $values[(string) $row->config_name] = (string) $row->config_value;
164        }
165
166        return $values;
167    }
168}