forked from yajra/pdo-via-oci8
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathOci8.php
413 lines (362 loc) · 11.3 KB
/
Oci8.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
<?php
/**
* PDO userspace driver proxying calls to PHP OCI8 driver
*/
namespace Intersvyaz\Pdo;
use PDO;
/**
* Oci8 class to mimic the interface of the PDO class
* This class extends PDO but overrides all of its methods. It does this so
* that instanceof checks and type-hinting of existing code will work
* seamlessly.
*/
class Oci8 extends PDO
{
// New param type for clob and blob support.
const PARAM_BLOB = OCI_B_BLOB;
const PARAM_CLOB = OCI_B_CLOB;
const LOB_SQL = 0;
const LOB_PL_SQL = 1;
/**
* @var resource Database handler
*/
private $dbh;
/**
* @var array Driver options
*/
private $options = [];
/**
* @var bool Whether currently in a transaction
*/
private $inTransaction = false;
/**
* Creates a PDO instance representing a connection to a database
*
* @param $dsn
* @param $username [optional]
* @param $password [optional]
* @param array $options [optional]
* @throws Oci8Exception
*/
public function __construct($dsn, $username, $password, $options = [])
{
$dbName = $this->parseDsn($dsn, 'dbname');
$charset = $this->parseDsn($dsn, 'charset', 'AL32UTF8');
$this->connect($username, $password, $dbName, $charset, $options ?: []);
// Save the options
$this->options = $options;
}
/**
* Prepares a statement for execution and returns a statement object
*
* @param string $statement This must be a valid SQL statement for the
* target database server.
* @param array $options [optional] This array holds one or more key=>value
* pairs to set attribute values for the PDOStatement object that this
* method returns.
* @throws Oci8Exception
* @return Oci8Statement
*/
public function prepare($statement, $options = null)
{
$sth = @oci_parse($this->dbh, $statement);
if (!$sth) {
$e = oci_error($this->dbh);
throw new Oci8Exception($e['message']);
}
if ($options === null) {
$options = $this->options;
}
if (!is_array($options)) {
$options = [];
}
return new Oci8Statement($sth, $this, $options);
}
/**
* Initiates a transaction
*
* @throws Oci8Exception
* @return bool TRUE on success or FALSE on failure
*/
public function beginTransaction()
{
if ($this->inTransaction()) {
throw new Oci8Exception('There is already an active transaction');
}
$this->inTransaction = true;
return true;
}
/**
* Checks if inside a transaction
*
* @return bool TRUE if a transaction is currently active, and FALSE if not.
*/
public function inTransaction()
{
return $this->inTransaction;
}
/**
* Commits a transaction
*
* @throws Oci8Exception
* @return bool TRUE on success or FALSE on failure.
*/
public function commit()
{
if (!$this->inTransaction()) {
throw new Oci8Exception('There is no active transaction');
}
if (oci_commit($this->dbh)) {
$this->inTransaction = false;
return true;
}
return false;
}
/**
* Rolls back a transaction
*
* @throws Oci8Exception
* @return bool TRUE on success or FALSE on failure.
*/
public function rollBack()
{
if (!$this->inTransaction()) {
throw new Oci8Exception('There is no active transaction');
}
if (oci_rollback($this->dbh)) {
$this->inTransaction = false;
return true;
}
return false;
}
/**
* Sets an attribute on the database handle
*
* @param int $attribute
* @param mixed $value
* @return bool TRUE on success or FALSE on failure.
*/
public function setAttribute($attribute, $value)
{
$this->options[$attribute] = $value;
return true;
}
/**
* Executes an SQL statement and returns the number of affected rows
*
* @param string $statement The SQL statement to prepare and execute.
* @return int The number of rows that were modified or deleted by the SQL
* statement you issued.
*/
public function exec($statement)
{
$stmt = $this->prepare($statement);
$stmt->execute();
return $stmt->rowCount();
}
/**
* Executes an SQL statement, returning the results as a
* Intersvyaz\Pdo\Oci8\Oci8Statement object
*
* @param string $statement The SQL statement to prepare and execute.
* @param int|null $fetchMode The fetch mode must be one of the
* PDO::FETCH_* constants.
* @param mixed|null $modeArg Column number, class name or object.
* @param array|null $ctorArgs Constructor arguments.
* @return Oci8Statement
*/
public function query($statement, $fetchMode = null, $modeArg = null, array $ctorArgs = [])
{
$stmt = $this->prepare($statement);
$stmt->execute();
if ($fetchMode) {
$stmt->setFetchMode($fetchMode, $modeArg, $ctorArgs);
}
return $stmt;
}
/**
* Method not implemented
* @param string $name Sequence name; no use in this context
* @return mixed Last sequence number or 0 if sequence does not exist
*/
public function lastInsertId($name = null)
{
throw new Oci8Exception("Method not implemented");
}
/**
* Fetch the SQLSTATE associated with the last operation on the database
* handle
* While this returns an error code, it merely emulates the action. If
* there are no errors, it returns the success SQLSTATE code (00000).
* If there are errors, it returns HY000. See errorInfo() to retrieve
* the actual Oracle error code and message.
*
* @return string
*/
public function errorCode()
{
$error = $this->errorInfo();
return $error[0];
}
/**
* Returns extended error information for the last operation on the database
* handle
* The array consists of the following fields:
* 0 SQLSTATE error code (a five characters alphanumeric identifier
* defined in the ANSI SQL standard).
* 1 Driver-specific error code.
* 2 Driver-specific error message.
*
* @return array Error information
*/
public function errorInfo()
{
$e = oci_error($this->dbh);
if (is_array($e)) {
return [
'HY000',
$e['code'],
$e['message']
];
}
return ['00000', null, null];
}
/**
* Retrieve a database connection attribute
*
* @param int $attribute
* @return mixed A successful call returns the value of the requested PDO
* attribute. An unsuccessful call returns null.
*/
public function getAttribute($attribute)
{
if ($attribute == PDO::ATTR_DRIVER_NAME) {
return "oci8";
}
if (isset($this->options[$attribute])) {
return $this->options[$attribute];
}
return null;
}
/**
* Special non PDO function used to start cursors in the database
* Remember to call oci_free_statement() on your cursor
*
* @access public
* @return mixed New statement handle, or FALSE on error.
*/
public function getNewCursor()
{
return oci_new_cursor($this->dbh);
}
/**
* Special non PDO function used to start descriptor in the database
* Remember to call oci_free_statement() on your cursor
*
* @access public
* @param int $type One of OCI_DTYPE_FILE, OCI_DTYPE_LOB or OCI_DTYPE_ROWID.
* @return mixed New LOB or FILE descriptor on success, FALSE on error.
*/
public function getNewDescriptor($type = OCI_D_LOB)
{
return oci_new_descriptor($this->dbh, $type);
}
/**
* Special non PDO function used to close an open cursor in the database
*
* @access public
* @param mixed $cursor A valid OCI statement identifier.
* @return mixed Returns TRUE on success or FALSE on failure.
*/
public function closeCursor($cursor)
{
return oci_free_statement($cursor);
}
/**
* Special non PDO function
* Allocates new collection object
*
* @param string $typeName Should be a valid named type (uppercase).
* @param string $schema Should point to the scheme, where the named type was created.
* The name of the current user is the default value.
* @return \OCI_Collection
*/
public function getNewCollection($typeName, $schema)
{
return oci_new_collection($this->dbh, $typeName, $schema);
}
/**
* Places quotes around the input string
* If you are using this function to build SQL statements, you are strongly
* recommended to use prepare() to prepare SQL statements with bound
* parameters instead of using quote() to interpolate user input into an SQL
* statement. Prepared statements with bound parameters are not only more
* portable, more convenient, immune to SQL injection, but are often much
* faster to execute than interpolated queries, as both the server and
* client side can cache a compiled form of the query.
*
* @param string $string The string to be quoted.
* @param int $paramType Provides a data type hint for drivers that have
* alternate quoting styles
* @return string Returns a quoted string that is theoretically safe to pass
* into an SQL statement.
* @todo Implement support for $paramType.
*/
public function quote($string, $paramType = PDO::PARAM_STR)
{
return "'" . str_replace("'", "''", $string) . "'";
}
/**
* Special non PDO function to check if sequence exists
*
* @param string $name
* @return boolean
*/
public function checkSequence($name)
{
if (!$name) {
return false;
}
$stmt = $this->query("select count(*)
from all_sequences
where
sequence_name=upper('{$name}')
and sequence_owner=upper(user)
", PDO::FETCH_COLUMN);
return $stmt->fetch();
}
/**
* Parse DSN string and get $param value.
* @param string $dsn
* @param string $param
* @param mixed $default
* @return string|null
*/
private function parseDsn($dsn, $param, $default = null)
{
if (preg_match('/' . $param . '=(?<param>[^;]+)/', $dsn, $mathes)) {
return $mathes['param'];
}
return $default;
}
/**
* Connect to database
* @param string $username
* @param string $password
* @param string $dbName
* @param string $charset
* @param array $options
*/
private function connect($username, $password, $dbName, $charset, array $options = [])
{
if (array_key_exists(PDO::ATTR_PERSISTENT, $options) && $options[PDO::ATTR_PERSISTENT]) {
$this->dbh = @oci_pconnect($username, $password, $dbName, $charset);
} else {
$this->dbh = @oci_connect($username, $password, $dbName, $charset);
}
if (!$this->dbh) {
$e = oci_error();
throw new Oci8Exception($e['message']);
}
}
}