You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ryzom-core/code/web/api/common/db_lib.php

521 lines
14 KiB
PHP

<?php
/* Copyright (C) 2009 Winch Gate Property Limited
*
* This file is part of ryzom_api.
* ryzom_api is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ryzom_api 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ryzom_api. If not, see <http://www.gnu.org/licenses/>.
*/
define('SQL_DEF_TEXT', 0);
define('SQL_DEF_BOOLEAN', 1);
define('SQL_DEF_INT', 2);
define('SQL_DEF_DATE', 3);
// Wrapper for SQL database interactions
class ServerDatabase
{
var $hostname = '';
var $username = '';
var $password = '';
var $database = '';
var $_connection = Null;
function ServerDatabase($host='', $user='', $passwd='', $dbname='')
{
if (($host != '') && ($user != '') && ($dbname != ''))
{
$this->hostname = $host;
$this->username = $user;
$this->password = $passwd;
$this->database = $dbname;
}
if (($this->hostname != '') && ($this->username != '') && ($this->database != ''))
$this->_connection = new mysqli($this->hostname, $this->username, $this->password, $this->database);
}
function close()
{
$this->_connection->close();
}
function query($sql_statement)
{
$result = $this->_connection->query($sql_statement);
if (!$result)
alert('MYSQL', $this->get_error(), 2);
return $result;
}
function select_db($dbname) {
$this->database = $dbname;
$this->_connection->select_db($dbname);
}
function num_rows($result)
{
return $result->num_rows;
}
function fetch_row($result, $result_type=MYSQLI_BOTH)
{
if (gettype($result) == "object")
return $result->fetch_array($result_type);
return NULL;
}
function fetch_assoc($result)
{
if (gettype($result) == "object")
return $result->fetch_assoc();
return NULL;
}
function query_single_row($sql_statement)
{
$result = $this->query($sql_statement);
if (gettype($result) == "object")
return $result->fetch_array();
return NULL;
}
function free_result($result)
{
$result->free();
}
function get_error()
{
return $this->_connection->errno.': '.$this->_connection->error;
}
function last_insert_id()
{
return $this->_connection->insert_id;
}
function escape_string($escapestr) {
return $this->_connection->real_escape_string($escapestr);
}
function change_to($host,$user,$pass,$dbname)
{
/*$this->close();
$this->hostname = $host;
$this->username = $user;
$this->password = $pass;
$this->database = $dbname;
$this->ServerDatabase();*/
}
}
class ryDB {
private static $_instances = array();
private $db;
private $defs = array();
private $errors = '';
private function __construct($db_name) {
global $_RYZOM_API_CONFIG;
$this->db_name = $db_name;
$this->db = new ServerDatabase(RYAPI_WEBDB_HOST, RYAPI_WEBDB_LOGIN, RYAPI_WEBDB_PASS, $db_name);
$this->db->query("SET NAMES utf8");
}
public static function getInstance($db_name) {
if (!array_key_exists($db_name, self::$_instances))
self::$_instances[$db_name] = new ryDB($db_name);
self::$_instances[$db_name]->db->select_db($db_name);
return self::$_instances[$db_name];
}
function setDbDefs($table, $defs, $check=true) {
if ($check)
{
$result = $this->db->query('SHOW FIELDS FROM '.$table);
if (!$result)
die("Table $table not found in database");
$fields = array_keys($defs);
while ($row = $this->db->fetch_row($result)) {
if (in_array($row['Field'], $fields))
unset($fields[array_search($row['Field'], $fields)]);
else
alert('DbLib', 'Missing field '.$row['Field']." on DbDef of table [$table] of database [$this->db_name] !", 2);
}
if ($fields)
die('Missing fields ['.implode('] [', $fields)."] in table [$table] of database [$this->db_name] !");
}
$this->defs[$table] = $defs;
}
function getDefs($table) {
if ($this->hasDbDefs($table))
return $this->defs[$table];
alert('DBLIB', "Please add tables to '$this->db_name' defs using setDbDefs('$table', \$defs)", 2);
}
function hasDbDefs($table) {
return array_key_exists($table, $this->defs);
}
function getErrors() {
return $this->db->get_error();
}
function now() {
return date('Y-m-d H:i:s', time());
}
function toDate($timestamp) {
return date('Y-m-d H:i:s', $timestamp);
}
function fromDate($string_date) {
return strtotime($string_date);
}
function addDbTableProp($table, $props) {
$this->props[$table] = $props;
}
function sqlEscape($escapestr) {
return $this->db->escape_string($escapestr);
}
function insertID() {
return $this->db->last_insert_id();
}
/// DIRECT QUERY
function sqlQuery($sql, $index = false, $result_type = MYSQLI_BOTH) {
$result = $this->db->query($sql);
if (!$result)
return NULL;
if($index !== false && !is_array($index)){
$index = array($index);
}
$ret = array();
while ($row = $this->db->fetch_row($result, $result_type)) {
if($index !== false) {
// if $index is ['id1', 'id2'], then this code executes as
// $ret[$row['id1']][$row['id2']] = $row
$current = &$ret;
foreach($index as $key){
if(!isset($row[$key]))
alert('DBLIB', "Requested index field ($key) was not selected from db");
$current = &$current[$row[$key]];
}
$current = $row;
} else
$ret[] = $row;
}
return $ret;
}
/// QUERY ///
function sqlSelect($table, $props, $values=array(), $extra='') {
if ($table) {
$sql = "SELECT\n\t";
$params = array();
$test = array();
if (!$props)
alert('DBLIB', "Bad Select on [$table] : missing props");
foreach($props as $name => $type)
$params[] = '`'.$this->sqlEscape($name).'`';
foreach($values as $name => $value) {
if ($name[0] == '=')
$test[] = '('.$this->sqlEscape(substr($name, 1)).' LIKE '.var_export($value, true).')';
else
$test[] = '('.$this->sqlEscape($name).' = '.var_export($value, true).')';
}
$sql .= implode(",\n\t", $params)."\nFROM\n\t".$table."\n";
if ($test)
$sql .= "WHERE\n\t".implode("\nAND\n\t", $test);
}
if ($extra)
$sql .= "\n".$extra;
return $sql.';';
}
function querySingle($table, $values=array(), $extra='') {
$sql = $this->sqlSelect($table, $this->getDefs($table), $values, $extra);
$result = $this->sqlQuery($sql, false, MYSQLI_BOTH);
if(empty($result))
return NULL;
return $result[0];
}
function querySingleAssoc($table, $values=array(), $extra='') {
$sql = $this->sqlSelect($table, $this->getDefs($table), $values, $extra);
$result = $this->sqlQuery($sql, false, MYSQLI_ASSOC);
if(empty($result))
return NULL;
return $result[0];
}
function query($table, $values=array(), $extra='', $index = false, $result_type = MYSQLI_BOTH) {
$sql = $this->sqlSelect($table, $this->getDefs($table), $values, $extra);
return $this->sqlQuery($sql, $index, $result_type);
}
function queryAssoc($table, $values=array(), $extra='', $index = false) {
return $this->query($table, $values, $extra, $index, MYSQLI_ASSOC);
}
function queryIndex($table, $index, $values=array(), $extra='') {
return $this->query($table, $values, $extra, $index, MYSQLI_ASSOC);
}
/// INSERT ///
function sqlInsert($table, $props, $vals) {
$sql = 'INSERT INTO '.$table.' ';
$params = array();
$values = array();
foreach($props as $name => $type) {
if (!isset($vals[$name]))
continue;
$params[] = '`'.$name.'`';
switch ($type) {
case SQL_DEF_BOOLEAN:
$values[] = $vals[$name]?1:0;
break;
case SQL_DEF_INT:
$values[] = $vals[$name];
break;
case SQL_DEF_DATE: // date
if (is_string($vals[$name]))
$values[] = "'".$this->sqlEscape($vals[$name])."'";
else
$values[] = "'".$this->toDate($vals[$name])."'";
break;
default:
$values[] = "'".$this->sqlEscape($vals[$name])."'";
break;
}
}
$sql .= "(\n\t".implode(",\n\t", $params)."\n) VALUES (\n\t".implode(",\n\t", $values)."\n);";
return $sql;
}
function insert($table, $values) {
$sql = $this->sqlInsert($table, $this->getDefs($table), $values);
$this->db->query($sql);
return $this->db->last_insert_id();
}
/// DELETE ///
function sqlDelete($table, $values=array(), $where='') {
$sql = "DELETE FROM\n\t".$table."\n";
$test = array();
foreach($values as $name => $value)
$test[] = '('.$this->sqlEscape($name).' = '.var_export($value, true).')';
if ($test or $where)
$sql .= "WHERE\n\t";
if ($test)
$sql .= implode("\nAND\n\t", $test);
if ($where)
$sql .= "\n".$where;
return $sql.';';
}
function delete($table, $values=array(), $where='') {
$sql = $this->sqlDelete($table, $values, $where);
$result = $this->db->query($sql);
return $result;
}
/// UPDATE ///
function sqlUpdate($table, $props, $vals, $tests, $extra) {
$sql = 'UPDATE '.$table.' SET ';
$params = array();
$test = array();
$values = array();
foreach($props as $name => $type) {
if (!array_key_exists($name, $vals))
continue;
switch ($type) {
case SQL_DEF_BOOLEAN:
$values[] = '`'.$name.'` = '.($vals[$name]?'1':'0');
break;
case SQL_DEF_DATE:
if (is_string($vals[$name]))
$values[] = '`'.$name.'` = \''.$this->sqlEscape($vals[$name]).'\'';
else
$values[] = '`'.$name.'` = \''.$this->toDate($vals[$name]).'\'';
break;
default:
$values[] = '`'.$name.'` = \''.$this->sqlEscape($vals[$name]).'\'';
break;
}
}
$sql .= "\n\t".implode(",\n\t", $values)."\n";
foreach($tests as $name => $value) {
$test[] = '('.$this->sqlEscape($name).' = '.var_export($value, true).')';
}
if ($test)
$sql .= "WHERE\n\t".implode("\nAND\n\t", $test);
$sql .= "\n".$extra;
return $sql;
}
function update($table, $values=array(), $test=array(), $extra='') {
$sql = $this->sqlUpdate($table, $this->getDefs($table), $values, $test, $extra);
$result = $this->db->query($sql);
return $result;
}
function sqlInsertOrUpdate($table, $props, $vals) {
$sql = $this->sqlInsert($table, $props, $vals);
$sql = substr($sql, 0, strlen($sql)-1);
$params = array();
$test = array();
$values = array();
foreach($props as $prop) {
if (!array_key_exists($prop[2], $vals))
continue;
$type = $prop[0];
$check = $prop[1];
$name = $prop[2];
if ($type{0} == '#')
$type = substr($type, 1);
if (($type{0} == '>') or ($type == 'id'))
continue;
switch ($type) {
case 'trad':
$values[] = '`'.$name."` = '".$this->sqlEscape($vals[$name])."'";
break;
case 'textarea':
case 'string':
case 'option':
$values[] = '`'.$name."` = '".$this->sqlEscape($vals[$name])."'";
break;
case 'id':
case 'int':
case 'float':
$values[] = '`'.$name.'` = '.$this->sqlEscape($vals[$name]);
break;
case 'bool':
$values[] = '`'.$name.'` = '.($vals[$name]?'1':'0');
break;
}
}
$sql .= "\nON DUPLICATE KEY UPDATE\n\t".implode(",\n\t", $values)."\n";
return $sql;
}
function insertOrUpdate($table, $values) {
$sql = $this->sqlInsertOrUpdate($table, $this->getDefs($table), $values);
return $result;
}
/// Display
function getTableHtml($name, $params, $values, $order_by='')
{
$ret = '<table cellpadding="0" cellspacing="0" width="100%">';
$tr_header = '<td align="left" height="32px">&nbsp;';
$tr_header .= implode('</td><td align="left">&nbsp;', array_keys($params)).'</td>';
$ret .= _s('t header', $tr_header);
$i = 0;
if (!$values)
return '';
$current_section = '';
foreach ($values as $rows) {
if ($order_by && $rows[$order_by] != $current_section) {
$current_section = $rows[$order_by];
if ($current_section != '0')
$ret .= _s('t row ', '<td>'._s('section', $current_section).'</td>'.str_repeat('<td>'._s('section', '&nbsp;').'</td>', count($params)-1));
}
$td = '';
foreach ($params as $test => $param)
$td .= '<td align="left" height="22px">&nbsp;'.$rows[$param].'</td>';
$ret .= _s('t row '.strval($i % 2), $td);
$i++;
}
$ret .= '</table>';
return $ret;
}
/// Update Database Structure
static function updateDatabaseStruct($defs)
{
if (file_exists(RYAPP_PATH.'database.versions'))
$versions = unserialize(file_get_contents(RYAPP_PATH.'database.versions'));
else
$versions = array();
$c = "Updating DB Structure...\n";
foreach ($defs as $dbname => $tables) {
$db = new ServerDatabase(RYAPI_WEBDB_HOST, RYAPI_WEBDB_LOGIN, RYAPI_WEBDB_PASS, $dbname);
$db->query("SET NAMES utf8");
$c .= "\n Selected DB '$dbname'\n";
foreach ($tables as $table => $sql)
{
$version = count($sql);
if (array_key_exists($table, $versions))
$diff = $version - $versions[$table];
else {
$versions[$table] = 0;
$diff = $version;
}
$c .= " Table '$table' need v$version (current v".strval($versions[$table].') => ');
if ($diff > 0) {
$sql_to_run = array_slice($sql, $versions[$table], $diff);
foreach($sql_to_run as $sql_run) {
if ($sql_run) {
$c .= "Run sql... ";
$result = $db->query($sql_run);
} else
$c .= "KO!!!";
}
if ($result) {
$c .= "OK";
$versions[$table] = $version;
}
} else
$c .= "OK";
$c .= "\n";
}
$c .= "\n";
$db->close();
}
file_put_contents(RYAPP_PATH.'database.versions', serialize($versions));
return '<pre>'.$c.'<pre>';
}
}
?>