PluginProbe
SQLite Database Integration / 2.2.16
SQLite Database Integration v2.2.16
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 / sqlite-ast / class-wp-sqlite-connection.php

class-wp-sqlite-connection.php in SQLite Database Integration 2.2.16, at wp-includes/sqlite-ast/class-wp-sqlite-connection.php

214 lines 6.6 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 // Return all values (except null) as strings.
95 $this->pdo->setAttribute( PDO::ATTR_STRINGIFY_FETCHES, true );
96
97 // Configure SQLite journal mode.
98 $journal_mode = $options['journal_mode'] ?? null;
99 if ( $journal_mode && in_array( $journal_mode, self::SQLITE_JOURNAL_MODES, true ) ) {
100 $this->query( 'PRAGMA journal_mode = ' . $journal_mode );
101 }
102 }
103
104 /**
105 * Execute a query in SQLite.
106 *
107 * @param string $sql The query to execute.
108 * @param array $params The query parameters.
109 * @throws PDOException When the query execution fails.
110 * @return PDOStatement The PDO statement object.
111 */
112 public function query( string $sql, array $params = array() ): PDOStatement {
113 if ( $this->query_logger ) {
114 ( $this->query_logger )( $sql, $params );
115 }
116 $stmt = $this->pdo->prepare( $sql );
117 $stmt->execute( $params );
118 return $stmt;
119 }
120
121 /**
122 * Prepare a SQLite query for execution.
123 *
124 * @param string $sql The query to prepare.
125 * @return PDOStatement The prepared statement.
126 * @throws PDOException When the query preparation fails.
127 */
128 public function prepare( string $sql ): PDOStatement {
129 if ( $this->query_logger ) {
130 ( $this->query_logger )( $sql, array() );
131 }
132 return $this->pdo->prepare( $sql );
133 }
134
135 /**
136 * Returns the ID of the last inserted row.
137 *
138 * @return string The ID of the last inserted row.
139 */
140 public function get_last_insert_id(): string {
141 return $this->pdo->lastInsertId();
142 }
143
144 /**
145 * Quote a value for use in a query.
146 *
147 * @param mixed $value The value to quote.
148 * @param int $type The type of the value.
149 * @return string The quoted value.
150 */
151 public function quote( $value, int $type = PDO::PARAM_STR ): string {
152 return $this->pdo->quote( $value, $type );
153 }
154
155 /**
156 * Quote an SQLite identifier.
157 *
158 * Wraps the identifier in backticks and escapes backtick characters within.
159 *
160 * ---
161 *
162 * Quoted identifiers in SQLite are represented by string constants:
163 *
164 * A string constant is formed by enclosing the string in single quotes (').
165 * A single quote within the string can be encoded by putting two single
166 * quotes in a row - as in Pascal. C-style escapes using the backslash
167 * character are not supported because they are not standard SQL.
168 *
169 * See: https://www.sqlite.org/lang_expr.html#literal_values_constants_
170 *
171 * Although sparsely documented, this applies to backtick and double quoted
172 * string constants as well, so only the quote character needs to be escaped.
173 *
174 * For more details, see the grammar for SQLite table and column names:
175 *
176 * - https://github.com/sqlite/sqlite/blob/873fc5dff2a781251f2c9bd2c791a5fac45b7a2b/src/tokenize.c#L395-L419
177 * - https://github.com/sqlite/sqlite/blob/873fc5dff2a781251f2c9bd2c791a5fac45b7a2b/src/parse.y#L321-L338
178 *
179 * ---
180 *
181 * We use backtick quotes instead of the SQL standard double quotes, due to
182 * an SQLite quirk causing double-quoted strings to be accepted as literals:
183 *
184 * This misfeature means that a misspelled double-quoted identifier will
185 * be interpreted as a string literal, rather than generating an error.
186 *
187 * See: https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted
188 *
189 * @param string $unquoted_identifier The unquoted identifier value.
190 * @return string The quoted identifier value.
191 */
192 public function quote_identifier( string $unquoted_identifier ): string {
193 return '`' . str_replace( '`', '``', $unquoted_identifier ) . '`';
194 }
195
196 /**
197 * Get the PDO object.
198 *
199 * @return PDO
200 */
201 public function get_pdo(): PDO {
202 return $this->pdo;
203 }
204
205 /**
206 * Set a logger for the queries.
207 *
208 * @param callable(string, array): void $logger A query logger callback.
209 */
210 public function set_query_logger( callable $logger ): void {
211 $this->query_logger = $logger;
212 }
213 }
214