2019-05-30 07:32:17 +03:00
< ? php
/**
*
* @ package Kleeja
2020-04-11 22:45:48 +02:00
* @ copyright ( c ) 2007 Kleeja . net
2019-05-30 07:32:17 +03:00
* @ license ./ docs / license . txt
*
*/
//no for directly open
2024-10-07 17:15:05 +01:00
if ( ! defined ( 'IN_COMMON' ))
{
2019-05-30 07:32:17 +03:00
exit ();
}
if ( ! defined ( 'SQL_LAYER' )) :
2024-10-07 17:15:05 +01:00
define ( 'SQL_LAYER' , 'sqlite' );
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
class KleejaDatabase
2019-05-30 07:32:17 +03:00
{
2024-10-07 17:15:05 +01:00
/** @var SQLITE3 */
private $connect_id = null ;
/** @var SQLite3Result */
private $result = null ;
public $dbprefix = '' ;
private $dbname = '' ;
public $query_num = 0 ;
private $in_transaction = 0 ;
public $debugr = [];
private $show_errors = true ;
/**
* connect
*
* @ param string $location path of sqlite database
* @ param string $db_username not needed
* @ param string $db_password not needed
* @ param string $db_name not needed
* @ param string $dbprefix tables prefix
*/
public function __construct ( $location , $db_username , $db_password , $db_name , $dbprefix )
{
try
{
if ( class_exists ( 'SQLite3' ))
{
$this -> connect_id = new SQLite3 ( PATH . $db_name , SQLITE3_OPEN_READWRITE );
}
else
{
$this -> error_msg ( 'SQLite3 extension is not installed in your server!' );
}
}
catch ( Exception $e )
{
//...
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
$this -> dbprefix = $dbprefix ;
$this -> dbname = $db_name ;
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
//no error
if ( defined ( 'SQL_NO_ERRORS' ))
{
$this -> show_errors = false ;
}
if ( ! $this -> connect_id )
{
//loggin -> no database -> close connection
$this -> close ();
$this -> error_msg ( 'We can not connect to the sqlite database, check location or existence of the SQLite dirver ...' );
return false ;
}
//connecting
kleeja_log ( '[Connected] : ' . kleeja_get_page ());
return $this -> connect_id ;
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
public function __destruct ()
{
2019-05-30 07:32:17 +03:00
$this -> close ();
}
2024-10-07 17:15:05 +01:00
public function is_connected ()
{
return ! ( is_null ( $this -> connect_id ) || empty ( $this -> connect_id ));
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
// close the connection
public function close ()
{
if ( ! $this -> is_connected ())
{
return true ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
// Commit any remaining transactions
if ( $this -> in_transaction )
{
$this -> query ( 'COMMIT;' );
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
//loggin -> close connection
kleeja_log ( '[Closing connection] : ' . kleeja_get_page ());
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( ! is_resource ( $this -> connect_id ))
{
return true ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
return @ mysqli_close ( $this -> connect_id );
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
// encoding functions
public function set_utf8 ()
{
//$this->set_names('utf8');
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
public function set_names ( $charset )
{
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
public function client_encoding ()
{
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
public function version ()
{
return SQLite3 :: version ()[ 'versionString' ];
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
/**
* execute a query
*
* @ param string $query
* @ param boolean $transaction
* @ return bool
*/
public function query ( $query , $transaction = false )
{
//no connection
if ( ! $this -> is_connected ())
{
return false ;
}
2019-05-30 07:32:17 +03:00
//
2024-10-07 17:15:05 +01:00
// Remove any pre-existing queries
2019-05-30 07:32:17 +03:00
//
2024-10-07 17:15:05 +01:00
unset ( $this -> result );
if ( strpos ( $query , 'CREATE TABLE' ) !== false || strpos ( $query , 'ALTER DATABASE' ) !== false )
{
$sqlite_types = [
'/AUTO_INCREMENT/i' => '' ,
'/VARCHAR\s?(\\([0-9]+\\))?/i' => 'TEXT' ,
'/COLLATE\s+([a-z0-9_]+)/i' => '' ,
'/(TINY|SMALL|MEDIUM|BIG)?INT\s?(\([0-9]+\))?\s?(UNSIGNED)?/i' => 'INTEGER ' ,
'/(TINY|MEDIUM|LONG)?TEXT/i' => 'TEXT' ,
'/KEY\s`?([a-z0-9_]+)`?\s\(`?([a-z0-9_]+)`?(\([0-9]+\))?\)\s?,?/i' => '' ,
'/\)(\s{0,4}ENGINE=([a-z0-9_]+))?(\s{0,4}DEFAULT)?(\s{0,4}CHARSET=([a-z0-9_]+))?(\s{0,4}COLLATE=([a-z0-9_]+))?(\s{0,4}AUTOINCREMENT)?(\s{0,4}=\s?1)?(\s{0,4};)?/i' => ')' ,
'/,\s+\)/' => ')' ,
'/INTEGER\s{0,4}NOT\s{0,4}NULL/i' => 'INTEGER' ,
];
//todo extract keys and add as CREATE INDEX index_name ON table (column);
foreach ( $sqlite_types as $old_type => $new_type )
{
$query = preg_replace ( $old_type , $new_type , $query );
}
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
if ( ! empty ( $query ))
{
//debug
$srartum_sql = get_microtime ();
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( $transaction && ! $this -> in_transaction )
{
$this -> query ( 'BEGIN;' );
$this -> in_transaction = true ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
$this -> result = @ $this -> connect_id -> query ( $query );
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
//debug .. //////////////
$this -> debugr [ $this -> query_num + 1 ] = [ $query , sprintf ( '%.5f' , get_microtime () - $srartum_sql )];
////////////////
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( ! $this -> result )
{
$this -> error_msg ( 'Error In query' );
}
else
{
//let's debug it
kleeja_log ( '[Query] : --> ' . $query );
}
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
else
{
if ( $this -> in_transaction )
{
$this -> result = $this -> connect_id -> query ( 'COMMIT;' );
}
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
//is there any result
if ( $this -> result )
{
if ( $this -> in_transaction )
{
$this -> in_transaction = false ;
if ( ! $this -> connect_id -> query ( 'COMMIT;' ))
{
$this -> connect_id -> query ( 'ROLLBACK;' );
return false ;
}
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
$this -> query_num ++ ;
return $this -> result ;
}
else
{
if ( $this -> in_transaction )
{
2019-05-30 07:32:17 +03:00
$this -> connect_id -> query ( 'ROLLBACK;' );
2024-10-07 17:15:05 +01:00
$this -> in_transaction = false ;
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
return false ;
2019-05-30 07:32:17 +03:00
}
}
2024-10-07 17:15:05 +01:00
/**
* build structured query [ 'SELECT' => ... , 'FROM' => ... , ... ]
*
* @ param array $query
* @ return string
*/
public function build ( $query )
{
$sql = '' ;
if ( isset ( $query [ 'SELECT' ]) && isset ( $query [ 'FROM' ]))
{
$sql = 'SELECT ' . $query [ 'SELECT' ] . ' FROM ' . $query [ 'FROM' ];
if ( isset ( $query [ 'JOINS' ]))
{
foreach ( $query [ 'JOINS' ] as $cur_join )
{
$sql .= ' ' . key ( $cur_join ) . ' ' . current ( $cur_join ) . ' ON ' . $cur_join [ 'ON' ];
}
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( ! empty ( $query [ 'WHERE' ]))
{
$sql .= ' WHERE ' . $query [ 'WHERE' ];
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( ! empty ( $query [ 'GROUP BY' ]))
{
$sql .= ' GROUP BY ' . $query [ 'GROUP BY' ];
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
if ( ! empty ( $query [ 'HAVING' ]))
{
$sql .= ' HAVING ' . $query [ 'HAVING' ];
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( ! empty ( $query [ 'ORDER BY' ]))
{
$sql .= ' ORDER BY ' . $query [ 'ORDER BY' ];
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( ! empty ( $query [ 'LIMIT' ]))
{
$sql .= ' LIMIT ' . $query [ 'LIMIT' ];
}
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
elseif ( isset ( $query [ 'INSERT' ]))
{
$sql = 'INSERT INTO ' . $query [ 'INTO' ];
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( ! empty ( $query [ 'INSERT' ]))
{
$sql .= ' (' . $query [ 'INSERT' ] . ')' ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
$sql .= ' VALUES(' . $query [ 'VALUES' ] . ')' ;
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
elseif ( isset ( $query [ 'UPDATE' ]))
{
$sql = 'UPDATE ' . $query [ 'UPDATE' ] . ' SET ' . $query [ 'SET' ];
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( ! empty ( $query [ 'WHERE' ]))
{
$sql .= ' WHERE ' . $query [ 'WHERE' ];
}
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
elseif ( isset ( $query [ 'DELETE' ]))
{
$sql = 'DELETE FROM ' . $query [ 'DELETE' ];
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( ! empty ( $query [ 'WHERE' ]))
{
$sql .= ' WHERE ' . $query [ 'WHERE' ];
}
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
elseif ( isset ( $query [ 'REPLACE' ]))
{
$sql = 'REPLACE INTO ' . $query [ 'INTO' ];
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( ! empty ( $query [ 'REPLACE' ]))
{
$sql .= ' (' . $query [ 'REPLACE' ] . ')' ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
$sql .= ' VALUES(' . $query [ 'VALUES' ] . ')' ;
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
return $this -> query ( $sql );
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
/**
* free the memmory from the last results
*
* @ param SQLite3Result $query_id optional
* @ return bool
*/
public function freeresult ( $query_id = 0 )
{
if ( ! $query_id )
{
$query_id = $this -> result ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( $query_id )
{
$query_id -> finalize ();
return true ;
}
else
{
return false ;
}
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
/**
* fetch results ( alias of fetch_array )
*
* @ param SQLite3Result $query_id
* @ return array
*/
public function fetch ( $query_id = 0 )
{
return $this -> fetch_array ( $query_id );
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
/**
* fetch results
*
* @ param SQLite3Result $query_id
* @ return array
*/
public function fetch_array ( $query_id = 0 )
{
if ( ! $query_id )
{
$query_id = $this -> result ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( $query_id && $query_id -> numColumns () > 0 )
{
return $query_id -> fetchArray ( SQLITE3_ASSOC );
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
return false ;
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
/**
* return number of rows of result ( not efficient )
*
* @ param SQLite3Result $query_id
* @ return int
*/
public function num_rows ( $query_id = 0 )
{
if ( ! $query_id )
{
$query_id = $this -> result ;
}
2019-05-30 07:32:17 +03:00
2019-05-30 19:55:49 +03:00
2024-10-07 17:15:05 +01:00
if ( $query_id && $results = $query_id -> numColumns ())
{
return $results ;
}
2019-05-30 19:55:49 +03:00
2024-10-07 17:15:05 +01:00
return false ;
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
/**
* return the id of latest inserted record
*
* @ return int
*/
public function insert_id ()
{
return $this -> is_connected () ? $this -> connect_id -> lastInsertRowID () : false ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
/**
* extra escape
*
* @ param string $msg
* @ return string
*/
public function escape ( $msg )
{
$msg = htmlspecialchars ( $msg , ENT_QUOTES );
$msg = $this -> real_escape ( $msg );
return $msg ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
/**
* escape
* @ param string $msg
* @ return int | string
*/
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
public function real_escape ( $msg )
{
return SQLite3 :: escapeString ( $msg );
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
/**
* number of affected rows by latest action
*
* @ return int
*/
public function affected ()
{
return $this -> is_connected () ? $this -> connect_id -> changes () : false ;
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
/**
* information
*
* @ return string
*/
public function server_info ()
{
return 'SQLite3 ' . $this -> version ();
2019-05-30 07:32:17 +03:00
}
2024-10-07 17:15:05 +01:00
/**
* present error messages
*
* @ param string $msg
* @ return void
*/
private function error_msg ( $msg )
{
if ( ! $this -> show_errors || ( defined ( 'SQL_NO_ERRORS' ) || defined ( 'MYSQL_NO_ERRORS' )))
{
kleeja_log ( 'SQLite3: ' . $msg );
return false ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
list ( $error_no , $error_msg ) = $this -> get_error ();
$error_sql = $this -> connect_id ? @ current ( $this -> debugr [ $this -> query_num + 1 ]) : '' ;
//some ppl want hide their table names
if ( ! defined ( 'DEV_STAGE' ))
{
$error_sql = preg_replace_callback ( " # \ s { 1,3}`* { $this -> dbprefix } ([a-z0-9]+)`* \ s { 1,3}# " , function ( $m ) {
return ' <span style="color:blue">' . substr ( $m [ 1 ], 0 , 1 ) . '</span> ' ;
}, $error_sql );
$error_msg = preg_replace_callback ( " # { $this -> dbname } . { $this -> dbprefix } ([a-z0-9]+)# " , function ( $m ) {
return ' <span style="color:blue">' . substr ( $m [ 1 ], 0 , 1 ) . '</span> ' ;
}, $error_msg );
$error_sql = preg_replace_callback ( " # \ s { 1,3}(from|update|into) \ s { 1,3}([a-z0-9]+) \ s { 1,3}#i " , function ( $m ) {
return $m [ 1 ] . ' <span style="color:blue">' . substr ( $m [ 2 ], 0 , 1 ) . '</span> ' ;
}, $error_sql );
$error_msg = preg_replace_callback ( " # \ s { 1,3}(from|update|into) \ s { 1,3}([a-z0-9]+) \ s { 1,3}#i " , function ( $m ) {
return $m [ 1 ] . ' <span style="color:blue">' . substr ( $m [ 2 ], 0 , 1 ) . '</span> ' ;
}, $error_msg );
$error_msg = preg_replace_callback ( " # \ s'([^']+)'@'([^']+)'#i " , function ( $m ) {
return ' <span style="color:blue">hidden</span>@' . $m [ 2 ] . ' ' ;
}, $error_msg );
$error_sql = preg_replace ( " #password \ s*= \ s*'[^']+'#i " , " password='<span style= \" color:blue \" >hidden</span>' " , $error_sql );
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
//is this error related to updating?
$updating_related = false ;
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( strpos ( $error_msg , 'Unknown column' ) !== false || strpos ( $error_msg , 'no such table' ) !== false )
{
$updating_related = true ;
}
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
header ( 'HTTP/1.1 500 Internal Server Error' );
$error_message = '<html><head><title>MYSQL ERROR</title>' ;
$error_message .= " <style>BODY { font-family:'Tahoma',serif;font-size:12px;}.error { }</style></head><body> " ;
$error_message .= '<br />' ;
$error_message .= '<div class="error">' ;
$error_message .= " <a href='#' onclick='window.location.reload( false );'>click to Refresh this page ...</a><br /> " ;
$error_message .= '<h2>Sorry , We encountered a MySQL error: ' . ( $msg != '' ? $msg : '' ) . '</h2>' ;
if ( $error_sql != '' )
{
$error_message .= " <br />--[query]-------------------------- <br /> $error_sql <br />---------------------------------<br /><br /> " ;
}
$error_message .= " [ $error_no : $error_msg ] <br /> " ;
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
if ( $updating_related )
{
global $config ;
$error_message .= '<br /><strong>Your Kleeja database might be old, try to update it now from: ' . rtrim ( $config [ 'siteurl' ], '/' ) . '/install</strong>' ;
$error_message .= " <br /><br><strong>If this error happened after installing a plugin, add <span style= \" background-color:#ccc; padding:2px \" >define('STOP_PLUGINS', true);</span> to end of config.php file.</strong> " ;
}
$error_message .= " <br /><br /><strong>Script: Kleeja <br /><a href='https://kleeja.net'>Kleeja Website</a></strong> " ;
$error_message .= '</b></div>' ;
$error_message .= '</body></html>' ;
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
print $error_message ;
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
//loggin -> error
kleeja_log ( '[SQL ERROR] : "' . $error_no . ' : ' . $error_msg . '" -->' );
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
@ $this -> close ();
2019-05-30 07:32:17 +03:00
2024-10-07 17:15:05 +01:00
exit ();
2019-05-30 07:32:17 +03:00
}
2020-12-25 18:21:25 +01:00
2024-10-07 17:15:05 +01:00
/**
* return last error as [ code , message ]
*
* @ return array
*/
public function get_error ()
{
if ( $this -> connect_id )
{
return [ $this -> connect_id -> lastErrorCode (), $this -> connect_id -> lastErrorMsg ()];
}
else
{
return [ 0 , 'uknown-error-not-connected' ];
}
}
public function showErrors ()
{
$this -> show_errors = true ;
}
public function hideErrors ()
{
$this -> show_errors = false ;
}
2020-12-25 18:21:25 +01:00
}
2019-05-30 07:32:17 +03:00
endif ;