* define('USE_MYSQL', true);
*
*
* If you want to use SQLite, the line below will do. Or simply removing the line will
* be enough.
*
*
* define('USE_MYSQL', false);
*
*/
if (defined('USE_MYSQL') && USE_MYSQL) {
return;
}
function pdo_log_error($message, $data = null)
{
if (strpos($_SERVER['SCRIPT_NAME'], 'wp-admin') !== false) {
$admin_dir = '';
} else {
$admin_dir = 'wp-admin/';
}
die(<<
$message
$data
HTML ); } if (version_compare(PHP_VERSION, '5.4', '<')) { pdo_log_error('PHP version on this server is too old.', sprintf("Your server is running PHP version %d but this SQLite driver requires at least 5.4", phpversion())); } if (! extension_loaded('pdo')) { pdo_log_error('PHP PDO Extension is not loaded.', 'Your PHP installation appears to be missing the PDO extension which is required for this version of WordPress.'); } if (! extension_loaded('pdo_sqlite')) { pdo_log_error('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.'); } /** * Notice: * Your scripts have the permission to create directories or files on your server. * If you write in your wp-config.php like below, we take these definitions. * define('DB_DIR', '/full_path_to_the_database_directory/'); * define('DB_FILE', 'database_file_name'); */ /** * FQDBDIR is a directory where the sqlite database file is placed. * If DB_DIR is defined, it is used as FQDBDIR. */ if (defined('DB_DIR')) { if (substr(DB_DIR, -1, 1) != '/') { define('FQDBDIR', DB_DIR . '/'); } else { define('FQDBDIR', DB_DIR); } } else { if (defined('WP_CONTENT_DIR')) { define('FQDBDIR', WP_CONTENT_DIR . '/database/'); } else { define('FQDBDIR', ABSPATH . 'wp-content/database/'); } } /** * FQDB is a database file name. If DB_FILE is defined, it is used * as FQDB. */ if (defined('DB_FILE')) { define('FQDB', FQDBDIR . DB_FILE); } else { define('FQDB', FQDBDIR . '.ht.sqlite'); } /** * This class defines user defined functions(UDFs) for PDO library. * * These functions replace those used in the SQL statement with the PHP functions. * * Usage: * *
* new PDOSQLiteUDFS(ref_to_pdo_obj);
*
*
* This automatically enables ref_to_pdo_obj to replace the function in the SQL statement
* to the ones defined here.
*/
class PDOSQLiteUDFS
{
/**
* The class constructor
*
* Initializes the use defined functions to PDO object with PDO::sqliteCreateFunction().
*
* @param PDO $pdo
*/
public function __construct($pdo)
{
if (! $pdo) {
wp_die('Database is not initialized.', 'Database Error');
}
foreach ($this->functions as $f => $t) {
$pdo->sqliteCreateFunction($f, [$this, $t]);
}
}
/**
* array to define MySQL function => function defined with PHP.
*
* Replaced functions must be public.
*
* @var array
*/
private $functions = [
'month' => 'month',
'year' => 'year',
'day' => 'day',
'unix_timestamp' => 'unix_timestamp',
'now' => 'now',
'char_length' => 'char_length',
'md5' => 'md5',
'curdate' => 'curdate',
'rand' => 'rand',
'substring' => 'substring',
'dayofmonth' => 'day',
'second' => 'second',
'minute' => 'minute',
'hour' => 'hour',
'date_format' => 'dateformat',
'from_unixtime' => 'from_unixtime',
'date_add' => 'date_add',
'date_sub' => 'date_sub',
'adddate' => 'date_add',
'subdate' => 'date_sub',
'localtime' => 'now',
'localtimestamp' => 'now',
'isnull' => 'isnull',
'if' => '_if',
'regexpp' => 'regexp',
'concat' => 'concat',
'field' => 'field',
'log' => 'log',
'least' => 'least',
'greatest' => 'greatest',
'get_lock' => 'get_lock',
'release_lock' => 'release_lock',
'ucase' => 'ucase',
'lcase' => 'lcase',
'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 extract the month value from the date.
*
* @param string representing the date formatted as 0000-00-00.
*
* @return string representing the number of the month between 1 and 12.
*/
public function month($field)
{
$t = strtotime($field);
return date('n', $t);
}
/**
* Method to extract the year value from the date.
*
* @param string representing the date formatted as 0000-00-00.
*
* @return string representing the number of the year.
*/
public function year($field)
{
$t = strtotime($field);
return date('Y', $t);
}
/**
* Method to extract the day value from the date.
*
* @param string 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)
{
$t = strtotime($field);
return date('j', $t);
}
/**
* 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 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 SECOND() function.
*
* @param string representing the time formatted as '00:00:00'.
*
* @return number of unsigned integer
*/
public function second($field)
{
$t = strtotime($field);
return intval(date("s", $t));
}
/**
* Method to emulate MySQL MINUTE() function.
*
* @param string representing the time formatted as '00:00:00'.
*
* @return number of unsigned integer
*/
public function minute($field)
{
$t = strtotime($field);
return intval(date("i", $t));
}
/**
* Method to emulate MySQL HOUR() function.
*
* @param string representing the time formatted as '00:00:00'.
*
* @return number
*/
public function hour($time)
{
list($hours) = explode(":", $time);
return intval($hours);
}
/**
* Method to emulate MySQL FROM_UNIXTIME() function.
*
* @param integer of unix timestamp
* @param string to indicate the way of formatting(optional)
*
* @return string formatted as '0000-00-00 00:00:00'.
*/
public function from_unixtime($field, $format = null)
{
//convert to ISO time
$date = date("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 date("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 date("Y-m-d");
}
/**
* Method to emulate MySQL CHAR_LENGTH() function.
*
* @param string
*
* @return int unsigned integer for the length of the argument.
*/
public function char_length($field)
{
return strlen($field);
}
/**
* Method to emulate MySQL MD5() function.
*
* @param string
*
* @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 SUBSTRING() function.
*
* This function rewrites the function name to SQLite compatible substr(),
* which can manipulate UTF-8 characters.
*
* @param string $text
* @param integer $pos representing the start point.
* @param integer $len representing the length of the substring(optional).
*
* @return string
*/
public function substring($text, $pos, $len = null)
{
return "substr($text, $pos, $len)";
}
/**
* 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
*
* @return string formatted according to $format
*/
public function dateformat($date, $format)
{
$mysql_php_date_formats = [
'%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',
];
$t = strtotime($date);
$format = strtr($format, $mysql_php_date_formats);
$output = date($format, $t);
return $output;
}
/**
* Method to emulate MySQL DATE_ADD() function.
*
* This function adds the time value of $interval expression to $date.
* $interval is a single quoted strings rewritten by SQLiteQueryDriver::rewrite_query().
* It is calculated in the private function deriveInterval().
*
* @param string $date representing the start date.
* @param string $interval representing the expression of the time to add.
*
* @return string date formatted as '0000-00-00 00:00:00'.
* @throws Exception
*/
public function date_add($date, $interval)
{
$interval = $this->deriveInterval($interval);
switch (strtolower($date)) {
case "curdate()":
$objDate = new DateTime($this->curdate());
$objDate->add(new DateInterval($interval));
$formatted = $objDate->format("Y-m-d");
break;
case "now()":
$objDate = new DateTime($this->now());
$objDate->add(new DateInterval($interval));
$formatted = $objDate->format("Y-m-d H:i:s");
break;
default:
$objDate = new DateTime($date);
$objDate->add(new DateInterval($interval));
$formatted = $objDate->format("Y-m-d H:i:s");
}
return $formatted;
}
/**
* Method to emulate MySQL DATE_SUB() function.
*
* This function subtracts the time value of $interval expression from $date.
* $interval is a single quoted strings rewritten by SQLiteQueryDriver::rewrite_query().
* It is calculated in the private function deriveInterval().
*
* @param string $date representing the start date.
* @param string $interval representing the expression of the time to subtract.
*
* @return string date formatted as '0000-00-00 00:00:00'.
* @throws Exception
*/
public function date_sub($date, $interval)
{
$interval = $this->deriveInterval($interval);
switch (strtolower($date)) {
case "curdate()":
$objDate = new DateTime($this->curdate());
$objDate->sub(new DateInterval($interval));
$returnval = $objDate->format("Y-m-d");
break;
case "now()":
$objDate = new DateTime($this->now());
$objDate->sub(new DateInterval($interval));
$returnval = $objDate->format("Y-m-d H:i:s");
break;
default:
$objDate = new DateTime($date);
$objDate->sub(new DateInterval($interval));
$returnval = $objDate->format("Y-m-d H:i:s");
}
return $returnval;
}
/**
* Method to calculate the interval time between two dates value.
*
* @access private
*
* @param string $interval white space separated expression.
*
* @return string representing the time to add or substract.
*/
private function deriveInterval($interval)
{
$interval = trim(substr(trim($interval), 8));
$parts = explode(' ', $interval);
foreach ($parts as $part) {
if (! empty($part)) {
$_parts[] = $part;
}
}
$type = strtolower(end($_parts));
switch ($type) {
case "second":
$unit = 'S';
return 'PT' . $_parts[0] . $unit;
break;
case "minute":
$unit = 'M';
return 'PT' . $_parts[0] . $unit;
break;
case "hour":
$unit = 'H';
return 'PT' . $_parts[0] . $unit;
break;
case "day":
$unit = 'D';
return 'P' . $_parts[0] . $unit;
break;
case "week":
$unit = 'W';
return 'P' . $_parts[0] . $unit;
break;
case "month":
$unit = 'M';
return 'P' . $_parts[0] . $unit;
break;
case "year":
$unit = 'Y';
return 'P' . $_parts[0] . $unit;
break;
case "minute_second":
list($minutes, $seconds) = explode(':', $_parts[0]);
return 'PT' . $minutes . 'M' . $seconds . 'S';
case "hour_second":
list($hours, $minutes, $seconds) = explode(':', $_parts[0]);
return 'PT' . $hours . 'H' . $minutes . 'M' . $seconds . 'S';
case "hour_minute":
list($hours, $minutes) = explode(':', $_parts[0]);
return 'PT' . $hours . 'H' . $minutes . 'M';
case "day_second":
$days = intval($_parts[0]);
list($hours, $minutes, $seconds) = explode(':', $_parts[1]);
return 'P' . $days . 'D' . 'T' . $hours . 'H' . $minutes . 'M' . $seconds . 'S';
case "day_minute":
$days = intval($_parts[0]);
list($hours, $minutes) = explode(':', $parts[1]);
return 'P' . $days . 'D' . 'T' . $hours . 'H' . $minutes . 'M';
case "day_hour":
$days = intval($_parts[0]);
$hours = intval($_parts[1]);
return 'P' . $days . 'D' . 'T' . $hours . 'H';
case "year_month":
list($years, $months) = explode('-', $_parts[0]);
return 'P' . $years . 'Y' . $months . 'M';
}
}
/**
* Method to emulate MySQL DATE() function.
*
* @param string $date formatted as unix time.
*
* @return string formatted as '0000-00-00'.
*/
public function date($date)
{
return date("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 various types $field
*
* @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 unknonw $expression the statement to be evaluated as true or false.
* @param unknown $true statement or value returned if $expression is true.
* @param unknown $false statement or value returned if $expression is false.
*
* @return unknown
*/
public function _if($expression, $true, $false)
{
return ($expression == true) ? $true : $false;
}
/**
* Method to emulate MySQL REGEXP() function.
*
* @param string $field haystack
* @param string $pattern : regular expression to match.
*
* @return integer 1 if matched, 0 if not matched.
*/
public function regexp($field, $pattern)
{
$pattern = str_replace('/', '\/', $pattern);
$pattern = "/" . $pattern . "/i";
return preg_match($pattern, $field);
}
/**
* Method to emulate MySQL CONCAT() function.
*
* SQLite does have CONCAT() function, but it has a different syntax from MySQL.
* So this function must be manipulated here.
*
* @param string
*
* @return NULL if the argument is null | string conatenated if the argument is given.
*/
public function concat()
{
$returnValue = "";
$argsNum = func_num_args();
$argsList = func_get_args();
for ($i = 0; $i < $argsNum; $i++) {
if (is_null($argsList[$i])) {
return null;
}
$returnValue .= $argsList[$i];
}
return $returnValue;
}
/**
* 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.
*
* @param int...|float... variable number of string, integer or double
*
* @return int unsigned integer
*/
public function field()
{
global $wpdb;
$numArgs = func_num_args();
if ($numArgs < 2 or is_null(func_get_arg(0))) {
return 0;
} else {
$arg_list = func_get_args();
}
$searchString = array_shift($arg_list);
$str_to_check = substr($searchString, 0, strpos($searchString, '.'));
$str_to_check = str_replace($wpdb->prefix, '', $str_to_check);
if ($str_to_check && in_array(trim($str_to_check), $wpdb->tables)) {
return 0;
}
for ($i = 0; $i < $numArgs - 1; $i++) {
if ($searchString === strtolower($arg_list[$i])) {
return $i + 1;
}
}
return 0;
}
/**
* Method to emulate MySQL LOG() function.
*
* Used with one argument, it returns the natural logarithm of X.
*
* LOG(X)
*
* Used with two arguments, it returns the natural logarithm of X base B.
*
* LOG(B, X)
*
* 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.
*
* @param integer representing the base of the logarithm, which is optional.
* @param double value to turn into logarithm.
*
* @return double | NULL
*/
public function log()
{
$numArgs = func_num_args();
if ($numArgs == 1) {
$arg1 = func_get_arg(0);
return log($arg1);
} elseif ($numArgs == 2) {
$arg1 = func_get_arg(0);
$arg2 = func_get_arg(1);
return log($arg1) / log($arg2);
} else {
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
* @param integer $timeout
*
* @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
*
* @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
*
* @return string SQLite compatible function name.
*/
public function ucase($string)
{
return "upper($string)";
}
/**
* Method to emulate MySQL LCASE() function.
*
*
* This is MySQL alias for lower() function. This function rewrites it
* to SQLite compatible name lower().
*
* @param string
*
* @return string SQLite compatible function name.
*/
public function lcase($string)
{
return "lower($string)";
}
/**
* Method to emulate MySQL INET_NTOA() function.
*
* This function gets 4 or 8 bytes integer and turn it into the network address.
*
* @param unsigned 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
*
* @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
* @param string end
*
* @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 needle
* @param string haystack
* @param integer position
*
* @return integer
*/
public function locate($substr, $str, $pos = 0)
{
if (! extension_loaded('mbstring')) {
if (($val = strpos($str, $substr, $pos)) !== false) {
return $val + 1;
} else {
return 0;
}
} else {
if (($val = mb_strpos($str, $substr, $pos)) !== false) {
return $val + 1;
} else {
return 0;
}
}
}
/**
* Method to return GMT date in the string format.
*
* @param none
*
* @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.
*
* @param none
*
* @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.
*
* @param none
*
* @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.
*
* @param none
*
* @return string representing the version number: major_version.minor_version
*/
public function version()
{
//global $required_mysql_version;
//return $required_mysql_version;
return '5.5';
}
}
/**
* This class extends PDO class and does the real work.
*
* It accepts a request from wpdb class, initialize PDO instance,
* execute SQL statement, and returns the results to WordPress.
*/
class PDOEngine extends PDO
{
/**
* Class variable to check if there is an error.
*
* @var boolean
*/
public $is_error = false;
/**
* Class variable which is used for CALC_FOUND_ROW query.
*
* @var unsigned integer
*/
public $found_rows_result = null;
/**
* Class variable used for query with ORDER BY FIELD()
*
* @var array of the object
*/
public $pre_ordered_results = null;
/**
* Class variable to store the rewritten queries.
*
* @var array
* @access private
*/
private $rewritten_query;
/**
* Class variable to have what kind of query to execute.
*
* @var string
* @access private
*/
private $query_type;
/**
* Class variable to store the result of the query.
*
* @var array reference to the PHP object
* @access private
*/
private $results = null;
/**
* Class variable to store the results of the query.
*
* This is for the backward compatibility.
*
* @var array reference to the PHP object
* @access private
*/
private $_results = null;
/**
* Class variable to reference to the PDO instance.
*
* @var PDO object
* @access private
*/
private $pdo;
/**
* Class variable to store the query string prepared to execute.
*
* @var string|array
*/
private $prepared_query;
/**
* Class variable to store the values in the query string.
*
* @var array
* @access private
*/
private $extracted_variables = [];
/**
* Class variable to store the error messages.
*
* @var array
* @access private
*/
private $error_messages = [];
/**
* Class variable to store the file name and function to cause error.
*
* @var array
* @access private
*/
private $errors;
/**
* Class variable to store the query strings.
*
* @var array
*/
public $queries = [];
/**
* Class variable to store the affected row id.
*
* @var unsigned integer
* @access private
*/
private $last_insert_id;
/**
* Class variable to store the number of rows affected.
*
* @var unsigned integer
*/
private $affected_rows;
/**
* Class variable to store the queried column info.
*
* @var array
*/
private $column_data;
/**
* Variable to emulate MySQL affected row.
*
* @var integer
*/
private $num_rows;
/**
* Return value from query().
*
* Each query has its own return value.
*
* @var mixed
*/
private $return_value;
/**
* Variable to determine which insert query to use.
*
* Whether VALUES clause in the INSERT query can take multiple values or not
* depends on the version of SQLite library. We check the version and set
* this varable to true or false.
*
* @var boolean
*/
private $can_insert_multiple_rows = false;
/**
*
* @var integer
*/
private $param_num;
/**
* Varible to check if there is an active transaction.
* @var boolean
* @access protected
*/
protected $has_active_transaction = false;
/**
* Constructor
*
* Create PDO object, set user defined functions and initialize other settings.
* Don't use parent::__construct() because this class does not only returns
* PDO instance but many others jobs.
*
* Constructor definition is changed since version 1.7.1.
*
* @param none
*/
function __construct()
{
register_shutdown_function([$this, '__destruct']);
if (! is_file(FQDB)) {
$this->prepare_directory();
}
$dsn = 'sqlite:' . FQDB;
if (isset($GLOBALS['@pdo'])) {
$this->pdo = $GLOBALS['@pdo'];
} else {
$locked = false;
$status = 0;
do {
try {
$this->pdo = new PDO($dsn, null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
new PDOSQLiteUDFS($this->pdo);
$GLOBALS['@pdo'] = $this->pdo;
} catch (PDOException $ex) {
$status = $ex->getCode();
if ($status == 5 || $status == 6) {
$locked = true;
} else {
$err_message = $ex->getMessage();
}
}
} while ($locked);
if ($status > 0) {
$message = 'Database initialization error!Sorry, we cannot determine the type of query that is requested.
The query is %s
", 'sqlite-integration'), $statement); $this->set_error(__LINE__, __FUNCTION__, $bailoutString); } switch (strtolower($this->query_type)) { case 'set': $this->return_value = false; break; case 'foundrows': $_column = ['FOUND_ROWS()' => '']; $column = []; if (! is_null($this->found_rows_result)) { $this->num_rows = $this->found_rows_result; $_column['FOUND_ROWS()'] = $this->num_rows; //foreach ($this->found_rows_result[0] as $key => $value) { //$_column['FOUND_ROWS()'] = $value; //} $column[] = new ObjectArray($_column); $this->results = $column; $this->found_rows_result = null; } break; case 'insert': if ($this->can_insert_multiple_rows) { $this->execute_insert_query_new($statement); } else { $this->execute_insert_query($statement); } break; case 'create': $result = $this->execute_create_query($statement); $this->return_value = $result; break; case 'alter': $result = $this->execute_alter_query($statement); $this->return_value = $result; break; case 'show_variables': $this->return_value = $this->show_variables_workaround($statement); break; case 'showstatus': $this->return_value = $this->show_status_workaround($statement); break; case 'drop_index': $pattern = '/^\\s*(DROP\\s*INDEX\\s*.*?)\\s*ON\\s*(.*)/im'; if (preg_match($pattern, $statement, $match)) { $drop_query = 'ALTER TABLE ' . trim($match[2]) . ' ' . trim($match[1]); $this->query_type = 'alter'; $result = $this->execute_alter_query($drop_query); $this->return_value = $result; } else { $this->return_value = false; } break; default: $engine = $this->prepare_engine($this->query_type); $this->rewritten_query = $engine->rewrite_query($statement, $this->query_type); if (! is_null($this->pre_ordered_results)) { $this->results = $this->pre_ordered_results; $this->num_rows = $this->return_value = count($this->results); $this->pre_ordered_results = null; break; } $this->queries[] = "Rewritten:\n$this->rewritten_query"; $this->extract_variables(); $prepared_query = $this->prepare_query(); $this->execute_query($prepared_query); if (! $this->is_error) { $this->process_results($engine); } else { // Error } break; } if (defined('PDO_DEBUG') && PDO_DEBUG === true) { file_put_contents(FQDBDIR . 'debug.txt', $this->get_debug_info(), FILE_APPEND); } return $this->return_value; } /** * Method to return inserted row id. */ public function get_insert_id() { return $this->last_insert_id; } /** * Method to return the number of rows affected. */ public function get_affected_rows() { return $this->affected_rows; } /** * Method to return the queried column names. * * These data are meaningless for SQLite. So they are dummy emulating * MySQL columns data. * * @return array of the object */ public function get_columns() { if (! empty($this->results)) { $primary_key = [ 'meta_id', 'comment_ID', 'link_ID', 'option_id', 'blog_id', 'option_name', 'ID', 'term_id', 'object_id', 'term_taxonomy_id', 'umeta_id', 'id', ]; $unique_key = ['term_id', 'taxonomy', 'slug']; $data = [ 'name' => '', // column name 'table' => '', // table name 'max_length' => 0, // max length of the column 'not_null' => 1, // 1 if not null 'primary_key' => 0, // 1 if column has primary key 'unique_key' => 0, // 1 if column has unique key 'multiple_key' => 0, // 1 if column doesn't have unique key 'numeric' => 0, // 1 if column has numeric value 'blob' => 0, // 1 if column is blob 'type' => '', // type of the column 'unsigned' => 0, // 1 if column is unsigned integer 'zerofill' => 0 // 1 if column is zero-filled ]; if (preg_match("/\s*FROM\s*(.*)?\s*/i", $this->rewritten_query, $match)) { $table_name = trim($match[1]); } else { $table_name = ''; } foreach ($this->results[0] as $key => $value) { $data['name'] = $key; $data['table'] = $table_name; if (in_array($key, $primary_key)) { $data['primary_key'] = 1; } elseif (in_array($key, $unique_key)) { $data['unique_key'] = 1; } else { $data['multiple_key'] = 1; } $this->column_data[] = new ObjectArray($data); $data['name'] = ''; $data['table'] = ''; $data['primary_key'] = 0; $data['unique_key'] = 0; $data['multiple_key'] = 0; } return $this->column_data; } else { return null; } } /** * Method to return the queried result data. * * @return mixed */ public function get_query_results() { return $this->results; } /** * Method to return the number of rows from the queried result. */ public function get_num_rows() { return $this->num_rows; } /** * Method to return the queried results according to the query types. * * @return mixed */ public function get_return_value() { return $this->return_value; } /** * Method to return error messages. * * @return string */ public function get_error_message() { if (count($this->error_messages) === 0) { $this->is_error = false; $this->error_messages = []; return ''; } $output = '' . ob_get_contents() . ''; ob_end_clean(); return $output; } /** * Method to return information about query string for debugging. * * @return string */ private function get_debug_info() { $output = ''; foreach ($this->queries as $q) { $output .= $q . "\n"; } return $output; } /** * Method to clear previous data. */ private function flush() { $this->rewritten_query = ''; $this->query_type = ''; $this->results = null; $this->_results = null; $this->last_insert_id = null; $this->affected_rows = null; $this->column_data = []; $this->num_rows = null; $this->return_value = null; $this->extracted_variables = []; $this->error_messages = []; $this->is_error = false; $this->queries = []; $this->param_num = 0; } /** * Method to include the apropreate class files. * * It is not a good habit to change the include files programatically. * Needs to be fixed some other way. * * @param string $query_type * * @return object reference to apropreate driver */ private function prepare_engine($query_type = null) { if (stripos($query_type, 'create') !== false) { $engine = new CreateQuery(); } elseif (stripos($query_type, 'alter') !== false) { $engine = new AlterQuery(); } else { $engine = new PDOSQLiteDriver(); } return $engine; } /** * Method to create a PDO statement object from the query string. * * @return PDOStatement */ private function prepare_query() { $this->queries[] = "Prepare:\n" . $this->prepared_query; $reason = 0; $message = ''; $statement = null; do { try { $statement = $this->pdo->prepare($this->prepared_query); } catch (PDOException $err) { $reason = $err->getCode(); $message = $err->getMessage(); } } while (5 == $reason || 6 == $reason); if ($reason > 0) { $err_message = sprintf("Problem preparing the PDO SQL Statement. Error was: %s", $message); $this->set_error(__LINE__, __FUNCTION__, $err_message); } return $statement; } /** * Method to execute PDO statement object. * * This function executes query and sets the variables to give back to WordPress. * The variables are class fields. So if success, no return value. If failure, it * returns void and stops. * * @param object $statement of PDO statement * * @return boolean */ private function execute_query($statement) { $reason = 0; $message = ''; if (! is_object($statement)) { return false; } if (count($this->extracted_variables) > 0) { $this->queries[] = "Executing:\n" . var_export($this->extracted_variables, true); do { if ($this->query_type == 'update' || $this->query_type == 'replace') { try { $this->beginTransaction(); $statement->execute($this->extracted_variables); $this->commit(); } catch (PDOException $err) { $reason = $err->getCode(); $message = $err->getMessage(); $this->rollBack(); } } else { try { $statement->execute($this->extracted_variables); } catch (PDOException $err) { $reason = $err->getCode(); $message = $err->getMessage(); } } } while (5 == $reason || 6 == $reason); } else { $this->queries[] = 'Executing: (no parameters)'; do { if ($this->query_type == 'update' || $this->query_type == 'replace') { try { $this->beginTransaction(); $statement->execute(); $this->commit(); } catch (PDOException $err) { $reason = $err->getCode(); $message = $err->getMessage(); $this->rollBack(); } } else { try { $statement->execute(); } catch (PDOException $err) { $reason = $err->getCode(); $message = $err->getMessage(); } } } while (5 == $reason || 6 == $reason); } if ($reason > 0) { $err_message = sprintf("Error while executing query! Error message was: %s", $message); $this->set_error(__LINE__, __FUNCTION__, $err_message); return false; } else { $this->_results = $statement->fetchAll(PDO::FETCH_OBJ); } //generate the results that $wpdb will want to see switch ($this->query_type) { case 'insert': case 'update': case 'replace': $this->last_insert_id = $this->pdo->lastInsertId(); $this->affected_rows = $statement->rowCount(); $this->return_value = $this->affected_rows; break; case 'select': case 'show': case 'showcolumns': case 'showindex': case 'describe': case 'desc': case 'check': case 'analyze': //case "foundrows": $this->num_rows = count($this->_results); $this->return_value = $this->num_rows; break; case 'delete': $this->affected_rows = $statement->rowCount(); $this->return_value = $this->affected_rows; break; case 'alter': case 'drop': case 'create': case 'optimize': case 'truncate': if ($this->is_error) { $this->return_value = false; } else { $this->return_value = true; } break; } } /** * Method to extract field data to an array and prepare the query statement. * * If original SQL statement is CREATE query, this function does nothing. */ private function extract_variables() { if ($this->query_type == 'create') { $this->prepared_query = $this->rewritten_query; return; } //long queries can really kill this $pattern = '/(? 10000000) { $query = preg_replace_callback($pattern, [$this, 'replace_variables_with_placeholders'], $this->rewritten_query); } else { do { if ($limit > 10000000) { $this->set_error(__LINE__, __FUNCTION__, 'The query is too big to parse properly'); break; //no point in continuing execution, would get into a loop } else { ini_set('pcre.backtrack_limit', $limit); $query = preg_replace_callback($pattern, [$this, 'replace_variables_with_placeholders'], $this->rewritten_query); } $limit = $limit * 10; } while (is_null($query)); //reset the pcre.backtrack_limit ini_set('pcre.backtrack_limit', $_limit); } if (isset($query)) { $this->queries[] = "With Placeholders:\n" . $query; $this->prepared_query = $query; } } /** * Call back function to replace field data with PDO parameter. * * @param string $matches * * @return string */ private function replace_variables_with_placeholders($matches) { //remove the wordpress escaping mechanism $param = stripslashes($matches[0]); //remove trailing spaces $param = trim($param); //remove the quotes at the end and the beginning if (in_array($param[strlen($param) - 1], ["'", '"'])) { $param = substr($param, 0, -1);//end } if (in_array($param[0], ["'", '"'])) { $param = substr($param, 1); //start } //$this->extracted_variables[] = $param; $key = ':param_' . $this->param_num++; $this->extracted_variables[] = $param; //return the placeholder //return ' ? '; return ' ' . $key . ' '; } /** * Method to determine which query type the argument is. * * It takes the query string ,determines the type and returns the type string. * If the query is the type that SQLite Integration can't executes, returns false. * * @param string $query * * @return boolean|string */ private function determine_query_type($query) { $result = preg_match('/^\\s*(SET|EXPLAIN|PRAGMA|SELECT\\s*FOUND_ROWS|SELECT|INSERT|UPDATE|REPLACE|DELETE|ALTER|CREATE|DROP\\s*INDEX|DROP|SHOW\\s*\\w+\\s*\\w+\\s*|DESCRIBE|DESC|TRUNCATE|OPTIMIZE|CHECK|ANALYZE)/i', $query, $match); if (! $result) { return false; } $this->query_type = strtolower($match[1]); if (stripos($this->query_type, 'found') !== false) { $this->query_type = 'foundrows'; } if (stripos($this->query_type, 'show') !== false) { if (stripos($this->query_type, 'show table status') !== false) { $this->query_type = 'showstatus'; } elseif (stripos($this->query_type, 'show tables') !== false || stripos($this->query_type, 'show full tables') !== false) { $this->query_type = 'show'; } elseif (stripos($this->query_type, 'show columns') !== false || stripos($this->query_type, 'show fields') !== false || stripos($this->query_type, 'show full columns') !== false) { $this->query_type = 'showcolumns'; } elseif (stripos($this->query_type, 'show index') !== false || stripos($this->query_type, 'show indexes') !== false || stripos($this->query_type, 'show keys') !== false) { $this->query_type = 'showindex'; } elseif (stripos($this->query_type, 'show variables') !== false || stripos($this->query_type, 'show global variables') !== false || stripos($this->query_type, 'show session variables') !== false) { $this->query_type = 'show_variables'; } else { return false; } } if (stripos($this->query_type, 'drop index') !== false) { $this->query_type = 'drop_index'; } return true; } /** * Method to execute INSERT query for SQLite version 3.7.11 or later. * * SQLite version 3.7.11 began to support multiple rows insert with values * clause. This is for that version or later. * * @param string $query */ private function execute_insert_query_new($query) { $engine = $this->prepare_engine($this->query_type); $this->rewritten_query = $engine->rewrite_query($query, $this->query_type); $this->queries[] = "Rewritten:\n" . $this->rewritten_query; $this->extract_variables(); $statement = $this->prepare_query(); $this->execute_query($statement); } /** * Method to execute INSERT query for SQLite version 3.7.10 or lesser. * * It executes the INSERT query for SQLite version 3.7.10 or lesser. It is * necessary to rewrite multiple row values. * * @param string $query */ private function execute_insert_query($query) { global $wpdb; $multi_insert = false; $statement = null; $engine = $this->prepare_engine($this->query_type); if (preg_match('/(INSERT.*?VALUES\\s*)(\(.*\))/imsx', $query, $matched)) { $query_prefix = $matched[1]; $values_data = $matched[2]; if (stripos($values_data, 'ON DUPLICATE KEY') !== false) { $exploded_parts = $values_data; } elseif (stripos($query_prefix, "INSERT INTO $wpdb->comments") !== false) { $exploded_parts = $values_data; } else { $exploded_parts = $this->parse_multiple_inserts($values_data); } $count = count($exploded_parts); if ($count > 1) { $multi_insert = true; } } if ($multi_insert) { $first = true; foreach ($exploded_parts as $value) { if (substr($value, -1, 1) === ')') { $suffix = ''; } else { $suffix = ')'; } $query_string = $query_prefix . ' ' . $value . $suffix; $this->rewritten_query = $engine->rewrite_query($query_string, $this->query_type); $this->queries[] = "Rewritten:\n" . $this->rewritten_query; $this->extracted_variables = []; $this->extract_variables(); if ($first) { $statement = $this->prepare_query(); $this->execute_query($statement); $first = false; } else { $this->execute_query($statement); } } } else { $this->rewritten_query = $engine->rewrite_query($query, $this->query_type); $this->queries[] = "Rewritten:\n" . $this->rewritten_query; $this->extract_variables(); $statement = $this->prepare_query(); $this->execute_query($statement); } } /** * Method to help rewriting multiple row values insert query. * * It splits the values clause into an array to execute separately. * * @param string $values * * @return array */ private function parse_multiple_inserts($values) { $tokens = preg_split("/(''|(?prepare_engine($this->query_type); $rewritten_query = $engine->rewrite_query($query); $reason = 0; $message = ''; //$queries = explode(";", $this->rewritten_query); try { $this->beginTransaction(); foreach ($rewritten_query as $single_query) { $this->queries[] = "Executing:\n" . $single_query; $single_query = trim($single_query); if (empty($single_query)) { continue; } $this->pdo->exec($single_query); } $this->commit(); } catch (PDOException $err) { $reason = $err->getCode(); $message = $err->getMessage(); if (5 == $reason || 6 == $reason) { $this->commit(); } else { $this->rollBack(); } } if ($reason > 0) { $err_message = sprintf("Problem in creating table or index. Error was: %s", $message); $this->set_error(__LINE__, __FUNCTION__, $err_message); return false; } return true; } /** * Method to execute ALTER TABLE query. * * @param string * * @return boolean */ private function execute_alter_query($query) { $engine = $this->prepare_engine($this->query_type); $reason = 0; $message = ''; $re_query = ''; $rewritten_query = $engine->rewrite_query($query, $this->query_type); if (is_array($rewritten_query) && array_key_exists('recursion', $rewritten_query)) { $re_query = $rewritten_query['recursion']; unset($rewritten_query['recursion']); } try { $this->beginTransaction(); if (is_array($rewritten_query)) { foreach ($rewritten_query as $single_query) { $this->queries[] = "Executing:\n" . $single_query; $single_query = trim($single_query); if (empty($single_query)) { continue; } $this->pdo->exec($single_query); } } else { $this->queries[] = "Executing:\n" . $rewritten_query; $rewritten_query = trim($rewritten_query); $this->pdo->exec($rewritten_query); } $this->commit(); } catch (PDOException $err) { $reason = $err->getCode(); $message = $err->getMessage(); if (5 == $reason || 6 == $reason) { $this->commit(); usleep(10000); } else { $this->rollBack(); } } if ($re_query != '') { $this->query($re_query); } if ($reason > 0) { $err_message = sprintf("Problem in executing alter query. Error was: %s", $message); $this->set_error(__LINE__, __FUNCTION__, $err_message); return false; } return true; } /** * Method to execute SHOW VARIABLES query * * This query is meaningless for SQLite. This function returns null data with some * exceptions and only avoids the error message. * * @param string * * @return bool */ private function show_variables_workaround($query) { $dummy_data = ['Variable_name' => '', 'Value' => null]; $pattern = '/SHOW\\s*VARIABLES\\s*LIKE\\s*(.*)?$/im'; if (preg_match($pattern, $query, $match)) { $value = str_replace("'", '', $match[1]); $dummy_data['Variable_name'] = trim($value); // this is set for Wordfence Security Plugin if ($value == 'max_allowed_packet') { $dummy_data['Value'] = 1047552; } else { $dummy_data['Value'] = ''; } } $_results[] = new ObjectArray($dummy_data); $this->results = $_results; $this->num_rows = count($this->results); $this->return_value = $this->num_rows; return true; } /** * Method to execute SHOW TABLE STATUS query. * * This query is meaningless for SQLite. This function return dummy data. * * @param string * * @return bool */ private function show_status_workaround($query) { $pattern = '/^SHOW\\s*TABLE\\s*STATUS\\s*LIKE\\s*(.*?)$/im'; if (preg_match($pattern, $query, $match)) { $table_name = str_replace("'", '', $match[1]); } else { $table_name = ''; } $dummy_data = [ 'Name' => $table_name, 'Engine' => '', 'Version' => '', 'Row_format' => '', 'Rows' => 0, 'Avg_row_length' => 0, 'Data_length' => 0, 'Max_data_length' => 0, 'Index_length' => 0, 'Data_free' => 0, 'Auto_increment' => 0, 'Create_time' => '', 'Update_time' => '', 'Check_time' => '', 'Collation' => '', 'Checksum' => '', 'Create_options' => '', 'Comment' => '', ]; $_results[] = new ObjectArray($dummy_data); $this->results = $_results; $this->num_rows = count($this->results); $this->return_value = $this->num_rows; return true; } /** * Method to format the queried data to that of MySQL. * * @param string $engine */ private function process_results($engine) { if (in_array($this->query_type, ['describe', 'desc', 'showcolumns'])) { $this->convert_to_columns_object(); } elseif ('showindex' === $this->query_type) { $this->convert_to_index_object(); } elseif (in_array($this->query_type, ['check', 'analyze'])) { $this->convert_result_check_or_analyze(); } else { $this->results = $this->_results; } } /** * Method to format the error messages and put out to the file. * * When $wpdb::suppress_errors is set to true or $wpdb::show_errors is set to false, * the error messages are ignored. * * @param string $line where the error occurred. * @param string $function to indicate the function name where the error occurred. * @param string $message * * @return boolean */ private function set_error($line, $function, $message) { global $wpdb; $this->errors[] = ["line" => $line, "function" => $function]; $this->error_messages[] = $message; $this->is_error = true; if ($wpdb->suppress_errors) { return false; } if (! $wpdb->show_errors) { return false; } file_put_contents(FQDBDIR . 'debug.txt', "Line $line, Function: $function, Message: $message \n", FILE_APPEND); } /** * Method to change the queried data to PHP object format. * * It takes the associative array of query results and creates a numeric * array of anonymous objects * * @access private */ private function convert_to_object() { $_results = []; if (count($this->results) === 0) { echo $this->get_error_message(); } else { foreach ($this->results as $row) { $_results[] = new ObjectArray($row); } } $this->results = $_results; } /** * Method to convert the SHOW COLUMNS query data to an object. * * It rewrites pragma results to mysql compatible array * when query_type is describe, we use sqlite pragma function. * * @access private */ private function convert_to_columns_object() { $_results = []; $_columns = [ //Field names MySQL SHOW COLUMNS returns 'Field' => "", 'Type' => "", 'Null' => "", 'Key' => "", 'Default' => "", 'Extra' => "", ]; if (empty($this->_results)) { echo $this->get_error_message(); } else { foreach ($this->_results as $row) { $_columns['Field'] = $row->name; $_columns['Type'] = $row->type; $_columns['Null'] = $row->notnull ? "NO" : "YES"; $_columns['Key'] = $row->pk ? "PRI" : ""; $_columns['Default'] = $row->dflt_value; $_results[] = new ObjectArray($_columns); } } $this->results = $_results; } /** * Method to convert SHOW INDEX query data to PHP object. * * It rewrites the result of SHOW INDEX to the Object compatible with MySQL * added the WHERE clause manipulation (ver 1.3.1) * * @access private */ private function convert_to_index_object() { $_results = []; $_columns = [ 'Table' => "", 'Non_unique' => "",// unique -> 0, not unique -> 1 'Key_name' => "",// the name of the index 'Seq_in_index' => "",// column sequence number in the index. begins at 1 'Column_name' => "", 'Collation' => "",//A(scend) or NULL 'Cardinality' => "", 'Sub_part' => "",// set to NULL 'Packed' => "",// How to pack key or else NULL 'Null' => "",// If column contains null, YES. If not, NO. 'Index_type' => "",// BTREE, FULLTEXT, HASH, RTREE 'Comment' => "", ]; if (count($this->_results) == 0) { echo $this->get_error_message(); } else { foreach ($this->_results as $row) { if ($row->type == 'table' && ! stripos($row->sql, 'primary')) { continue; } if ($row->type == 'index' && stripos($row->name, 'sqlite_autoindex') !== false) { continue; } switch ($row->type) { case 'table': $pattern1 = '/^\\s*PRIMARY.*\((.*)\)/im'; $pattern2 = '/^\\s*(\\w+)?\\s*.*PRIMARY.*(?!\()/im'; if (preg_match($pattern1, $row->sql, $match)) { $col_name = trim($match[1]); $_columns['Key_name'] = 'PRIMARY'; $_columns['Non_unique'] = 0; $_columns['Column_name'] = $col_name; } elseif (preg_match($pattern2, $row->sql, $match)) { $col_name = trim($match[1]); $_columns['Key_name'] = 'PRIMARY'; $_columns['Non_unique'] = 0; $_columns['Column_name'] = $col_name; } break; case 'index': if (stripos($row->sql, 'unique') !== false) { $_columns['Non_unique'] = 0; } else { $_columns['Non_unique'] = 1; } if (preg_match('/^.*\((.*)\)/i', $row->sql, $match)) { $col_name = str_replace("'", '', $match[1]); $_columns['Column_name'] = trim($col_name); } $_columns['Key_name'] = $row->name; break; default: break; } $_columns['Table'] = $row->tbl_name; $_columns['Collation'] = null; $_columns['Cardinality'] = 0; $_columns['Sub_part'] = null; $_columns['Packed'] = null; $_columns['Null'] = 'NO'; $_columns['Index_type'] = 'BTREE'; $_columns['Comment'] = ''; $_results[] = new ObjectArray($_columns); } if (stripos($this->queries[0], 'WHERE') !== false) { preg_match('/WHERE\\s*(.*)$/im', $this->queries[0], $match); list($key, $value) = explode('=', $match[1]); $key = trim($key); $value = preg_replace("/[\';]/", '', $value); $value = trim($value); foreach ($_results as $result) { if (! empty($result->$key) && is_scalar($result->$key) && stripos($value, $result->$key) !== false) { unset($_results); $_results[] = $result; break; } } } } $this->results = $_results; } /** * Method to the CHECK query data to an object. * * @access private */ private function convert_result_check_or_analyze() { $results = []; if ($this->query_type == 'check') { $_columns = [ 'Table' => '', 'Op' => 'check', 'Msg_type' => 'status', 'Msg_text' => 'OK', ]; } else { $_columns = [ 'Table' => '', 'Op' => 'analyze', 'Msg_type' => 'status', 'Msg_text' => 'Table is already up to date', ]; } $_results[] = new ObjectArray($_columns); $this->results = $_results; } /** * Method to check SQLite library version. * * This is used for checking if SQLite can execute multiple rows insert. * * @return version number string or 0 * @access private */ private function get_sqlite_version() { try { $statement = $this->pdo->prepare('SELECT sqlite_version()'); $statement->execute(); $result = $statement->fetch(PDO::FETCH_NUM); return $result[0]; } catch (PDOException $err) { return '0'; } } /** * Method to call PDO::beginTransaction(). * * @see PDO::beginTransaction() * @return boolean */ #[\ReturnTypeWillChange] public function beginTransaction() { if ($this->has_active_transaction) { return false; } $this->has_active_transaction = $this->pdo->beginTransaction(); return $this->has_active_transaction; } /** * Method to call PDO::commit(). * * @see PDO::commit() */ #[\ReturnTypeWillChange] public function commit() { $isSuccess = $this->pdo->commit(); $this->has_active_transaction = false; return $isSuccess; } /** * Method to call PDO::rollBack(). * * @see PDO::rollBack() */ #[\ReturnTypeWillChange] public function rollBack() { $isSuccess = $this->pdo->rollBack(); $this->has_active_transaction = false; return $isSuccess; } } /** * Class to change queried data to PHP object. * * @author kjm */ #[\AllowDynamicProperties] class ObjectArray { function __construct($data = null, &$node = null) { foreach ($data as $key => $value) { if (is_array($value)) { if (! $node) { $node =& $this; } $node->$key = new \stdClass(); self::__construct($value, $node->$key); } else { if (! $node) { $node =& $this; } $node->$key = $value; } } } } /** * This class extends wpdb and replaces it. * * It also rewrites some methods that use mysql specific functions. */ class wpsqlitedb extends \wpdb { /** * Database Handle * @var PDOEngine */ protected $dbh; /** * Constructor * * Unlike wpdb, no credentials are needed. */ public function __construct() { parent::__construct('', '', '', ''); } /** * 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 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 = []) { } /** * 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 * @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 $string to escape * * @return string escaped */ function _real_escape($string) { return addslashes($string); } /** * 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 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() : ''; if (! empty($err)) { $str = $err[2]; } else { $str = ''; } } $EZSQL_ERROR[] = ['query' => $this->last_query, 'error_str' => $str]; if ($this->suppress_errors) { return false; } wp_load_translations_early(); if ($caller = $this->get_caller()) { $error_str = sprintf(__('WordPress database error %1$s for query %2$s made by %3$s'), $str, $this->last_query, $caller); } else { $error_str = sprintf(__('WordPress database error %1$s for query %2$s'), $str, $this->last_query); } 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); print "
WordPress database error: [$str]
$query