@@ -311,68 +311,105 @@ class WP_SQLite_Driver {
311311 /**
312312 * Constructor.
313313 *
314- * Create PDO object, set user defined functions and initialize other settings.
315- * Don't use parent::__construct() because this class does not only returns
316- * PDO instance but many others jobs.
314+ * Set up an SQLite connection and the MySQL-on-SQLite driver.
317315 *
318- * @param string $db_name The database name. In WordPress, the value of DB_NAME.
319- * @param PDO|null $pdo The PDO object.
316+ * @param array $options {
317+ * An array of options.
318+ *
319+ * @type string $database Database name.
320+ * The name of the emulated MySQL database.
321+ * @type string|null $path Optional. SQLite database path.
322+ * For in-memory database, use ':memory:'.
323+ * Must be set when PDO instance is not provided.
324+ * @type PDO|null $connection Optional. PDO instance with SQLite connection.
325+ * If not provided, a new PDO instance will be created.
326+ * @type int|null $timeout Optional. SQLite timeout in seconds.
327+ * The time to wait for a writable lock.
328+ * @type string|null $sqlite_journal_mode Optional. SQLite journal mode.
329+ * }
320330 */
321- public function __construct ( string $ db_name , ?PDO $ pdo = null ) {
322- $ this ->db_name = $ db_name ;
323- if ( ! $ pdo ) {
324- if ( ! is_file ( FQDB ) ) {
331+ public function __construct ( array $ options ) {
332+ // Database name.
333+ if ( ! isset ( $ options ['database ' ] ) || ! is_string ( $ options ['database ' ] ) ) {
334+ throw new InvalidArgumentException ( 'Option "database" is required. ' );
335+ }
336+ $ this ->db_name = $ options ['database ' ];
337+
338+ // Database connection.
339+ if ( isset ( $ options ['connection ' ] ) && $ options ['connection ' ] instanceof PDO ) {
340+ $ this ->pdo = $ options ['connection ' ];
341+ }
342+
343+ // Create a PDO connection if it is not provided.
344+ if ( ! $ this ->pdo ) {
345+ if ( ! isset ( $ options ['path ' ] ) || ! is_string ( $ options ['path ' ] ) ) {
346+ throw new InvalidArgumentException ( 'Option "path" is required when "connection" is not provided. ' );
347+ }
348+ $ path = $ options ['path ' ];
349+
350+ if ( ':memory: ' !== $ path && ! is_file ( $ path ) ) {
325351 $ this ->prepare_directory ();
326352 }
327353
328354 try {
329- $ options = array (
330- PDO ::ATTR_ERRMODE => PDO ::ERRMODE_EXCEPTION ,
331- PDO ::ATTR_STRINGIFY_FETCHES => true ,
332- PDO ::ATTR_TIMEOUT => self ::DEFAULT_TIMEOUT ,
333- );
334-
335- $ dsn = 'sqlite: ' . FQDB ;
336- $ pdo = new PDO ( $ dsn , null , null , $ options );
337- } catch ( PDOException $ ex ) {
355+ $ this ->pdo = new PDO ( 'sqlite: ' . $ path );
356+ } catch ( PDOException $ e ) {
338357 $ this ->error_messages [] = sprintf (
339358 '<p>%s</p><p>%s</p><p>%s</p> ' ,
340359 'Database initialization error! ' ,
341- 'Code: ' . $ ex ->getCode (),
342- 'Error Message: ' . $ ex ->getMessage ()
360+ 'Code: ' . $ e ->getCode (),
361+ 'Error Message: ' . $ e ->getMessage ()
343362 );
344363 return ;
345364 }
346365 }
347366
348- WP_SQLite_PDO_User_Defined_Functions::register_for ( $ pdo );
367+ // Throw exceptions on error.
368+ $ this ->pdo ->setAttribute ( PDO ::ATTR_ERRMODE , PDO ::ERRMODE_EXCEPTION );
349369
350- // MySQL data comes across stringified by default.
351- $ pdo ->setAttribute ( PDO ::ATTR_STRINGIFY_FETCHES , true );
370+ // Configure SQLite timeout.
371+ if ( isset ( $ options ['timeout ' ] ) && is_int ( $ options ['timeout ' ] ) ) {
372+ $ timeout = $ options ['timeout ' ];
373+ } else {
374+ $ timeout = self ::DEFAULT_TIMEOUT ;
375+ }
376+ $ this ->pdo ->setAttribute ( PDO ::ATTR_TIMEOUT , $ timeout );
352377
353- $ this ->pdo = $ pdo ;
378+ // Return all values (except null) as strings.
379+ $ this ->pdo ->setAttribute ( PDO ::ATTR_STRINGIFY_FETCHES , true );
354380
355- $ this ->information_schema_builder = new WP_SQLite_Information_Schema_Builder (
356- $ this ->db_name ,
357- array ( $ this , 'execute_sqlite_query ' )
358- );
359- $ this ->information_schema_builder ->ensure_information_schema_tables ();
381+ // Enable foreign keys. By default, they are off.
382+ $ this ->pdo ->query ( 'PRAGMA foreign_keys = ON ' );
383+
384+ // Configure SQLite journal mode.
385+ if (
386+ isset ( $ options ['sqlite_journal_mode ' ] )
387+ && in_array (
388+ $ options ['sqlite_journal_mode ' ],
389+ array ( 'DELETE ' , 'TRUNCATE ' , 'PERSIST ' , 'MEMORY ' , 'WAL ' , 'OFF ' ),
390+ true
391+ )
392+ ) {
393+ $ this ->pdo ->query ( 'PRAGMA journal_mode = ' . $ options ['sqlite_journal_mode ' ] );
394+ }
395+
396+ // Register SQLite functions.
397+ WP_SQLite_PDO_User_Defined_Functions::register_for ( $ this ->pdo );
360398
361399 // Load MySQL grammar.
362400 if ( null === self ::$ grammar ) {
363401 self ::$ grammar = new WP_Parser_Grammar ( require self ::GRAMMAR_PATH );
364402 }
365403
404+ // Initialize information schema builder.
405+ $ this ->information_schema_builder = new WP_SQLite_Information_Schema_Builder (
406+ $ this ->db_name ,
407+ array ( $ this , 'execute_sqlite_query ' )
408+ );
409+ $ this ->information_schema_builder ->ensure_information_schema_tables ();
410+
366411 // Load SQLite version to a property used by WordPress health info.
367412 $ this ->client_info = $ this ->get_sqlite_version ();
368-
369- $ this ->pdo ->query ( 'PRAGMA foreign_keys = ON ' );
370- $ this ->pdo ->query ( 'PRAGMA encoding="UTF-8"; ' );
371-
372- $ valid_journal_modes = array ( 'DELETE ' , 'TRUNCATE ' , 'PERSIST ' , 'MEMORY ' , 'WAL ' , 'OFF ' );
373- if ( defined ( 'SQLITE_JOURNAL_MODE ' ) && in_array ( SQLITE_JOURNAL_MODE , $ valid_journal_modes , true ) ) {
374- $ this ->pdo ->query ( 'PRAGMA journal_mode = ' . SQLITE_JOURNAL_MODE );
375- }
376413 }
377414
378415 /**
0 commit comments