[ Index ]

PHP Cross Reference of Mambo 4.6.5

[ Variables ]     [ Functions ]     [ Classes ]     [ Constants ]     [ Statistics ]

title

Body

[close]

/includes/ -> database.php (source)

   1  <?php
   2  /**
   3  * @package Mambo
   4  * @subpackage Database
   5  * @author Mambo Foundation Inc see README.php
   6  * @copyright (C) 2000 - 2009 Mambo Foundation Inc.
   7  * See COPYRIGHT.php for copyright notices and details.
   8  * @license GNU/GPL Version 2, see LICENSE.php
   9  *
  10  * Redistributions of files must retain the above copyright notice.
  11  *
  12  * Mambo is free software; you can redistribute it and/or
  13  * modify it under the terms of the GNU General Public License
  14  * as published by the Free Software Foundation; version 2 of the License.
  15  */
  16  
  17  /**
  18  * Database connector class
  19  */
  20  class database {
  21      /** @var string Internal variable to hold the query sql */
  22      var $_sql='';
  23      /** @var int Internal variable to hold the database error number */
  24      var $_errorNum=0;
  25      /** @var string Internal variable to hold the database error message */
  26      var $_errorMsg='';
  27      /** @var string Internal variable to hold the prefix used on all database tables */
  28      var $_table_prefix='';
  29      /** @var Internal variable to hold the connector resource */
  30      var $_resource='';
  31      /** @var Internal variable to hold the last query cursor */
  32      var $_cursor=null;
  33      /** @var boolean Debug option */
  34      var $_debug=0;
  35      /** @var array A log of queries */
  36      var $_log=array();
  37      /** @var string Null date */
  38      var $_null_date='0000-00-00 00:00:00';
  39  
  40      /**
  41      * Database object constructor
  42      * @param string Database host
  43      * @param string Database user name
  44      * @param string Database user password
  45      * @param string Database name
  46      * @param string Common prefix for all tables
  47      */
  48  	function database( $host='localhost', $user, $pass, $db, $table_prefix ) {
  49          global $configuration,$charset;
  50          // perform a number of fatality checks, then die gracefully
  51          if (!function_exists( 'mysql_connect' )) $this->forceOffline(1);
  52          if (!($this->_resource = @mysql_connect( $host, $user, $pass ))) $this->forceOffline(2);
  53          if (!mysql_select_db($db)) $this->forceOffline(3);
  54          $this->_table_prefix = $table_prefix;
  55          if(floatval(mysql_get_client_info())>=4.1){
  56              $mysql_charsets = $this->getCharsets();
  57              $charset = isset($charset) ? trim($charset) : '';
  58              $charset = isset($configuration)?$configuration->mosConfig_charset:$charset;
  59              $charset = in_array($charset, array_keys($mysql_charsets)) ? $charset : $this->getCharsetFromDb();
  60              $charset = in_array($charset, array_keys($mysql_charsets)) ? $charset : 'utf-8';
  61              $cs=$mysql_charsets[$charset];
  62              mysql_query( "SET CHARSET '" .$cs. "'" );
  63          }
  64      }
  65  
  66  	function getCharsetFromDb() {
  67          static $charset_from_db;
  68          if (!isset($charset_from_db)) {
  69              //retrieve from database
  70              $query = 'show variables like "character_set_database";';
  71              $this->setQuery( $query );
  72              $results = $this->loadObjectList();
  73              $charset_from_db = $results[0]->Value;
  74          }
  75          return $charset_from_db;
  76      }
  77  
  78  	function getCharsets() {
  79          static $mysql_charsets;
  80          if (!isset($mysql_charsets)) {
  81              $mysql_charsets = array();
  82              $mysql_charsets['utf-8']='utf8';
  83              $mysql_charsets['iso-8859-1']='latin1';
  84              $mysql_charsets['iso-8859-15']='latin1';
  85              $mysql_charsets['koi8-r']='koi8r';
  86              $mysql_charsets['windows-1251']='cp1251';
  87              $mysql_charsets['cp1251']='cp1251';
  88              $mysql_charsets['gb2312']='gb2312';
  89              $mysql_charsets['gb18030']='gb2312';
  90              $mysql_charsets['gbk']='gb2312';
  91              $mysql_charsets['big5-hkscs']='big5';
  92              $mysql_charsets['big5']='big5';
  93              $mysql_charsets['euc-tw']='gb2312';
  94              $mysql_charsets['iso-8859-2']='latin2';
  95              $mysql_charsets['windows-1250']='latin2';
  96              $mysql_charsets['iso-8859-7']='latin7';
  97              $mysql_charsets['iso-8859-8-i']='hebrew';
  98              $mysql_charsets['iso-8859-8']='hebrew';
  99              $mysql_charsets['sjis']='sjis';
 100              $mysql_charsets['windows-1257']='latin7';
 101              $mysql_charsets['iso-8859-13']='latin7';
 102              $mysql_charsets['cp-866']='cp1251';
 103              $mysql_charsets['iso-8859-5']='latin5';
 104              $mysql_charsets['koi8-u']='koi8r';
 105              $mysql_charsets['windows-1252']='latin1';
 106              $mysql_charsets['tis-620']='tis620';
 107              $mysql_charsets['iso-8859-9']='latin5';
 108              $mysql_charsets['windows-1256']='cp1256';
 109              $mysql_charsets['georgian-ps']='geostd8';
 110              $mysql_charsets['euc-jp']='eucjpms';
 111              $mysql_charsets['euc-kr']='euckr';
 112              $mysql_charsets['iso-8859-6']='cp1256';
 113              $mysql_charsets['windows-1258']='latin1'; //No better match
 114          }
 115          return $mysql_charsets;
 116      }
 117  
 118  	function forceOffline ($error_number) {
 119              $mosSystemError = $error_number;
 120              $basePath = dirname( __FILE__ );
 121              include $basePath . '/../configuration.php';
 122              include $basePath . '/../offline.php';
 123              exit();
 124      }
 125      
 126  	function getNullDate () {
 127          return $this->_null_date;
 128      }
 129      /**
 130      * @param int
 131      */
 132  	function debug( $level ) {
 133          $this->_debug = intval( $level );
 134      }
 135  
 136  	function debug_trace () {
 137          trigger_error( $this->_errorNum, E_USER_NOTICE );
 138          //echo "<pre>" . $this->_sql . "</pre>\n";
 139          if (function_exists('debug_backtrace')) {
 140              foreach(debug_backtrace() as $back) {
 141                  if (@$back['file']) {
 142                      echo '<br />'.$back['file'].':'.$back['line'];
 143                  }
 144              }
 145          }
 146      }
 147      /**
 148      * @return int The error number for the most recent query
 149      */
 150  	function getErrorNum() {
 151          return $this->_errorNum;
 152      }
 153      /**
 154      * @return string The error message for the most recent query
 155      */
 156  	function getErrorMsg() {
 157          return str_replace( array( "\n", "'" ), array( '\n', "\'" ), $this->_errorMsg );
 158      }
 159      /**
 160      * Get a database escaped string
 161      * @return string
 162      */
 163  	function getEscaped( $text ) {
 164          if (phpversion() < '4.3.0') {
 165              return mysql_escape_string( $text );
 166          } else {
 167              return mysql_real_escape_string( $text );
 168          }
 169      }
 170      /**
 171      * Get a quoted database escaped string
 172      * @return string
 173      */
 174  	function Quote( $text ) {
 175          if (phpversion() < '4.3.0') {
 176              return '\'' . mysql_escape_string( $text ) . '\'';
 177          } else {
 178              return '\'' . mysql_real_escape_string( $text ) . '\'';
 179          }
 180      }
 181      /**
 182      * Sets the SQL query string for later execution.
 183      *
 184      * @param string The SQL query
 185      */
 186  	function setBareQuery ($sql) {
 187          $this->_sql = $sql;
 188      }
 189      /**
 190      * Sets the SQL query string for later execution.
 191      *
 192      * This function replaces a string identifier <var>$prefix</var> with the
 193      * string held is the <var>_table_prefix</var> class variable.
 194      *
 195      * @param string The SQL query
 196      * @param string The common table prefix
 197      */
 198  	function setQuery( $sql, $prefix='#__' ) {
 199          $this->setBareQuery ($this->replacePrefix($sql, $prefix));
 200  //      This is maintenance code for catching particular SQL statements
 201  //        if (strpos($this->_sql,'SELECT menutype') === 0) debug_print_backtrace();
 202      }
 203  
 204      /**
 205       * This function replaces a string identifier <var>$prefix</var> with the
 206       * string held is the <var>_table_prefix</var> class variable.
 207       *
 208       * @param string The SQL query
 209       * @param string The common table prefix
 210       * @author thede, David McKinnis
 211       */
 212  	function replacePrefix ($sql, $prefix='#__') {
 213          $done = '';
 214          while (strlen($sql)) {
 215              $single = preg_match("/\'([^\\\']|\\.)*'/", $sql,$matches_single,PREG_OFFSET_CAPTURE);
 216              if ($double = preg_match('/\"([^\\\"]|\\.)*"/', $sql,$matches_double,PREG_OFFSET_CAPTURE) OR $single) {
 217                  if ($single == 0 OR ($double AND $matches_double[0][1] < $matches_single[0][1])) {
 218                      $done .= str_replace($prefix, $this->_table_prefix, substr($sql,0,$matches_double[0][1])).$matches_double[0][0];
 219                      $sql = substr($sql,$matches_double[0][1]+strlen($matches_double[0][0]));
 220                  }
 221                  else {
 222                      $done .= str_replace($prefix, $this->_table_prefix, substr($sql,0,$matches_single[0][1])).$matches_single[0][0];
 223                      $sql = substr($sql,$matches_single[0][1]+strlen($matches_single[0][0]));
 224                  }
 225              }
 226              else return $done.str_replace($prefix, $this->_table_prefix,$sql);
 227          }
 228          return $done;
 229      }
 230      /**
 231      * @return string The current value of the internal SQL vairable
 232      */
 233  	function getQuery($sql='') {
 234          if ($sql == '') $sql = $this->_sql;
 235          return "<pre>" . htmlspecialchars( $sql ) . "</pre>";
 236      }
 237      /**
 238      * Execute the query
 239      * @return mixed A database resource if successful, FALSE if not.
 240      */
 241  	function query($sql = '') {
 242          global $mosConfig_debug;
 243          if ($sql == '') $sql = $this->_sql;
 244          if ($this->_debug) $this->_log[] = $sql;
 245          if ($this->_cursor = mysql_query($sql, $this->_resource)) {
 246              $this->_errorNum = 0;
 247              $this->_errorMsg = '';
 248              return $this->_cursor;
 249          }
 250          else {
 251              $this->_errorNum = mysql_errno( $this->_resource );
 252              $this->_errorMsg = mysql_error( $this->_resource )." SQL=$sql";
 253              if ($this->_debug) $this->debug_trace();
 254              return false;
 255          }
 256      }
 257  
 258  	function query_batch( $abort_on_error=true, $p_transaction_safe = false) {
 259          $this->_errorNum = 0;
 260          $this->_errorMsg = '';
 261          if ($p_transaction_safe) {
 262              $si = mysql_get_server_info();
 263              preg_match_all( "/(\d+)\.(\d+)\.(\d+)/i", $si, $m );
 264              $prefix = '';
 265              if ($m[1] >= 4) $prefix = 'START TRANSACTION; ';
 266              elseif ($m[2] >= 23) {
 267                  if ($m[3] >= 19) $prefix = 'BEGIN WORK; ';
 268                  elseif ($m[3] >= 17) $prefix = 'BEGIN; ';
 269              }
 270              if ($prefix) $this->_sql = $prefix.$this->_sql.'; COMMIT;';
 271          }
 272          $query_split = preg_split ("/[;]+/", $this->_sql);
 273          $error = 0;
 274          foreach ($query_split as $command_line) {
 275              $command_line = trim( $command_line );
 276              if ($command_line != '') {
 277                  if (!$this->query($command_line)) {
 278                      $error = 1;
 279                      if ($abort_on_error) {
 280                          return $this->_cursor;
 281                      }
 282                  }
 283              }
 284          }
 285          return $error ? false : true;
 286      }
 287  
 288      /**
 289      * Diagnostic function
 290      */
 291  	function explain() {
 292          if (!($cur = $this->query("EXPLAIN ".$this->_sql))) return null;
 293          $headline = $header = $body = '';
 294          $buf = '<table cellspacing="1" cellpadding="2" border="0" bgcolor="#000000" align="center">';
 295          $buf .= $this->getQuery("EXPLAIN ".$this->_sql);
 296          while ($row = mysql_fetch_assoc($cur)) {
 297              $body .= "<tr>";
 298              foreach ($row as $k=>$v) {
 299                  if ($headline == '') $header .= "<th bgcolor=\"#ffffff\">$k</th>";
 300                  $body .= "<td bgcolor=\"#ffffff\">$v</td>";
 301              }
 302              $headline = $header;
 303              $body .= "</tr>";
 304          }
 305          $buf .= "<tr>$headline</tr>$body</table><br />&nbsp;";
 306          mysql_free_result( $cur );
 307          return "<div style=\"background-color:#FFFFCC\" align=\"left\">$buf</div>";
 308      }
 309      /**
 310      * @return int The number of rows returned from the most recent query - SELECT only
 311      */
 312  	function getNumRows( $cur=null ) {
 313          return mysql_num_rows( $cur ? $cur : $this->_cursor );
 314      }
 315  
 316      /**
 317      * @return int The number of rows affected by the most recent query - INSERT, UPDATE, DELETE
 318      */
 319  	function getAffectedRows(  ) {
 320          return mysql_affected_rows( $this->_resource );
 321      }
 322  
 323      /**
 324      * Load an array of retrieved database objects or values
 325      * @param int Database cursor
 326      * @param string The field name of a primary key
 327      * @return array If <var>key</var> is empty as sequential list of returned records.
 328      * If <var>key</var> is not empty then the returned array is indexed by the value
 329      * the database key.  Returns <var>null</var> if the query fails.
 330      */
 331      function &retrieveResults ($key='', $max=0, $result_type='row') {
 332          $results = array();
 333          $sql_function = 'mysql_fetch_'.$result_type;
 334          if ($cur = $this->query()) {
 335              while ($row = $sql_function($cur)) {
 336                  if ($key != '') {
 337                      if ( is_array($row) ) {
 338                          $results[$row[$key]] = $row;
 339                      } else {
 340                          $results[$row->$key] = $row;
 341                      }
 342                  } else {
 343                      $results[] = $row;
 344                  }
 345                  if ($max AND count($results) >= $max) break;
 346              }
 347              mysql_free_result($cur);
 348          }
 349          return $results;
 350      }
 351      /**
 352      * This method loads the first field of the first row returned by the query.
 353      *
 354      * @return The value returned in the query or null if the query failed.
 355      */
 356  	function loadResult() {
 357          $results =& $this->retrieveResults('', 1, 'row');
 358          if (count($results)) return $results[0][0];
 359          else return null;
 360      }
 361  
 362      /**
 363      * Load an array of single field results into an array
 364      */
 365  	function loadResultArray($numinarray = 0) {
 366          $results =& $this->retrieveResults('', 0, 'row');
 367          $values = array();
 368          foreach ($results as $result) $values[] = $result[$numinarray];
 369          if (count($values)) return $values;
 370          else return null;
 371      }
 372      /**
 373      * Load a assoc list of database rows
 374      * @param string The field name of a primary key
 375      * @return array If <var>key</var> is empty as sequential list of returned records.
 376      */
 377  	function loadAssocList( $key='' ) {
 378          $results =& $this->retrieveResults($key, 0, 'assoc');
 379          if (count($results)) return $results;
 380          else return null;
 381      }
 382      /**
 383      * Copy the named array content into the object as properties
 384      * only existing properties of object are filled. when undefined in hash, properties wont be deleted
 385      * @param array the input array
 386      * @param obj byref the object to fill of any class
 387      * @param string
 388      * @param boolean
 389      */
 390  	function mosBindArrayToObject( $array, &$obj, $ignore='', $prefix=NULL, $checkSlashes=true ) {
 391          if (!is_array($array) OR !is_object($obj)) return false;
 392          if ($prefix == null) $prefix = '';
 393          foreach (get_object_vars($obj) as $k => $v) {
 394              if( substr( $k, 0, 1 ) != '_' AND strpos($ignore, $k) === false) {
 395                  if (isset($array[$prefix.$k])) {
 396                      $obj->$k = ($checkSlashes AND get_magic_quotes_gpc()) ? $this->mosStripslashes( $array[$prefix.$k] ) : $array[$prefix.$k];
 397                  }
 398              }
 399          }
 400          return true;
 401      }
 402  
 403      /**
 404      * Strip slashes from strings or arrays of strings
 405      * @param value the input string or array
 406      */
 407  	function mosStripslashes(&$value) {
 408          if (is_string($value)) $ret = stripslashes($value);
 409          else {
 410              if (is_array($value)) {
 411                  $ret = array();
 412                  while (list($key,$val) = each($value)) {
 413                      $ret[$key] = $this->mosStripslashes($val);
 414                  } // while
 415              } else $ret = $value;
 416          } // if
 417          return $ret;
 418      } // mosStripSlashes
 419  
 420      /**
 421      * This global function loads the first row of a query into an object
 422      *
 423      * If an object is passed to this function, the returned row is bound to the existing elements of <var>object</var>.
 424      * If <var>object</var> has a value of null, then all of the returned query fields returned in the object.
 425      * @param string The SQL query
 426      * @param object The address of variable
 427      */
 428  	function loadObject( &$object ) {
 429          if ($object != null) {
 430              $results =& $this->retrieveResults('', 1, 'assoc');
 431              if (count($results)) {
 432                  $this->mosBindArrayToObject($results[0], $object, null, null, false);
 433                  return true;
 434              }
 435          }
 436          else {
 437              $results =& $this->retrieveResults('', 1, 'object');
 438              if (count($results)) {
 439                  $object = $results[0];
 440                  return true;
 441              }
 442              else $object = null;
 443          }
 444          return false;
 445      }
 446      /**
 447      * Load a list of database objects
 448      * @param string The field name of a primary key
 449      * @return array If <var>key</var> is empty as sequential list of returned records.
 450      * If <var>key</var> is not empty then the returned array is indexed by the value
 451      * the database key.  Returns <var>null</var> if the query fails.
 452      */
 453  	function loadObjectList( $key='' ) {
 454          $results =& $this->retrieveResults($key, 0, 'object');
 455          if (count($results)) return $results;
 456          else return null;
 457      }
 458      /**
 459      * @return The first row of the query.
 460      */
 461  	function loadRow() {
 462          $results =& $this->retrieveResults('', 1, 'row');
 463          if (count($results)) return $results[0];
 464          else return null;
 465      }
 466      /**
 467      * Load a list of database rows (numeric column indexing)
 468      * @param string The field name of a primary key
 469      * @return array If <var>key</var> is empty as sequential list of returned records.
 470      * If <var>key</var> is not empty then the returned array is indexed by the value
 471      * the database key.  Returns <var>null</var> if the query fails.
 472      */
 473  	function loadRowList( $key='' ) {
 474          $results =& $this->retrieveResults('', 0, 'row');
 475          if (count($results)) return $results;
 476          else return null;
 477      }
 478  
 479      /**
 480      * Document::db_insertObject()
 481      *
 482      * { Description }
 483      *
 484      * @param [type] $keyName
 485      * @param [type] $verbose
 486      */
 487  	function insertObject( $table, &$object, $keyName = NULL, $verbose=false ) {
 488          $fmtsql = "INSERT INTO $table ( %s ) VALUES ( %s ) ";
 489          $fmtsql = $this->replacePrefix($fmtsql);
 490          $fields = array();
 491          foreach (get_object_vars( $object ) as $k => $v) {
 492              if (is_array($v) OR is_object($v) OR $v === NULL OR $k[0] == '_') continue;
 493              $fields[] = "`$k`";
 494              $values[] = "'" . $this->getEscaped( $v ) . "'";
 495          }
 496          if (!isset($fields)) die ('class database method insertObject - no fields');
 497          $this->setBareQuery( sprintf( $fmtsql, implode( ",", $fields ), implode( ",", $values ) ) );
 498          ($verbose) && print "$sql<br />\n";
 499          if (!$this->query()) return false;
 500          $id = mysql_insert_id();
 501          ($verbose) && print "id=[$id]<br />\n";
 502          if ($keyName && $id) $object->$keyName = $id;
 503          return true;
 504      }
 505  
 506      /**
 507      * Document::db_updateObject()
 508      *
 509      * { Description }
 510      *
 511      * @param [type] $updateNulls
 512      */
 513  	function updateObject( $table, &$object, $keyName, $updateNulls=true ) {
 514          $fmtsql = "UPDATE $table SET %s WHERE %s";
 515          $fmtsql = $this->replacePrefix($fmtsql);
 516          $tmp = array();
 517          foreach (get_object_vars( $object ) as $k => $v) {
 518              if (is_array($v) OR is_object($v) OR $k[0] == '_' OR ($v === null AND !$updateNulls)) continue;
 519              if( $k == $keyName ) { // PK not to be updated
 520                  $where = "$keyName='" . $this->getEscaped( $v ) . "'";
 521                  continue;
 522              }
 523              if ($v) $v = $this->getEscaped($v);
 524              $tmp[] = "`$k`='$v'";
 525          }
 526          if (!isset($tmp)) return true;
 527          if (!isset($where)) die ('database class updateObject method - no key value');
 528          $this->setBareQuery( sprintf( $fmtsql, implode( ",", $tmp ) , $where ) );
 529          return $this->query();
 530      }
 531  
 532      /**
 533      * @param boolean If TRUE, displays the last SQL statement sent to the database
 534      * @return string A standised error message
 535      */
 536  	function stderr( $showSQL = false ) {
 537          return "DB function failed with error number $this->_errorNum"
 538          ."<br /><font color=\"red\">$this->_errorMsg</font>"
 539          .($showSQL ? "<br />SQL = <pre>$this->_sql</pre>" : '');
 540      }
 541  
 542  	function insertid()
 543      {
 544          return mysql_insert_id();
 545      }
 546  
 547  	function getVersion()
 548      {
 549          return mysql_get_server_info();
 550      }
 551  
 552      /**
 553      * Fudge method for ADOdb compatibility
 554      */
 555  	function GenID( $foo1=null, $foo2=null ) {
 556          return '0';
 557      }
 558      /**
 559      * @return array A list of all the tables in the database
 560      */
 561  	function getTableList() {
 562          $this->setQuery( 'SHOW tables' );
 563          $this->query();
 564          return $this->loadResultArray();
 565      }
 566      /**
 567      * @param array A list of table names
 568      * @return array A list the create SQL for the tables
 569      */
 570  	function getTableCreate( $tables ) {
 571          $result = array();
 572  
 573          foreach ($tables as $tblval) {
 574              $this->setQuery( 'SHOW CREATE table ' . $tblval );
 575              $this->query();
 576              $result[$tblval] = $this->loadResultArray( 1 );
 577          }
 578  
 579          return $result;
 580      }
 581      /**
 582      * @param array A list of table names
 583      * @return array An array of fields by table
 584      */
 585  	function getTableFields( $tables ) {
 586          $result = array();
 587  
 588          foreach ($tables as $tblval) {
 589              $this->setQuery( 'SHOW FIELDS FROM ' . $tblval );
 590              $this->query();
 591              $fields = $this->loadObjectList();
 592              foreach ($fields as $field) {
 593                  $result[$tblval][$field->Field] = preg_replace("/[(0-9)]/",'', $field->Type );
 594              }
 595          }
 596  
 597          return $result;
 598      }
 599  
 600  	function displayLogged () {
 601          echo count($this->_log).' queries executed';
 602          echo '<pre>';
 603           foreach ($this->_log as $k=>$sql) {
 604               echo $k+1 . "\n" . $sql . '<hr />';
 605          }
 606      }
 607  
 608      /* Helper method - maybe should go into database itself */
 609  	function doSQL ($sql) {
 610          $this->setQuery($sql);
 611          if (!$this->query()) {
 612              echo "<script> alert('".$this->getErrorMsg()."'); window.history.go(-1); </script>\n";
 613              exit();
 614          }
 615      }
 616  
 617      /* Helper method - maybe could go into database itself */
 618      function &doSQLget ($sql, $classname) {
 619          $this->setQuery($sql);
 620          $rows = $this->loadObjectList();
 621          $target = get_class_vars($classname);
 622          if ($rows) {
 623              foreach ($rows as $row) {
 624                  $next = new $classname(0);
 625                  foreach ($target as $field=>$value) {
 626                      if (isset($row->$field)) $next->$field = $row->$field;
 627                  }
 628                  $result[] = $next;
 629              }
 630          }
 631          else $result = array();
 632          return $result;
 633      }
 634  
 635  
 636  }
 637  
 638  class mamboDatabase extends database {
 639  
 640  	function mamboDatabase () {
 641          $host = mamboCore::get('mosConfig_host');
 642          $user = mamboCore::get('mosConfig_user');
 643          $pw = mamboCore::get('mosConfig_password');
 644          $db = mamboCore::get('mosConfig_db');
 645          $prefix = mamboCore::get('mosConfig_dbprefix');
 646          parent::database($host, $user, $pw, $db, $prefix);
 647      }
 648  
 649      function &getInstance () {
 650          static $instance;
 651          if (!is_object($instance)) $instance = new mamboDatabase();
 652          return $instance;
 653      }
 654  }
 655  
 656  /**
 657  * mosDBAbstractRow Abstract Class.
 658  * @abstract
 659  * @package Mambo
 660  * @subpackage Database
 661  *
 662  * Parent classes to all database derived objects.  Customisation will generally
 663  * not involve tampering with this object.
 664  * @package Mambo
 665  * @author Martin Brampton counterpoint@mambo-foundation.org
 666  */
 667  class mosDBAbstractRow {
 668      /** @var string Name of the table in the db schema relating to child class */
 669      var $_tbl = '';
 670      /** @var string Name of the primary key field in the table */
 671      var $_tbl_key = '';
 672      /** @var string Error message */
 673      var $_error = '';
 674  
 675      /**
 676      *    Object constructor to set table and key field
 677      *
 678      *    Can be overloaded/supplemented by the child class
 679      *    @param string $table name of the table in the db schema relating to child class
 680      *    @param string $key name of the primary key field in the table
 681      */
 682  	function mosDBAbstractRow ($table='', $keyname='id', $db='') {
 683          if ($table) $this->_tbl = $table;
 684          else $this->_tbl = $this->tableName();
 685          $this->_tbl_key = $keyname;
 686          if (is_object($db)) $this->_db = $db;
 687      }
 688  
 689      /**
 690      *    generic check method
 691      *
 692      *    can be overloaded/supplemented by the child class
 693      *    @return boolean True if the object is ok
 694      */
 695  	function check() {
 696          return true;
 697      }
 698  
 699      /**
 700      * Checks if this object lacks the property given by the parameter
 701      * @param string The name of the property
 702      * @return bool
 703      */
 704  	function lacks( $property ) {
 705          $thisclass = get_class($this);
 706          if (array_key_exists( $property, get_class_vars($thisclass) )) return false;
 707          $this->_error = T_(sprintf('WARNING: %s does not support %s.', $thisclass, $property));
 708          return true;
 709      }
 710  
 711      /**
 712      /* Move a database row object up or down through the ordering
 713      /* @param int positive to move up, negative to move down
 714      /* @param string Additional conditions on the WHERE clause to limit the effect
 715      */
 716  	function move( $direction, $where='' ) {
 717          $compops = array (-1 => '<', 0 => '=', 1 => '>');
 718          $relation = $compops[($direction>0)-($direction<0)];
 719          $ordering = ($relation == '<' ? 'DESC' : 'ASC');
 720          $k = $this->_tbl_key;
 721          $o1 = $this->ordering;
 722          $k1 = $this->$k;
 723          $database = isset($this->_db) ? $this->_db : mamboDatabase::getInstance();
 724          $sql = "SELECT $k, ordering FROM $this->_tbl WHERE ordering $relation $o1";
 725          $sql .= ($where ? "\n AND $where" : '').' ORDER BY ordering '.$ordering.' LIMIT 1';
 726          $database->setQuery( $sql );
 727          if ($database->loadObject($row)) {
 728              $o2 = $row->ordering;
 729              $k2 = $row->$k;
 730              $sql = "UPDATE $this->_tbl SET ordering = (ordering=$o1)*$o2 + (ordering=$o2)*$o1 WHERE $k = $k1 OR $k = $k2";
 731              $database->doSQL($sql);
 732          }
 733      }
 734      /**
 735      * Compacts the ordering sequence of the selected records
 736      * @param string Additional conditions on WHERE clause to limit ordering to a particular subset of records
 737      */
 738  	function updateOrder( $where='', $cfid=null, $order=null ) {
 739          if ($this->lacks('ordering')) return false;
 740          $k = $this->_tbl_key;
 741          if ($this->_tbl == "#__content_frontpage") $order2 = ", content_id DESC";
 742          else $order2 = "";
 743  
 744          $database = isset($this->_db) ? $this->_db : mamboDatabase::getInstance();
 745          
 746          if (!is_null($cfid) AND !is_null($order)) {
 747              foreach ($cfid as $i=>$id) {
 748                  $o = intval($order[$i]);
 749                  $set[] = "(id=$id)*$o";
 750              }
 751              $sql = "UPDATE $this->_tbl SET ordering = ".implode(' + ', $set).' WHERE id IN ('.implode(',', $cfid).')';
 752              $database->doSQL($sql);
 753          }
 754          
 755          $sql = "SELECT $k, ordering FROM $this->_tbl "
 756          . ($where ? "\nWHERE $where" : '')
 757          . "\nORDER BY ordering$order2";
 758          $database->setQuery($sql);
 759          if (!$rows = $database->loadObjectList()) {
 760              $this->_error = $database->getErrorMsg();
 761              return false;
 762          }
 763          $i = 1;
 764          foreach ($rows as $row) {
 765              $sql = "UPDATE $this->_tbl SET ordering=$i WHERE $k = ".$row->$k;
 766              $database->doSQL($sql);
 767              $i++;
 768          }
 769          return true;
 770      }
 771  
 772  }
 773      
 774  
 775  /**
 776  * mosDBTable Abstract Class.
 777  * @abstract
 778  * @package Mambo
 779  * @subpackage Database
 780  *
 781  * Parent classes to all database derived objects.  Customisation will generally
 782  * not involve tampering with this object.
 783  * @package Mambo
 784  * @author Andrew Eddie <eddieajau@users.sourceforge.net
 785  */
 786  class mosDBTable extends mosDBAbstractRow {
 787      /** @var mosDatabase Database connector */
 788      var $_db = null;
 789  
 790      /**
 791       *    @return bool True if DB query failed.  Sets the error message
 792       */
 793  	function queryTestFailure () {
 794          if ($this->_db->query()) return false;
 795          $this->_error = $this->_db->getErrorMsg();
 796          return true;
 797      }
 798      /**
 799       * Filters public properties
 800       * @access protected
 801       * @param array List of fields to ignore
 802       */
 803  	function filter( $ignoreList=null ) {
 804           $callcheck = array('InputFilter', 'process');
 805           if (!is_callable($callcheck)) require_once(mamboCore::get('mosConfig_absolute_path').'/includes/phpInputFilter/class.inputfilter.php');
 806          // specific filters
 807          // $iFilter =& new InputFilter(); - previous call to original Php Input Filter
 808          $iFilter =& mosInputFilter::getInstance(); // use extended Php Input Filter class from mambo.php
 809          if (is_array($ignoreList)) foreach ($this->getPublicProperties() as $k) {
 810              if (!in_array($k, $ignoreList)) $this->$k = $iFilter->process($this->$k);
 811          }
 812          else foreach ($this->getPublicProperties() as $k) $this->$k = $iFilter->process($this->$k);
 813      }
 814      /**
 815       *    @return string Returns the error message
 816       */
 817  	function getError() {
 818          return $this->_error;
 819      }
 820      /**
 821      * Gets the value of the class variable
 822      * @param string The name of the class variable
 823      * @return mixed The value of the class var (or null if no var of that name exists)
 824      */
 825  	function get( $_property ) {
 826          if(isset( $this->$_property )) return $this->$_property;
 827          else return null;
 828      }
 829      /**
 830       * Returns an array of public properties
 831       * @return array
 832       */
 833  	function getPublicProperties() {
 834          static $cache = null;
 835          if (is_null( $cache )) {
 836              $cache = array();
 837              foreach (get_class_vars( get_class( $this ) ) as $key=>$val) {
 838                  if (substr( $key, 0, 1 ) != '_') {
 839                      $cache[] = $key;
 840                  }
 841              }
 842          }
 843          return $cache;
 844      }
 845      /**
 846      * Set the value of the class variable
 847      * @param string The name of the class variable
 848      * @param mixed The value to assign to the variable
 849      */
 850  	function set( $_property, $_value ) {
 851          $this->$_property = $_value;
 852      }
 853      /**
 854      *    binds a named array/hash to this object
 855      *
 856      *    can be overloaded/supplemented by the child class
 857      *    @param array $hash named array
 858      *    @return null|string    null is operation was satisfactory, otherwise returns an error
 859      */
 860  	function bind( $array, $ignore="" ) {
 861          $database =& mamboDatabase::getInstance();
 862          if (is_array($array)) return $database->mosBindArrayToObject($array, $this, $ignore);
 863          $this->_error = strtolower(get_class( $this ))."::bind failed.";
 864          return false;
 865      }
 866  
 867      /**
 868      *    binds an array/hash to this object
 869      *    @param int $oid optional argument, if not specifed then the value of current key is used
 870      *    @return any result from the database operation
 871      */
 872  	function load( $oid=null ) {
 873          $k = $this->_tbl_key;
 874          if ($oid !== null) {
 875              $this->$k = $this->_db->getEscaped($oid);
 876          }    
 877          if ($this->$k === null) return false;
 878          $this->_db->setQuery("SELECT * FROM $this->_tbl WHERE $this->_tbl_key='".$this->$k."'" );
 879          return $this->_db->loadObject($this);
 880      }
 881  
 882      /**
 883      * Inserts a new row if id is zero or updates an existing row in the database table
 884      *
 885      * Can be overloaded/supplemented by the child class
 886      * @param boolean If false, null object variables are not updated
 887      * @return null|string null if successful otherwise returns and error message
 888      */
 889  	function store( $updateNulls=false ) {
 890          $k = $this->_tbl_key;
 891          global $migrate;
 892          if( $this->$k && !$migrate) $ret = $this->_db->updateObject( $this->_tbl, $this, $this->_tbl_key, $updateNulls );
 893          else $ret = $this->_db->insertObject( $this->_tbl, $this, $this->_tbl_key );
 894          if( !$ret ) {
 895              $this->_error = strtolower(get_class( $this ))."::store failed <br />" . $this->_db->getErrorMsg();
 896              return false;
 897          } else return true;
 898      }
 899  
 900      /**
 901      *    Default delete method
 902      *
 903      *    can be overloaded/supplemented by the child class
 904      *    @return true if successful otherwise returns and error message
 905      */
 906  	function delete( $oid=null ) {
 907          $k = $this->_tbl_key;
 908          if ($oid) $this->$k = intval( $oid );
 909          $this->_db->setQuery( "DELETE FROM $this->_tbl WHERE $this->_tbl_key = '".$this->$k."'" );
 910          if ($this->queryTestFailure()) return false;
 911          return true;
 912      }
 913  
 914  	function checkout( $who, $oid=null ) {
 915          if ($this->lacks('checked_out')) return false;
 916          $k = $this->_tbl_key;
 917          if ($oid !== null) $this->$k = $oid;
 918          $time = date( "Y-m-d H:i:s" );
 919          if (intval( $who )) {
 920              // new way of storing editor, by id
 921              $this->_db->setQuery( "UPDATE $this->_tbl"
 922              . "\nSET checked_out='$who', checked_out_time='$time'"
 923              . "\nWHERE $this->_tbl_key='".$this->$k."'"
 924              );
 925          } else {
 926              // old way of storing editor, by name
 927              $this->_db->setQuery( "UPDATE $this->_tbl"
 928              . "\nSET checked_out='1', checked_out_time='$time', editor='".$who."' "
 929              . "\nWHERE $this->_tbl_key='".$this->$k."'"
 930              );
 931          }
 932          return $this->_db->query();
 933      }
 934  
 935  	function checkin( $oid=null ) {
 936          if ($this->lacks('checked_out')) return false;
 937          $k = $this->_tbl_key;
 938          if ($oid !== null) $this->$k = $oid;
 939          $time = date("H:i:s");
 940          $this->_db->setQuery( "UPDATE $this->_tbl"
 941          . "\nSET checked_out='0', checked_out_time='0000-00-00 00:00:00'"
 942          . "\nWHERE $this->_tbl_key='".$this->$k."'"
 943          );
 944          return $this->_db->query();
 945      }
 946  
 947  	function hit( $oid=null ) {
 948          $k = $this->_tbl_key;
 949          if ($oid !== null) $this->$k = intval( $oid );
 950          $key = $this->$k;
 951          $this->_db->setQuery( "UPDATE $this->_tbl SET hits=(hits+1) WHERE $this->_tbl_key='$key'" );
 952          $this->_db->query();
 953  
 954          if (mamboCore::get('mosConfig_enable_log_items')) {
 955              $now = date( "Y-m-d" );
 956              $this->_db->setQuery( "SELECT hits"
 957              . "\nFROM #__core_log_items"
 958              . "\nWHERE time_stamp='$now' AND item_table='$this->_tbl' AND item_id='$key'"
 959              );
 960              $hits = intval( $this->_db->loadResult() );
 961              if ($hits) $this->_db->setQuery( "UPDATE #__core_log_items SET hits=(hits+1)"
 962                  . "\nWHERE time_stamp='$now' AND item_table='$this->_tbl' AND item_id='".$this->$k."'"
 963                  );
 964              else $this->_db->setQuery( "INSERT INTO #__core_log_items VALUES"
 965                  . "\n('$now','$this->_tbl','".$this->$k."','1')"
 966                  );
 967              $this->_db->query();
 968          }
 969      }
 970  
 971      /**
 972      * Generic save function
 973      * @param array Source array for binding to class vars
 974      * @param string Filter for the order updating
 975      * @returns TRUE if completely successful, FALSE if partially or not succesful.
 976      */
 977  	function save( $source, $order_filter ) {
 978          if (!$this->bind($_POST) OR !$this->check() OR !$this->store()OR !$this->checkin()) return false;
 979          $filter_value = $this->$order_filter;
 980          $this->updateOrder( $order_filter ? "`$order_filter`='$filter_value'" : "" );
 981          $this->_error = '';
 982          return true;
 983      }
 984  
 985      /**
 986      * Generic Publish/Unpublish function
 987      * @param array An array of id numbers
 988      * @param integer 0 if unpublishing, 1 if publishing
 989      * @param integer The id of the user performnig the operation
 990      */
 991  	function publish_array( $cid=null, $publish=1, $myid=0 ) {
 992          if (!is_array( $cid ) OR count( $cid ) < 1) {
 993              $this->_error = "No items selected.";
 994              return false;
 995          }
 996          $cids = implode( ',', $cid );
 997          $this->_db->setQuery( "UPDATE $this->_tbl SET published='$publish'"
 998          . "\nWHERE $this->_tbl_key IN ($cids) AND (checked_out=0 OR checked_out='$myid')"
 999          );
1000          if ($this->queryTestFailure()) return false;
1001          if (count( $cid ) == 1) $this->checkin( $cid[0] );
1002          return true;
1003      }
1004  
1005      /**
1006      * Export item list to xml
1007      * @param boolean Map foreign keys to text values
1008      */
1009  	function toXML( $mapKeysToText=false ) {
1010          $xml = '<record table="' . $this->_tbl . '"';
1011          if ($mapKeysToText) $xml .= ' mapkeystotext="true"';
1012          $xml .= '>';
1013          foreach (get_object_vars($this) as $k => $v) {
1014              if ($v === null OR is_array($v) OR is_object($v)) continue;
1015              if ($k[0] == '_') continue; // internal field
1016              $xml .= '<' . $k . '><![CDATA[' . $v . ']]></' . $k . '>';
1017          }
1018          $xml .= '</record>';
1019          return $xml;
1020      }
1021  }
1022  
1023  /**
1024  * Abstract class for classes where the objects of the class can be relatively easily
1025  *  stored in a single database table.  Can usually be adapted to more complex cases.
1026  * Requires child classes to implement: tableName(), notSQL().
1027  * tableName() must return the name of the database table, using #__ in the usual Mambo way
1028  * notSQL() must return an array of strings, where each string is the name of a
1029  *  variable that is NOT in the database table, or is not written explicitly,
1030  *  e.g. the auto-increment key.  If this is the ONLY non-SQL field, then the
1031  *  child class need not implement it, as that it is already in the abstract class.
1032  * Child classes may implement timeStampField, in which case it must return the name
1033  *  of a field that will have a timestamp placed in it whenever the DB is written.
1034  */
1035  
1036  class mosTableEntry extends mosDBAbstractRow {
1037  
1038      /* Stores all POST data where the name matches an object variable name */
1039  	function addPostData () {
1040          foreach (get_class_vars(get_class($this)) as $field=>$value) {
1041              if ($field!='id' AND $field[1] != '_' AND isset($_POST[$field])) {
1042                  $this->$field = trim($_POST[$field]);
1043              }
1044          }
1045          $this->forceBools();
1046        }
1047  
1048      /* Provided in case child class does not implement it.  Can force any values */
1049      /* within some limited range.  In particular, can force bools to be 0 or 1 */
1050  	function forceBools () {
1051          return;
1052      }
1053  
1054      /* Updates an existing DB entry with the object's current values */
1055  	function updateObjectDB () {
1056          $this->prepareValues();
1057          $database = mamboDatabase::getInstance();
1058          $database->doSQL($this->updateSQL());
1059      }
1060  
1061      /* Deletes the current object from the DB */
1062  	function delete () {
1063          $table = $this->tableName();
1064          $sql = "DELETE FROM $table WHERE id=$this->id";
1065          $database = mamboDatabase::getInstance();
1066          $database->doSQL($sql);
1067      }
1068  
1069      /* Provided in case the child class does not provide a method for timeStampField */
1070  	function timeStampField () {
1071          return '';
1072      }
1073  
1074      /* Provides SQL for updating the DB with the contents of the current object */
1075  	function updateSQL () {
1076          $tabname = $this->tableName();
1077          $sql = "UPDATE $tabname SET %s WHERE id=$this->id";
1078          $exclude = $this->notSQL();
1079          foreach (get_class_vars(get_class($this)) as $field=>$value) {
1080              if (!in_array($field,$exclude) AND $field[0] != '_') $setter[] = $field."='".$this->$field."'";
1081          }
1082          $timestamp = $this->timeStampField();
1083          if ($timestamp) $setter[] = $timestamp."='".date('Y-m-d H:i:s')."'";
1084          return sprintf($sql,implode(',', $setter));
1085      }
1086  
1087      /* Default method for identifying fields not to be written to the DB */
1088      /* The child classes may override this and return more items in the array */
1089  	function notSQL () {
1090          return array ('id');
1091      }
1092  
1093      /* Provides SQL to insert the current object into the DB */
1094  	function insertSQL () {
1095          $tabname = $this->tableName();
1096          $sql = "INSERT INTO $tabname (%s) VALUES (%s)";
1097          $exclude = $this->notSQL();
1098          foreach (get_class_vars(get_class($this)) as $field=>$value) {
1099              if (!in_array($field,$exclude) AND $field[0] != '_') {
1100                  $infields[] = $field;
1101                  $values[] = "'".$this->$field."'";
1102              }
1103          }
1104          $timestamp = $this->timeStampField();
1105          if ($timestamp) {
1106              $infields[] = $timestamp;
1107              $values[] = "'".date('Y-m-d H:i:s')."'";
1108          }
1109          return sprintf($sql, implode(',', $infields), implode(',', $values));
1110      }
1111  
1112      /* Copies any matching fields from some arbitrary object into the current object */
1113  	function setValues (&$anObject) {
1114          foreach (get_class_vars(get_class($this)) as $field=>$value) {
1115              if ($field != 'id' AND isset($anObject->$field)) $this->$field = $anObject->$field;
1116          }
1117      }
1118  
1119      /* Ensures values can safely be written to DB; assumes magic quotes forced off */
1120  	function prepareValues () {
1121          $database = mamboDatabase::getInstance();
1122          foreach (get_class_vars(get_class($this)) as $field=>$value) {
1123              if (!is_numeric($this->$field) AND is_string($this->$field)) $this->$field = $database->getEscaped($this->$field);
1124          }
1125      }
1126  
1127      /* Takes some arbitrary SELECT type SQL and places the first or only result into the current object */
1128  	function readDataBase($sql) {
1129          $database = mamboDatabase::getInstance();
1130          $database->setQuery( $sql );
1131          if (!$database->loadObject($this)) $this->id = 0;
1132      }
1133  
1134  }
1135  
1136  ?>