00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071 class aliroDataCache {
00072 public $records = array();
00073 }
00074
00075 class databaseException extends Exception {
00076 public $dbname = '';
00077 public $sql = '';
00078 public $number = 0;
00079
00080 public function __construct ($dbname, $message, $sql, $number, $dbtrace) {
00081 parent::__construct($message, $number);
00082 $this->dbname = $dbname;
00083 $this->sql = $sql;
00084 $this->dbtrace = $dbtrace;
00085 }
00086
00087 }
00088
00089 class databaseInterfaceFactory {
00090
00091 public static function getInterface () {
00092 if (function_exists( 'mysqli_connect' )) return new mysqliInterface;
00093 if (function_exists( 'mysql_connect' )) return new mysqlInterface;
00094 return null;
00095 }
00096
00097 }
00098
00099 abstract class aliroAbstractDatabase {
00100 protected static $stats = array();
00101 protected $_sql='';
00102 protected $_cached=false;
00103 protected $_errorNum=0;
00104 protected $_errorMsg='';
00105 protected $_table_prefix='';
00106 protected $_resource='';
00107 protected $_cursor=null;
00108 protected $_log=array();
00109 protected $DBname = '';
00110 protected $DBInfo = null;
00111 protected $cache = null;
00112 protected $interface = null;
00113
00114 public function __construct( $host='localhost', $user, $pass, $db, $table_prefix, $return_on_error=false ) {
00115
00116 $this->DBname = $db;
00117 if (!$this->interface = databaseInterfaceFactory::getInterface()) {
00118 if ($return_on_error) {
00119 $this->_errorNum = _ALIRO_DB_NO_INTERFACE;
00120 return;
00121 }
00122 $this->forceOffline(_ALIRO_DB_NO_INTERFACE);
00123 }
00124 if (!($this->_resource = $this->interface->connect($host, $user, $pass, $db))) {
00125 $this->_errorMsg = $this->interface->connectError();
00126 if ($return_on_error) {
00127 $this->_errorNum = _ALIRO_DB_CONNECT_FAILED;
00128 return;
00129 }
00130 $this->forceOffline(_ALIRO_DB_CONNECT_FAILED);
00131 }
00132 $this->interface->setCharset('utf8');
00133 $this->cache = new aliroSimpleCache('aliroAbstractDatabase', true);
00134 $this->DBInfo = $this->cache->get($host.$db.$user.$table_prefix, _ALIRO_DATABASE_CACHE_TIME);
00135 if (!$this->DBInfo) {
00136 $this->DBinfo = new stdClass();
00137 $this->DBInfo->DBTables = array();
00138 $this->DBInfo->DBFields = array();
00139 $this->DBInfo->DBFieldsByName = array();
00140 }
00141 $this->_table_prefix = $table_prefix;
00142 $this->getTableInfo();
00143 }
00144
00145 public function __destruct () {
00146 try {
00147 @session_write_close();
00148 if (aliro::getInstance()->installed) $this->saveStats();
00149 } catch (databaseException $exception) {
00150 exit('DB Error during shutdown');
00151 }
00152 }
00153
00154 private function clearCache () {
00155 $this->cache->clean();
00156 }
00157
00158 private function getTableInfo () {
00159 if (count($this->DBInfo->DBTables) == 0) {
00160 $this->setBareQuery ("SHOW TABLES");
00161 if ($results = $this->loadResultArray()) foreach ($results as $result) $this->DBInfo->DBTables[] = $this->restoreOnePrefix($result);
00162 $this->cache->save($this->DBInfo);
00163 }
00164 }
00165
00166 private function storeFields ($tablename) {
00167 if (!isset($this->DBInfo->DBFields[$tablename])) {
00168 $this->DBInfo->DBFields[$tablename] = $this->doSQLget("SHOW FIELDS FROM `$tablename`");
00169 $this->DBInfo->DBFieldsByName[$tablename] = array();
00170 foreach ($this->DBInfo->DBFields[$tablename] as $field) $this->DBInfo->DBFieldsByName[$tablename][$field->Field] = $field;
00171 $this->cache->save($this->DBInfo);
00172 }
00173 }
00174
00175 public function getName () {
00176 return $this->DBname;
00177 }
00178
00179 public function getAllFieldInfo ($tablename) {
00180 $this->storeFields($tablename);
00181 return $this->DBInfo->DBFields[$tablename];
00182 }
00183
00184 public function getAllFieldNames ($tablename) {
00185 $this->storeFields($tablename);
00186 return array_keys($this->DBInfo->DBFieldsByName[$tablename]);
00187 }
00188
00189 public function addFieldIfMissing ($tablename, $fieldname, $fieldspec, $alterIfPresent=false) {
00190 if (in_array($fieldname, $this->getAllFieldNames($tablename))) {
00191 if ($alterIfPresent) return $this->alterField($tablename, $fieldname, $fieldspec);
00192 return false;
00193 }
00194 $this->doSQL("ALTER TABLE $tablename ADD `$fieldname` ".$fieldspec);
00195 $this->clearCache();
00196 return true;
00197 }
00198
00199 public function alterField ($tablename, $fieldname, $fieldspec) {
00200 if (in_array($fieldname, $this->getAllFieldNames($tablename))) return false;
00201 $this->doSQL("ALTER TABLE $tablename CHANGE COLUMN `$fieldname` ".$fieldspec);
00202 $this->clearCache();
00203 return true;
00204 }
00205
00206 public function getFieldInfo ($tablename, $fieldname) {
00207 $this->storeFields($tablename);
00208 return isset($this->DBInfo->DBFieldsByName[$tablename][$fieldname]) ? $this->DBInfo->DBFieldsByName[$tablename][$fieldname] : null;
00209 }
00210
00211 public function setFieldValue ($value) {
00212 if (is_numeric($value)) {
00213 if ((string) $value == (string) (int) $value) return (string) $value;
00214 else return "'".(string) $value."'";
00215 }
00216 $value = $this->getEscaped($value);
00217 return "'".$value."'";
00218 }
00219
00220
00221 public function tableExists ($tablename) {
00222 return in_array($tablename, $this->DBInfo->DBTables);
00223 }
00224
00225 protected function forceOffline ($error_number) {
00226 new aliroOffline ($error_number);
00227
00228
00229 exit();
00230 }
00231
00232
00233 public function getErrorNum() {
00234 return $this->_errorNum;
00235 }
00236
00237
00238 public function getErrorMsg() {
00239 return str_replace( array( "\n", "'" ), array( '\n', "'" ), $this->_errorMsg );
00240 }
00241
00242
00243 public function getEscaped($text) {
00244 return $this->interface->getEscaped($text);
00245 }
00246
00247
00248 public function Quote( $text ) {
00249 return '\''.$this->getEscaped($text).'\'';
00250 }
00251
00252
00253 protected function setBareQuery($sql) {
00254 $this->_sql = $sql;
00255 }
00256
00257
00258 public function setQuery( $sql, $cached=false, $prefix='#__' ) {
00259 $this->_sql = $this->replacePrefix($sql, $prefix);
00260 $this->_cached = $cached;
00261 }
00262
00263
00264 public function replacePrefix ($sql, $prefix='#__') {
00265 $text = $sql;
00266 $result = '';
00267 while ($text) {
00268 $firstquote = $this->nonzeromin(strpos($text, "'"), strpos($text, '"'));
00269 if ($firstquote) {
00270 $result .= str_replace($prefix, $this->_table_prefix, substr($text,0,$firstquote));
00271 $text = substr($text, $firstquote);
00272 $endquote = $this->findMatchingQuote($text, $text[0]);
00273 $result .= substr($text, 0, $endquote+1);
00274 $text = substr($text, $endquote+1);
00275 }
00276 else {
00277 $result .= str_replace($prefix, $this->_table_prefix, $text);
00278 break;
00279 }
00280 }
00281 return $result;
00282 }
00283
00284 private function nonzeromin ($x, $y) {
00285 if (false === $x) return $y;
00286 if (false === $y) return $x;
00287 return min($x, $y);
00288 }
00289
00290 private function findMatchingQuote ($text, $quote) {
00291 $skip = 1;
00292 do {
00293 $endquote = $quote ? strpos($text, $quote, $skip) : strlen($text) - 1;
00294 if ($endquote) $skip = $endquote+1;
00295 }
00296 while ($endquote AND '\\' == $text[$endquote-1]);
00297 if ($endquote) return $endquote;
00298 else return strlen($text)-1;
00299 }
00300
00301 public function restoreOnePrefix ($tablename) {
00302 if (substr($tablename, 0, strlen($this->_table_prefix)) === $this->_table_prefix) return '#__'.substr($tablename, strlen($this->_table_prefix));
00303 else return $tablename;
00304 }
00305
00306
00307 public function getQuery ($sql='') {
00308 if ($sql == '') $sql = $this->_sql;
00309 return "<pre>" . htmlspecialchars( $sql ) . "</pre>";
00310 }
00311
00312 public function query ($sql='') {
00313 if (empty($sql)) $sql = $this->_sql;
00314 $timer = new aliroProfiler('Database timer');
00315 if ($this->_cursor = $this->interface->query($sql)) {
00316 $this->_errorNum = 0;
00317 $this->_errorMsg = '';
00318 $stats = new stdClass;
00319 $stats->timer = $timer->getElapsed();
00320 $stats->trace = aliroRequest::trace(false);
00321 $stats->sql = $sql;
00322 self::$stats[] = $stats;
00323 $this->_log[] = htmlspecialchars($sql).'<br />'.$timer->mark('secs for query').'<br />'.$stats->trace;
00324 return $this->_cursor;
00325 }
00326 else {
00327 $this->_errorNum = $this->interface->errno();
00328 $this->_errorMsg = $this->interface->error()." SQL=$sql";
00329 throw new databaseException ($this->DBname, $this->_errorMsg, $this->_sql, $this->_errorNum, aliroRequest::trace());
00330 }
00331 }
00332
00333
00334 public function doSQL ($sql) {
00335 $this->setQuery($sql);
00336 return $this->query();
00337 }
00338
00339
00340 public function doSQLget ($sql, $classname='stdClass', $key='', $max=0) {
00341 $this->setQuery($sql);
00342 $rows = $this->retrieveResults ($key, 0, 'object');
00343 if ('stdClass' == $classname) return $max ? array_slice($rows, 0, $max) : $rows;
00344 $result = array();
00345 foreach ($rows as $sub=>$row) {
00346 $next = new $classname();
00347 foreach (get_object_vars($row) as $field=>$value) $next->$field = $value;
00348 $result[$sub] = $next;
00349 if ($max AND count($result) >= $max) return $result;
00350 }
00351 return $result;
00352 }
00353
00354 public function query_batch() {
00355 $this->_errorNum = 0;
00356 $this->_errorMsg = '';
00357 if ($this->interface->multiQuery($this->_sql)) {
00358 do $result = $this->interface->storeResult();
00359 while ($this->interface->nextResult());
00360 }
00361 }
00362
00363 public function getNumRows ($cur=null) {
00364 return $this->interface->getNumRows($cur);
00365 }
00366
00367 public function getAffectedRows () {
00368 return $this->interface->getAffectedRows();
00369 }
00370
00371 protected function retrieveResults ($key='', $max=0, $result_type='row') {
00372 $results = array();
00373 if (!in_array($result_type, array ('row', 'object', 'assoc'))) die ('unexpected result type='.$result_type);
00374 $sql_function = $this->interface->getFetchFunc().$result_type;
00375 if ($cur = $this->query()) {
00376 while ($row = $sql_function($cur)) {
00377 if ($key != '') $results[(is_array($row) ? $row[$key] : $row->$key)] = $row;
00378 else $results[] = $row;
00379 if ($max AND count($results) >= $max) break;
00380 }
00381 $this->interface->freeResultSet($cur);
00382 }
00383 return $results;
00384 }
00385
00386 public function loadResult() {
00387 $results = $this->retrieveResults('', 1, 'row');
00388 if (count($results)) return $results[0][0];
00389 else return null;
00390 }
00391
00392 public function loadResultArray($numinarray = 0) {
00393 $results = $this->retrieveResults('', 0, 'row');
00394 foreach ($results as $result) $values[] = $result[$numinarray];
00395 return isset($values) ? $values : null;
00396 }
00397
00398 public function loadAssocList( $key='' ) {
00399 $results = $this->retrieveResults($key, 0, 'assoc');
00400 if (count($results)) return $results;
00401 else return null;
00402 }
00403
00404
00405 public function mosBindArrayToObject( $array, $obj, $ignore='', $prefix=NULL, $checkSlashes=true ) {
00406 if (!is_array($array) OR !is_object($obj)) return false;
00407 if ($prefix == null) $prefix = '';
00408 foreach (get_object_vars($obj) as $k => $v) {
00409 if( substr( $k, 0, 1 ) != '_' AND strpos($ignore, $k) === false) {
00410 if (isset($array[$prefix.$k])) {
00411 $obj->$k = ($checkSlashes AND get_magic_quotes_gpc()) ? $this->mosStripslashes( $array[$prefix.$k] ) : $array[$prefix.$k];
00412 }
00413 }
00414 }
00415 return true;
00416 }
00417
00418
00419 public function mosStripslashes($value) {
00420 if (is_string($value)) $ret = stripslashes($value);
00421 elseif (is_array($value)) {
00422 $ret = array();
00423 foreach ($value as $key=>$val) $ret[$key] = $this->mosStripslashes($val);
00424 }
00425 else $ret = $value;
00426 return $ret;
00427 }
00428
00429
00430 public function loadObject(&$object) {
00431 if (!is_object($object)) $results = $this->retrieveResults('', 1, 'object');
00432 else $results = $this->retrieveResults('', 1, 'assoc');
00433 if (0 == count($results)) return false;
00434 if (!is_object($object)) $object = $results[0];
00435 else {
00436 if (is_subclass_of($object, 'aliroDBGeneralRow')) $object->bind($results[0], '', false);
00437 else foreach (get_object_vars($object) as $k => $v) {
00438 if ($k[0] != '_' AND isset($results[0][$k])) $object->$k = $results[0][$k];
00439 }
00440 }
00441 return true;
00442 }
00443
00444 public function loadObjectList( $key='' ) {
00445 $results = $this->retrieveResults($key, 0, 'object');
00446 if (count($results)) return $results;
00447 else return null;
00448 }
00449
00450 public function loadRow() {
00451 $results = $this->retrieveResults('', 1, 'row');
00452 if (count($results)) return $results[0];
00453 else return null;
00454 }
00455
00456 public function loadRowList( $key='' ) {
00457 $results = $this->retrieveResults($key, 0, 'row');
00458 if (count($results)) return $results;
00459 else return null;
00460 }
00461
00462 public function insertObject ($table, $object, $keyName=NULL) {
00463 $dbfields = $this->getAllFieldNames($table);
00464 foreach ($dbfields as $name) {
00465 if (!isset($object->$name) OR is_array($object->$name) OR is_object($object->$name)) continue;
00466 $fields[] = "`$name`";
00467 $values[] = $this->setFieldValue($object->$name);
00468 }
00469 if (isset($fields)) {
00470 $result = $this->doInsertion ($table, implode( ",", $fields ), implode( ",", $values ));
00471
00472 $autoinc = $this->insertid();
00473 if ($autoinc AND $keyName AND !is_array($keyName)) $object->$keyName = $autoinc;
00474 return $result;
00475 }
00476 else {
00477 trigger_error (sprintf(T_('Insert into table %s but no fields'), $this->tableName));
00478 echo aliroRequest::trace();
00479 return false;
00480 }
00481 }
00482
00483 private function doInsertion ($table, $fields, $values) {
00484 return $this->doSQL("INSERT INTO $table ($fields) VALUES ($values)");
00485 }
00486
00487 public function updateObject ($table, $object, $keyName, $updateNulls=true) {
00488 $dbfields = $this->getAllFieldNames($table);
00489 foreach ($dbfields as $name) {
00490 if (!isset($object->$name) OR is_array($object->$name) OR is_object($object->$name)) {
00491 if ($updateNulls) $value = "''";
00492 else continue;
00493 }
00494 else $value = $this->setFieldValue($object->$name);
00495 $setter = "`$name` = $value";
00496 if (is_array($keyName) AND in_array($name, $keyName)) $where[] = $setter;
00497 elseif ($name == $keyName) $where[] = $setter;
00498 else $setters[] = $setter;
00499 }
00500 if (!isset($where)) {
00501 trigger_error (sprintf(T_('Update table %s but no key fields'), $table));
00502 return false;
00503 }
00504 if (isset($setters)) return $this->doUpdate ($table, implode (', ', $setters), implode (' AND ' , $where));
00505 return true;
00506 }
00507
00508 private function doUpdate ($table, $setters, $conditions) {
00509 return $this->doSQL("UPDATE $table SET $setters WHERE $conditions");
00510 }
00511
00512
00513 public function stderr( $showSQL = false ) {
00514 return "DB function failed with error number $this->_errorNum"
00515 ."<br /><font color=\"red\">$this->_errorMsg</font>"
00516 .($showSQL ? "<br />SQL = <pre>$this->_sql</pre>" : '');
00517 }
00518
00519 public function insertid() {
00520 return $this->interface->insertid();
00521 }
00522
00523 public function getVersion()
00524 {
00525 return $this->interface->getVersion();
00526 }
00527
00531 public function GenID () {
00532 return '0';
00533 }
00534
00535
00536
00537 public function getTableList() {
00538 $this->setQuery('SHOW tables');
00539 return $this->loadResultArray();
00540 }
00541
00542
00543 public function getTableCreate( $tables ) {
00544 $result = array();
00545
00546 foreach ($tables as $tblval) {
00547 $this->setQuery( 'SHOW CREATE table ' . $tblval );
00548 $this->query();
00549 $result[$tblval] = $this->loadResultArray( 1 );
00550 }
00551
00552 return $result;
00553 }
00554
00555
00556
00557 public function getTableFields( $tables ) {
00558 $result = array();
00559
00560 foreach ($tables as $tblval) {
00561 $fields = $this->doSQLget ( 'SHOW FIELDS FROM ' . $tblval );
00562 foreach ($fields as $field) {
00563 $result[$tblval][$field->Field] = preg_replace("/[(0-9)]/",'', $field->Type );
00564 }
00565 }
00566
00567 return $result;
00568 }
00569
00570 public function getCount () {
00571 return count($this->_log);
00572 }
00573
00574 public function getLogged () {
00575 $text = '<h4>'.$this->getCount().' queries executed</h4>';
00576 foreach ($this->_log as $k=>$sql) $text .= "\n".($k+1)."<br />".$sql.'<hr />';
00577 if (count($this->_log)) return $text;
00578 else return '';
00579 }
00580
00581 private function saveStats () {
00582 new aliroObjectSorter(self::$stats, 'timer');
00583 $n = count(self::$stats);
00584 if ($n > 0) {
00585 $median = self::$stats[intval($n/2)]->timer;
00586 $total = 0.0;
00587 foreach (self::$stats as $stat) $total += $stat->timer;
00588 $mean = $total/$n;
00589 $var = 0.0;
00590 foreach (self::$stats as $stat) $var += ($stat->timer - $mean) * ($stat->timer - $mean);
00591 $stdev = sqrt($var);
00592 $best = self::$stats[0]->timer;
00593 $worst = self::$stats[$n-1]->timer;
00594 $elapsed = aliro::getInstance()->getElapsed();
00595 $memory = memory_get_usage();
00596 $uri = htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES, 'UTF-8');
00597 aliroCoreDatabase::getInstance()->doSQL("INSERT INTO #__query_stats (count, mean, median, stdev, best, worst, total, elapsed, memory, uri) VALUES ($n, '$mean', '$median', '$stdev', '$best', '$worst', '$total', '$elapsed', '$memory', '$uri')");
00598 $queryid = $this->insertid();
00599 $stats = self::$stats;
00600 foreach ($stats as $stat) {
00601 if (0.50 < $stat->timer) {
00602 $querytext = htmlspecialchars($stat->sql, ENT_QUOTES);
00603 $tracetext = $this->getEscaped($stat->trace, ENT_QUOTES);
00604 $sql = "INSERT INTO {$this->_table_prefix}query_slow (queryid, time, trace, querytext) VALUES ($queryid, '$stat->timer', '$tracetext', '$querytext')";
00605 $this->interface->query($sql);
00606 }
00607 }
00608 }
00609 self::$stats = array();
00610 }
00611
00612 }
00613
00614 class database extends aliroAbstractDatabase {
00615
00616 }
00617
00618 class aliroDatabaseHandler extends aliroAbstractDatabase {
00619
00620 }
00621
00622 class aliroDatabase {
00623
00624 protected static $instance = __CLASS__;
00625 protected $database;
00626
00627 protected function __construct () {
00628 $credentials = aliroCore::getConfigData('credentials.php');
00629 $this->database = new aliroDatabaseHandler ($credentials['dbhost'], $credentials['dbusername'], $credentials['dbpassword'], $credentials['dbname'], $credentials['dbprefix']);
00630 }
00631
00632 protected function __clone () {
00633
00634 }
00635
00636 public function __call ($method, $args) {
00637 return call_user_func_array(array($this->database, $method), $args);
00638 }
00639
00640
00641 public function loadObject (&$object) {
00642 return $this->database->loadObject($object);
00643 }
00644
00645 public static function getInstance () {
00646 return is_object(self::$instance) ? self::$instance : (self::$instance = new self::$instance);
00647 }
00648
00649 }
00650
00651 class mamboDatabase extends aliroDatabase {
00652
00653 }
00654
00655
00656 class joomlaDatabase extends aliroDatabase {
00657 protected static $instance = __CLASS__;
00658
00659 public static function getInstance () {
00660 return is_object(self::$instance) ? self::$instance : (self::$instance = new self::$instance);
00661 }
00662
00663 public function loadObject () {
00664 $object = null;
00665 $this->database->loadObject($object);
00666 return $object;
00667 }
00668 }
00669
00670 class aliroCoreDatabase extends aliroDatabase {
00671
00672 protected static $instance = __CLASS__;
00673
00674 protected function __construct () {
00675 $credentials = aliroCore::getConfigData('corecredentials.php');
00676 $this->database = new aliroDatabaseHandler ($credentials['dbhost'], $credentials['dbusername'], $credentials['dbpassword'], $credentials['dbname'], $credentials['dbprefix']);
00677 }
00678
00679 public static function getInstance () {
00680 return is_object(self::$instance) ? self::$instance : (self::$instance = new self::$instance);
00681 }
00682 }