Subversion Repositories oidplus

Rev

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

Rev Author Line No. Line
1232 daniel-mar 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 OIDplusDatabaseConnectionSqlSrv extends OIDplusDatabaseConnection {
27
 
28
        /**
29
         * @var mixed|null
30
         */
31
        private $conn = null;
32
 
33
        /**
34
         * @var string|null
35
         */
36
        private $last_error = null;
37
 
38
        /**
1233 daniel-mar 39
         * @var int
40
         */
41
        private $rowsAffected = 0;
42
 
43
        /**
44
         * @return int
45
         */
46
        public function rowsAffected(): int {
47
                return $this->rowsAffected;
48
        }
49
 
50
        /**
1232 daniel-mar 51
         * @param string $sql
52
         * @param array|null $prepared_args
53
         * @return OIDplusQueryResultSqlSrv
54
         * @throws OIDplusException
55
         */
56
        public function doQuery(string $sql, array $prepared_args=null): OIDplusQueryResult {
57
                $this->last_error = null;
58
                try {
59
                        $res = sqlsrv_query($this->conn, $sql, $prepared_args,
60
                                array(
61
                                        // SQLSRV_CURSOR_FORWARD ('forward', default)
62
                                        // Lets you move one row at a time starting at the first row of the result set until you reach the end of the result set.
63
                                        // => Does not work with sqlsrv_num_rows();
64
 
65
                                        // SQLSRV_CURSOR_STATIC ('static')
66
                                        // Lets you access rows in any order but will not reflect changes in the database.
67
                                        // => Does not work with transaction rollback?! (Testcase failed)
68
 
69
                                        // SQLSRV_CURSOR_DYNAMIC
70
                                        // Lets you access rows in any order and will reflect changes in the database.
71
                                        // => Does not work with sqlsrv_num_rows();
72
 
73
                                        // SQLSRV_CURSOR_KEYSET ('keyset')
74
                                        // Lets you access rows in any order. However, a keyset cursor does not update the row count if a row is deleted from the table (a deleted row is returned with no values).
75
                                        // => Does not work with transaction rollback?! (Testcase failed)
76
 
77
                                        // SQLSRV_CURSOR_CLIENT_BUFFERED ('buffered')
78
                                        // Lets you access rows in any order. Creates a client-side cursor query.
79
                                        // => Seems to work fine
80
 
81
                                        'Scrollable' => SQLSRV_CURSOR_CLIENT_BUFFERED
82
                                )
83
                        );
84
                } catch (\Exception $e) {
85
                        $this->last_error = $e->getMessage();
86
                        throw new OIDplusSQLException($sql, $e->getMessage());
87
                }
88
 
89
                if ($res === false) {
90
                        $this->last_error = print_r(sqlsrv_errors(), true);
91
                        throw new OIDplusSQLException($sql, $this->error());
92
                } else {
1233 daniel-mar 93
                        if (str_starts_with(trim(strtolower($sql)),'select')) {
94
                                $this->rowsAffected = sqlsrv_num_rows($res);
95
                        } else {
96
                                $this->rowsAffected = sqlsrv_rows_affected($res);
97
                        }
1232 daniel-mar 98
                        return new OIDplusQueryResultSqlSrv($res);
99
                }
100
        }
101
 
102
 
103
        /**
104
         * @return string
105
         */
106
        public function error(): string {
107
                $err = $this->last_error;
108
                if ($err === null) $err = '';
109
                return $err;
110
        }
111
 
112
        /**
113
         * @return string
114
         */
115
        private static function get_sqlsrv_dll_name(): string {
116
                ob_start();
117
                phpinfo(INFO_GENERAL);
118
                $x = ob_get_contents();
119
                ob_end_clean();
120
 
121
                $architecture =
122
                        preg_match('@Architecture.+(x86|x64)@', $x, $m) ? $m[1] : '*';
123
 
124
                $threadsafety =
125
                        preg_match('@Thread Safety.+(enabled|disabled)@', $x, $m)
126
                        ? ($m[1] == 'enabled' ? 'ts' : 'nts') : '*';
127
 
128
                $m = explode('.',phpversion());
129
                $version = $m[0].$m[1];
130
 
131
                // e.g. php_sqlsrv_82_ts_x64.dll
1235 daniel-mar 132
                return "php_sqlsrv_{$version}_{$threadsafety}_{$architecture}.dll";
1232 daniel-mar 133
        }
134
 
135
        /**
136
         * @return void
137
         * @throws OIDplusConfigInitializationException
138
         * @throws OIDplusException
139
         */
140
        protected function doConnect()/*: void*/ {
141
                // Download here: https://learn.microsoft.com/en-us/sql/connect/php/download-drivers-php-sql-server?view=sql-server-ver16
142
                if (!function_exists('sqlsrv_connect')) throw new OIDplusException(_L('PHP extension "%1" not installed',self::get_sqlsrv_dll_name()));
143
 
144
                // Try connecting to the database
1233 daniel-mar 145
                $servername = OIDplus::baseConfig()->getValue('SQLSRV_SERVER',   'localhost\oidplus');
1232 daniel-mar 146
                $username   = OIDplus::baseConfig()->getValue('SQLSRV_USERNAME', '');
147
                $password   = OIDplus::baseConfig()->getValue('SQLSRV_PASSWORD', '');
148
                $database   = OIDplus::baseConfig()->getValue('SQLSRV_DATABASE', 'oidplus');
1233 daniel-mar 149
                $options    = OIDplus::baseConfig()->getValue('SQLSRV_OPTIONS',  array());
1232 daniel-mar 150
 
1233 daniel-mar 151
                if (!isset($options['Database'])) $options['Database'] = $database;
152
                if (!isset($options['CharacterSet'])) $options['CharacterSet'] = 'UTF-8';
1232 daniel-mar 153
                if ($username != '') {
1233 daniel-mar 154
                        if (!isset($options['UID'])) $options['UID'] = $username;
155
                        if (!isset($options['PWD'])) $options['PWD'] = $password;
1232 daniel-mar 156
                }
157
 
1233 daniel-mar 158
                $this->conn = @sqlsrv_connect($servername, $options);
1232 daniel-mar 159
 
160
                if (!$this->conn) {
161
                        $message = print_r(sqlsrv_errors(), true);
162
                        throw new OIDplusConfigInitializationException(trim(_L('Connection to the database failed!').' '.$message));
163
                }
164
 
165
                $this->last_error = null;
166
        }
167
 
168
        /**
169
         * @return void
170
         */
171
        protected function doDisconnect()/*: void*/ {
172
                if (!is_null($this->conn)) {
173
                        sqlsrv_close($this->conn);
174
                        $this->conn = null;
175
                }
176
        }
177
 
178
        /**
179
         * @var bool
180
         */
181
        private $intransaction = false;
182
 
183
        /**
184
         * @return bool
185
         */
186
        public function transaction_supported(): bool {
187
                return true;
188
        }
189
 
190
        /**
191
         * @return int
192
         */
193
        public function transaction_level(): int {
194
                return $this->intransaction ? 1 : 0;
195
        }
196
 
197
        /**
198
         * @return void
199
         * @throws OIDplusException
200
         */
201
        public function transaction_begin()/*: void*/ {
202
                if ($this->intransaction) throw new OIDplusException(_L('Nested transactions are not supported by this database plugin.'));
1233 daniel-mar 203
                if (sqlsrv_begin_transaction($this->conn)) $this->intransaction = true;
1232 daniel-mar 204
        }
205
 
206
        /**
207
         * @return void
208
         */
209
        public function transaction_commit()/*: void*/ {
1233 daniel-mar 210
                if (sqlsrv_commit($this->conn)) $this->intransaction = false;
1232 daniel-mar 211
        }
212
 
213
        /**
214
         * @return void
215
         */
216
        public function transaction_rollback()/*: void*/ {
1233 daniel-mar 217
                if (sqlsrv_rollback($this->conn)) $this->intransaction = false;
1232 daniel-mar 218
        }
219
 
220
        /**
221
         * @param bool $mustExist
222
         * @return OIDplusSqlSlangPlugin|null
223
         * @throws OIDplusConfigInitializationException
224
         */
225
        protected function doGetSlang(bool $mustExist=true)/*: ?OIDplusSqlSlangPlugin*/ {
226
                $slang = OIDplus::getSqlSlangPlugin('mssql');
227
                if (is_null($slang)) {
228
                        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.','mssql'));
229
                }
230
                return $slang;
231
        }
232
 
233
        /**
234
         * @return array
235
         */
236
        public function getExtendedInfo(): array {
1233 daniel-mar 237
                $servername = OIDplus::baseConfig()->getValue('SQLSRV_SERVER',   'localhost\oidplus');
1232 daniel-mar 238
                $username   = OIDplus::baseConfig()->getValue('SQLSRV_USERNAME', '');
239
                $password   = OIDplus::baseConfig()->getValue('SQLSRV_PASSWORD', '');
240
                $database   = OIDplus::baseConfig()->getValue('SQLSRV_DATABASE', 'oidplus');
1233 daniel-mar 241
                $options    = OIDplus::baseConfig()->getValue('SQLSRV_OPTIONS',  array());
242
 
243
                $ary_info = array(
1232 daniel-mar 244
                        _L('Hostname') => $servername,
245
                        _L('Username') => $username,
246
                        _L('Password') => $password != '' ? '('._L('redacted').')' : '',
247
                        _L('Database') => $database
248
                );
1233 daniel-mar 249
                foreach ($options as $name => $val) {
250
                        $ary_info[_L('Option %1',$name)] = '"'.$val.'"';
251
                }
1234 daniel-mar 252
                return $ary_info;
1232 daniel-mar 253
        }
254
}