| 1 |
<?php |
| 2 |
|
| 3 |
/* |
| 4 |
* The SQLite driver uses PDO. Enable PDO function calls: |
| 5 |
* phpcs:disable WordPress.DB.RestrictedClasses.mysql__PDO |
| 6 |
* |
| 7 |
* PDO uses camel case naming, enable non-snake case: |
| 8 |
* phpcs:disable WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid |
| 9 |
*/ |
| 10 |
|
| 11 |
/** |
| 12 |
* For backward compatibility with dependencies that use their own loader scripts |
| 13 |
* (e.g., WP CLI SQLite Command), ensure the PDO-based classes are loaded. |
| 14 |
*/ |
| 15 |
require_once __DIR__ . '/class-wp-mysql-on-sqlite.php'; |
| 16 |
require_once __DIR__ . '/class-wp-mysql-on-sqlite-statement.php'; |
| 17 |
|
| 18 |
/** |
| 19 |
* Deprecated: A proxy of the WP_MySQL_On_SQLite class preserving the legacy API. |
| 20 |
* |
| 21 |
* This class temporarily preserves the legacy constructor and result API while |
| 22 |
* consumers transition to the PDO-based WP_MySQL_On_SQLite API. |
| 23 |
* |
| 24 |
* @deprecated 3.0.0 Use WP_MySQL_On_SQLite instead. |
| 25 |
*/ |
| 26 |
class WP_SQLite_Driver { |
| 27 |
/** |
| 28 |
* The emulated MySQL client library version. |
| 29 |
* |
| 30 |
* This is a mysqli-like property that is needed to avoid a PHP warning in |
| 31 |
* the WordPress health info. The "WP_Debug_Data::get_wp_database()" method |
| 32 |
* calls "$wpdb->dbh->client_info" - a mysqli-specific abstraction leak. |
| 33 |
* |
| 34 |
* @TODO: This should be fixed in WordPress core. |
| 35 |
* |
| 36 |
* See: |
| 37 |
* https://github.com/WordPress/wordpress-develop/blob/bcdca3f9925f1d3eca7b78d231837c0caf0c8c24/src/wp-admin/includes/class-wp-debug-data.php#L1579 |
| 38 |
* |
| 39 |
* @var string |
| 40 |
*/ |
| 41 |
public $client_info; |
| 42 |
|
| 43 |
/** |
| 44 |
* The MySQL-on-SQLite driver instance. |
| 45 |
* |
| 46 |
* @var WP_MySQL_On_SQLite |
| 47 |
*/ |
| 48 |
private $mysql_on_sqlite_driver; |
| 49 |
|
| 50 |
/** |
| 51 |
* Statement returned for the last emulated query. |
| 52 |
* |
| 53 |
* @var WP_MySQL_On_SQLite_Statement|null |
| 54 |
*/ |
| 55 |
private $last_statement; |
| 56 |
|
| 57 |
/** |
| 58 |
* Results of the last emulated query. |
| 59 |
* |
| 60 |
* @var mixed |
| 61 |
*/ |
| 62 |
private $last_result; |
| 63 |
|
| 64 |
/** |
| 65 |
* Constructor. |
| 66 |
* |
| 67 |
* Set up an SQLite connection and the MySQL-on-SQLite driver. |
| 68 |
* |
| 69 |
* @param WP_SQLite_Connection $connection A SQLite database connection. |
| 70 |
* @param string $database The database name. |
| 71 |
* @param int $mysql_version The emulated MySQL version as an integer. |
| 72 |
* |
| 73 |
* @throws WP_MySQL_On_SQLite_Exception When the driver initialization fails. |
| 74 |
*/ |
| 75 |
public function __construct( |
| 76 |
WP_SQLite_Connection $connection, |
| 77 |
string $database, |
| 78 |
int $mysql_version = WP_MySQL_On_SQLite::DEFAULT_MYSQL_VERSION |
| 79 |
) { |
| 80 |
$this->mysql_on_sqlite_driver = new WP_MySQL_On_SQLite( |
| 81 |
sprintf( 'mysql-on-sqlite:dbname=%s', str_replace( ';', ';;', $database ) ), |
| 82 |
null, |
| 83 |
null, |
| 84 |
array( |
| 85 |
'mysql_version' => $mysql_version, |
| 86 |
'sqlite_pdo' => $connection->get_pdo(), |
| 87 |
'sqlite_journal_mode' => $connection->query( 'PRAGMA journal_mode' )->fetchColumn(), |
| 88 |
) |
| 89 |
); |
| 90 |
$this->client_info = $this->mysql_on_sqlite_driver->client_info; |
| 91 |
|
| 92 |
$this->mysql_on_sqlite_driver->setAttribute( PDO::ATTR_STRINGIFY_FETCHES, true ); |
| 93 |
} |
| 94 |
|
| 95 |
/** |
| 96 |
* Get the SQLite connection instance. |
| 97 |
* |
| 98 |
* @return WP_SQLite_Connection |
| 99 |
*/ |
| 100 |
public function get_connection(): WP_SQLite_Connection { |
| 101 |
return $this->mysql_on_sqlite_driver->get_connection(); |
| 102 |
} |
| 103 |
|
| 104 |
/** |
| 105 |
* Get the version of the SQLite engine. |
| 106 |
* |
| 107 |
* @return string SQLite engine version as a string. |
| 108 |
*/ |
| 109 |
public function get_sqlite_version(): string { |
| 110 |
return $this->mysql_on_sqlite_driver->get_sqlite_version(); |
| 111 |
} |
| 112 |
|
| 113 |
/** |
| 114 |
* Get the SQLite driver version saved in the database. |
| 115 |
* |
| 116 |
* The saved driver version corresponds to the latest version of the SQLite |
| 117 |
* driver that was used to initialize and configure the SQLite database. |
| 118 |
* |
| 119 |
* @return string SQLite driver version as a string. |
| 120 |
* @throws PDOException When the query execution fails. |
| 121 |
*/ |
| 122 |
public function get_saved_driver_version(): string { |
| 123 |
return $this->mysql_on_sqlite_driver->get_saved_driver_version(); |
| 124 |
} |
| 125 |
|
| 126 |
/** |
| 127 |
* Check if a specific SQL mode is active. |
| 128 |
* |
| 129 |
* @param string $mode The SQL mode to check. |
| 130 |
* @return bool True if the SQL mode is active, false otherwise. |
| 131 |
*/ |
| 132 |
public function is_sql_mode_active( string $mode ): bool { |
| 133 |
return $this->mysql_on_sqlite_driver->is_sql_mode_active( $mode ); |
| 134 |
} |
| 135 |
|
| 136 |
/** |
| 137 |
* Get the last executed MySQL query. |
| 138 |
* |
| 139 |
* @return string|null |
| 140 |
*/ |
| 141 |
public function get_last_mysql_query(): ?string { |
| 142 |
return $this->mysql_on_sqlite_driver->get_last_mysql_query(); |
| 143 |
} |
| 144 |
|
| 145 |
/** |
| 146 |
* Get SQLite queries executed for the last MySQL query. |
| 147 |
* |
| 148 |
* @return array{ sql: string, params: array }[] |
| 149 |
*/ |
| 150 |
public function get_last_sqlite_queries(): array { |
| 151 |
return $this->mysql_on_sqlite_driver->get_last_sqlite_queries(); |
| 152 |
} |
| 153 |
|
| 154 |
/** |
| 155 |
* Get the auto-increment value generated for the last query. |
| 156 |
* |
| 157 |
* @return int|string |
| 158 |
*/ |
| 159 |
public function get_insert_id() { |
| 160 |
$last_insert_id = $this->mysql_on_sqlite_driver->lastInsertId(); |
| 161 |
if ( is_numeric( $last_insert_id ) ) { |
| 162 |
$last_insert_id = (int) $last_insert_id; |
| 163 |
} |
| 164 |
return $last_insert_id; |
| 165 |
} |
| 166 |
|
| 167 |
/** |
| 168 |
* @param string $query Full SQL statement string. |
| 169 |
* @param int $fetch_mode PDO fetch mode. Default is PDO::FETCH_OBJ. |
| 170 |
* @param array ...$fetch_mode_args Additional fetch mode arguments. |
| 171 |
* |
| 172 |
* @return mixed Return value, depending on the query type. |
| 173 |
* |
| 174 |
* @throws WP_MySQL_On_SQLite_Exception When the query execution fails. |
| 175 |
*/ |
| 176 |
public function query( string $query, $fetch_mode = PDO::FETCH_OBJ, ...$fetch_mode_args ) { |
| 177 |
$this->last_statement = null; |
| 178 |
$stmt = $this->mysql_on_sqlite_driver->query( $query, $fetch_mode, ...$fetch_mode_args ); |
| 179 |
$this->last_statement = $stmt; |
| 180 |
|
| 181 |
if ( $stmt->columnCount() > 0 ) { |
| 182 |
$this->last_result = $stmt->fetchAll( $fetch_mode ); |
| 183 |
} else { |
| 184 |
$this->last_result = $stmt->rowCount(); |
| 185 |
} |
| 186 |
return $this->last_result; |
| 187 |
} |
| 188 |
|
| 189 |
/** |
| 190 |
* Tokenize a MySQL query and initialize a parser. |
| 191 |
* |
| 192 |
* @param string $query The MySQL query to parse. |
| 193 |
* @return WP_MySQL_Parser A parser initialized for the MySQL query. |
| 194 |
*/ |
| 195 |
public function create_parser( string $query ): WP_MySQL_Parser { |
| 196 |
return $this->mysql_on_sqlite_driver->create_parser( $query ); |
| 197 |
} |
| 198 |
|
| 199 |
/** |
| 200 |
* Get results of the last query. |
| 201 |
* |
| 202 |
* @return mixed |
| 203 |
*/ |
| 204 |
public function get_query_results() { |
| 205 |
return $this->last_result; |
| 206 |
} |
| 207 |
|
| 208 |
/** |
| 209 |
* Get return value of the last query() function call. |
| 210 |
* |
| 211 |
* @return mixed |
| 212 |
*/ |
| 213 |
public function get_last_return_value() { |
| 214 |
return $this->last_result; |
| 215 |
} |
| 216 |
|
| 217 |
/** |
| 218 |
* Get the number of columns returned by the last emulated query. |
| 219 |
* |
| 220 |
* @return int |
| 221 |
*/ |
| 222 |
public function get_last_column_count(): int { |
| 223 |
return null === $this->last_statement ? 0 : $this->last_statement->columnCount(); |
| 224 |
} |
| 225 |
|
| 226 |
/** |
| 227 |
* Get column metadata for results of the last emulated query. |
| 228 |
* |
| 229 |
* @return array |
| 230 |
*/ |
| 231 |
public function get_last_column_meta(): array { |
| 232 |
if ( null === $this->last_statement ) { |
| 233 |
return array(); |
| 234 |
} |
| 235 |
|
| 236 |
$column_meta = array(); |
| 237 |
$column_count = $this->last_statement->columnCount(); |
| 238 |
for ( $i = 0; $i < $column_count; $i++ ) { |
| 239 |
$column_meta[] = $this->last_statement->getColumnMeta( $i ); |
| 240 |
} |
| 241 |
return $column_meta; |
| 242 |
} |
| 243 |
|
| 244 |
/** |
| 245 |
* Execute a query in SQLite. |
| 246 |
* |
| 247 |
* @param string $sql The query to execute. |
| 248 |
* @param array $params The query parameters. |
| 249 |
* @throws PDOException When the query execution fails. |
| 250 |
* @return PDOStatement The PDO statement object. |
| 251 |
*/ |
| 252 |
public function execute_sqlite_query( string $sql, array $params = array() ): PDOStatement { |
| 253 |
return $this->mysql_on_sqlite_driver->execute_sqlite_query( $sql, $params ); |
| 254 |
} |
| 255 |
|
| 256 |
/** |
| 257 |
* Begin a new transaction or nested transaction. |
| 258 |
*/ |
| 259 |
public function beginTransaction(): void { |
| 260 |
$this->mysql_on_sqlite_driver->beginTransaction(); |
| 261 |
} |
| 262 |
|
| 263 |
/** |
| 264 |
* A temporary alias for backward compatibility. |
| 265 |
* |
| 266 |
* @see self::beginTransaction() |
| 267 |
*/ |
| 268 |
public function begin_transaction(): void { |
| 269 |
$this->beginTransaction(); |
| 270 |
} |
| 271 |
|
| 272 |
/** |
| 273 |
* Commit the current transaction or nested transaction. |
| 274 |
*/ |
| 275 |
public function commit(): void { |
| 276 |
$this->mysql_on_sqlite_driver->commit(); |
| 277 |
} |
| 278 |
|
| 279 |
/** |
| 280 |
* Rollback the current transaction or nested transaction. |
| 281 |
*/ |
| 282 |
public function rollback(): void { |
| 283 |
$this->mysql_on_sqlite_driver->rollback(); |
| 284 |
} |
| 285 |
} |
| 286 |
|