PluginProbe
SQLite Database Integration / 2.2.15
SQLite Database Integration v2.2.15
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.15, at wp-includes/sqlite-ast/class-wp-sqlite-connection.php

200 lines 6.2 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 * Returns the ID of the last inserted row.
123 *
124 * @return string The ID of the last inserted row.
125 */
126 public function get_last_insert_id(): string {
127 return $this->pdo->lastInsertId();
128 }
129
130 /**
131 * Quote a value for use in a query.
132 *
133 * @param mixed $value The value to quote.
134 * @param int $type The type of the value.
135 * @return string The quoted value.
136 */
137 public function quote( $value, int $type = PDO::PARAM_STR ): string {
138 return $this->pdo->quote( $value, $type );
139 }
140
141 /**
142 * Quote an SQLite identifier.
143 *
144 * Wraps the identifier in backticks and escapes backtick characters within.
145 *
146 * ---
147 *
148 * Quoted identifiers in SQLite are represented by string constants:
149 *
150 * A string constant is formed by enclosing the string in single quotes (').
151 * A single quote within the string can be encoded by putting two single
152 * quotes in a row - as in Pascal. C-style escapes using the backslash
153 * character are not supported because they are not standard SQL.
154 *
155 * See: https://www.sqlite.org/lang_expr.html#literal_values_constants_
156 *
157 * Although sparsely documented, this applies to backtick and double quoted
158 * string constants as well, so only the quote character needs to be escaped.
159 *
160 * For more details, see the grammar for SQLite table and column names:
161 *
162 * - https://github.com/sqlite/sqlite/blob/873fc5dff2a781251f2c9bd2c791a5fac45b7a2b/src/tokenize.c#L395-L419
163 * - https://github.com/sqlite/sqlite/blob/873fc5dff2a781251f2c9bd2c791a5fac45b7a2b/src/parse.y#L321-L338
164 *
165 * ---
166 *
167 * We use backtick quotes instead of the SQL standard double quotes, due to
168 * an SQLite quirk causing double-quoted strings to be accepted as literals:
169 *
170 * This misfeature means that a misspelled double-quoted identifier will
171 * be interpreted as a string literal, rather than generating an error.
172 *
173 * See: https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted
174 *
175 * @param string $unquoted_identifier The unquoted identifier value.
176 * @return string The quoted identifier value.
177 */
178 public function quote_identifier( string $unquoted_identifier ): string {
179 return '`' . str_replace( '`', '``', $unquoted_identifier ) . '`';
180 }
181
182 /**
183 * Get the PDO object.
184 *
185 * @return PDO
186 */
187 public function get_pdo(): PDO {
188 return $this->pdo;
189 }
190
191 /**
192 * Set a logger for the queries.
193 *
194 * @param callable(string, array): void $logger A query logger callback.
195 */
196 public function set_query_logger( callable $logger ): void {
197 $this->query_logger = $logger;
198 }
199 }
200