Subversion Repositories oidplus

Rev

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