Subversion Repositories oidplus

Rev

Rev 1086 | Rev 1130 | 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.         /**
  31.          * @param string $sql
  32.          * @param array|null $prepared_args
  33.          * @return OIDplusQueryResultOci
  34.          * @throws OIDplusException
  35.          */
  36.         public function doQuery(string $sql, array $prepared_args=null): OIDplusQueryResult {
  37.                 $this->last_error = null;
  38.  
  39.                 $mode = $this->intransaction ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS;
  40.  
  41.                 if (is_null($prepared_args)) {
  42.                         $res = @oci_parse($this->conn, $sql);
  43.                         if ($res === false) {
  44.                                 $this->last_error = oci_error($this->conn);
  45.                                 throw new OIDplusSQLException($sql, _L('Cannot prepare statement').': '.$this->error());
  46.                         } else if (!@oci_execute($res, $mode)) {
  47.                                 $this->last_error = oci_error($res);
  48.                                 throw new OIDplusSQLException($sql, $this->error());
  49.                         } else {
  50.                                 return new OIDplusQueryResultOci($res);
  51.                         }
  52.                         //oci_free_statement($res); // will be done in OIDplusQueryResultOci::__destruct()
  53.                 } else {
  54.                         if (!is_array($prepared_args)) {
  55.                                 throw new OIDplusException(_L('"prepared_args" must be either NULL or an ARRAY.'));
  56.                         }
  57.  
  58.                         // convert ? ? ? to :param1 :param2 :param3
  59.                         $count = 0;
  60.                         $sql = preg_replace_callback('@\\?@', function($found) {
  61.                                 static $i = 0;
  62.                                 $i++;
  63.                                 return ":param$i";
  64.                         }, $sql, count($prepared_args), $count);
  65.  
  66.                         $res = @oci_parse($this->conn, $sql);
  67.                         if ($res === false) {
  68.                                 $this->last_error = oci_error($this->conn);
  69.                                 throw new OIDplusSQLException($sql, _L('Cannot prepare statement').': '.$this->error());
  70.                         }
  71.  
  72.                         $i = 0;
  73.                         foreach ($prepared_args as $value) {
  74.                                 $i++;
  75.                                 if ($i > $count) break;
  76.                                 if (is_bool($value)) $value = $value ? 1 : 0;
  77.                                 $paramname = ":param$i";
  78.                                 $$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 !
  79.                                 if (@oci_bind_by_name($res, $paramname, $$paramname) === false) {
  80.                                         $this->last_error = oci_error($res);
  81.                                         throw new OIDplusSQLException($sql, _L('Cannot bind parameter %1 to value %2',$paramname,$$paramname).': '.$this->error());
  82.                                 }
  83.                         }
  84.  
  85.                         if (!@oci_execute($res, $mode)) {
  86.                                 $this->last_error = oci_error($res);
  87.                                 throw new OIDplusSQLException($sql, $this->error());
  88.                         } else {
  89.                                 return new OIDplusQueryResultOci($res);
  90.                         }
  91.                         //oci_free_statement($res); // will be done in OIDplusQueryResultOci::__destruct()
  92.  
  93.                 }
  94.         }
  95.  
  96.         /**
  97.          * @return string
  98.          */
  99.         public function error(): string {
  100.                 $err = $this->last_error;
  101.                 if ($err == null) $err = '';
  102.                 /*
  103.                 array(4) {
  104.                   ["code"]=>
  105.                   int(1)
  106.                   ["message"]=>
  107.                   string(55) "ORA-00001: unique constraint (HR.SYS_C0012493) violated"
  108.                   ["offset"]=>
  109.                   int(0)
  110.                   ["sqltext"]=>
  111.                   string(118) "insert into config (name, description, value, protected, visible) values (:param1, :param2, :param3, :param4, :param5)"
  112.                 }
  113.                 */
  114.                 if (isset($err['message'])) return $err['message'];
  115.                 return $err;
  116.         }
  117.  
  118.         /**
  119.          * @return void
  120.          * @throws OIDplusConfigInitializationException
  121.          * @throws OIDplusException
  122.          */
  123.         protected function doConnect()/*: void*/ {
  124.                 if (!function_exists('oci_connect')) throw new OIDplusConfigInitializationException(_L('PHP extension "%1" not installed','OCI8'));
  125.  
  126.                 // Try connecting to the database
  127.                 ob_start();
  128.                 $err = '';
  129.                 try {
  130.                         $conn_str = OIDplus::baseConfig()->getValue('OCI_CONN_STR', 'localhost/XE');
  131.                         $username = OIDplus::baseConfig()->getValue('OCI_USERNAME', 'hr');
  132.                         $password = OIDplus::baseConfig()->getValue('OCI_PASSWORD', 'oracle');
  133.                         $this->conn = oci_connect($username, $password, $conn_str, "AL32UTF8" /*, $session_mode*/);
  134.                 } finally {
  135.                         $err = ob_get_contents();
  136.                         ob_end_clean();
  137.  
  138.                         $tmp = oci_error();
  139.                         if ($tmp !== false) {
  140.                                 $err .= $tmp['message'];
  141.                         }
  142.                 }
  143.  
  144.                 if (!$this->conn) {
  145.                         throw new OIDplusConfigInitializationException(trim(_L('Connection to the database failed!').' ' . strip_tags($err)));
  146.                 }
  147.  
  148.                 $this->last_error = null;
  149.         }
  150.  
  151.         /**
  152.          * @return void
  153.          */
  154.         protected function doDisconnect()/*: void*/ {
  155.                 if (!is_null($this->conn)) {
  156.                         oci_close($this->conn);
  157.                         $this->conn = null;
  158.                 }
  159.         }
  160.  
  161.         /**
  162.          * @var bool
  163.          */
  164.         private $intransaction = false;
  165.  
  166.         /**
  167.          * @return bool
  168.          */
  169.         public function transaction_supported(): bool {
  170.                 return true;
  171.         }
  172.  
  173.         /**
  174.          * @return int
  175.          */
  176.         public function transaction_level(): int {
  177.                 return $this->intransaction ? 1 : 0;
  178.         }
  179.  
  180.         /**
  181.          * @return void
  182.          * @throws OIDplusException
  183.          */
  184.         public function transaction_begin()/*: void*/ {
  185.                 if ($this->intransaction) throw new OIDplusException(_L('Nested transactions are not supported by this database plugin.'));
  186.                 // Later, in oci_execute() we will include OCI_NO_AUTO_COMMIT
  187.                 $this->intransaction = true;
  188.         }
  189.  
  190.         /**
  191.          * @return void
  192.          */
  193.         public function transaction_commit()/*: void*/ {
  194.                 oci_commit($this->conn);
  195.                 $this->intransaction = false;
  196.         }
  197.  
  198.         /**
  199.          * @return void
  200.          */
  201.         public function transaction_rollback()/*: void*/ {
  202.                 oci_rollback($this->conn);
  203.                 $this->intransaction = false;
  204.         }
  205.  
  206.         /**
  207.          * @param bool $mustExist
  208.          * @return OIDplusSqlSlangPlugin|null
  209.          * @throws OIDplusConfigInitializationException
  210.          */
  211.         protected function doGetSlang(bool $mustExist=true)/*: ?OIDplusSqlSlangPlugin*/ {
  212.                 $slang = OIDplus::getSqlSlangPlugin('oracle');
  213.                 if (is_null($slang)) {
  214.                         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'));
  215.                 }
  216.                 return $slang;
  217.         }
  218. }
  219.