PluginProbe
SQLite Database Integration / 2.2.22
SQLite Database Integration v2.2.22
3.0.2 3.0.1 trunk 2.1.13 2.1.14 2.1.15 2.1.16 2.2.0 2.2.1 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.2.2 2.2.20 2.2.21 2.2.22 2.2.23 2.2.3 All 32 releases
sqlite-database-integration / wp-includes / database / sqlite / class-wp-sqlite-connection.php

class-wp-sqlite-connection.php in SQLite Database Integration 2.2.22, at wp-includes/database/sqlite/class-wp-sqlite-connection.php

211 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php declare(strict_types = 1);
2
3 /*
4 * The SQLite connection uses PDO. Enable PDO function calls:
5 * phpcs:disable WordPress.DB.RestrictedClasses.mysql__PDO
6 */
7
8 /**
9 * SQLite connection.
10 *
11 * This class configures and encapsulates the connection to an SQLite database.
12 * It requires PDO with the SQLite driver, and currently, it is only a simple
13 * wrapper that leaks some of the PDO APIs (returns PDOStatement values, etc.).
14 * In the future, we may abstract it away from PDO and support SQLite3 as well.
15 */
16 class WP_SQLite_Connection {
17 /**
18 * The default timeout in seconds for SQLite to wait for a writable lock.
19 */
20 const DEFAULT_SQLITE_TIMEOUT = 10;
21
22 /**
23 * The supported SQLite journal modes.
24 *
25 * See: https://www.sqlite.org/pragma.html#pragma_journal_mode
26 */
27 const SQLITE_JOURNAL_MODES = array(
28 'DELETE',
29 'TRUNCATE',
30 'PERSIST',
31 'MEMORY',
32 'WAL',
33 'OFF',
34 );
35
36 /**
37 * The PDO connection for SQLite.
38 *
39 * @var PDO
40 */
41 private $pdo;
42
43 /**
44 * A query logger callback.
45 *
46 * @var callable(string, array): void
47 */
48 private $query_logger;
49
50 /**
51 * Constructor.
52 *
53 * Set up an SQLite connection.
54 *
55 * @param array $options {
56 * An array of options.
57 *
58 * @type string|null $path Optional. SQLite database path.
59 * For in-memory database, use ':memory:'.
60 * Must be set when PDO instance is not provided.
61 * @type PDO|null $pdo Optional. PDO instance with SQLite connection.
62 * If not provided, a new PDO instance will be created.
63 * @type int|null $timeout Optional. SQLite timeout in seconds.
64 * The time to wait for a writable lock.
65 * @type string|null $journal_mode Optional. SQLite journal mode.
66 * }
67 *
68 * @throws InvalidArgumentException When some connection options are invalid.
69 * @throws PDOException When the driver initialization fails.
70 */
71 public function __construct( array $options ) {
72 // Setup PDO connection.
73 if ( isset( $options['pdo'] ) && $options['pdo'] instanceof PDO ) {
74 $this->pdo = $options['pdo'];
75 } else {
76 if ( ! isset( $options['path'] ) || ! is_string( $options['path'] ) ) {
77 throw new InvalidArgumentException( 'Option "path" is required when "connection" is not provided.' );
78 }
79 $pdo_class = PHP_VERSION_ID >= 80400 ? PDO\SQLite::class : PDO::class;
80 $this->pdo = new $pdo_class( 'sqlite:' . $options['path'] );
81 }
82
83 // Throw exceptions on error.
84 $this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
85
86 // Configure SQLite timeout.
87 if ( isset( $options['timeout'] ) && is_int( $options['timeout'] ) ) {
88 $timeout = $options['timeout'];
89 } else {
90 $timeout = self::DEFAULT_SQLITE_TIMEOUT;
91 }
92 $this->pdo->setAttribute( PDO::ATTR_TIMEOUT, $timeout );
93
94 // Configure SQLite journal mode.
95 $journal_mode = $options['journal_mode'] ?? null;
96 if ( $journal_mode && in_array( $journal_mode, self::SQLITE_JOURNAL_MODES, true ) ) {
97 $this->query( 'PRAGMA journal_mode = ' . $journal_mode );
98 }
99 }
100
101 /**
102 * Execute a query in SQLite.
103 *
104 * @param string $sql The query to execute.
105 * @param array $params The query parameters.
106 * @throws PDOException When the query execution fails.
107 * @return PDOStatement The PDO statement object.
108 */
109 public function query( string $sql, array $params = array() ): PDOStatement {
110 if ( $this->query_logger ) {
111 ( $this->query_logger )( $sql, $params );
112 }
113 $stmt = $this->pdo->prepare( $sql );
114 $stmt->execute( $params );
115 return $stmt;
116 }
117
118 /**
119 * Prepare a SQLite query for execution.
120 *
121 * @param string $sql The query to prepare.
122 * @return PDOStatement The prepared statement.
123 * @throws PDOException When the query preparation fails.
124 */
125 public function prepare( string $sql ): PDOStatement {
126 if ( $this->query_logger ) {
127 ( $this->query_logger )( $sql, array() );
128 }
129 return $this->pdo->prepare( $sql );
130 }
131
132 /**
133 * Returns the ID of the last inserted row.
134 *
135 * @return string The ID of the last inserted row.
136 */
137 public function get_last_insert_id(): string {
138 return $this->pdo->lastInsertId();
139 }
140
141 /**
142 * Quote a value for use in a query.
143 *
144 * @param mixed $value The value to quote.
145 * @param int $type The type of the value.
146 * @return string The quoted value.
147 */
148 public function quote( $value, int $type = PDO::PARAM_STR ): string {
149 return $this->pdo->quote( $value, $type );
150 }
151
152 /**
153 * Quote an SQLite identifier.
154 *
155 * Wraps the identifier in backticks and escapes backtick characters within.
156 *
157 * ---
158 *
159 * Quoted identifiers in SQLite are represented by string constants:
160 *
161 * A string constant is formed by enclosing the string in single quotes (').
162 * A single quote within the string can be encoded by putting two single
163 * quotes in a row - as in Pascal. C-style escapes using the backslash
164 * character are not supported because they are not standard SQL.
165 *
166 * See: https://www.sqlite.org/lang_expr.html#literal_values_constants_
167 *
168 * Although sparsely documented, this applies to backtick and double quoted
169 * string constants as well, so only the quote character needs to be escaped.
170 *
171 * For more details, see the grammar for SQLite table and column names:
172 *
173 * - https://github.com/sqlite/sqlite/blob/873fc5dff2a781251f2c9bd2c791a5fac45b7a2b/src/tokenize.c#L395-L419
174 * - https://github.com/sqlite/sqlite/blob/873fc5dff2a781251f2c9bd2c791a5fac45b7a2b/src/parse.y#L321-L338
175 *
176 * ---
177 *
178 * We use backtick quotes instead of the SQL standard double quotes, due to
179 * an SQLite quirk causing double-quoted strings to be accepted as literals:
180 *
181 * This misfeature means that a misspelled double-quoted identifier will
182 * be interpreted as a string literal, rather than generating an error.
183 *
184 * See: https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted
185 *
186 * @param string $unquoted_identifier The unquoted identifier value.
187 * @return string The quoted identifier value.
188 */
189 public function quote_identifier( string $unquoted_identifier ): string {
190 return '`' . str_replace( '`', '``', $unquoted_identifier ) . '`';
191 }
192
193 /**
194 * Get the PDO object.
195 *
196 * @return PDO
197 */
198 public function get_pdo(): PDO {
199 return $this->pdo;
200 }
201
202 /**
203 * Set a logger for the queries.
204 *
205 * @param callable(string, array): void $logger A query logger callback.
206 */
207 public function set_query_logger( callable $logger ): void {
208 $this->query_logger = $logger;
209 }
210 }
211