Subversion Repositories oidplus

Rev

Rev 789 | Rev 854 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 - 2022 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('INSIDE_OIDPLUS')) die();
  21.  
  22. class OIDplusDatabaseConnectionOci extends OIDplusDatabaseConnection {
  23.         private $conn = null;
  24.         private $last_error = null; // do the same like MySQL+PDO, just to be equal in the behavior
  25.  
  26.         public function doQuery(string $sql, /*?array*/ $prepared_args=null): OIDplusQueryResult {
  27.                 $this->last_error = null;
  28.  
  29.                 $mode = $this->intransaction ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
  30.  
  31.                 if (is_null($prepared_args)) {
  32.                         $res = @oci_parse($this->conn, $sql);
  33.                         if ($res === false) {
  34.                                 $this->last_error = oci_error($this->conn);
  35.                                 throw new OIDplusSQLException($sql, _L('Cannot prepare statement').': '.$this->error());
  36.                         } else if (!@oci_execute($res, $mode)) {
  37.                                 $this->last_error = oci_error($res);
  38.                                 throw new OIDplusSQLException($sql, $this->error());
  39.                         } else {
  40.                                 return new OIDplusQueryResultOci($res);
  41.                         }
  42.                         //oci_free_statement($res); // will be done in OIDplusQueryResultOci::__destruct()
  43.                 } else {
  44.                         if (!is_array($prepared_args)) {
  45.                                 throw new OIDplusException(_L('"prepared_args" must be either NULL or an ARRAY.'));
  46.                         }
  47.  
  48.                         // convert ? ? ? to :param1 :param2 :param3
  49.                         $count = 0;
  50.                         $sql = preg_replace_callback('@\\?@', function($found) {
  51.                                 static $i = 0;
  52.                                 $i++;
  53.                                 return ":param$i";
  54.                         }, $sql, count($prepared_args), $count);
  55.  
  56.                         $res = @oci_parse($this->conn, $sql);
  57.                         if ($res === false) {
  58.                                 $this->last_error = oci_error($this->conn);
  59.                                 throw new OIDplusSQLException($sql, _L('Cannot prepare statement').': '.$this->error());
  60.                         }
  61.  
  62.                         $i = 0;
  63.                         foreach ($prepared_args as $a) {
  64.                                 $i++;
  65.                                 if ($i > $count) break;
  66.                                 if (is_bool($a)) $value = $a ? 1 : 0;
  67.                                 $paramname = ":param$i";
  68.                                 $$paramname = $a; // It is VERY important to clone the variable in this stupid way, because the binding will be done to the memory address of $a !
  69.                                 if (@oci_bind_by_name($res, $paramname, $$paramname) === false) {
  70.                                         $this->last_error = oci_error($res);
  71.                                         throw new OIDplusSQLException($sql, _L('Cannot bind parameter %1 to value %2',$paramname,$$paramname).': '.$this->error());
  72.                                 }
  73.                         }
  74.  
  75.                         if (!@oci_execute($res, $mode)) {
  76.                                 $this->last_error = oci_error($res);
  77.                                 throw new OIDplusSQLException($sql, $this->error());
  78.                         } else {
  79.                                 return new OIDplusQueryResultOci($res);
  80.                         }
  81.                         //oci_free_statement($res); // will be done in OIDplusQueryResultOci::__destruct()
  82.  
  83.                 }
  84.         }
  85.  
  86.         public function error(): string {
  87.                 $err = $this->last_error;
  88.                 if ($err == null) $err = '';
  89.                 /*
  90.                 array(4) {
  91.                   ["code"]=>
  92.                   int(1)
  93.                   ["message"]=>
  94.                   string(55) "ORA-00001: unique constraint (HR.SYS_C0012493) violated"
  95.                   ["offset"]=>
  96.                   int(0)
  97.                   ["sqltext"]=>
  98.                   string(118) "insert into config (name, description, value, protected, visible) values (:param1, :param2, :param3, :param4, :param5)"
  99.                 }
  100.                 */
  101.                 if (isset($err['message'])) return $err['message'];
  102.                 return $err;
  103.         }
  104.  
  105.         protected function doConnect()/*: void*/ {
  106.                 if (!function_exists('oci_connect')) throw new OIDplusConfigInitializationException(_L('PHP extension "%1" not installed','OCI8'));
  107.  
  108.                 // Try connecting to the database
  109.                 ob_start();
  110.                 $err = '';
  111.                 try {
  112.                         $conn_str = OIDplus::baseConfig()->getValue('OCI_CONN_STR', 'localhost/XE');
  113.                         $username = OIDplus::baseConfig()->getValue('OCI_USERNAME', 'hr');
  114.                         $password = OIDplus::baseConfig()->getValue('OCI_PASSWORD', 'oracle');
  115.                         $this->conn = oci_connect($username, $password, $conn_str, "AL32UTF8" /*, $session_mode*/);
  116.                 } finally {
  117.                         # TODO: this does not seem to work?! (at least not for CLI)
  118.                         $err = ob_get_contents();
  119.                         ob_end_clean();
  120.                 }
  121.  
  122.                 if (!$this->conn) {
  123.                         throw new OIDplusConfigInitializationException(_L('Connection to the database failed!').' ' . strip_tags($err));
  124.                 }
  125.  
  126.                 $this->last_error = null;
  127.         }
  128.  
  129.         protected function doDisconnect()/*: void*/ {
  130.                 if (!is_null($this->conn)) {
  131.                         oci_close($this->conn);
  132.                         $this->conn = null;
  133.                 }
  134.         }
  135.  
  136.         private $intransaction = false;
  137.  
  138.         public function transaction_supported(): bool {
  139.                 return true;
  140.         }
  141.  
  142.         public function transaction_level(): int {
  143.                 return $this->intransaction ? 1 : 0;
  144.         }
  145.  
  146.         public function transaction_begin()/*: void*/ {
  147.                 if ($this->intransaction) throw new OIDplusException(_L('Nested transactions are not supported by this database plugin.'));
  148.                 // Later, in oci_execute() we will include OCI_NO_AUTO_COMMIT
  149.                 $this->intransaction = true;
  150.         }
  151.  
  152.         public function transaction_commit()/*: void*/ {
  153.                 oci_commit($this->conn);
  154.                 $this->intransaction = false;
  155.         }
  156.  
  157.         public function transaction_rollback()/*: void*/ {
  158.                 oci_rollback($this->conn);
  159.                 $this->intransaction = false;
  160.         }
  161.  
  162.         protected function doGetSlang(bool $mustExist=true)/*: ?OIDplusSqlSlangPlugin*/ {
  163.                 $slang = OIDplus::getSqlSlangPlugin('oracle');
  164.                 if (is_null($slang)) {
  165.                         throw new OIDplusConfigInitializationException(_L('SQL-Slang plugin "%1" is missing. Please check if it exists in the directory "plugin/sqlSlang". If it is not existing, please recover it from an SVN snapshot or OIDplus TAR.GZ file.','oracle'));
  166.                 }
  167.                 return $slang;
  168.         }
  169. }
  170.