Subversion Repositories oidplus

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20. if (!defined('IN_OIDPLUS')) die();
  21.  
  22. define('OIDPLUS_MYSQL_QUERYLOG', false);
  23.  
  24. if (OIDPLUS_MYSQL_QUERYLOG) {
  25.         function CallingFunctionName() {
  26.                 $ex = new Exception();
  27.                 $trace = $ex->getTrace();
  28.                 if (!isset($trace[2])) return '(main)';
  29.                 $final_call = $trace[2];
  30.                 return $final_call['file'].':'.$final_call['line'].'/'.$final_call['function'].'()';
  31.         }
  32. }
  33.  
  34. class OIDplusDataBaseMySQLi extends OIDplusDataBase {
  35.         private $mysqli;
  36.         private $last_query;
  37.         private $prepare_cache = array();
  38.  
  39.         public static function name() {
  40.                 return "MySQL";
  41.         }
  42.  
  43.         public function query($sql, $prepared_args=null) {
  44.                 $this->last_query = $sql;
  45.                 if (OIDPLUS_MYSQL_QUERYLOG) file_put_contents("query.log", "$sql <== ".CallingFunctionName()."\n", FILE_APPEND);
  46.                 if (is_null($prepared_args)) {
  47.                         return $this->mysqli->query($sql, MYSQLI_STORE_RESULT);
  48.                 } else {
  49.                         if (!is_array($prepared_args)) {
  50.                                 throw new Exception("'prepared_args' must be either NULL or an ARRAY.");
  51.                         }
  52.                         if (isset($this->prepare_cache[$sql])) {
  53.                                 $ps = $this->prepare_cache[$sql];
  54.                         } else {
  55.                                 $ps = $this->mysqli->prepare($sql);
  56.                                 if (!$ps) {
  57.                                         throw new Exception("Cannot prepare statement '$sql'");
  58.                                 }
  59.                                 $this->prepare_cache[$sql] = $ps;
  60.                         }
  61.  
  62.                         bind_placeholder_vars($ps,$prepared_args);
  63.                         if (!$ps->execute()) return false;
  64.                         $res = $ps->get_result();
  65.                         if ($res === false) return true; // A non-SELECT statement does not give a result-set, but it is still successful
  66.                         return $res;
  67.                 }
  68.         }
  69.         public function num_rows($res) {
  70.                 if (!is_object($res)) {
  71.                         throw new Exception("num_rows called on non object. Last query: ".$this->last_query);
  72.                 } else {
  73.                         return $res->num_rows;
  74.                 }
  75.         }
  76.         public function fetch_array($res) {
  77.                 if (!is_object($res)) {
  78.                         throw new Exception("fetch_array called on non object. Last query: ".$this->last_query);
  79.                 } else {
  80.                         return $res->fetch_array(MYSQLI_BOTH);
  81.                 }
  82.         }
  83.         public function fetch_object($res) {
  84.                 if (!is_object($res)) {
  85.                         throw new Exception("fetch_object called on non object. Last query: ".$this->last_query);
  86.                 } else {
  87.                         return $res->fetch_object("stdClass");
  88.                 }
  89.         }
  90.         public function insert_id() {
  91.                 return $this->mysqli->insert_id;
  92.         }
  93.         public function error() {
  94.                 return !empty($this->mysqli->connect_error) ? $this->mysqli->connect_error : $this->mysqli->error;
  95.         }
  96.  
  97.         public function connect() {
  98.                 if (OIDPLUS_MYSQL_QUERYLOG) file_put_contents("query.log", '');
  99.  
  100.                 $html = OIDPLUS_HTML_OUTPUT;
  101.  
  102.                 // Try connecting to the database
  103.                 list($hostname,$port) = explode(':', OIDPLUS_MYSQL_HOST.':'.ini_get("mysqli.default_port"));
  104.                 $this->mysqli = @new mysqli($hostname, OIDPLUS_MYSQL_USERNAME, base64_decode(OIDPLUS_MYSQL_PASSWORD), OIDPLUS_MYSQL_DATABASE, $port);
  105.                 if (!empty($this->mysqli->connect_error) || ($this->mysqli->connect_errno != 0)) {
  106.                         if ($html) {
  107.                                 echo "<h1>Error</h1><p>Database connection failed! (".$e->getMessage().")</p>";
  108.                                 if (is_dir(__DIR__.'/../../setup')) {
  109.                                         echo '<p>If you believe that the login credentials are wrong, please run <a href="setup/">setup</a> again.</p>';
  110.                                 }
  111.                         } else {
  112.                                 echo "Error: Database connection failed! (".$e->getMessage().")";
  113.                                 if (is_dir(__DIR__.'/../../setup')) {
  114.                                         echo ' If you believe that the login credentials are wrong, please run setup again.';
  115.                                 }
  116.                         }
  117.                         die();
  118.                 }
  119.  
  120.                 $this->query("SET NAMES 'utf8'");
  121.                 $this->afterConnect($html);
  122.                 $this->connected = true;
  123.         }
  124.  
  125.         private $intransaction = false;
  126.  
  127.         public function transaction_begin() {
  128.                 if ($this->intransaction) throw new Exception("Nested transactions are not supported by this database plugin.");
  129.                 $this->mysqli->autocommit(true);
  130.                 $this->intransaction = true;
  131.         }
  132.  
  133.         public function transaction_commit() {
  134.                 $this->mysqli->commit();
  135.                 $this->mysqli->autocommit(false);
  136.                 $this->intransaction = false;
  137.         }
  138.  
  139.         public function transaction_rollback() {
  140.                 $this->mysqli->rollback();
  141.                 $this->mysqli->autocommit(false);
  142.                 $this->intransaction = false;
  143.         }
  144.  
  145. }
  146.  
  147. function bind_placeholder_vars(&$stmt,$params,$debug=0) {
  148.     // Credit to: Dave Morgan
  149.     // Code ripped from: http://www.devmorgan.com/blog/2009/03/27/dydl-part-3-dynamic-binding-with-mysqli-php/
  150.     if ($params != null) {
  151.         $types = '';                        //initial sting with types
  152.         foreach ($params as $param) {        //for each element, determine type and add
  153.             if (is_int($param)) {
  154.                 $types .= 'i';              //integer
  155.             } elseif (is_float($param)) {
  156.                 $types .= 'd';              //double
  157.             } elseif (is_string($param)) {
  158.                 $types .= 's';              //string
  159.             } else {
  160.                 $types .= 'b';              //blob and unknown
  161.             }
  162.         }
  163.  
  164.         $bind_names = array();
  165.         $bind_names[] = $types;             //first param needed is the type string
  166.                                 // eg:  'issss'
  167.  
  168.         for ($i=0; $i<count($params);$i++) {    //go through incoming params and added em to array
  169.             $bind_name = 'bind' . $i;       //give them an arbitrary name
  170.             $$bind_name = $params[$i];      //add the parameter to the variable variable
  171.             $bind_names[] = &$$bind_name;   //now associate the variable as an element in an array
  172.         }
  173.  
  174.         if ($debug) {
  175.             echo "\$bind_names:<br />\n";
  176.             var_dump($bind_names);
  177.             echo "<br />\n";
  178.         }
  179.         //error_log("better_mysqli has params ".print_r($bind_names, 1));
  180.         //call the function bind_param with dynamic params
  181.         call_user_func_array(array($stmt,'bind_param'),$bind_names);
  182.         return true;
  183.     }else{
  184.         return false;
  185.     }
  186. }
  187.  
  188. function bind_result_array($stmt, &$row) {
  189.     // Credit to: Dave Morgan
  190.     // Code ripped from: http://www.devmorgan.com/blog/2009/03/27/dydl-part-3-dynamic-binding-with-mysqli-php/
  191.     $meta = $stmt->result_metadata();
  192.     while ($field = $meta->fetch_field()) {
  193.         $params[] = &$row[$field->name];
  194.     }
  195.     call_user_func_array(array($stmt, 'bind_result'), $params);
  196.     return true;
  197. }
  198.  
  199. // https://stackoverflow.com/questions/17219214/how-to-bind-in-mysqli-dynamically
  200.  
  201. OIDplus::registerDatabasePlugin(new OIDplusDataBaseMySQLi());
  202.