This commit is contained in:
emmymayo
2025-02-05 23:15:46 +01:00
commit 7269c99357
16995 changed files with 3389680 additions and 0 deletions
@@ -0,0 +1,363 @@
<?php
/**
* Extend and replace the wpdb class.
*
* @package wp-sqlite-integration
* @since 1.0.0
*/
/**
* This class extends wpdb and replaces it.
*
* It also rewrites some methods that use mysql specific functions.
*/
class WP_SQLite_DB extends wpdb {
/**
* Database Handle
*
* @var WP_SQLite_Translator
*/
protected $dbh;
/**
* Constructor
*
* Unlike wpdb, no credentials are needed.
*/
public function __construct() {
parent::__construct( '', '', '', '' );
$this->charset = 'utf8mb4';
}
/**
* Method to set character set for the database.
*
* This overrides wpdb::set_charset(), only to dummy out the MySQL function.
*
* @see wpdb::set_charset()
*
* @param resource $dbh The resource given by mysql_connect.
* @param string $charset Optional. The character set. Default null.
* @param string $collate Optional. The collation. Default null.
*/
public function set_charset( $dbh, $charset = null, $collate = null ) {
}
/**
* Method to get the character set for the database.
* Hardcoded to utf8mb4 for now.
*
* @param string $table The table name.
* @param string $column The column name.
*
* @return string The character set.
*/
public function get_col_charset( $table, $column ) {
// Hardcoded for now.
return 'utf8mb4';
}
/**
* Method to dummy out wpdb::set_sql_mode()
*
* @see wpdb::set_sql_mode()
*
* @param array $modes Optional. A list of SQL modes to set.
*/
public function set_sql_mode( $modes = array() ) {
}
/**
* Closes the current database connection.
* Noop in SQLite.
*
* @return bool True to indicate the connection was successfully closed.
*/
public function close() {
return true;
}
/**
* Method to select the database connection.
*
* This overrides wpdb::select(), only to dummy out the MySQL function.
*
* @see wpdb::select()
*
* @param string $db MySQL database name. Not used.
* @param resource|null $dbh Optional link identifier.
*/
public function select( $db, $dbh = null ) {
$this->ready = true;
}
/**
* Method to escape characters.
*
* This overrides wpdb::_real_escape() to avoid using mysql_real_escape_string().
*
* @see wpdb::_real_escape()
*
* @param string $str The string to escape.
*
* @return string escaped
*/
public function _real_escape( $str ) {
return addslashes( $str );
}
/**
* Method to dummy out wpdb::esc_like() function.
*
* WordPress 4.0.0 introduced esc_like() function that adds backslashes to %,
* underscore and backslash, which is not interpreted as escape character
* by SQLite. So we override it and dummy out this function.
*
* @param string $text The raw text to be escaped. The input typed by the user should have no
* extra or deleted slashes.
*
* @return string Text in the form of a LIKE phrase. The output is not SQL safe. Call $wpdb::prepare()
* or real_escape next.
*/
public function esc_like( $text ) {
return $text;
}
/**
* Method to put out the error message.
*
* This overrides wpdb::print_error(), for we can't use the parent class method.
*
* @see wpdb::print_error()
*
* @global array $EZSQL_ERROR Stores error information of query and error string.
*
* @param string $str The error to display.
*
* @return bool|void False if the showing of errors is disabled.
*/
public function print_error( $str = '' ) {
global $EZSQL_ERROR;
if ( ! $str ) {
$err = $this->dbh->get_error_message() ? $this->dbh->get_error_message() : '';
$str = empty( $err ) ? '' : $err[2];
}
$EZSQL_ERROR[] = array(
'query' => $this->last_query,
'error_str' => $str,
);
if ( $this->suppress_errors ) {
return false;
}
wp_load_translations_early();
$caller = $this->get_caller();
$caller = $caller ? $caller : '(unknown)';
$error_str = sprintf(
'WordPress database error %1$s for query %2$s made by %3$s',
$str,
$this->last_query,
$caller
);
error_log( $error_str );
if ( ! $this->show_errors ) {
return false;
}
if ( is_multisite() ) {
$msg = "WordPress database error: [$str]\n{$this->last_query}\n";
if ( defined( 'ERRORLOGFILE' ) ) {
error_log( $msg, 3, ERRORLOGFILE );
}
if ( defined( 'DIEONDBERROR' ) ) {
wp_die( $msg );
}
} else {
$str = htmlspecialchars( $str, ENT_QUOTES );
$query = htmlspecialchars( $this->last_query, ENT_QUOTES );
printf(
'<div id="error"><p class="wpdberror">WordPress database error: [%1$s] %2$s</p></div>',
$str,
'<code>' . $query . '</code>'
);
}
}
/**
* Method to flush cached data.
*
* This overrides wpdb::flush(). This is not necessarily overridden, because
* $result will never be resource.
*
* @see wpdb::flush
*/
public function flush() {
$this->last_result = array();
$this->col_info = null;
$this->last_query = null;
$this->rows_affected = 0;
$this->num_rows = 0;
$this->last_error = '';
$this->result = null;
}
/**
* Method to do the database connection.
*
* This overrides wpdb::db_connect() to avoid using MySQL function.
*
* @see wpdb::db_connect()
*
* @param bool $allow_bail Not used.
* @return void
*/
public function db_connect( $allow_bail = true ) {
if ( $this->dbh ) {
return;
}
$this->init_charset();
$pdo = null;
if ( isset( $GLOBALS['@pdo'] ) ) {
$pdo = $GLOBALS['@pdo'];
}
$this->dbh = new WP_SQLite_Translator( $pdo );
$this->last_error = $this->dbh->get_error_message();
if ( $this->last_error ) {
return false;
}
$GLOBALS['@pdo'] = $this->dbh->get_pdo();
$this->ready = true;
}
/**
* Method to dummy out wpdb::check_connection()
*
* @param bool $allow_bail Not used.
*
* @return bool
*/
public function check_connection( $allow_bail = true ) {
return true;
}
/**
* Method to execute the query.
*
* This overrides wpdb::query(). In fact, this method does all the database
* access jobs.
*
* @see wpdb::query()
*
* @param string $query Database query.
*
* @return int|false Number of rows affected/selected or false on error
*/
public function query( $query ) {
if ( ! $this->ready ) {
return false;
}
$query = apply_filters( 'query', $query );
$this->flush();
$this->func_call = "\$db->query(\"$query\")";
$this->last_query = $query;
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
$this->timer_start();
}
$this->result = $this->dbh->query( $query );
++$this->num_queries;
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
$this->queries[] = array( $query, $this->timer_stop(), $this->get_caller() );
}
$this->last_error = $this->dbh->get_error_message();
if ( $this->last_error ) {
$this->print_error( $this->last_error );
return false;
}
if ( preg_match( '/^\\s*(set|create|alter|truncate|drop|optimize)\\s*/i', $query ) ) {
return $this->dbh->get_return_value();
}
if ( preg_match( '/^\\s*(insert|delete|update|replace)\s/i', $query ) ) {
$this->rows_affected = $this->dbh->get_affected_rows();
if ( preg_match( '/^\s*(insert|replace)\s/i', $query ) ) {
$this->insert_id = $this->dbh->get_insert_id();
}
return $this->rows_affected;
}
$this->last_result = $this->dbh->get_query_results();
$this->num_rows = $this->dbh->get_num_rows();
return $this->num_rows;
}
/**
* Method to set the class variable $col_info.
*
* This overrides wpdb::load_col_info(), which uses a mysql function.
*
* @see wpdb::load_col_info()
*/
protected function load_col_info() {
if ( $this->col_info ) {
return;
}
$this->col_info = $this->dbh->get_columns();
}
/**
* Method to return what the database can do.
*
* This overrides wpdb::has_cap() to avoid using MySQL functions.
* SQLite supports subqueries, but not support collation, group_concat and set_charset.
*
* @see wpdb::has_cap()
*
* @param string $db_cap The feature to check for. Accepts 'collation',
* 'group_concat', 'subqueries', 'set_charset',
* 'utf8mb4', or 'utf8mb4_520'.
*
* @return bool Whether the database feature is supported, false otherwise.
*/
public function has_cap( $db_cap ) {
return 'subqueries' === strtolower( $db_cap );
}
/**
* Method to return database version number.
*
* This overrides wpdb::db_version() to avoid using MySQL function.
* It returns mysql version number, but it means nothing for SQLite.
* So it return the newest mysql version.
*
* @see wpdb::db_version()
*/
public function db_version() {
return '8.0';
}
/**
* Retrieves full database server information.
*
* @return string|false Server info on success, false on failure.
*/
public function db_server_info() {
return SQLite3::version()['versionString'];
}
}
@@ -0,0 +1,762 @@
<?php
/**
* Custom functions for the SQLite implementation.
*
* @package wp-sqlite-integration
* @since 1.0.0
*/
/**
* This class defines user defined functions(UDFs) for PDO library.
*
* These functions replace those used in the SQL statement with the PHP functions.
*
* Usage:
*
* <code>
* new WP_SQLite_PDO_User_Defined_Functions(ref_to_pdo_obj);
* </code>
*
* This automatically enables ref_to_pdo_obj to replace the function in the SQL statement
* to the ones defined here.
*/
class WP_SQLite_PDO_User_Defined_Functions {
/**
* The class constructor
*
* Initializes the use defined functions to PDO object with PDO::sqliteCreateFunction().
*
* @param PDO $pdo The PDO object.
*/
public function __construct( $pdo ) {
if ( ! $pdo ) {
wp_die( 'Database is not initialized.', 'Database Error' );
}
foreach ( $this->functions as $f => $t ) {
$pdo->sqliteCreateFunction( $f, array( $this, $t ) );
}
}
/**
* Array to define MySQL function => function defined with PHP.
*
* Replaced functions must be public.
*
* @var array
*/
private $functions = array(
'month' => 'month',
'monthnum' => 'month',
'year' => 'year',
'day' => 'day',
'hour' => 'hour',
'minute' => 'minute',
'second' => 'second',
'week' => 'week',
'weekday' => 'weekday',
'dayofweek' => 'dayofweek',
'dayofmonth' => 'dayofmonth',
'unix_timestamp' => 'unix_timestamp',
'now' => 'now',
'md5' => 'md5',
'curdate' => 'curdate',
'rand' => 'rand',
'from_unixtime' => 'from_unixtime',
'localtime' => 'now',
'localtimestamp' => 'now',
'isnull' => 'isnull',
'if' => '_if',
'regexp' => 'regexp',
'field' => 'field',
'log' => 'log',
'least' => 'least',
'greatest' => 'greatest',
'get_lock' => 'get_lock',
'release_lock' => 'release_lock',
'ucase' => 'ucase',
'lcase' => 'lcase',
'unhex' => 'unhex',
'inet_ntoa' => 'inet_ntoa',
'inet_aton' => 'inet_aton',
'datediff' => 'datediff',
'locate' => 'locate',
'utc_date' => 'utc_date',
'utc_time' => 'utc_time',
'utc_timestamp' => 'utc_timestamp',
'version' => 'version',
);
/**
* Method to return the unix timestamp.
*
* Used without an argument, it returns PHP time() function (total seconds passed
* from '1970-01-01 00:00:00' GMT). Used with the argument, it changes the value
* to the timestamp.
*
* @param string $field Representing the date formatted as '0000-00-00 00:00:00'.
*
* @return number of unsigned integer
*/
public function unix_timestamp( $field = null ) {
return is_null( $field ) ? time() : strtotime( $field );
}
/**
* Method to emulate MySQL FROM_UNIXTIME() function.
*
* @param int $field The unix timestamp.
* @param string $format Indicate the way of formatting(optional).
*
* @return string
*/
public function from_unixtime( $field, $format = null ) {
// Convert to ISO time.
$date = gmdate( 'Y-m-d H:i:s', $field );
return is_null( $format ) ? $date : $this->dateformat( $date, $format );
}
/**
* Method to emulate MySQL NOW() function.
*
* @return string representing current time formatted as '0000-00-00 00:00:00'.
*/
public function now() {
return gmdate( 'Y-m-d H:i:s' );
}
/**
* Method to emulate MySQL CURDATE() function.
*
* @return string representing current time formatted as '0000-00-00'.
*/
public function curdate() {
return gmdate( 'Y-m-d' );
}
/**
* Method to emulate MySQL MD5() function.
*
* @param string $field The string to be hashed.
*
* @return string of the md5 hash value of the argument.
*/
public function md5( $field ) {
return md5( $field );
}
/**
* Method to emulate MySQL RAND() function.
*
* SQLite does have a random generator, but it is called RANDOM() and returns random
* number between -9223372036854775808 and +9223372036854775807. So we substitute it
* with PHP random generator.
*
* This function uses mt_rand() which is four times faster than rand() and returns
* the random number between 0 and 1.
*
* @return int
*/
public function rand() {
return mt_rand( 0, 1 );
}
/**
* Method to emulate MySQL DATEFORMAT() function.
*
* @param string $date Formatted as '0000-00-00' or datetime as '0000-00-00 00:00:00'.
* @param string $format The string format.
*
* @return string formatted according to $format
*/
public function dateformat( $date, $format ) {
$mysql_php_date_formats = array(
'%a' => 'D',
'%b' => 'M',
'%c' => 'n',
'%D' => 'jS',
'%d' => 'd',
'%e' => 'j',
'%H' => 'H',
'%h' => 'h',
'%I' => 'h',
'%i' => 'i',
'%j' => 'z',
'%k' => 'G',
'%l' => 'g',
'%M' => 'F',
'%m' => 'm',
'%p' => 'A',
'%r' => 'h:i:s A',
'%S' => 's',
'%s' => 's',
'%T' => 'H:i:s',
'%U' => 'W',
'%u' => 'W',
'%V' => 'W',
'%v' => 'W',
'%W' => 'l',
'%w' => 'w',
'%X' => 'Y',
'%x' => 'o',
'%Y' => 'Y',
'%y' => 'y',
);
$time = strtotime( $date );
$format = strtr( $format, $mysql_php_date_formats );
return gmdate( $format, $time );
}
/**
* Method to extract the month value from the date.
*
* @param string $field Representing the date formatted as 0000-00-00.
*
* @return string Representing the number of the month between 1 and 12.
*/
public function month( $field ) {
/*
* From https://www.php.net/manual/en/datetime.format.php:
*
* n - Numeric representation of a month, without leading zeros.
* 1 through 12
*/
return intval( gmdate( 'n', strtotime( $field ) ) );
}
/**
* Method to extract the year value from the date.
*
* @param string $field Representing the date formatted as 0000-00-00.
*
* @return string Representing the number of the year.
*/
public function year( $field ) {
/*
* From https://www.php.net/manual/en/datetime.format.php:
*
* Y - A full numeric representation of a year, 4 digits.
*/
return intval( gmdate( 'Y', strtotime( $field ) ) );
}
/**
* Method to extract the day value from the date.
*
* @param string $field Representing the date formatted as 0000-00-00.
*
* @return string Representing the number of the day of the month from 1 and 31.
*/
public function day( $field ) {
/*
* From https://www.php.net/manual/en/datetime.format.php:
*
* j - Day of the month without leading zeros.
* 1 to 31.
*/
return intval( gmdate( 'j', strtotime( $field ) ) );
}
/**
* Method to emulate MySQL SECOND() function.
*
* @see https://www.php.net/manual/en/datetime.format.php
*
* @param string $field Representing the time formatted as '00:00:00'.
*
* @return number Unsigned integer
*/
public function second( $field ) {
/*
* From https://www.php.net/manual/en/datetime.format.php:
*
* s - Seconds, with leading zeros (00 to 59)
*/
return intval( gmdate( 's', strtotime( $field ) ) );
}
/**
* Method to emulate MySQL MINUTE() function.
*
* @param string $field Representing the time formatted as '00:00:00'.
*
* @return int
*/
public function minute( $field ) {
/*
* From https://www.php.net/manual/en/datetime.format.php:
*
* i - Minutes with leading zeros.
* 00 to 59.
*/
return intval( gmdate( 'i', strtotime( $field ) ) );
}
/**
* Method to emulate MySQL HOUR() function.
*
* Returns the hour for time, in 24-hour format, from 0 to 23.
* Importantly, midnight is 0, not 24.
*
* @param string $time Representing the time formatted, like '14:08:12'.
*
* @return int
*/
public function hour( $time ) {
/*
* From https://www.php.net/manual/en/datetime.format.php:
*
* H 24-hour format of an hour with leading zeros.
* 00 through 23.
*/
return intval( gmdate( 'H', strtotime( $time ) ) );
}
/**
* Covers MySQL WEEK() function.
*
* Always assumes $mode = 1.
*
* @TODO: Support other modes.
*
* From https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_week:
*
* > Returns the week number for date. The two-argument form of WEEK()
* > enables you to specify whether the week starts on Sunday or Monday
* > and whether the return value should be in the range from 0 to 53
* > or from 1 to 53. If the mode argument is omitted, the value of the
* > default_week_format system variable is used.
* >
* > The following table describes how the mode argument works:
* >
* > Mode First day of week Range Week 1 is the first week …
* > 0 Sunday 0-53 with a Sunday in this year
* > 1 Monday 0-53 with 4 or more days this year
* > 2 Sunday 1-53 with a Sunday in this year
* > 3 Monday 1-53 with 4 or more days this year
* > 4 Sunday 0-53 with 4 or more days this year
* > 5 Monday 0-53 with a Monday in this year
* > 6 Sunday 1-53 with 4 or more days this year
* > 7 Monday 1-53 with a Monday in this year
*
* @param string $field Representing the date.
* @param int $mode The mode argument.
*/
public function week( $field, $mode ) {
/*
* From https://www.php.net/manual/en/datetime.format.php:
*
* W - ISO-8601 week number of year, weeks starting on Monday.
* Example: 42 (the 42nd week in the year)
*
* Week 1 is the first week with a Thursday in it.
*/
return intval( gmdate( 'W', strtotime( $field ) ) );
}
/**
* Simulates WEEKDAY() function in MySQL.
*
* Returns the day of the week as an integer.
* The days of the week are numbered 0 to 6:
* * 0 for Monday
* * 1 for Tuesday
* * 2 for Wednesday
* * 3 for Thursday
* * 4 for Friday
* * 5 for Saturday
* * 6 for Sunday
*
* @param string $field Representing the date.
*
* @return int
*/
public function weekday( $field ) {
/*
* date('N') returns 1 (for Monday) through 7 (for Sunday)
* That's one more than MySQL.
* Let's subtract one to make it compatible.
*/
return intval( gmdate( 'N', strtotime( $field ) ) ) - 1;
}
/**
* Method to emulate MySQL DAYOFMONTH() function.
*
* @see https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_dayofmonth
*
* @param string $field Representing the date.
*
* @return int Returns the day of the month for date as a number in the range 1 to 31.
*/
public function dayofmonth( $field ) {
return intval( gmdate( 'j', strtotime( $field ) ) );
}
/**
* Method to emulate MySQL DAYOFWEEK() function.
*
* > Returns the weekday index for date (1 = Sunday, 2 = Monday, …, 7 = Saturday).
* > These index values correspond to the ODBC standard. Returns NULL if date is NULL.
*
* @param string $field Representing the date.
*
* @return int Returns the weekday index for date (1 = Sunday, 2 = Monday, …, 7 = Saturday).
*/
public function dayofweek( $field ) {
/**
* From https://www.php.net/manual/en/datetime.format.php:
*
* `w` Numeric representation of the day of the week
* 0 (for Sunday) through 6 (for Saturday)
*/
return intval( gmdate( 'w', strtotime( $field ) ) ) + 1;
}
/**
* Method to emulate MySQL DATE() function.
*
* @see https://www.php.net/manual/en/datetime.format.php
*
* @param string $date formatted as unix time.
*
* @return string formatted as '0000-00-00'.
*/
public function date( $date ) {
return gmdate( 'Y-m-d', strtotime( $date ) );
}
/**
* Method to emulate MySQL ISNULL() function.
*
* This function returns true if the argument is null, and true if not.
*
* @param mixed $field The field to be tested.
*
* @return boolean
*/
public function isnull( $field ) {
return is_null( $field );
}
/**
* Method to emulate MySQL IF() function.
*
* As 'IF' is a reserved word for PHP, function name must be changed.
*
* @param mixed $expression The statement to be evaluated as true or false.
* @param mixed $truthy Statement or value returned if $expression is true.
* @param mixed $falsy Statement or value returned if $expression is false.
*
* @return mixed
*/
public function _if( $expression, $truthy, $falsy ) {
return ( true === $expression ) ? $truthy : $falsy;
}
/**
* Method to emulate MySQL REGEXP() function.
*
* @param string $pattern Regular expression to match.
* @param string $field Haystack.
*
* @return integer 1 if matched, 0 if not matched.
*/
public function regexp( $pattern, $field ) {
/*
* If the original query says REGEXP BINARY
* the comparison is byte-by-byte and letter casing now
* matters since lower- and upper-case letters have different
* byte codes.
*
* The REGEXP function can't be easily made to accept two
* parameters, so we'll have to use a hack to get around this.
*
* If the first character of the pattern is a null byte, we'll
* remove it and make the comparison case-sensitive. This should
* be reasonably safe since PHP does not allow null bytes in
* regular expressions anyway.
*/
if ( "\x00" === $pattern[0] ) {
$pattern = substr( $pattern, 1 );
$flags = '';
} else {
// Otherwise, the search is case-insensitive.
$flags = 'i';
}
$pattern = str_replace( '/', '\/', $pattern );
$pattern = '/' . $pattern . '/' . $flags;
return preg_match( $pattern, $field );
}
/**
* Method to emulate MySQL FIELD() function.
*
* This function gets the list argument and compares the first item to all the others.
* If the same value is found, it returns the position of that value. If not, it
* returns 0.
*
* @return int
*/
public function field() {
$num_args = func_num_args();
if ( $num_args < 2 || is_null( func_get_arg( 0 ) ) ) {
return 0;
}
$arg_list = func_get_args();
$search_string = strtolower( array_shift( $arg_list ) );
for ( $i = 0; $i < $num_args - 1; $i++ ) {
if ( strtolower( $arg_list[ $i ] ) === $search_string ) {
return $i + 1;
}
}
return 0;
}
/**
* Method to emulate MySQL LOG() function.
*
* Used with one argument, it returns the natural logarithm of X.
* <code>
* LOG(X)
* </code>
* Used with two arguments, it returns the natural logarithm of X base B.
* <code>
* LOG(B, X)
* </code>
* In this case, it returns the value of log(X) / log(B).
*
* Used without an argument, it returns false. This returned value will be
* rewritten to 0, because SQLite doesn't understand true/false value.
*
* @return double|null
*/
public function log() {
$num_args = func_num_args();
if ( 1 === $num_args ) {
$arg1 = func_get_arg( 0 );
return log( $arg1 );
}
if ( 2 === $num_args ) {
$arg1 = func_get_arg( 0 );
$arg2 = func_get_arg( 1 );
return log( $arg1 ) / log( $arg2 );
}
return null;
}
/**
* Method to emulate MySQL LEAST() function.
*
* This function rewrites the function name to SQLite compatible function name.
*
* @return mixed
*/
public function least() {
$arg_list = func_get_args();
return min( $arg_list );
}
/**
* Method to emulate MySQL GREATEST() function.
*
* This function rewrites the function name to SQLite compatible function name.
*
* @return mixed
*/
public function greatest() {
$arg_list = func_get_args();
return max( $arg_list );
}
/**
* Method to dummy out MySQL GET_LOCK() function.
*
* This function is meaningless in SQLite, so we do nothing.
*
* @param string $name Not used.
* @param integer $timeout Not used.
*
* @return string
*/
public function get_lock( $name, $timeout ) {
return '1=1';
}
/**
* Method to dummy out MySQL RELEASE_LOCK() function.
*
* This function is meaningless in SQLite, so we do nothing.
*
* @param string $name Not used.
*
* @return string
*/
public function release_lock( $name ) {
return '1=1';
}
/**
* Method to emulate MySQL UCASE() function.
*
* This is MySQL alias for upper() function. This function rewrites it
* to SQLite compatible name upper().
*
* @param string $content String to be converted to uppercase.
*
* @return string SQLite compatible function name.
*/
public function ucase( $content ) {
return "upper($content)";
}
/**
* Method to emulate MySQL LCASE() function.
*
* This is MySQL alias for lower() function. This function rewrites it
* to SQLite compatible name lower().
*
* @param string $content String to be converted to lowercase.
*
* @return string SQLite compatible function name.
*/
public function lcase( $content ) {
return "lower($content)";
}
/**
* Method to emulate MySQL UNHEX() function.
*
* For a string argument str, UNHEX(str) interprets each pair of characters
* in the argument as a hexadecimal number and converts it to the byte represented
* by the number. The return value is a binary string.
*
* @param string $number Number to be unhexed.
*
* @return string Binary string
*/
public function unhex( $number ) {
return pack( 'H*', $number );
}
/**
* Method to emulate MySQL INET_NTOA() function.
*
* This function gets 4 or 8 bytes integer and turn it into the network address.
*
* @param integer $num Long integer.
*
* @return string
*/
public function inet_ntoa( $num ) {
return long2ip( $num );
}
/**
* Method to emulate MySQL INET_ATON() function.
*
* This function gets the network address and turns it into integer.
*
* @param string $addr Network address.
*
* @return int long integer
*/
public function inet_aton( $addr ) {
return absint( ip2long( $addr ) );
}
/**
* Method to emulate MySQL DATEDIFF() function.
*
* This function compares two dates value and returns the difference.
*
* @param string $start Start date.
* @param string $end End date.
*
* @return string
*/
public function datediff( $start, $end ) {
$start_date = new DateTime( $start );
$end_date = new DateTime( $end );
$interval = $end_date->diff( $start_date, false );
return $interval->format( '%r%a' );
}
/**
* Method to emulate MySQL LOCATE() function.
*
* This function returns the position if $substr is found in $str. If not,
* it returns 0. If mbstring extension is loaded, mb_strpos() function is
* used.
*
* @param string $substr Needle.
* @param string $str Haystack.
* @param integer $pos Position.
*
* @return integer
*/
public function locate( $substr, $str, $pos = 0 ) {
if ( ! extension_loaded( 'mbstring' ) ) {
$val = strpos( $str, $substr, $pos );
if ( false !== $val ) {
return $val + 1;
}
return 0;
}
$val = mb_strpos( $str, $substr, $pos );
if ( false !== $val ) {
return $val + 1;
}
return 0;
}
/**
* Method to return GMT date in the string format.
*
* @return string formatted GMT date 'dddd-mm-dd'
*/
public function utc_date() {
return gmdate( 'Y-m-d', time() );
}
/**
* Method to return GMT time in the string format.
*
* @return string formatted GMT time '00:00:00'
*/
public function utc_time() {
return gmdate( 'H:i:s', time() );
}
/**
* Method to return GMT time stamp in the string format.
*
* @return string formatted GMT timestamp 'yyyy-mm-dd 00:00:00'
*/
public function utc_timestamp() {
return gmdate( 'Y-m-d H:i:s', time() );
}
/**
* Method to return MySQL version.
*
* This function only returns the current newest version number of MySQL,
* because it is meaningless for SQLite database.
*
* @return string representing the version number: major_version.minor_version
*/
public function version() {
return '5.5';
}
}
@@ -0,0 +1,343 @@
<?php
/**
* Class WP_SQLite_Query_Rewriter
*
* @package wp-sqlite-integration
*/
/**
* The query rewriter class.
*/
class WP_SQLite_Query_Rewriter {
/**
* An array of input token objects.
*
* @var WP_SQLite_Token[]
*/
public $input_tokens = array();
/**
* An array of output token objects.
*
* @var WP_SQLite_Token[]
*/
public $output_tokens = array();
/**
* The current index.
*
* @var int
*/
public $index = -1;
/**
* The maximum index.
*
* @var int
*/
public $max = -1;
/**
* The call stack.
*
* @var array
*/
public $call_stack = array();
/**
* The current depth.
*
* @var int
*/
public $depth = 0;
/**
* The current token.
*
* @var WP_SQLite_Token
*/
private $token;
/**
* The last function call.
*
* @var WP_SQLite_Token
*/
private $last_function_call;
/**
* Constructor.
*
* @param WP_SQLite_Token[] $input_tokens Array of token objects.
*/
public function __construct( $input_tokens ) {
$this->input_tokens = $input_tokens;
$this->max = count( $input_tokens );
}
/**
* Returns the updated query.
*
* @return string
*/
public function get_updated_query() {
$query = '';
foreach ( $this->output_tokens as $token ) {
$query .= $token->token;
}
return $query;
}
/**
* Add a token to the output.
*
* @param WP_SQLite_Token $token Token object.
*/
public function add( $token ) {
if ( $token ) {
$this->output_tokens[] = $token;
}
}
/**
* Add multiple tokens to the output.
*
* @param WP_SQLite_Token[] $tokens Array of token objects.
*/
public function add_many( $tokens ) {
$this->output_tokens = array_merge( $this->output_tokens, $tokens );
}
/**
* Replaces all tokens.
*
* @param WP_SQLite_Token[] $tokens Array of token objects.
*/
public function replace_all( $tokens ) {
$this->output_tokens = $tokens;
}
/**
* Peek at the next tokens and return one that matches the given criteria.
*
* @param array $query Optional. Search query.
* [
* 'type' => string|null, // Token type.
* 'flags' => int|null, // Token flags.
* 'values' => string|null, // Token values.
* ].
*
* @return WP_SQLite_Token
*/
public function peek( $query = array() ) {
$type = isset( $query['type'] ) ? $query['type'] : null;
$flags = isset( $query['flags'] ) ? $query['flags'] : null;
$values = isset( $query['value'] )
? ( is_array( $query['value'] ) ? $query['value'] : array( $query['value'] ) )
: null;
$i = $this->index;
while ( ++$i < $this->max ) {
if ( $this->input_tokens[ $i ]->matches( $type, $flags, $values ) ) {
return $this->input_tokens[ $i ];
}
}
}
/**
* Move forward and return the next tokens that match the given criteria.
*
* @param int $nth The nth token to return.
*
* @return WP_SQLite_Token
*/
public function peek_nth( $nth ) {
$found = 0;
for ( $i = $this->index + 1;$i < $this->max;$i++ ) {
$token = $this->input_tokens[ $i ];
if ( ! $token->is_semantically_void() ) {
++$found;
}
if ( $found === $nth ) {
return $this->input_tokens[ $i ];
}
}
}
/**
* Consume all the tokens.
*
* @param array $query Search query.
*
* @return void
*/
public function consume_all( $query = array() ) {
while ( $this->consume( $query ) ) {
// Do nothing.
}
}
/**
* Consume the next tokens and return one that matches the given criteria.
*
* @param array $query Search query.
* [
* 'type' => null, // Optional. Token type.
* 'flags' => null, // Optional. Token flags.
* 'values' => null, // Optional. Token values.
* ].
*
* @return WP_SQLite_Token|null
*/
public function consume( $query = array() ) {
$tokens = $this->move_forward( $query );
$this->output_tokens = array_merge( $this->output_tokens, $tokens );
return $this->token;
}
/**
* Drop the last consumed token and return it.
*
* @return WP_SQLite_Token|null
*/
public function drop_last() {
return array_pop( $this->output_tokens );
}
/**
* Skip over the next tokens and return one that matches the given criteria.
*
* @param array $query Search query.
* [
* 'type' => null, // Optional. Token type.
* 'flags' => null, // Optional. Token flags.
* 'values' => null, // Optional. Token values.
* ].
*
* @return WP_SQLite_Token|null
*/
public function skip( $query = array() ) {
$this->skip_and_return_all( $query );
return $this->token;
}
/**
* Skip over the next tokens until one matches the given criteria,
* and return all the skipped tokens.
*
* @param array $query Search query.
* [
* 'type' => null, // Optional. Token type.
* 'flags' => null, // Optional. Token flags.
* 'values' => null, // Optional. Token values.
* ].
*
* @return WP_SQLite_Token[]
*/
public function skip_and_return_all( $query = array() ) {
$tokens = $this->move_forward( $query );
/*
* When skipping over whitespaces, make sure to consume
* at least one to avoid SQL syntax errors.
*/
foreach ( $tokens as $token ) {
if ( $token->matches( WP_SQLite_Token::TYPE_WHITESPACE ) ) {
$this->add( $token );
break;
}
}
return $tokens;
}
/**
* Returns the next tokens that match the given criteria.
*
* @param array $query Search query.
* [
* 'type' => string|null, // Optional. Token type.
* 'flags' => int|null, // Optional. Token flags.
* 'values' => string|null, // Optional. Token values.
* ].
*
* @return array
*/
private function move_forward( $query = array() ) {
$type = isset( $query['type'] ) ? $query['type'] : null;
$flags = isset( $query['flags'] ) ? $query['flags'] : null;
$values = isset( $query['value'] )
? ( is_array( $query['value'] ) ? $query['value'] : array( $query['value'] ) )
: null;
$depth = isset( $query['depth'] ) ? $query['depth'] : null;
$buffered = array();
while ( true ) {
if ( ++$this->index >= $this->max ) {
$this->token = null;
$this->call_stack = array();
break;
}
$this->token = $this->input_tokens[ $this->index ];
$this->update_call_stack();
$buffered[] = $this->token;
if (
( null === $depth || $this->depth === $depth )
&& $this->token->matches( $type, $flags, $values )
) {
break;
}
}
return $buffered;
}
/**
* Returns the last call stack element.
*
* @return array|null
*/
public function last_call_stack_element() {
return count( $this->call_stack ) ? $this->call_stack[ count( $this->call_stack ) - 1 ] : null;
}
/**
* Updates the call stack.
*
* @return void
*/
private function update_call_stack() {
if ( $this->token->flags & WP_SQLite_Token::FLAG_KEYWORD_FUNCTION ) {
$this->last_function_call = $this->token->value;
}
if ( WP_SQLite_Token::TYPE_OPERATOR === $this->token->type ) {
switch ( $this->token->value ) {
case '(':
if ( $this->last_function_call ) {
array_push(
$this->call_stack,
array(
'function' => $this->last_function_call,
'depth' => $this->depth,
)
);
$this->last_function_call = null;
}
++$this->depth;
break;
case ')':
--$this->depth;
$call_parent = $this->last_call_stack_element();
if (
$call_parent &&
$call_parent['depth'] === $this->depth
) {
array_pop( $this->call_stack );
}
break;
}
}
}
}
@@ -0,0 +1,327 @@
<?php
/**
* This file is a port of the Token class from the PHPMyAdmin/sql-parser library.
*
* @package wp-sqlite-integration
* @see https://github.com/phpmyadmin/sql-parser
*/
/**
* Defines a token along with a set of types and flags and utility functions.
*
* An array of tokens will result after parsing the query.
*
* A structure representing a lexeme that explicitly indicates its categorization for the purpose of parsing.
*/
class WP_SQLite_Token {
/**
* This type is used when the token is invalid or its type cannot be
* determined because of the ambiguous context. Further analysis might be
* required to detect its type.
*/
const TYPE_NONE = 0;
/**
* SQL specific keywords: SELECT, UPDATE, INSERT, etc.
*/
const TYPE_KEYWORD = 1;
/**
* Any type of legal operator.
*
* Arithmetic operators: +, -, *, /, etc.
* Logical operators: ===, <>, !==, etc.
* Bitwise operators: &, |, ^, etc.
* Assignment operators: =, +=, -=, etc.
* SQL specific operators: . (e.g. .. WHERE database.table ..),
* * (e.g. SELECT * FROM ..)
*/
const TYPE_OPERATOR = 2;
/**
* Spaces, tabs, new lines, etc.
*/
const TYPE_WHITESPACE = 3;
/**
* Any type of legal comment.
*
* Bash (#), C (/* *\/) or SQL (--) comments:
*
* -- SQL-comment
*
* #Bash-like comment
*
* /*C-like comment*\/
*
* or:
*
* /*C-like
* comment*\/
*
* Backslashes were added to respect PHP's comments syntax.
*/
const TYPE_COMMENT = 4;
/**
* Boolean values: true or false.
*/
const TYPE_BOOL = 5;
/**
* Numbers: 4, 0x8, 15.16, 23e42, etc.
*/
const TYPE_NUMBER = 6;
/**
* Literal strings: 'string', "test".
* Some of these strings are actually symbols.
*/
const TYPE_STRING = 7;
/**
* Database, table names, variables, etc.
* For example: ```SELECT `foo`, `bar` FROM `database`.`table`;```.
*/
const TYPE_SYMBOL = 8;
/**
* Delimits an unknown string.
* For example: ```SELECT * FROM test;```, `test` is a delimiter.
*/
const TYPE_DELIMITER = 9;
/**
* Labels in LOOP statement, ITERATE statement etc.
* For example (only for begin label):
* begin_label: BEGIN [statement_list] END [end_label]
* begin_label: LOOP [statement_list] END LOOP [end_label]
* begin_label: REPEAT [statement_list] ... END REPEAT [end_label]
* begin_label: WHILE ... DO [statement_list] END WHILE [end_label].
*/
const TYPE_LABEL = 10;
// Flags that describe the tokens in more detail.
// All keywords must have flag 1 so `Context::isKeyword` method doesn't
// require strict comparison.
const FLAG_KEYWORD_RESERVED = 2;
const FLAG_KEYWORD_COMPOSED = 4;
const FLAG_KEYWORD_DATA_TYPE = 8;
const FLAG_KEYWORD_KEY = 16;
const FLAG_KEYWORD_FUNCTION = 32;
// Numbers related flags.
const FLAG_NUMBER_HEX = 1;
const FLAG_NUMBER_FLOAT = 2;
const FLAG_NUMBER_APPROXIMATE = 4;
const FLAG_NUMBER_NEGATIVE = 8;
const FLAG_NUMBER_BINARY = 16;
// Strings related flags.
const FLAG_STRING_SINGLE_QUOTES = 1;
const FLAG_STRING_DOUBLE_QUOTES = 2;
// Comments related flags.
const FLAG_COMMENT_BASH = 1;
const FLAG_COMMENT_C = 2;
const FLAG_COMMENT_SQL = 4;
const FLAG_COMMENT_MYSQL_CMD = 8;
// Operators related flags.
const FLAG_OPERATOR_ARITHMETIC = 1;
const FLAG_OPERATOR_LOGICAL = 2;
const FLAG_OPERATOR_BITWISE = 4;
const FLAG_OPERATOR_ASSIGNMENT = 8;
const FLAG_OPERATOR_SQL = 16;
// Symbols related flags.
const FLAG_SYMBOL_VARIABLE = 1;
const FLAG_SYMBOL_BACKTICK = 2;
const FLAG_SYMBOL_USER = 4;
const FLAG_SYMBOL_SYSTEM = 8;
const FLAG_SYMBOL_PARAMETER = 16;
/**
* The token it its raw string representation.
*
* @var string
*/
public $token;
/**
* The value this token contains (i.e. token after some evaluation).
*
* @var mixed
*/
public $value;
/**
* The keyword value this token contains, always uppercase.
*
* @var mixed|string|null
*/
public $keyword = null;
/**
* The type of this token.
*
* @var int
*/
public $type;
/**
* The flags of this token.
*
* @var int
*/
public $flags;
/**
* The position in the initial string where this token started.
*
* The position is counted in chars, not bytes, so you should
* use mb_* functions to properly handle utf-8 multibyte chars.
*
* @var int|null
*/
public $position;
/**
* Constructor.
*
* @param string $token The value of the token.
* @param int $type The type of the token.
* @param int $flags The flags of the token.
*/
public function __construct( $token, $type = 0, $flags = 0 ) {
$this->token = $token;
$this->type = $type;
$this->flags = $flags;
$this->value = $this->extract();
}
/**
* Check if the token matches the given parameters.
*
* @param int|null $type The type of the token.
* @param int|null $flags The flags of the token.
* @param array|null $values The values of the token.
*
* @return bool
*/
public function matches( $type = null, $flags = null, $values = null ) {
if ( null === $type && null === $flags && ( null === $values || array() === $values ) ) {
return ! $this->is_semantically_void();
}
return (
( null === $type || $this->type === $type )
&& ( null === $flags || ( $this->flags & $flags ) )
&& ( null === $values || in_array( strtoupper( $this->value ?? '' ), $values, true ) )
);
}
/**
* Check if the token is semantically void (i.e. whitespace or comment).
*
* @return bool
*/
public function is_semantically_void() {
return $this->matches( self::TYPE_WHITESPACE ) || $this->matches( self::TYPE_COMMENT );
}
/**
* Does little processing to the token to extract a value.
*
* If no processing can be done it will return the initial string.
*
* @return mixed
*/
private function extract() {
switch ( $this->type ) {
case self::TYPE_KEYWORD:
$this->keyword = strtoupper( $this->token ?? '' );
if ( ! ( $this->flags & self::FLAG_KEYWORD_RESERVED ) ) {
/*
* Unreserved keywords should stay the way they are
* because they might represent field names.
*/
return $this->token;
}
return $this->keyword;
case self::TYPE_WHITESPACE:
return ' ';
case self::TYPE_BOOL:
return strtoupper( $this->token ?? '' ) === 'TRUE';
case self::TYPE_NUMBER:
$ret = str_replace( '--', '', $this->token ); // e.g. ---42 === -42.
if ( $this->flags & self::FLAG_NUMBER_HEX ) {
$ret = str_replace( array( '-', '+' ), '', $this->token );
if ( $this->flags & self::FLAG_NUMBER_NEGATIVE ) {
$ret = -hexdec( $ret );
} else {
$ret = hexdec( $ret );
}
} elseif ( ( $this->flags & self::FLAG_NUMBER_APPROXIMATE ) || ( $this->flags & self::FLAG_NUMBER_FLOAT ) ) {
$ret = (float) $ret;
} elseif ( ! ( $this->flags & self::FLAG_NUMBER_BINARY ) ) {
$ret = (int) $ret;
}
return $ret;
case self::TYPE_STRING:
// Trims quotes.
$str = $this->token;
$str = mb_substr( $str, 1, -1, 'UTF-8' );
// Removes surrounding quotes.
$quote = $this->token[0];
$str = str_replace( $quote . $quote, $quote, $str );
/*
* Finally unescapes the string.
*
* `stripcslashes` replaces escape sequences with their
* representation.
*/
$str = stripcslashes( $str );
return $str;
case self::TYPE_SYMBOL:
$str = $this->token;
if ( isset( $str[0] ) && ( '@' === $str[0] ) ) {
/*
* `mb_strlen($str)` must be used instead of `null` because
* in PHP 5.3- the `null` parameter isn't handled correctly.
*/
$str = mb_substr(
$str,
! empty( $str[1] ) && ( '@' === $str[1] ) ? 2 : 1,
mb_strlen( $str ),
'UTF-8'
);
}
if ( isset( $str[0] ) && ( ':' === $str[0] ) ) {
$str = mb_substr( $str, 1, mb_strlen( $str ), 'UTF-8' );
}
if ( isset( $str[0] ) && ( ( '`' === $str[0] ) || ( '"' === $str[0] ) || ( '\'' === $str[0] ) ) ) {
$quote = $str[0];
$str = str_replace( $quote . $quote, $quote, $str );
$str = mb_substr( $str, 1, -1, 'UTF-8' );
}
return $str;
}
return $this->token;
}
}
@@ -0,0 +1,65 @@
<?php
/**
* Main integration file.
*
* @package wp-sqlite-integration
* @since 1.0.0
*/
// Require the constants file.
require_once dirname( __DIR__, 2 ) . '/constants.php';
// Bail early if DB_ENGINE is not defined as sqlite.
if ( ! defined( 'DB_ENGINE' ) || 'sqlite' !== DB_ENGINE ) {
return;
}
if ( ! extension_loaded( 'pdo' ) ) {
wp_die(
new WP_Error(
'pdo_not_loaded',
sprintf(
'<h1>%1$s</h1><p>%2$s</p>',
'PHP PDO Extension is not loaded',
'Your PHP installation appears to be missing the PDO extension which is required for this version of WordPress and the type of database you have specified.'
)
),
'PHP PDO Extension is not loaded.'
);
}
if ( ! extension_loaded( 'pdo_sqlite' ) ) {
wp_die(
new WP_Error(
'pdo_driver_not_loaded',
sprintf(
'<h1>%1$s</h1><p>%2$s</p>',
'PDO Driver for SQLite is missing',
'Your PHP installation appears not to have the right PDO drivers loaded. These are required for this version of WordPress and the type of database you have specified.'
)
),
'PDO Driver for SQLite is missing.'
);
}
require_once __DIR__ . '/class-wp-sqlite-lexer.php';
require_once __DIR__ . '/class-wp-sqlite-query-rewriter.php';
require_once __DIR__ . '/class-wp-sqlite-translator.php';
require_once __DIR__ . '/class-wp-sqlite-token.php';
require_once __DIR__ . '/class-wp-sqlite-pdo-user-defined-functions.php';
require_once __DIR__ . '/class-wp-sqlite-db.php';
require_once __DIR__ . '/install-functions.php';
/*
* Debug: Cross-check with MySQL.
* This is for debugging purpose only and requires files
* that are present in the GitHub repository
* but not the plugin published on WordPress.org.
*/
$crosscheck_tests_file_path = dirname( __DIR__, 2 ) . '/tests/class-wp-sqlite-crosscheck-db.php';
if ( defined( 'SQLITE_DEBUG_CROSSCHECK' ) && SQLITE_DEBUG_CROSSCHECK && file_exists( $crosscheck_tests_file_path ) ) {
require_once $crosscheck_tests_file_path;
$GLOBALS['wpdb'] = new WP_SQLite_Crosscheck_DB();
} else {
$GLOBALS['wpdb'] = new WP_SQLite_DB();
}
@@ -0,0 +1,230 @@
<?php
/**
* Main integration file.
*
* @package wp-sqlite-integration
* @since 1.0.0
*/
/**
* Function to create tables according to the schemas of WordPress.
*
* This is executed only once while installation.
*
* @since 1.0.0
*
* @return boolean
*
* @throws PDOException If the database connection fails.
*/
function sqlite_make_db_sqlite() {
include_once ABSPATH . 'wp-admin/includes/schema.php';
$table_schemas = wp_get_db_schema();
$queries = explode( ';', $table_schemas );
try {
$pdo = new PDO( 'sqlite:' . FQDB, null, null, array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ) ); // phpcs:ignore WordPress.DB.RestrictedClasses
} catch ( PDOException $err ) {
$err_data = $err->errorInfo; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$message = 'Database connection error!<br />';
$message .= sprintf( 'Error message is: %s', $err_data[2] );
wp_die( $message, 'Database Error!' );
}
$translator = new WP_SQLite_Translator( $pdo );
$query = null;
try {
$translator->begin_transaction();
foreach ( $queries as $query ) {
$query = trim( $query );
if ( empty( $query ) ) {
continue;
}
$result = $translator->query( $query );
if ( false === $result ) {
throw new PDOException( $translator->get_error_message() );
}
}
$translator->commit();
} catch ( PDOException $err ) {
$err_data = $err->errorInfo; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$err_code = $err_data[1];
$translator->rollback();
$message = sprintf(
'Error occurred while creating tables or indexes...<br />Query was: %s<br />',
var_export( $query, true )
);
$message .= sprintf( 'Error message is: %s', $err_data[2] );
wp_die( $message, 'Database Error!' );
}
/*
* Debug: Cross-check with MySQL.
* This is for debugging purpose only and requires files
* that are present in the GitHub repository
* but not the plugin published on WordPress.org.
*/
if ( defined( 'SQLITE_DEBUG_CROSSCHECK' ) && SQLITE_DEBUG_CROSSCHECK ) {
$host = DB_HOST;
$port = 3306;
if ( str_contains( $host, ':' ) ) {
$host_parts = explode( ':', $host );
$host = $host_parts[0];
$port = $host_parts[1];
}
$dsn = 'mysql:host=' . $host . '; port=' . $port . '; dbname=' . DB_NAME;
$pdo_mysql = new PDO( $dsn, DB_USER, DB_PASSWORD, array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ) ); // phpcs:ignore WordPress.DB.RestrictedClasses.mysql__PDO
$pdo_mysql->query( 'SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";' );
$pdo_mysql->query( 'SET time_zone = "+00:00";' );
foreach ( $queries as $query ) {
$query = trim( $query );
if ( empty( $query ) ) {
continue;
}
try {
$pdo_mysql->beginTransaction();
$pdo_mysql->query( $query );
} catch ( PDOException $err ) {
$err_data = $err->errorInfo; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$err_code = $err_data[1];
// phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
if ( 5 == $err_code || 6 == $err_code ) {
// If the database is locked, commit again.
$pdo_mysql->commit();
} else {
$pdo_mysql->rollBack();
$message = sprintf(
'Error occurred while creating tables or indexes...<br />Query was: %s<br />',
var_export( $query, true )
);
$message .= sprintf( 'Error message is: %s', $err_data[2] );
wp_die( $message, 'Database Error!' );
}
}
}
}
$pdo = null;
return true;
}
if ( ! function_exists( 'wp_install' ) ) {
/**
* Installs the site.
*
* Runs the required functions to set up and populate the database,
* including primary admin user and initial options.
*
* @since 1.0.0
*
* @param string $blog_title Site title.
* @param string $user_name User's username.
* @param string $user_email User's email.
* @param bool $is_public Whether the site is public.
* @param string $deprecated Optional. Not used.
* @param string $user_password Optional. User's chosen password. Default empty (random password).
* @param string $language Optional. Language chosen. Default empty.
* @return array {
* Data for the newly installed site.
*
* @type string $url The URL of the site.
* @type int $user_id The ID of the site owner.
* @type string $password The password of the site owner, if their user account didn't already exist.
* @type string $password_message The explanatory message regarding the password.
* }
*/
function wp_install( $blog_title, $user_name, $user_email, $is_public, $deprecated = '', $user_password = '', $language = '' ) {
if ( ! empty( $deprecated ) ) {
_deprecated_argument( __FUNCTION__, '2.6.0' );
}
wp_check_mysql_version();
wp_cache_flush();
/* SQLite changes: Replace the call to make_db_current_silent() with sqlite_make_db_sqlite(). */
sqlite_make_db_sqlite(); // phpcs:ignore PHPCompatibility.Extensions.RemovedExtensions.sqliteRemoved
populate_options();
populate_roles();
update_option( 'blogname', $blog_title );
update_option( 'admin_email', $user_email );
update_option( 'blog_public', $is_public );
// Freshness of site - in the future, this could get more specific about actions taken, perhaps.
update_option( 'fresh_site', 1 );
if ( $language ) {
update_option( 'WPLANG', $language );
}
$guessurl = wp_guess_url();
update_option( 'siteurl', $guessurl );
// If not a public site, don't ping.
if ( ! $is_public ) {
update_option( 'default_pingback_flag', 0 );
}
/*
* Create default user. If the user already exists, the user tables are
* being shared among sites. Just set the role in that case.
*/
$user_id = username_exists( $user_name );
$user_password = trim( $user_password );
$email_password = false;
$user_created = false;
if ( ! $user_id && empty( $user_password ) ) {
$user_password = wp_generate_password( 12, false );
$message = __( '<strong><em>Note that password</em></strong> carefully! It is a <em>random</em> password that was generated just for you.', 'sqlite-database-integration' );
$user_id = wp_create_user( $user_name, $user_password, $user_email );
update_user_meta( $user_id, 'default_password_nag', true );
$email_password = true;
$user_created = true;
} elseif ( ! $user_id ) {
// Password has been provided.
$message = '<em>' . __( 'Your chosen password.', 'sqlite-database-integration' ) . '</em>';
$user_id = wp_create_user( $user_name, $user_password, $user_email );
$user_created = true;
} else {
$message = __( 'User already exists. Password inherited.', 'sqlite-database-integration' );
}
$user = new WP_User( $user_id );
$user->set_role( 'administrator' );
if ( $user_created ) {
$user->user_url = $guessurl;
wp_update_user( $user );
}
wp_install_defaults( $user_id );
wp_install_maybe_enable_pretty_permalinks();
flush_rewrite_rules();
wp_new_blog_notification( $blog_title, $guessurl, $user_id, ( $email_password ? $user_password : __( 'The password you chose during installation.', 'sqlite-database-integration' ) ) );
wp_cache_flush();
/**
* Fires after a site is fully installed.
*
* @since 3.9.0
*
* @param WP_User $user The site owner.
*/
do_action( 'wp_install', $user );
return array(
'url' => $guessurl,
'user_id' => $user_id,
'password' => $user_password,
'password_message' => $message,
);
}
}