<?php
/**
* SimpleSQL - The Simplest Way to Query
* Copyright (c) 2004 Paul Williamson <webmaster@protonage.net>
*
* This class is ment to shortcut common MySQL database access tasks.
* FOR PHP 5 ONLY! This class will not work on any versons < PHP5!
* Also, This class was designed for users who want to use the new
* PHP5 Improved MySQL Extension (mysqli) but do not have MySQL > 4.1.
* Mysqli allows you to access the functionality provided by MySQL > 4.1
* Later on in the future, I will add a layer to this class that allows
* mysqli functionality.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* @category Database
* @package SimpleSQL
* @author Paul Williamson <webmaster@protonage.net>
* @readme ./docs/README.txt
* @example ./docs/EXAMPLES.txt
* @standards ./docs/CODING_STANDARDS.txt
* @version $Id: simplesql.class.php5,v 2.0 2004/08/08 19:37:45 paul Exp $
*/
define ('FETCH_ASSOC', 0);
define ('FETCH_NUM', 1);
define ('FETCH_OBJECT', 2);
class simplesql
{
/**
* Constants;
*
* VERSION; SimpleSQL Version @const string
* LOG_PATH; Path to logs @const string
* MAX_SIZE; Max size of log file @const int
* DATE_FORMAT; Date Format for Log @const string
* @access global
*/
const VERSION = 'SimpleSQL v2.0.1 PHP5';
const LOG_PATH = '.';
const MAX_SIZE = 2000000;
const DATE_FORMAT = 'F jS, Y, H:i:s T';
/**
* Database Variables;
*
* You may hardcode these variables and not have to worry
* about providing any arguments when you create a new class
*
* $fetch_default; How the result is returned @var int
*
* $host; Database Server @var string
* $user; Database Username @var string
* $pass; Database Password @var string
* $dbname; Database name @var string
* $socket; Database Socket @var string
* $port; Database Port @var int
*
* $resultrows; Number of rows from query @var int
* $table; Last table used in query @var string
*
* $connection; Connection link identifier @var resource
* $preg_sql_noquote; Regular Expression
* pattern for fix_sql() @var string
* $preg_sql_quote; Regular Expression
* pattern for fix_sql() @var string
* $preg_lst_noquote; Regular Expression
* pattern for fix_list() @var string
* $preg_lst_quote; Regular Expression
* pattern for fix_list() @var string
* Be careful with backrefs
* @access mixed
*/
public $fetch_default = FETCH_OBJECT;
protected $host = 'localhost';
protected $user = 'root';
protected $pass = '';
protected $dbname = 'database';
protected $socket;
protected $port = 3306;
protected $resultrows = 0;
protected $table = '';
private $connection = false;
private $preg_sql_noquote = '/^([a-zA-Z0-9\._]+ *= *[^, ]+)((, )|$)(.*)/i';
private $preg_sql_quote = '/^([a-zA-Z0-9\._]+( *= *[\'"`].*?[\'"`])?)((, )|$)(.*)/i';
private $preg_lst_noquote = '/^([a-zA-Z0-9\._]*) *(,|$) *(.*)$/is';
private $preg_lst_quote = '/^{quote}(.*?){quote} *(,|$) *(.*)$/is';
/**
* Common Variables;
*
* $tracklog; Put every event in array @var bool
* $showlog; Print log array to screen (debug) @var bool
* $logtofile; Log tracks to a data file @var bool
* $logfulltofile; Entire log array is @var bool
* serilized into file
* @access protected
*/
protected $common = array(
'tracklog' => true,
'showlog' => false,
'logtofile' => true,
'logfulltofile' => true
);
/**
* Debug Variables;
* $session; Session ID for class @var string
* $birth; Time the class began @var float
* $querytime; Time spent executing queries @var float
* $querycount; Number of executed queries @var int
* $log; Log of actions & errors @var array
* $log; Log of just errors @var array
* $result; Result from query @var resource
*/
protected $session;
protected $birth = 0;
protected $querytime = 0;
protected $querycount = 0;
protected $log = array();
protected $error_log = array();
protected $result = array();
/**
* Constructor; sets up connection varables and connects
*
* @param string $db_host Server
* @param string $db_user Username
* @param string $db_pass Password
* @param string $db_name Database Name
* @param int $db_port Database Port
* @param string $db_socket Database Socket
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* @return void
*/
function __construct($db_host = NULL, $db_user = NULL,
$db_pass = NULL, $db_name = NULL,
$db_port = 3306, $db_socket = '')
{
$this->birth = $this->timenow();
$this->session = md5(round($this->birth, 3)*1000);
$this->log=array(
'session' => $this->session,
'date' => date("F jS, Y, H:i:s T"),
'birth' => $this->birth,
'age' => $this->age(),
'mysql' => array()
);
$this->set_param($db_host, 'host');
$this->set_param($db_user, 'user');
$this->set_param($db_pass, 'pass');
$this->set_param($db_name, 'dbname');
$this->set_param($db_port, 'port');
$this->set_param($db_socket, 'socket');
try {
$this->connect();
} catch (Exception $e) {
$this->track('error', '__construct', self::VERSION
. " session Id: {$this->log['session']}"
. ' could not establish a connection'
. ' to server ' . $this->host . ' and'
. ' therefore could not be created.'
. ' View error log for more details.',
$e
);
trigger_error(self::VERSION
. " session Id: {$this->log['session']} could not"
. ' be created! The class will now terminate. Exception:'
. ' ' . $e->getmessage(),
E_USER_ERROR
);
}
$this->track('event', '__construct', self::VERSION
. " session Id: {$this->log['session']} has been"
. " constructed @ {$this->log['date']}"
);
}
/**
* Destruct; destroys connection and reports logs
*
* @param boolean $re Reconnect
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* @return void
*/
function __destruct()
{
$this->kill();
$this->track('event', '__destruct', self::VERSION
. " session Id: {$this->log['session']} has been"
. ' destructed @ ' . date(self::DATE_FORMAT) . '.'
);
$this->log['mysql'] = array_merge($this->log['mysql'],
array(
'querycount' => $this->querycount,
'querytime' => $this->querytime
)
);
$this->log['age'] = $this->age();
if ($this->common['logtofile']) $this->do_log();
if ($this->common['logfulltofile']) $this->do_fulllog();
if ($this->common['showlog']) $this->printlog();
}
/**
* Call; Picks up any undeclaired methods
*
* @param string $m Method called
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* @return void
*/
function __call($m)
{
$from = trim($m);
$this->track('error', $from,
'Method does not exists!'
);
}
/**
* Get; Get a variable's value
*
* @param string $var variable
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* @return mixed
*/
function __get($var)
{
if (isset($this->common[$var])) {
return $this->common[$var];
} else {
$this->track('error',
'__get',
"Variable {$var} does not exists"
);
return null;
}
}
/**
* Set; Set a variable's value
*
* @param string $var variable
* @param string $val value to be assigned
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* @return void
*/
function __set($var, $val)
{
if (isset($this->common[$var])) {
$this->common[$var] = $val;
} else {
$this->track('error',
'__set',
"Variable {$var} does not exists"
);
}
}
/**
* SelectDB; Selects the database
*
* @param string $db Database to select
* @param resouce $conn conection resource
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return boolean
*/
public function selectdb($db, $conn)
{
if (!@mysql_select_db($db, $conn)) {
$this->track('error', 'connect',
"Could not select database {$this->dbname}.",
mysql_error($this->connection)
);
$this->connection = false;
return false;
}
return true;
}
/**
* Connect; connects to a database
*
* @param boolean $re Reconnect
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return boolean
*/
public function connect($re = false)
{
$server = "{$this->host}:{$this->port}"
. (!$this->socket ? '' : $this->socket);
$r = $re ? 'Rec' : 'C';
if (!$this->connection || !$re) {
if (($this->connection = @mysql_connect($server, $this->user, $this->pass)) === false) {
$this->track('error', 'connect',
"Could not open a connection to `{$server}`.",
mysql_error()
);
throw new Exception(mysql_error($this->connection));
return false;
}
if (!$this->selectdb($this->dbname, $this->connection)) {
throw new Exception('Could not select database: ' . $this->dbname);
return false;
}
} else {
if (!mysql_ping($this->connection)) {
$this->track('error', 'connect',
"Could not reconnect to `{$server}`.",
mysql_error($this->connection)
);
throw new Exception(mysql_error($this->connection));
return false;
}
}
$this->track('event', 'connect',
"{$r}onnected to `{$server}`."
);
return true;
}
/**
* Query; Sends query to database
*
* @param string $query Query String
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return resource
*/
final public function query($query)
{
$start = $this->timenow();
$result = @mysql_query($query, $this->connection);
$querytime = $this->timenow()-$start;
$querydeath = $this->age();
if ($result !== false) {
$this->track('event', 'query',
"Query: {$query}", '',
$querydeath
);
} else {
$this->track('error', 'query',
"Query Failed: `{$query}`",
mysql_error($this->connection),
$querydeath
);
throw new Exception(mysql_error($this->connection));
return false;
}
$this->querytime += $querytime;
$this->querycount ++;
$data = array();
if (substr(trim(strtoupper($query)), 0, 6) == 'SELECT') {
$data = mysql_fetch_array(mysql_query("EXPLAIN {$query}",
$this->connection),
MYSQL_ASSOC
);
$this->resultrows = mysql_num_rows($result);
}
$type = '';
$rows = 0;
$key = '';
$possible_keys = '';
$Extra = '';
$intensity = 1;
extract($data);
if ($querytime > 0.05) $intensity++;
if ($querytime > 0.1) $intensity++;
if ($querytime > 1) $intensity++;
if ($type == 'ALL') $intensity++;
if ($type == 'index') $intensity++;
if ($type == 'range') $intensity++;
if ($type == 'ref') $intensity++;
if ($rows >= 200) $intensity++;
if (!empty($possible_keys) && empty($key)) {
$intensity++;
}
if ((strpos($Extra, 'Using filesort') !== false) || (strpos($Extra, 'Using temporary') !== false)) {
$intensity++;
}
$data['query'] = $query;
$data['query_time'] = $querytime;
$data['query_death'] = $querydeath;
$data['query_intensity'] = $intensity;
$data['result_rows'] = $this->resultrows;
$this->log['mysql'][] = $data;
if (isset($Comment)) {
$this->track('error', 'query',
"Query Warning",
$Comment
);
throw new Exception($Comment);
$result = false;
}
return $result;
}
/**
* Fetch_Query; Send a query and fetch the results
*
* @param mixed $query Query that will be sent
* @param int $fetch Controls the result datatype
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return mixed
*/
public function fetch_query($query, $fetch = NULL)
{
$this->set_param($fetch, 'fetch_default');
$return = false;
try {
if (($result = $this->query($query)) !== false) {
while($row = $this->fetch($result, $fetch)) {
$return[] = $row;
}
$this->result = $return;
}
} catch (Exception $e) {
$this->track('error', 'fetch_query',
$e->getmessage()
);
}
$this->track('event', 'fetch_query',
'Successful Call (Query: `' . $query . '`)'
);
return $return;
}
/**
* Fetch_Col; Fetch (a) Coloumn(s) of data from the database
*
* @param mixed $field Field(s) that will be used
* @param mixed $table Table that will be used
* @param int $fetch Controls the result datatype
* @param string $order field to order by (append DESC if needed)
* @param mixed $limit Limit the result to this number
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return mixed
*/
public function fetch_col($field = NULL, $table = NULL,
$fetch = NULL, $order = NULL, $limit = NULL)
{
$this->set_param($table, 'table');
$this->set_param($fetch, 'fetch_default');
$sql = array();
$return = false;
if (empty($field)) {
unset($field);
$field = $this->fetch(
$this->query("SHOW COLUMNS FROM {$table}"),
FETCH_NUM
);
$field = $field[0];
}
for ($z = 0; $z < 2; $z++) {
$a = $z ? 'table' : 'field';
$sql[$a] = is_array($$a) ? implode(", ", $$a) : $$a;
}
$sql['order'] = $order;
$sql['limit'] = $limit;
try {
$query = $this->fix_sql($sql);
if (($result = $this->query($query)) !== false) {
while($row = $this->fetch($result, $fetch)) {
$return[] = $row;
}
$this->result = $return;
}
} catch (Exception $e) {
$this->track('error', 'fetch_col',
$e->getmessage()
);
}
$this->track('event', 'fetch_col',
'Successful Call (Query: `' . $query . '`)'
);
return $return;
}
/**
* Fetch_Row; Fetch (a) Row(s) of data from the database
*
* This function is nearly identical to Fetch Column but instead
* this function will fetch all fields in the table instead of
* the ones listed in the arguments. Also this function has all
* optional arguments.
*
* @param mixed $field Field(s) that will be used
* @param mixed $table Table that will be used
* @param int $fetch Controls the result datatype (array or object)
* @param string $order field to order by (append DESC if needed)
* @param mixed $limit Limit the result to this number
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return mixed
*/
public function fetch_row($field = NULL, $table = NULL,
$fetch = NULL, $order = NULL, $limit = NULL)
{
$this->set_param($table, 'table');
$this->set_param($fetch, 'fetch_default');
$sql = array();
$return = false;
for ($z = 0; $z < 2; $z++) {
$a = $z ? 'table' : 'field';
$sql[$a] = is_array($$a) ? implode(", ", $$a) : $$a;
}
$sql['order'] = $order;
$sql['limit'] = $limit;
try {
$query = $this->fix_sql($sql, 'ALL');
if (($result = $this->query($query)) !== false) {
while($row = $this->fetch($result, $fetch)) {
$return[] = $row;
}
$this->result = $return;
}
} catch (Exception $e) {
$this->track('error', 'fetch_row',
$e->getmessage()
);
}
$this->track('event', 'fetch_row',
'Successful Call (Query: `' . $query . '`)'
);
return $return;
}
/**
* Fetch_Search; Fetch data bassed on a search pattern
*
* This function will pass a regular expression string ($find)
* to the fix_sql private function.
*
* @param string $find The varable that will be found in the DB (regexp)
* @param mixed $field Field(s) that will be used
* @param mixed $table Table that will be used
* @param int $fetch Controls the result datatype (array or object)
* @param string $order field to order by (append DESC if needed)
* @param mixed $limit Limit the result to this number
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return mixed
*/
public function fetch_search($find, $field = NULL, $table = NULL,
$fetch = NULL, $order = NULL, $limit = NULL)
{
$this->set_param($table, 'table');
$this->set_param($fetch, 'fetch_default');
$sql = array();
$return = false;
if (empty($field)) {
unset($field);
$field = $this->fetch(
$this->query("SHOW COLUMNS FROM {$table}"),
FETCH_NUM
);
$field = $field[0];
}
for ($z = 0; $z < 2; $z++) {
$a = $z ? 'table' : 'field';
$sql[$a] = is_array($$a) ? implode(", ", $$a) : $$a;
}
$sql['search'] = addcslashes($find, '\'');
$sql['order'] = $order;
$sql['limit'] = $limit;
try {
$query = $this->fix_sql($sql, 'ALL');
if (($result = $this->query($query)) !== false) {
while($row = $this->fetch($result, $fetch)) {
$return[] = $row;
}
$this->result = $return;
}
} catch (Exception $e) {
$this->track('error', 'fetch_search',
$e->getmessage()
);
}
$this->track('event', 'fetch_search',
'Successful Call (Query: `' . $query . '`)'
);
return $return;
}
/**
* Insert; Insert data into a table
*
* @param mixed $fields Field(s) that will inserted data
* @param mixed $values Value(s) that will be inserted
* @param string $table Table that will be used
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return boolean
*/
public function insert($fields, $values, $table = NULL)
{
$this->set_param($table, 'table');
$sql = array();
try {
for ($i = 0; $i < 2; $i++) {
$a = $i ? 'fields' : 'values';
${substr($a, 0, 5)} = !is_array($$a) ?
$this->fix_list($$a) :
$$a;
}
} catch (Exception $e) {
$this->track('error', 'insert',
$e->getmessage()
);
return false;
}
if (count($field) != count($value)) {
$this->track('error', 'insert',
'Fields and Values do not match in array size'
);
return false;
}
unset($fields);
foreach ($field as $f) {
if (!$this->field_exists($f, $table)) {
$this->track('error', 'insert',
"Field: {$f} does not exists in Table: {$table}"
);
return false;
}
$fields .= "{$f}, ";
}
$sql['insert'] = $table;
$sql['field'] = $this->list_trim($fields);
$sql['value'] = $value;
try {
$query = $this->fix_sql($sql);
$this->query($query);
} catch (Exception $e) {
$this->track('error', 'insert',
$e->getmessage()
);
return false;
}
$this->track('event', 'insert',
'Successful Call (Query: `' . $query . '`)'
);
return true;
}
/**
* Delete; Delete data from a table
*
* Call this function with limit argument set to -1
* to truncat the table.
*
* @param mixed $wfield Row that will be deleted
* @param boolean $pass A verification array, set it to true
* if you are sure you do not want to add a limit to a no-where-clause
* DELETE query
* @param string $table Table that will be used
* @param mixed $limit Limit the result to this number
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return boolean
*/
public function delete($wfield = NULL, $pass = false, $table = NULL, $limit = NULL)
{
$this->set_param($table, 'table');
$sql = array();
if (!$pass && is_null($wfield)) {
$this->track('error', 'delete',
'Function refused to execute query because '
. 'table ' . $table . ' would have been truncated. '
. 'To override this error message, set the second '
. 'argument ($pass) to true.'
);
return false;
}
$sql['delete'] = $table;
$sql['field'] = $wfield;
$sql['limit'] = $limit;
try {
$query = $this->fix_sql($sql);
$this->query($query);
} catch (Exception $e) {
$this->track('error', 'delete',
$e->getmessage()
);
return false;
}
$this->track('event', 'delete',
'Successful Call (Query: `' . $query . '`)'
);
return true;
}
/**
* Update; Update data in a table
*
* @param mixed $fields Field(s) that will be changed
* @param mixed $values Value(s) that will be assigned
* @param string $wfields Where fields clause
* @param string $table Table that will be used
* @param mixed $limit Limit the result to this number
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return boolean
*/
public function update($fields, $values, $wfields = NULL, $table = NULL, $limit = NULL)
{
$this->set_param($table, 'table');
$sql = array();
try {
for ($i = 0; $i < 2; $i++) {
$a = $i ? 'fields' : 'values';
${substr($a, 0, 5)} = !is_array($$a) ?
$this->fix_list($$a) :
$$a;
}
} catch (Exception $e) {
$this->track('error', 'update',
$e->getmessage()
);
return false;
}
if (count($field) != count($value)) {
$this->track('error', 'update',
'Fields and Values do not match in array size'
);
return false;
}
unset($fields);
$num = count($field);
for ($i = 0; $i < $num; $i++) {
$f = $field[$i];
$v = $value[$i];
if (!$this->field_exists($f, $table)) {
$this->track('error', 'update',
"Field: {$f} does not exists in Table: {$table}"
);
return false;
}
$fields .= "{$f}, ";
$set .= "`{$f}` = '" . addcslashes($v, '\'') . "', ";
}
$sql['update'] = $table;
$sql['field'] = $wfields;
$sql['set'] = $this->list_trim($set);
$sql['limit'] = $limit;
try {
$query = $this->fix_sql($sql);
$this->query($query);
} catch (Exception $e) {
$this->track('error', 'update',
$e->getmessage()
);
return false;
}
$this->track('event', 'update',
'Successful Call (Query: `' . $query . '`)'
);
return true;
}
/**
* Get_LastQuery; return string of the last executed query
*
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return string
*/
public function get_lastquery()
{
$num = count($this->log['mysql']) - 1;
return $this->log['mysql'][$num]['query'];
}
/**
* Get_Result; return result from last SELECT query
*
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return mixed
*/
public function get_result()
{
return $this->result;
}
/**
* Get_Rows; return num rows from last SELECT query
*
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return int
*/
public function get_rows()
{
return $this->resultrows;
}
/**
* Get_Log; return result from last SELECT query
*
* @param string $key If provided, function will return array of
* only the key given. track, mysql, session, etc..
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return array
*/
public function get_log($key = NULL)
{
return is_null($key) ? $this->log : $this->log[$key];
}
/**
* Get_Error; Return the last error (default) or if the first
* argument set to true then it will return the full error array
*
* @param boolean $full Return the full array (default false)
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return mixed
*/
public function get_error($full = false)
{
return $full ? $this->error_log : end($this->error_log);
}
/**
* Debug;
*
* Not quite finished with this function. I plan to
* make this function return a nice HTML layed out page
* of all the logs, stored in raw file, and optomization solutions.
*
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return string
*/
public function debug()
{
//incomplete
}
/**
* Field_Exists; See if field exists in givin table
*
* @param string $field Field to be found
* @param string $table Table that will be searched
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return boolean
*/
public function field_exists($field, $table = NULL)
{
$this->set_param($table, 'table');
$exists = false;
if (is_array($field) || is_array($table)) {
$this->track('error', 'field_exists',
'Function does not accept arrays'
);
return $exists;
}
try {
$result = mysql_query('SHOW COLUMNS FROM ' . $table, $this->connection);
while($row = $this->fetch($result, FETCH_ASSOC)) {
if (strtolower($row['Field']) == strtolower($field)) {
$exists = true;
break;
}
}
} catch(Exception $e) {
$this->track('error', 'field_exists',
$e->getmessage()
);
return $exists;
}
return $exists;
}
/**
* Track; Build log array
*
* @param string $type Type of entry
* @param string $from Function orgin
* @param string $msg Message
* @param string $error Server error message
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* @return void
*/
private function track($type, $from, $msg, $error = '', $age = '')
{
if (!empty($error)) $this->error_log[] = $error;
if (!$this->common['tracklog']) return;
$new_entry = array(
'type' => $type,
'from' => $from,
'msg' => trim($msg),
'age' => empty($age) ? $this->age() : $age
);
$this->log['track'][] = empty($error) ?
$new_entry :
array_merge($new_entry,
array(
'error' => $error
)
);
}
/**
* Fix_Sql; Fix the SQL array and return a queryable string
*
* @param array $sql SQL array
* @param string $type Type of fetch
* @author Paul Williamson <webmaster@protonage.net>
* @access protected
* @return string
*
* The long do statment in the fields case simply strips the field
* string into an array and then makes a where clause if needed
*/
final protected function fix_sql($sql, $Extra = NULL)
{
if (!is_array($sql)) {
$this->track(
"error", "fix_sql",
"No array found in `{$sql}`."
);
return false;
}
$sql_type = 'SELECT';
foreach ($sql as $type => $value) {
switch($type) {
case 'delete':
$delete = 'DELETE FROM `' . $value . '`';
$sql_type = 'DELETE';
break; //case 'delete'
case 'insert':
$insert = 'INSERT INTO `' . $value . '` ';
$sql_type = 'INSERT';
break; //case 'insert'
case 'update':
$update = 'UPDATE `' . $value . '`';
$sql_type = 'UPDATE';
break; //case 'insert'
case 'set':
$sql_set = 'SET ' . $value;
break; //case 'insert'
case 'value':
foreach ($value as $v) {
$sql_values .= "'" . addcslashes($v, '\'') . "', ";
}
$this->list_trim($sql_values);
break; //case 'insert'
case 'search':
$like = "LIKE '%{$value}%'";
break; //case 'search'
case 'table':
$tables = $sql['table'];
$from = 'FROM `' . $tables . '`';
break; //case 'table'
case 'field':
if (is_null($value)) {
$select = '*';
break;
}
$num_to_wheres = 0;
$buf = $value;
do {
$pre_buf = $buf;
for ($i = 0; $i < 2; $i++) {
$p = $i ? $this->preg_sql_noquote :
$this->preg_sql_quote;
$r = $i ? '$4' : '$5';
while(preg_match($p, $buf)) {
$fields[] = preg_replace($p, '$1', $buf);
$buf = preg_replace($p, $r, $buf);
if (preg_match('/=/', end($fields))) $num_to_wheres++;
}
}
if ($buf == $pre_buf) {
$this->track(
"error", "fix_sql",
"Parse error in Fields `{$value}`."
);
throw new Exception("Parse error in Fields `{$value}`.");
return false;
}
} while(!empty($buf));
$in_fields = array();
$num_fields = count($fields);
for ($i = 0; $i < $num_fields; $i++) {
$field = $fields[$i];
if (preg_match('/=/', $field)) {
$sql_where .= $this->fix_where($field);
}
$field = preg_replace(
'/(^[a-zA-Z0-9\._]+).*$/i',
'$1',
$field
);
if (array_search($field, $in_fields) === false) {
$sql_fields .= '`' . $field . '`, ';
}
$in_fields[] = $field;
}
$this->list_trim($sql_where, ' AND ');
$this->list_trim($sql_fields);
break; //case 'field'
case 'order':
$order = is_null($value) ? '' : 'ORDER BY ' . $value;
break; //case 'order'
case 'limit':
if (!preg_match('/^[0-9]+(, ?[0-9]+)?$/', $value)) {
$limit = $value;
} else {
$limit = '';
}
break; //case 'limit'
} //switch($type)
} //foreach ($sql as $type => $value)
$select = $Extra != 'ALL' ? 'SELECT ' .
$sql_fields :
'SELECT *';
$where = !empty($sql_where) ?
'WHERE ' . $sql_where :
'';
$insert = $insert . '(' . $sql_fields . ') VALUES (' . $sql_values . ')';
$like = !empty($sql_where) ?
$like : 'WHERE ' . $sql_fields .
' ' . $like;
switch($sql_type) {
case 'SELECT':
$query = "{$select} {$from} {$where} {$like} {$order} {$limit}";
break;
case 'INSERT':
$query = $insert;
break;
case 'UPDATE':
$query = "{$update} {$sql_set} {$where} {$limit}";
break;
case 'DELETE':
$query = "{$delete} {$where} {$limit}";
break;
}
return trim(preg_replace('/ +/', ' ', $query)) . ';';
}
/**
* Fix_Where; Return a where clause with proper MySQL syntax
*
* @param string $field Field that will be included in where clause
* @author Paul Williamson <webmaster@protonage.net>
* @access protected
* @return string
*/
protected function fix_where($field)
{
$parts = preg_split('/ *= */i', $field, 2);
$wfield = '`' . str_replace('`', '', $parts[0]) . '`=';
// addcslashes to prevent query parse error
$wfield .= '\'' . addcslashes(
preg_replace('/(^[\'"`])|([\'"`]$)/i', '',
$parts[1]
),
'\''
) . '\'';
return $wfield . ' AND ';
}
/**
* Fix_List; Return an array of broken elements from a list
* seperated by commas and quotes
*
* @param string $list List to be converted to array
* @author Paul Williamson <webmaster@protonage.net>
* @access protected
* @return array
*/
protected function fix_list($list)
{
$buf = $list;
$result = array();
do {
$pre_buf = $buf;
for ($i = 0; $i < 2; $i++) {
$q = $i ? preg_replace('/^ *([`\'"]).*/is', '$1', $buf) : '';
$p = $i ? str_replace('{quote}', $q, $this->preg_lst_quote) :
$this->preg_lst_noquote;
while (preg_match($p, $buf) && !empty($buf)) {
$result[] = preg_replace($p, '$1', $buf);
$buf = preg_replace($p, '$3', $buf);
}
}
if ($buf == $pre_buf) {
$this->track(
"error", "fix_list",
"Parse error in List `{$list}`."
);
throw new Exception("Parse error in List `{$list}`.");
return false;
}
} while(!empty($buf));
return $result;
}
/**
* Set_Param; Set an argument value if already exists
*
* @param string &$arg Argument varable
* @param string $name Argument name
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* @return void
*/
private function set_param(&$arg, $name)
{
$arg = (is_null($arg)) ? $this->$name : $arg;
if (is_null($arg)) {
$this->track('error', 'set_param',
'Parameter `' . $name . '` is not set'
);
throw new Exception('Parameter `' . $name . '` is not set');
trigger_error('Parameter `' . $name . '` is not set', E_USER_ERROR);
}
$this->$name = $arg;
}
/**
* Fetch; Impliment the mysql_fetch_* functions
*
* @param resource $res Query result
* @param int $fetch Fetch Type
* @author Paul Williamson <webmaster@protonage.net>
* @access protected
* @return mixed
*/
protected function fetch($res, $fetch)
{
switch ($fetch) {
case FETCH_ASSOC:
$this->fetch_default = $fetch;
return mysql_fetch_assoc($res);
break;
case FETCH_NUM:
$this->fetch_default = $fetch;
return mysql_fetch_row($res);
break;
case FETCH_OBJECT:
$this->fetch_default = $fetch;
return mysql_fetch_object($res);
break;
default:
return mysql_fetch_assoc($res);
}
}
/**
* List_Trim; Trim the end of a list from a loop
*
* Function will take out extra ', ' on the end of a list string
*
* @param string &$str String to trim
* @author Paul Williamson <webmaster@protonage.net>
* @access protected
* @return string
*/
protected function list_trim(&$str, $tpat = ', ')
{
$preg_trim = '/(.*?)' . $tpat . '$/i';
$str = preg_replace($preg_trim, '$1', $str);
return $str;
}
/**
* Kill; Disconnect from database
*
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* @return void
*/
private function kill()
{
@mysql_close($this->connection);
$this->track('event', 'kill',
"Connect to {$this->host} has been forced to close."
);
}
/**
* TimeNow; returns the current time
*
* In PHP 5, microtime() was givin a boolean
* param, if true, microtime() would return a float
* which is exactly what this function does.
*
* @author Paul Williamson <webmaster@protonage.net>
* @access protected
* @return float
*/
protected function timenow()
{
/**
* For PHP4:
*
* list($a, $b) = explode(' ', microtime());
* return $a + $b;
*
* PHP5 makes it less painful with the same result.
* Thanks to the new boolean argument provided by microtime()
*/
return microtime(true);
}
/**
* Age; how long the class has been alive
*
* @author Paul Williamson <webmaster@protonage.net>
* @access protected
* @return float microtime
*/
protected function age()
{
return $this->timenow()-$this->birth;
}
/**
* Do_FullLog; Write log track array to a file
*
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* return void
*/
/**
* Do_Log; Write full log array to a file
*
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* return void
*/
private function do_fulllog()
{
$path = self::LOG_PATH;
if (is_dir($path)) {
for ($i = 1; file_exists($file = $path . '/simplesql_raw.' . $i); $i++) {
if (filesize($file) < self::MAX_SIZE) break;
}
if ($handle = fopen($file, 'a')) {
$add = '$Id' . serialize($this->log) . '/$Id' . "\n";
if (fwrite($handle, $add) === false) {
trigger_error("Cannot write to file `{$file}`.", E_WARNING);
}
} else {
trigger_error("Cannot open file `{$file}` for append.", E_WARNING);
}
} else {
trigger_error("Cannot find path `{$path}`.", E_WARNING);
}
}
/**
* Do_Log; Write log track array to a file
*
* @author Paul Williamson <webmaster@protonage.net>
* @access private
* return void
*/
private function do_log()
{
if (!$this->common['tracklog']) return ;
$path = self::LOG_PATH;
if (is_dir($path)) {
for ($i = 1; file_exists($file = $path . '/simplesql_log.' . $i); $i++) {
if (filesize($file) < self::MAX_SIZE) break;
}
if ($handle = fopen($file, 'a')) {
$add = "\n\n=========================";
$add .= "\nLog for session ID {$this->log['session']}
@ {$this->log['date']}";
$add .= "\n=========================\n";
foreach ($this->log['track'] as $line => $arr) {
if (is_int($line)) {
$tab = strlen($arr['from']) < 8 ? "\t" : '';
$add .= "\t{$line}: " . strtoupper($arr['type'])
. "\t" . strtoupper($arr['from']) . "{$tab}\t{$arr['msg']}\n";
}
}
if (fwrite($handle, $add) === false) {
trigger_error("Cannot write to file `{$file}`.", E_WARNING);
}
} else {
trigger_error("Cannot open file `{$file}` for append.", E_WARNING);
}
} else {
trigger_error("Cannot find path `{$path}`.", E_WARNING);
}
}
/**
* PrintLog; Print the log array to the screen
*
* @author Paul Williamson <webmaster@protonage.net>
* @access public
* @return void
*/
public function printlog()
{
print '<pre>';
print_r($this->log);
print '</pre>';
}
} // end simplesql
?>
|