Source for file database.php

Documentation is available at database.php

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

Documentation generated on Mon, 05 May 2008 16:19:10 +0400 by phpDocumentor 1.4.0