Files
AtomicOld/api/Database.php
2026-02-14 19:34:54 +03:00

527 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Класс для доступа к базе данных
*
* @copyright 2011 Denis Pikusov
* @link http://simplacms.ru
* @author Denis Pikusov
*
*/
require_once('Simpla.php');
class Database extends Simpla
{
private $link;
private $res_id;
/**
* В конструкторе подключаем базу
*/
public function __construct()
{
parent::__construct();
$this->connect();
}
/**
* В деструкторе отсоединяемся от базы
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Подключение к базе данных
*/
public function connect()
{
// При повторном вызове возвращаем существующий линк
if(!empty($this->link))
return $this->link;
// Иначе пытаемся подключиться
if(!$this->link = mysql_connect($this->config->db_server, $this->config->db_user, $this->config->db_password))
{
trigger_error("Could not connect to the database. Check the config file.", E_USER_WARNING);
return false;
}
if(!mysql_select_db($this->config->db_name, $this->link))
{
trigger_error("Could not select the database.", E_USER_WARNING);
return false;
}
// Настраиваем соединение
if($this->config->db_charset)
mysql_query('SET NAMES '.$this->config->db_charset, $this->link);
if($this->config->db_sql_mode)
mysql_query('SET SESSION SQL_MODE = "'.$this->config->db_sql_mode.'"', $this->link);
if($this->config->timezone)
mysql_query('SET SESSION time_zone = "'.$this->config->db_timezone.'"', $this->link);
return $this->link;
}
/**
* Закрываем подключение к базе данных
*/
public function disconnect()
{
if(!@mysql_close($this->link))
return true;
else
return false;
}
/**
* Запрос к базе. Обазятелен первый аргумент - текст запроса.
* При указании других аргументов автоматически выполняется placehold() для запроса с подстановкой этих аргументов
*/
public function query()
{
$time_start = microtime(true);
$args = func_get_args();
$q = call_user_func_array(array($this, 'placehold'), $args);
if($this->link)
{
$this->res_id = mysql_query($q, $this->link);
}
else
{
$error_msg = "Could not execute query to database, wrong database link. [$q]";
trigger_error($error_msg, E_USER_WARNING);
return false;
}
if(!$this->res_id)
{
$error_msg = mysql_error($this->link).' ['.$q.']';
trigger_error($error_msg, E_USER_WARNING);
return false;
}
$time_end = microtime(true);
$exec_time = round(($time_end-$time_start)*1000, 0);
//print "$exec_time ms <br>$q<br><br>";
return $this->res_id;
}
/**
* Плейсхолдер для запросов. Пример работы: $query = $db->placehold('SELECT name FROM products WHERE id=?', $id);
*/
public function placehold()
{
$args = func_get_args();
$tmpl = array_shift($args);
// Заменяем все __ на префикс, но только необрамленные кавычками
$tmpl = preg_replace('/([^"\'0-9a-z_])__([a-z_]+[^"\'])/ui', "\$1".$this->config->db_prefix."\$2", $tmpl);
if(!empty($args))
{
$result = $this->sql_placeholder_ex($tmpl, $args, $error);
if ($result === false)
{
$error = "Placeholder substitution error. Diagnostics: \"$error\"";
trigger_error($error, E_USER_WARNING);
return false;
}
return $result;
}
else
return $tmpl;
}
/**
* Возвращает результаты запроса. Необязательный второй аргумент указывает какую колонку возвращать вместо всего массива колонок
*/
public function results($field = null)
{
$results = array();
if(!$this->res_id)
{
trigger_error(mysql_error($this->link), E_USER_WARNING);
return false;
}
if($this->num_rows() == 0)
return array();
while($row = mysql_fetch_object($this->res_id))
{
if(!empty($field) && isset($row->$field))
array_push($results, $row->$field);
else
array_push($results, $row);
}
return $results;
}
/**
* Возвращает первый результат запроса. Необязательный второй аргумент указывает какую колонку возвращать вместо всего массива колонок
*/
public function result($field = null)
{
$result = array();
if(!$this->res_id)
{
$this->error_msg = "Could not execute query to database, wrong result id";
return 0;
}
$row = mysql_fetch_object($this->res_id);
if(!empty($field) && isset($row->$field))
return $row->$field;
elseif(!empty($field) && !isset($row->$field))
return false;
else
return $row;
}
/**
* Возвращает последний вставленный id
*/
public function insert_id()
{
return mysql_insert_id($this->link);
}
/**
* Возвращает количество выбранных строк
*/
public function num_rows()
{
return mysql_num_rows($this->res_id);
}
/**
* Возвращает количество затронутых строк
*/
public function affected_rows()
{
return mysql_affected_rows($this->link);
}
/**
* Компиляция плейсхолдера
*/
private function sql_compile_placeholder($tmpl)
{
$compiled = array();
$p = 0; // текущая позиция в строке
$i = 0; // счетчик placeholder-ов
$has_named = false;
while(false !== ($start = $p = strpos($tmpl, "?", $p)))
{
// Определяем тип placeholder-а.
switch ($c = substr($tmpl, ++$p, 1))
{
case '%': case '@': case '#':
$type = $c; ++$p; break;
default:
$type = ''; break;
}
// Проверяем, именованный ли это placeholder: "?keyname"
if (preg_match('/^((?:[^\s[:punct:]]|_)+)/', substr($tmpl, $p), $pock))
{
$key = $pock[1];
if ($type != '#')
$has_named = true;
$p += strlen($key);
}
else
{
$key = $i;
if ($type != '#')
$i++;
}
// Сохранить запись о placeholder-е.
$compiled[] = array($key, $type, $start, $p - $start);
}
return array($compiled, $tmpl, $has_named);
}
/**
* Выполнение плейсхолдера
*/
private function sql_placeholder_ex($tmpl, $args, &$errormsg)
{
// Запрос уже разобран?.. Если нет, разбираем.
if (is_array($tmpl))
$compiled = $tmpl;
else
$compiled = $this->sql_compile_placeholder($tmpl);
list ($compiled, $tmpl, $has_named) = $compiled;
// Если есть хотя бы один именованный placeholder, используем
// первый аргумент в качестве ассоциативного массива.
if ($has_named)
$args = @$args[0];
// Выполняем все замены в цикле.
$p = 0; // текущее положение в строке
$out = ''; // результирующая строка
$error = false; // были ошибки?
foreach ($compiled as $num=>$e)
{
list ($key, $type, $start, $length) = $e;
// Pre-string.
$out .= substr($tmpl, $p, $start - $p);
$p = $start + $length;
$repl = ''; // текст для замены текущего placeholder-а
$errmsg = ''; // сообщение об ошибке для этого placeholder-а
do {
// Это placeholder-константа?
if ($type === '#')
{
$repl = @constant($key);
if (NULL === $repl)
$error = $errmsg = "UNKNOWN_CONSTANT_$key";
break;
}
// Обрабатываем ошибку.
if (!isset($args[$key]))
{
$error = $errmsg = "UNKNOWN_PLACEHOLDER_$key";
break;
}
// Вставляем значение в соответствии с типом placeholder-а.
$a = $args[$key];
if ($type === '')
{
// Скалярный placeholder.
if (is_array($a))
{
$error = $errmsg = "NOT_A_SCALAR_PLACEHOLDER_$key";
break;
}
$repl = is_int($a) || is_float($a) ? str_replace(',', '.', $a) : "'".addslashes($a)."'";
break;
}
// Иначе это массив или список.
if(is_object($a))
$a = get_object_vars($a);
if (!is_array($a))
{
$error = $errmsg = "NOT_AN_ARRAY_PLACEHOLDER_$key";
break;
}
if ($type === '@')
{
// Это список.
foreach ($a as $v)
{
if(is_null($v))
$r = "NULL";
else
$r = "'".@addslashes($v)."'";
$repl .= ($repl===''? "" : ",").$r;
}
}
elseif ($type === '%')
{
// Это набор пар ключ=>значение.
$lerror = array();
foreach ($a as $k=>$v)
{
if (!is_string($k))
$lerror[$k] = "NOT_A_STRING_KEY_{$k}_FOR_PLACEHOLDER_$key";
else
$k = preg_replace('/[^a-zA-Z0-9_]/', '_', $k);
if(is_null($v))
$r = "=NULL";
else
$r = "='".@addslashes($v)."'";
$repl .= ($repl===''? "" : ", ").$k.$r;
}
// Если была ошибка, составляем сообщение.
if (count($lerror))
{
$repl = '';
foreach ($a as $k=>$v)
{
if (isset($lerror[$k]))
{
$repl .= ($repl===''? "" : ", ").$lerror[$k];
}
else
{
$k = preg_replace('/[^a-zA-Z0-9_-]/', '_', $k);
$repl .= ($repl===''? "" : ", ").$k."=?";
}
}
$error = $errmsg = $repl;
}
}
} while (false);
if ($errmsg) $compiled[$num]['error'] = $errmsg;
if (!$error) $out .= $repl;
}
$out .= substr($tmpl, $p);
// Если возникла ошибка, переделываем результирующую строку
// в сообщение об ошибке (расставляем диагностические строки
// вместо ошибочных placeholder-ов).
if ($error)
{
$out = '';
$p = 0; // текущая позиция
foreach ($compiled as $num=>$e)
{
list ($key, $type, $start, $length) = $e;
$out .= substr($tmpl, $p, $start - $p);
$p = $start + $length;
if (isset($e['error']))
{
$out .= $e['error'];
}
else
{
$out .= substr($tmpl, $start, $length);
}
}
// Последняя часть строки.
$out .= substr($tmpl, $p);
$errormsg = $out;
return false;
}
else
{
$errormsg = false;
return $out;
}
}
public function dump($filename)
{
$h = fopen($filename, 'w');
$q = $this->placehold("SHOW FULL TABLES LIKE '__%';");
$result = mysql_query($q, $this->link);
while($row = mysql_fetch_row($result))
{
if($row[1] == 'BASE TABLE')
$this->dump_table($row[0], $h);
}
fclose($h);
}
function restore($filename)
{
$templine = '';
$h = fopen($filename, 'r');
// Loop through each line
if($h)
{
while(!feof($h))
{
$line = fgets($h);
// Only continue if it's not a comment
if (substr($line, 0, 2) != '--' && $line != '')
{
// Add this line to the current segment
$templine .= $line;
// If it has a semicolon at the end, it's the end of the query
if (substr(trim($line), -1, 1) == ';')
{
// Perform the query
mysql_query($templine, $this->link) or print('Error performing query \'<b>' . $templine . '</b>\': ' . mysql_error() . '<br /><br />');
// Reset temp variable to empty
$templine = '';
}
}
}
}
fclose($h);
}
private function dump_table($table, $h)
{
$sql = "SELECT * FROM `$table`;";
$result = mysql_query($sql, $this->link);
if($result)
{
fwrite($h, "/* Data for table $table */\n");
fwrite($h, "TRUNCATE TABLE `$table`;\n");
$num_rows = mysql_num_rows($result);
$num_fields = mysql_num_fields($result);
if($num_rows > 0)
{
$field_type=array();
$field_name = array();
$i=0;
while( $i < $num_fields)
{
$meta= mysql_fetch_field($result, $i);
array_push($field_type, $meta->type);
array_push($field_name, $meta->name);
$i++;
}
$fields = implode('`, `', $field_name);
fwrite($h, "INSERT INTO `$table` (`$fields`) VALUES\n");
$index=0;
while( $row= mysql_fetch_row($result))
{
fwrite($h, "(");
for( $i=0; $i < $num_fields; $i++)
{
if( is_null( $row[$i]))
fwrite($h, "null");
else
{
switch( $field_type[$i])
{
case 'int':
fwrite($h, $row[$i]);
break;
case 'string':
case 'blob' :
default:
fwrite($h, "'".mysql_real_escape_string($row[$i])."'");
}
}
if( $i < $num_fields-1)
fwrite($h, ",");
}
fwrite($h, ")");
if( $index < $num_rows-1)
fwrite($h, ",");
else
fwrite($h, ";");
fwrite($h, "\n");
$index++;
}
}
}
mysql_free_result($result);
fwrite($h, "\n");
}
public function escape($str)
{
return mysql_real_escape_string($str);
}
}