Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
786 daniel-mar 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)) {
787 daniel-mar 32
                        $res = @oci_parse($this->conn, $sql);
786 daniel-mar 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;
789 daniel-mar 66
                                if (is_bool($a)) $value = $a ? 1 : 0;
786 daniel-mar 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) {
789 daniel-mar 70
                                        $this->last_error = oci_error($res);
71
                                        throw new OIDplusSQLException($sql, _L('Cannot bind parameter %1 to value %2',$paramname,$$paramname).': '.$this->error());
786 daniel-mar 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
}