Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.00% covered (success)
85.00%
34 / 40
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
DatabaseHelper
85.00% covered (success)
85.00%
34 / 40
75.00% covered (warning)
75.00%
3 / 4
15.76
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
 alignTablePrefix
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 alignTablePrefixByPattern
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 buildInsertQueries
79.31% covered (warning)
79.31%
23 / 29
0.00% covered (danger)
0.00%
0 / 1
12.07
1<?php
2
3/**
4 * Helper class for database drivers.
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 * @author    Matteo Scaramuccia <matteo@phpmyfaq.de>
13 * @copyright 2012-2026 phpMyFAQ Team
14 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
15 * @link      https://www.phpmyfaq.de
16 * @since     2012-04-12
17 */
18
19declare(strict_types=1);
20
21namespace phpMyFAQ\Database;
22
23use phpMyFAQ\Configuration;
24use phpMyFAQ\Strings;
25
26/**
27 * Class Helper
28 *
29 * @package phpMyFAQ\Database
30 */
31readonly class DatabaseHelper
32{
33    /**
34     * Constructor.
35     */
36    public function __construct(
37        private Configuration $configuration,
38    ) {
39    }
40
41    /**
42     * Align the prefix of the table name used in the PMF backup file,
43     * from the (old) value of the system upon which the backup was performed
44     * to the (new) prefix of the system upon which the backup will be restored
45     * This alignment will be performed upon all the SQL query "patterns"
46     * provided within the PMF backup file.
47     */
48    public static function alignTablePrefix(string $query, string $oldValue, string $newValue): string
49    {
50        // Align DELETE FROM <prefix.tablename>
51        $query = self::alignTablePrefixByPattern($query, 'DELETE FROM', $oldValue, $newValue);
52        // Align INSERT INTO <prefix.tablename>
53        return self::alignTablePrefixByPattern($query, 'INSERT INTO', $oldValue, $newValue);
54    }
55
56    /**
57     * Align the prefix of the table name used in the PMF backup file,
58     * from the (old) value of the system upon which the backup was performed
59     * to the (new) prefix of the system upon which the backup will be restored.
60     * This alignment will be performed ONLY upon those given SQL queries starting
61     * with the given pattern.
62     */
63    private static function alignTablePrefixByPattern(
64        string $query,
65        string $startPattern,
66        string $oldValue,
67        string $newValue,
68    ): string {
69        $return = $query;
70        $matches = [];
71
72        Strings::preg_match_all('/^' . $startPattern . "\s+(\w+)(\s+|$)/i", $query, $matches);
73
74        if (($matches[1][0] ?? null) !== null) {
75            $oldTableFullName = (string) $matches[1][0];
76            $newTableFullName = $newValue . Strings::substr($oldTableFullName, Strings::strlen($oldValue));
77            $return = str_replace($oldTableFullName, $newTableFullName, $query);
78        }
79
80        return $return;
81    }
82
83    /**
84     * This function builds the queries for the backup.
85     *
86     * @return string[]
87     */
88    public function buildInsertQueries(string $query, string $table): array
89    {
90        $result = $this->configuration->getDb()->query($query);
91        if (!$result) {
92            return [];
93        }
94
95        $queries = [];
96
97        $queries[] = "\r\n-- Table: " . $table;
98
99        while (true) {
100            $row = $this->configuration->getDb()->fetchArray($result);
101            if ($row === false || $row === null || $row === []) {
102                break;
103            }
104
105            $columns = [];
106            $values = [];
107            foreach ($row as $key => $val) {
108                if (is_int($key)) {
109                    continue; // Fix for SQLite3
110                }
111
112                $columns[] = $key;
113                if ('rights' !== $key && is_numeric($val)) {
114                    $values[] = $val;
115                    continue;
116                }
117
118                if (is_null($val)) {
119                    $values[] = 'NULL';
120                    continue;
121                }
122
123                $values[] = sprintf("'%s'", $this->configuration->getDb()->escape((string) $val));
124            }
125
126            $queries[] = sprintf(
127                'INSERT INTO %s (%s) VALUES (%s);',
128                $table,
129                implode(',', $columns),
130                implode(',', $values),
131            );
132        }
133
134        return $queries;
135    }
136}