PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | vendor/wp-php-toolkit/reprint-server/src/class-sqlite-driver-pdo.php +285 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,285 @@
1 +<?php
2 +/**
3 + * PDO-compatible adapter for the SQLite Database Integration driver.
4 + *
5 + * MySQLDumpProducer expects a PDO connection — prepare(), query(), and the
6 + * statement methods fetch(), fetchAll(), fetchColumn(), execute().
7 + * On SQLite sites, the plugin's db.php drop-in loads a driver which translates
8 + * MySQL queries to SQLite. This adapter supplies the PDO operations which that
9 + * driver does not implement so the dump producer can use either supported
10 + * plugin version without knowing it is talking to SQLite.
11 + *
12 + * Every MySQL query goes through the active driver's translator, which converts
13 + * it to SQLite on the fly. The result is transparent: the dump producer sends
14 + * MySQL queries, gets rows back, and produces valid MySQL SQL output.
15 + */
16 +
17 +namespace WordPress\Reprint\Server;
18 +
19 +use PDO;
20 +use PDOStatement;
21 +
22 +/**
23 + * Wraps a supported SQLite Database Integration driver as a PDO connection.
24 + *
25 + * Only the methods that MySQLDumpProducer and the export endpoints actually
26 + * use are implemented. Anything else will trigger a clear PHP error rather
27 + * than silently misbehaving.
28 + */
29 +class SqliteDriverPDO
30 +{
31 + /** @var object The plugin's MySQL-on-SQLite driver. */
32 + private $driver;
33 +
34 + /** @var PDO The raw SQLite PDO for quote() delegation. */
35 + private $raw_pdo;
36 +
37 + /**
38 + * @param object $driver WP_SQLite_Driver or WP_MySQL_On_SQLite.
39 + * @param PDO $raw_pdo The underlying SQLite connection.
40 + */
41 + public function __construct($driver, PDO $raw_pdo)
42 + {
43 + $this->driver = $driver;
44 + $this->raw_pdo = $raw_pdo;
45 + }
46 +
47 + /**
48 + * Prepares a statement for execution.
49 + *
50 + * Returns a SqliteDriverPDOStatement that will substitute parameters
51 + * and execute through the driver when execute() is called.
52 + */
53 + public function prepare(string $sql): SqliteDriverPDOStatement
54 + {
55 + return new SqliteDriverPDOStatement($this->driver, $this->raw_pdo, $sql);
56 + }
57 +
58 + /**
59 + * Executes a query immediately and returns the result set.
60 + */
61 + public function query(string $sql): SqliteDriverPDOStatement
62 + {
63 + $stmt = new SqliteDriverPDOStatement($this->driver, $this->raw_pdo, $sql);
64 + $stmt->execute();
65 + return $stmt;
66 + }
67 +
68 + /**
69 + * Quotes a string for safe inclusion in a query.
70 + * Delegates to the underlying raw SQLite PDO.
71 + */
72 + public function quote(string $value, int $type = PDO::PARAM_STR): string
73 + {
74 + return $this->raw_pdo->quote($value, $type);
75 + }
76 +}
77 +
78 +/**
79 + * PDOStatement-compatible wrapper for MySQL-on-SQLite query results.
80 + *
81 + * Collects all result rows eagerly after execution and serves them
82 + * through fetch/fetchAll/fetchColumn.
83 + */
84 +class SqliteDriverPDOStatement
85 +{
86 + /** @var object The plugin's MySQL-on-SQLite driver. */
87 + private $driver;
88 +
89 + /** @var PDO The raw SQLite PDO for quote() delegation. */
90 + private $raw_pdo;
91 +
92 + /** @var string */
93 + private $sql;
94 +
95 + /** @var array Stored result rows after execution. */
96 + private $rows = [];
97 +
98 + /** @var int Current position for fetch(). */
99 + private $position = 0;
100 +
101 + /** @var array|null Parameters bound via bindValue(). */
102 + private $bound_params = null;
103 +
104 + /**
105 + * @param object $driver WP_SQLite_Driver or WP_MySQL_On_SQLite.
106 + * @param PDO $raw_pdo The underlying SQLite connection.
107 + * @param string $sql MySQL query to execute.
108 + */
109 + public function __construct($driver, PDO $raw_pdo, string $sql)
110 + {
111 + $this->driver = $driver;
112 + $this->raw_pdo = $raw_pdo;
113 + $this->sql = $sql;
114 + }
115 +
116 + /**
117 + * Executes the prepared statement.
118 + *
119 + * Substitutes bound parameters into the query, sends it through
120 + * the plugin's MySQL-on-SQLite driver, and stores the result rows.
121 + *
122 + * @param array|null $params Positional or named parameters.
123 + * @return bool True on success.
124 + */
125 + public function execute($params = null): bool
126 + {
127 + // Merge in any parameters set via bindValue().
128 + if ($params === null && $this->bound_params !== null) {
129 + $params = $this->bound_params;
130 + }
131 +
132 + $sql = $this->sql;
133 +
134 + if ($params !== null && count($params) > 0) {
135 + // Find ? placeholder positions outside of string literals.
136 + $positions = [];
137 + $len = strlen($sql);
138 + $in_single = false;
139 + $in_double = false;
140 + for ($i = 0; $i < $len; $i++) {
141 + $ch = $sql[$i];
142 + if ($ch === "'" && !$in_double) {
143 + $in_single = !$in_single;
144 + } elseif ($ch === '"' && !$in_single) {
145 + $in_double = !$in_double;
146 + } elseif ($ch === '?' && !$in_single && !$in_double) {
147 + $positions[] = $i;
148 + }
149 + }
150 +
151 + // Replace positional placeholders from right to left so
152 + // earlier offsets stay valid.
153 + for ($i = count($positions) - 1; $i >= 0; $i--) {
154 + if (!array_key_exists($i, $params)) {
155 + continue;
156 + }
157 + $quoted = $this->raw_pdo->quote($params[$i]);
158 + $sql = substr_replace($sql, $quoted, $positions[$i], 1);
159 + }
160 +
161 + // Named parameters (:name style).
162 + foreach ($params as $key => $value) {
163 + if (!is_string($key)) {
164 + continue;
165 + }
166 + $sql = str_replace($key, $this->raw_pdo->quote($value), $sql);
167 + }
168 + }
169 +
170 + $result = $this->driver->query($sql);
171 + $supply_table_types = false;
172 + if ($result === false && strcasecmp(trim($sql), 'SHOW FULL TABLES') === 0) {
173 + // The version 2 driver cannot parse SHOW FULL TABLES. Its public
174 + // SHOW TABLE STATUS path already removes SQLite system tables.
175 + $result = $this->driver->query('SHOW TABLE STATUS;');
176 + $supply_table_types = true;
177 + }
178 + if ($result instanceof PDOStatement) {
179 + $this->rows = $result->fetchAll(PDO::FETCH_ASSOC);
180 + } else {
181 + $result = $this->driver->get_query_results();
182 + $this->rows = is_array($result) ? $result : [];
183 + }
184 +
185 + if (
186 + count($this->rows) === 0 &&
187 + preg_match(
188 + '/\ASHOW (?:INDEX|FULL COLUMNS) FROM `([A-Za-z0-9_$]+)`\z/i',
189 + trim($sql),
190 + $matches
191 + )
192 + ) {
193 + // The version 2 parser treats backticks as part of the table name
194 + // in these SHOW forms. Retry only identifiers which are safe bare.
195 + $legacy_sql = str_replace("`{$matches[1]}`", $matches[1], trim($sql));
196 + $result = $this->driver->query($legacy_sql);
197 + if ($result instanceof PDOStatement) {
198 + $this->rows = $result->fetchAll(PDO::FETCH_ASSOC);
199 + } else {
200 + $result = $this->driver->get_query_results();
201 + $this->rows = is_array($result) ? $result : [];
202 + }
203 + }
204 +
205 + // The version 2 driver returns arrays of objects. Convert those rows
206 + // to the same associative arrays returned by the version 3 driver.
207 + foreach ($this->rows as $i => $row) {
208 + if (is_object($row)) {
209 + $this->rows[$i] = (array) $row;
210 + }
211 + if ($supply_table_types) {
212 + $this->rows[$i] = [
213 + 'Name' => $this->rows[$i]['Name'],
214 + 'Table_type' => 'BASE TABLE',
215 + ];
216 + }
217 + }
218 +
219 + $this->position = 0;
220 + return true;
221 + }
222 +
223 + /**
224 + * Fetches the next row from the result set.
225 + *
226 + * @param int $mode Fetch mode (ignored — always returns associative array).
227 + * @return array|false Associative array or false when exhausted.
228 + */
229 + public function fetch($mode = PDO::FETCH_ASSOC)
230 + {
231 + if ($this->position >= count($this->rows)) {
232 + return false;
233 + }
234 + return $this->rows[$this->position++];
235 + }
236 +
237 + /**
238 + * Returns all remaining rows from the result set.
239 + *
240 + * @param int $mode Fetch mode. Supports FETCH_ASSOC (default) and FETCH_COLUMN.
241 + * @return array
242 + */
243 + public function fetchAll($mode = PDO::FETCH_ASSOC)
244 + {
245 + $remaining = array_slice($this->rows, $this->position);
246 + $this->position = count($this->rows);
247 +
248 + if ($mode === PDO::FETCH_COLUMN) {
249 + return array_map(function ($row) {
250 + return reset($row);
251 + }, $remaining);
252 + }
253 +
254 + return $remaining;
255 + }
256 +
257 + /**
258 + * Returns a single column from the next row.
259 + *
260 + * @param int $column_number 0-indexed column number.
261 + * @return mixed|false The column value, or false if no more rows.
262 + */
263 + public function fetchColumn(int $column_number = 0)
264 + {
265 + $row = $this->fetch();
266 + if ($row === false) {
267 + return false;
268 + }
269 + $values = array_values($row);
270 + return $values[$column_number] ?? false;
271 + }
272 +
273 + /**
274 + * Binds a value to a named or positional parameter.
275 + */
276 + public function bindValue($parameter, $value, int $type = PDO::PARAM_STR): bool
277 + {
278 + if ($this->bound_params === null) {
279 + $this->bound_params = [];
280 + }
281 + $this->bound_params[$parameter] = $value;
282 + return true;
283 + }
284 +
285 +}