Subversion Repositories oidplus

Rev

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