2 /* Copyright (C) 2009 Winch Gate Property Limited
4 * This file is part of ryzom_api.
5 * ryzom_api is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * ryzom_api is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with ryzom_api. If not, see <http://www.gnu.org/licenses/>.
19 define('SQL_DEF_TEXT', 0);
20 define('SQL_DEF_BOOLEAN', 1);
21 define('SQL_DEF_INT', 2);
22 define('SQL_DEF_DATE', 3);
25 // Wrapper for SQL database interactions
33 var $_connection = Null;
35 function ServerDatabase($host='', $user='', $passwd='', $dbname='')
37 if (($host != '') && ($user != '') && ($dbname != ''))
39 $this->hostname
= $host;
40 $this->username
= $user;
41 $this->password
= $passwd;
42 $this->database
= $dbname;
45 if (($this->hostname
!= '') && ($this->username
!= '') && ($this->database
!= ''))
46 $this->_connection
= new mysqli($this->hostname
, $this->username
, $this->password
, $this->database
);
51 $this->_connection
->close();
54 function query($sql_statement)
56 $result = $this->_connection
->query($sql_statement);
58 alert('MYSQL', $this->get_error(), 2);
62 function select_db($dbname) {
63 $this->database
= $dbname;
64 $this->_connection
->select_db($dbname);
67 function num_rows($result)
69 return $result->num_rows
;
72 function fetch_row($result, $result_type=MYSQLI_BOTH
)
74 if (gettype($result) == "object")
75 return $result->fetch_array($result_type);
79 function fetch_assoc($result)
81 if (gettype($result) == "object")
82 return $result->fetch_assoc();
86 function query_single_row($sql_statement)
88 $result = $this->query($sql_statement);
89 if (gettype($result) == "object")
90 return $result->fetch_array();
95 function free_result($result)
102 return $this->_connection
->errno
.': '.$this->_connection
->error
;
105 function last_insert_id()
107 return $this->_connection
->insert_id
;
110 function escape_string($escapestr) {
111 return $this->_connection
->real_escape_string($escapestr);
114 function change_to($host,$user,$pass,$dbname)
117 $this->hostname = $host;
118 $this->username = $user;
119 $this->password = $pass;
120 $this->database = $dbname;
121 $this->ServerDatabase();*/
127 private static $_instances = array();
129 private $defs = array();
130 private $errors = '';
133 private function __construct($db_name) {
134 global $_RYZOM_API_CONFIG;
135 $this->db_name
= $db_name;
136 $this->db
= new ServerDatabase(RYAPI_WEBDB_HOST
, RYAPI_WEBDB_LOGIN
, RYAPI_WEBDB_PASS
, $db_name);
137 $this->db
->query("SET NAMES utf8mb4");
140 public static function getInstance($db_name) {
141 if (!array_key_exists($db_name, self
::$_instances))
142 self
::$_instances[$db_name] = new ryDB($db_name);
144 self
::$_instances[$db_name]->db
->select_db($db_name);
145 return self
::$_instances[$db_name];
148 function setDbDefs($table, $defs, $check=true) {
151 $result = $this->db
->query('SHOW FIELDS FROM '.$table);
153 die("Table [$table] not found in database [$this->db_name]");
157 $fields = array_keys($defs);
158 while ($row = $this->db
->fetch_row($result)) {
159 if (in_array($row['Field'], $fields))
160 unset($fields[array_search($row['Field'], $fields)]);
162 alert('DbLib', 'Missing field '.$row['Field']." on DbDef of table [$table] of database [$this->db_name] !", 2);
165 die('Missing fields ['.implode('] [', $fields)."] in table [$table] of database [$this->db_name] !");
167 $this->defs
[$table] = $defs;
170 function getDefs($table) {
171 if ($this->hasDbDefs($table))
172 return $this->defs
[$table];
174 alert('DBLIB', "Please add tables to '$this->db_name' defs using setDbDefs('$table', \$defs)", 2);
177 function hasDbDefs($table) {
178 return array_key_exists($table, $this->defs
);
181 function getErrors() {
182 return $this->db
->get_error();
186 return date('Y-m-d H:i:s', time());
189 function toDate($timestamp) {
190 return date('Y-m-d H:i:s', $timestamp);
193 function fromDate($string_date) {
194 return strtotime($string_date);
197 function addDbTableProp($table, $props) {
198 $this->props
[$table] = $props;
201 function sqlEscape($escapestr) {
202 return $this->db
->escape_string($escapestr);
205 function insertID() {
206 return $this->db
->last_insert_id();
211 function sqlQuery($sql, $index = false, $result_type = MYSQLI_BOTH
) {
212 $result = $this->db
->query($sql);
215 if($index !== false && !is_array($index)){
216 $index = array($index);
219 while ($row = $this->db
->fetch_row($result, $result_type)) {
220 if($index !== false) {
221 // if $index is ['id1', 'id2'], then this code executes as
222 // $ret[$row['id1']][$row['id2']] = $row
224 foreach($index as $key){
225 if(!isset($row[$key]))
226 alert('DBLIB', "Requested index field ($key) was not selected from db");
227 $current = &$current[$row[$key]];
238 function sqlSelect($table, $props, $values=array(), $extra='') {
244 alert('DBLIB', "Bad Select on [$table] : missing props");
246 foreach($props as $name => $type)
247 $params[] = '`'.$this->sqlEscape($name).'`';
249 foreach($values as $name => $value) {
251 $test[] = '('.$this->sqlEscape(substr($name, 1)).' LIKE '.var_export($value, true).')';
253 $test[] = '('.$this->sqlEscape($name).' = '.var_export($value, true).')';
255 $sql .= implode(",\n\t", $params)."\nFROM\n\t".$table."\n";
257 $sql .= "WHERE\n\t".implode("\nAND\n\t", $test);
266 function querySingle($table, $values=array(), $extra='') {
267 $sql = $this->sqlSelect($table, $this->getDefs($table), $values, $extra);
268 $result = $this->sqlQuery($sql, false, MYSQLI_BOTH
);
274 function querySingleAssoc($table, $values=array(), $extra='') {
275 $sql = $this->sqlSelect($table, $this->getDefs($table), $values, $extra);
276 $result = $this->sqlQuery($sql, false, MYSQLI_ASSOC
);
282 function query($table, $values=array(), $extra='', $index = false, $result_type = MYSQLI_BOTH
) {
283 $sql = $this->sqlSelect($table, $this->getDefs($table), $values, $extra);
284 return $this->sqlQuery($sql, $index, $result_type);
287 function queryAssoc($table, $values=array(), $extra='', $index = false) {
288 return $this->query($table, $values, $extra, $index, MYSQLI_ASSOC
);
291 function queryIndex($table, $index, $values=array(), $extra='') {
292 return $this->query($table, $values, $extra, $index, MYSQLI_ASSOC
);
297 function sqlInsert($table, $props, $vals) {
298 $sql = 'INSERT INTO '.$table.' ';
301 foreach($props as $name => $type) {
302 if (!isset($vals[$name]))
304 $params[] = '`'.$name.'`';
306 case SQL_DEF_BOOLEAN
:
307 $values[] = $vals[$name]?
1:0;
310 $values[] = $vals[$name];
312 case SQL_DEF_DATE
: // date
313 if (is_string($vals[$name]))
314 $values[] = "'".$this->sqlEscape($vals[$name])."'";
316 $values[] = "'".$this->toDate($vals[$name])."'";
319 $values[] = "'".$this->sqlEscape($vals[$name])."'";
323 $sql .= "(\n\t".implode(",\n\t", $params)."\n) VALUES (\n\t".implode(",\n\t", $values)."\n);";
327 function insert($table, $values) {
328 $sql = $this->sqlInsert($table, $this->getDefs($table), $values);
329 $this->db
->query($sql);
330 return $this->db
->last_insert_id();
334 function sqlDelete($table, $values=array(), $where='') {
335 $sql = "DELETE FROM\n\t".$table."\n";
337 foreach($values as $name => $value)
338 $test[] = '('.$this->sqlEscape($name).' = '.var_export($value, true).')';
343 $sql .= implode("\nAND\n\t", $test);
349 function delete($table, $values=array(), $where='') {
350 $sql = $this->sqlDelete($table, $values, $where);
351 $result = $this->db
->query($sql);
356 function sqlUpdate($table, $props, $vals, $tests, $extra) {
357 $sql = 'UPDATE '.$table.' SET ';
361 foreach($props as $name => $type) {
362 if (!array_key_exists($name, $vals))
365 case SQL_DEF_BOOLEAN
:
366 $values[] = '`'.$name.'` = '.($vals[$name]?
'1':'0');
369 if (is_string($vals[$name]))
370 $values[] = '`'.$name.'` = \''.$this->sqlEscape($vals[$name]).'\'';
372 $values[] = '`'.$name.'` = \''.$this->toDate($vals[$name]).'\'';
375 $values[] = '`'.$name.'` = \''.$this->sqlEscape($vals[$name]).'\'';
379 $sql .= "\n\t".implode(",\n\t", $values)."\n";
381 foreach($tests as $name => $value) {
382 $test[] = '('.$this->sqlEscape($name).' = '.var_export($value, true).')';
385 $sql .= "WHERE\n\t".implode("\nAND\n\t", $test);
393 function update($table, $values=array(), $test=array(), $extra='') {
394 $sql = $this->sqlUpdate($table, $this->getDefs($table), $values, $test, $extra);
395 $result = $this->db
->query($sql);
399 function sqlInsertOrUpdate($table, $props, $vals) {
400 $sql = $this->sqlInsert($table, $props, $vals);
401 $sql = substr($sql, 0, strlen($sql)-1);
405 foreach($props as $prop) {
406 if (!array_key_exists($prop[2], $vals))
412 $type = substr($type, 1);
413 if (($type{0} == '>') or ($type == 'id'))
417 $values[] = '`'.$name."` = '".$this->sqlEscape($vals[$name])."'";
422 $values[] = '`'.$name."` = '".$this->sqlEscape($vals[$name])."'";
427 $values[] = '`'.$name.'` = '.$this->sqlEscape($vals[$name]);
430 $values[] = '`'.$name.'` = '.($vals[$name]?
'1':'0');
434 $sql .= "\nON DUPLICATE KEY UPDATE\n\t".implode(",\n\t", $values)."\n";
438 function insertOrUpdate($table, $values) {
439 $sql = $this->sqlInsertOrUpdate($table, $this->getDefs($table), $values);
445 function getTableHtml($name, $params, $values, $order_by='')
447 $ret = '<table cellpadding="0" cellspacing="0" width="100%">';
448 $tr_header = '<td align="left" height="32px"> ';
449 $tr_header .= implode('</td><td align="left"> ', array_keys($params)).'</td>';
450 $ret .= _s('t header', $tr_header);
454 $current_section = '';
455 foreach ($values as $rows) {
456 if ($order_by && $rows[$order_by] != $current_section) {
457 $current_section = $rows[$order_by];
458 if ($current_section != '0')
459 $ret .= _s('t row ', '<td>'._s('section', $current_section).'</td>'.str_repeat('<td>'._s('section', ' ').'</td>', count($params)-1));
462 foreach ($params as $test => $param)
463 $td .= '<td align="left" height="22px"> '.$rows[$param].'</td>';
464 $ret .= _s('t row '.strval($i %
2), $td);
471 /// Update Database Structure
473 static function updateDatabaseStruct($defs)
475 if (file_exists(RYAPP_PATH
.'database.versions'))
476 $versions = unserialize(file_get_contents(RYAPP_PATH
.'database.versions'));
480 $c = "Updating DB Structure...\n";
481 foreach ($defs as $dbname => $tables) {
482 $db = new ServerDatabase(RYAPI_WEBDB_HOST
, RYAPI_WEBDB_LOGIN
, RYAPI_WEBDB_PASS
, $dbname);
483 $db->query("SET NAMES utf8mb4");
484 $c .= "\n Selected DB '$dbname'\n";
485 foreach ($tables as $table => $sql)
487 $version = count($sql);
488 if (array_key_exists($table, $versions))
489 $diff = $version - $versions[$table];
491 $versions[$table] = 0;
495 $c .= " Table '$table' need v$version (current v".strval($versions[$table].') => ');
498 $sql_to_run = array_slice($sql, $versions[$table], $diff);
499 foreach($sql_to_run as $sql_run) {
502 $result = $db->query($sql_run);
508 $versions[$table] = $version;
517 file_put_contents(RYAPP_PATH
.'database.versions', serialize($versions));
518 return '<pre>'.$c.'<pre>';