1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @copyright Copyright (C) eZ Systems AS. All rights reserved. |
5
|
|
|
* @license For full copyright and license information view LICENSE file distributed with this source code. |
6
|
|
|
*/ |
7
|
|
|
namespace eZ\Publish\Core\Persistence\Doctrine; |
8
|
|
|
|
9
|
|
|
use eZ\Publish\Core\Persistence\Database\DatabaseHandler; |
10
|
|
|
use eZ\Publish\Core\Persistence\Database\QueryException; |
11
|
|
|
use Doctrine\DBAL\Connection; |
12
|
|
|
use Doctrine\DBAL\DriverManager; |
13
|
|
|
use Doctrine\DBAL\DBALException; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Class ConnectionHandler. |
17
|
|
|
* |
18
|
|
|
* @deprecated Since 6.13, please use Doctrine DBAL instead (@ezpublish.persistence.connection) |
19
|
|
|
* it provides richer and more powerful DB abstraction which is also easier to use. |
20
|
|
|
*/ |
21
|
|
|
class ConnectionHandler implements DatabaseHandler |
|
|
|
|
22
|
|
|
{ |
23
|
|
|
/** @var \Doctrine\DBAL\Connection */ |
24
|
|
|
protected $connection; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @param string|array $dsn |
28
|
|
|
* |
29
|
|
|
* @return \Doctrine\DBAL\Connection |
30
|
|
|
*/ |
31
|
|
|
public static function createConnectionFromDSN($dsn) |
32
|
|
|
{ |
33
|
|
|
if (is_string($dsn)) { |
34
|
|
|
$parsed = self::parseDSN($dsn); |
35
|
|
|
} else { |
36
|
|
|
$parsed = $dsn; |
37
|
|
|
} |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @todo retry connection here. |
41
|
|
|
*/ |
42
|
|
|
return DriverManager::getConnection($parsed); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Create a Connection Handler from given Doctrine $connection. |
47
|
|
|
* |
48
|
|
|
* @param \Doctrine\DBAL\Connection $connection |
49
|
|
|
* |
50
|
|
|
* @return \eZ\Publish\Core\Persistence\Doctrine\ConnectionHandler |
51
|
|
|
*/ |
52
|
|
|
public static function createFromConnection(Connection $connection) |
53
|
|
|
{ |
54
|
|
|
$driver = $connection->getDriver()->getName(); |
|
|
|
|
55
|
|
|
|
56
|
|
|
if ($driver === 'pdo_sqlite') { |
57
|
|
|
return new ConnectionHandler\SqliteConnectionHandler($connection); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
if ($driver === 'pdo_pgsql') { |
61
|
|
|
return new ConnectionHandler\PostgresConnectionHandler($connection); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
return new self($connection); |
|
|
|
|
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Create a Connection Handler with corresponding Doctrine connection from DSN. |
69
|
|
|
* |
70
|
|
|
* @param string|array $dsn |
71
|
|
|
* |
72
|
|
|
* @return ConnectionHandler |
73
|
|
|
*/ |
74
|
|
|
public static function createFromDSN($dsn) |
75
|
|
|
{ |
76
|
|
|
if (is_string($dsn)) { |
77
|
|
|
$parsed = self::parseDSN($dsn); |
78
|
|
|
} else { |
79
|
|
|
$parsed = $dsn; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
$connection = DriverManager::getConnection($parsed); |
83
|
|
|
|
84
|
|
|
if ($parsed['driver'] === 'pdo_sqlite') { |
85
|
|
|
return new ConnectionHandler\SqliteConnectionHandler($connection); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
if ($parsed['driver'] === 'pdo_pgsql') { |
89
|
|
|
return new ConnectionHandler\PostgresConnectionHandler($connection); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
return new self($connection); |
|
|
|
|
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Returns the Data Source Name as a structure containing the various parts of the DSN. |
97
|
|
|
* |
98
|
|
|
* Additional keys can be added by appending a URI query string to the |
99
|
|
|
* end of the DSN. |
100
|
|
|
* |
101
|
|
|
* The format of the supplied DSN is in its fullest form: |
102
|
|
|
* <code> |
103
|
|
|
* driver://user:password@protocol+host/database?option=8&another=true |
104
|
|
|
* </code> |
105
|
|
|
* |
106
|
|
|
* Most variations are allowed: |
107
|
|
|
* <code> |
108
|
|
|
* driver://user:password@protocol+host:110//usr/db_file.db?mode=0644 |
109
|
|
|
* driver://user:password@host/dbname |
110
|
|
|
* driver://user:password@host |
111
|
|
|
* driver://user@host |
112
|
|
|
* driver://host/dbname |
113
|
|
|
* driver://host |
114
|
|
|
* driver |
115
|
|
|
* </code> |
116
|
|
|
* |
117
|
|
|
* This function is 'borrowed' from PEAR /DB.php . |
118
|
|
|
* |
119
|
|
|
* @license Apache2 from Zeta Components Database project |
120
|
|
|
* |
121
|
|
|
* @param string $dsn Data Source Name to be parsed |
122
|
|
|
* |
123
|
|
|
* @return array an associative array with the following keys: |
124
|
|
|
* + driver: Database backend used in PHP (mysql, odbc etc.) |
125
|
|
|
* + host: Host specification (hostname[:port]) |
126
|
|
|
* + dbname: Database to use on the DBMS server |
127
|
|
|
* + username: User name for login |
128
|
|
|
* + password: Password for login |
129
|
|
|
*/ |
130
|
|
|
public static function parseDSN($dsn) |
131
|
|
|
{ |
132
|
|
|
$parsed = [ |
133
|
|
|
'driver' => false, |
134
|
|
|
'user' => false, |
135
|
|
|
'password' => false, |
136
|
|
|
'host' => false, |
137
|
|
|
'port' => false, |
138
|
|
|
'unix_socket' => false, |
139
|
|
|
'dbname' => false, |
140
|
|
|
'memory' => false, |
141
|
|
|
'path' => false, |
142
|
|
|
]; |
143
|
|
|
|
144
|
|
|
// Find driver and dbsyntax |
145
|
|
|
if (($pos = strpos($dsn, '://')) !== false) { |
146
|
|
|
$str = substr($dsn, 0, $pos); |
147
|
|
|
$dsn = substr($dsn, $pos + 3); |
148
|
|
|
} else { |
149
|
|
|
$str = $dsn; |
150
|
|
|
$dsn = null; |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
// Get driver and dbsyntax |
154
|
|
|
// $str => driver(dbsyntax) |
155
|
|
|
if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { |
156
|
|
|
$parsed['driver'] = $arr[1]; |
157
|
|
|
} else { |
158
|
|
|
$parsed['driver'] = $str; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
if (empty($dsn)) { |
162
|
|
|
return $parsed; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
// Get (if found): username and password |
166
|
|
|
// $dsn => username:password@protocol+host/database |
167
|
|
|
if (($at = strrpos((string)$dsn, '@')) !== false) { |
168
|
|
|
$str = substr($dsn, 0, $at); |
169
|
|
|
$dsn = substr($dsn, $at + 1); |
170
|
|
|
if (($pos = strpos($str, ':')) !== false) { |
171
|
|
|
$parsed['user'] = rawurldecode(substr($str, 0, $pos)); |
172
|
|
|
$parsed['password'] = rawurldecode(substr($str, $pos + 1)); |
173
|
|
|
} else { |
174
|
|
|
$parsed['user'] = rawurldecode($str); |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
// Find protocol and host |
179
|
|
|
|
180
|
|
|
if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { |
181
|
|
|
// $dsn => proto(proto_opts)/database |
182
|
|
|
$proto = $match[1]; |
183
|
|
|
$proto_opts = $match[2] ?: false; |
184
|
|
|
$dsn = $match[3]; |
185
|
|
|
} else { |
186
|
|
|
// $dsn => protocol+host/database (old format) |
187
|
|
View Code Duplication |
if (strpos($dsn, '+') !== false) { |
|
|
|
|
188
|
|
|
list($proto, $dsn) = explode('+', $dsn, 2); |
189
|
|
|
} |
190
|
|
|
if (strpos($dsn, '/') !== false) { |
191
|
|
|
list($proto_opts, $dsn) = explode('/', $dsn, 2); |
192
|
|
|
} else { |
193
|
|
|
$proto_opts = $dsn; |
194
|
|
|
$dsn = null; |
195
|
|
|
} |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
// process the different protocol options |
199
|
|
|
$protocol = (!empty($proto)) ? $proto : 'tcp'; |
200
|
|
|
$proto_opts = rawurldecode($proto_opts); |
201
|
|
|
if ($protocol === 'tcp') { |
202
|
|
|
if (strpos($proto_opts, ':') !== false) { |
203
|
|
|
list($parsed['host'], $parsed['port']) = explode(':', $proto_opts); |
204
|
|
|
} else { |
205
|
|
|
$parsed['host'] = $proto_opts; |
206
|
|
|
} |
207
|
|
|
} elseif ($protocol === 'unix') { |
208
|
|
|
$parsed['unix_socket'] = $proto_opts; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
// Get dabase if any |
212
|
|
|
// $dsn => database |
213
|
|
|
if ($dsn) { |
214
|
|
|
if (($pos = strpos($dsn, '?')) === false) { |
215
|
|
|
// /database |
216
|
|
|
$parsed['dbname'] = rawurldecode($dsn); |
217
|
|
|
} else { |
218
|
|
|
// /database?param1=value1¶m2=value2 |
219
|
|
|
$parsed['dbname'] = rawurldecode(substr($dsn, 0, $pos)); |
220
|
|
|
$dsn = substr($dsn, $pos + 1); |
221
|
|
View Code Duplication |
if (strpos($dsn, '&') !== false) { |
|
|
|
|
222
|
|
|
$opts = explode('&', $dsn); |
223
|
|
|
} else { |
224
|
|
|
$opts = [$dsn]; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
foreach ($opts as $opt) { |
228
|
|
|
list($key, $value) = explode('=', $opt); |
229
|
|
|
if (!isset($parsed[$key])) { |
230
|
|
|
// don't allow params overwrite |
231
|
|
|
$parsed[$key] = rawurldecode($value); |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
if ($parsed['driver'] === 'sqlite') { |
238
|
|
|
if (isset($parsed['port']) && $parsed['port'] === 'memory') { |
239
|
|
|
$parsed['memory'] = true; |
240
|
|
|
unset($parsed['port']); |
241
|
|
|
unset($parsed['host']); |
242
|
|
|
} elseif (isset($parsed['dbname'])) { |
243
|
|
|
$parsed['path'] = $parsed['dbname']; |
244
|
|
|
unset($parsed['dbname']); |
245
|
|
|
unset($parsed['host']); |
246
|
|
|
} |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
$driverMap = [ |
250
|
|
|
'mysql' => 'pdo_mysql', |
251
|
|
|
'pgsql' => 'pdo_pgsql', |
252
|
|
|
'sqlite' => 'pdo_sqlite', |
253
|
|
|
]; |
254
|
|
|
|
255
|
|
|
if (isset($driverMap[$parsed['driver']])) { |
256
|
|
|
$parsed['driver'] = $driverMap[$parsed['driver']]; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
return array_filter( |
260
|
|
|
$parsed, |
261
|
|
|
function ($element) { |
262
|
|
|
return $element !== false; |
263
|
|
|
} |
264
|
|
|
); |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* @param \Doctrine\DBAL\Connection $connection |
269
|
|
|
*/ |
270
|
|
|
public function __construct(Connection $connection) |
271
|
|
|
{ |
272
|
|
|
$this->connection = $connection; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* @return \Doctrine\DBAL\Connection |
277
|
|
|
*/ |
278
|
|
|
public function getConnection() |
279
|
|
|
{ |
280
|
|
|
return $this->connection; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* @return string |
285
|
|
|
*/ |
286
|
|
|
public function getName() |
287
|
|
|
{ |
288
|
|
|
return $this->connection->getDatabasePlatform()->getName(); |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Begin a transaction. |
293
|
|
|
*/ |
294
|
|
|
public function beginTransaction() |
295
|
|
|
{ |
296
|
|
|
try { |
297
|
|
|
$this->connection->beginTransaction(); |
298
|
|
|
} catch (DBALException $e) { |
299
|
|
|
throw new QueryException($e->getMessage(), $e->getCode(), $e); |
|
|
|
|
300
|
|
|
} |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Commit a transaction. |
305
|
|
|
*/ |
306
|
|
|
public function commit() |
307
|
|
|
{ |
308
|
|
|
try { |
309
|
|
|
$this->connection->commit(); |
310
|
|
|
} catch (DBALException $e) { |
311
|
|
|
throw new QueryException($e->getMessage(), $e->getCode(), $e); |
|
|
|
|
312
|
|
|
} |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
/** |
316
|
|
|
* Rollback a transaction. |
317
|
|
|
*/ |
318
|
|
|
public function rollBack() |
319
|
|
|
{ |
320
|
|
|
try { |
321
|
|
|
$this->connection->rollBack(); |
322
|
|
|
} catch (DBALException $e) { |
323
|
|
|
throw new QueryException($e->getMessage(), $e->getCode(), $e); |
|
|
|
|
324
|
|
|
} |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
public function prepare($query) |
328
|
|
|
{ |
329
|
|
|
return $this->connection->prepare($query); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
/** |
333
|
|
|
* Retrieve the last auto incremet or sequence id. |
334
|
|
|
* |
335
|
|
|
* @param string $sequenceName |
336
|
|
|
* |
337
|
|
|
* @return string |
338
|
|
|
*/ |
339
|
|
|
public function lastInsertId($sequenceName = null) |
340
|
|
|
{ |
341
|
|
|
return $this->connection->lastInsertId($sequenceName); |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* @return bool |
346
|
|
|
*/ |
347
|
|
|
public function useSequences() |
348
|
|
|
{ |
349
|
|
|
return $this->connection->getDatabasePlatform()->supportsSequences(); |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* Execute a query against the database. |
354
|
|
|
* |
355
|
|
|
* @param string $query |
356
|
|
|
*/ |
357
|
|
|
public function exec($query) |
358
|
|
|
{ |
359
|
|
|
try { |
360
|
|
|
$this->connection->exec($query); |
361
|
|
|
} catch (DBALException $e) { |
362
|
|
|
throw new QueryException($e->getMessage(), $e->getCode(), $e); |
|
|
|
|
363
|
|
|
} |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Create Select Query object. |
368
|
|
|
* |
369
|
|
|
* @return \eZ\Publish\Core\Persistence\Database\SelectQuery |
370
|
|
|
*/ |
371
|
|
|
public function createSelectQuery() |
372
|
|
|
{ |
373
|
|
|
return new SelectDoctrineQuery($this->connection); |
|
|
|
|
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
/** |
377
|
|
|
* Create Insert Query object. |
378
|
|
|
* |
379
|
|
|
* @return \eZ\Publish\Core\Persistence\Database\InsertQuery |
380
|
|
|
*/ |
381
|
|
|
public function createInsertQuery() |
382
|
|
|
{ |
383
|
|
|
return new InsertDoctrineQuery($this->connection); |
|
|
|
|
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* Create update Query object. |
388
|
|
|
* |
389
|
|
|
* @return \eZ\Publish\Core\Persistence\Database\UpdateQuery |
390
|
|
|
*/ |
391
|
|
|
public function createUpdateQuery() |
392
|
|
|
{ |
393
|
|
|
return new UpdateDoctrineQuery($this->connection); |
|
|
|
|
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* Create a Delete Query object. |
398
|
|
|
* |
399
|
|
|
* @return \eZ\Publish\Core\Persistence\Database\DeleteQuery |
400
|
|
|
*/ |
401
|
|
|
public function createDeleteQuery() |
402
|
|
|
{ |
403
|
|
|
return new DeleteDoctrineQuery($this->connection); |
|
|
|
|
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* Creates an alias for $tableName, $columnName in $query. |
408
|
|
|
* |
409
|
|
|
* @param \eZ\Publish\Core\Persistence\Database\SelectQuery $query |
410
|
|
|
* @param string $columnName |
411
|
|
|
* @param string|null $tableName |
412
|
|
|
* |
413
|
|
|
* @return string |
414
|
|
|
*/ |
415
|
|
|
public function aliasedColumn($query, $columnName, $tableName = null) |
416
|
|
|
{ |
417
|
|
|
return $this->alias( |
418
|
|
|
$this->quoteColumn($columnName, $tableName), |
419
|
|
|
$this->quoteIdentifier( |
420
|
|
|
($tableName ? $tableName . '_' : '') . |
421
|
|
|
$columnName |
422
|
|
|
) |
423
|
|
|
); |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
/** |
427
|
|
|
* Returns a qualified identifier for $columnName in $tableName. |
428
|
|
|
* |
429
|
|
|
* @param string $columnName |
430
|
|
|
* @param string $tableName |
431
|
|
|
* |
432
|
|
|
* @return string |
433
|
|
|
*/ |
434
|
|
|
public function quoteColumn($columnName, $tableName = null) |
435
|
|
|
{ |
436
|
|
|
return |
437
|
|
|
($tableName ? $this->quoteTable($tableName) . '.' : '') . |
438
|
|
|
$this->quoteIdentifier($columnName); |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
/** |
442
|
|
|
* Returns a qualified identifier for $tableName. |
443
|
|
|
* |
444
|
|
|
* @param string $tableName |
445
|
|
|
* |
446
|
|
|
* @return string |
447
|
|
|
*/ |
448
|
|
|
public function quoteTable($tableName) |
449
|
|
|
{ |
450
|
|
|
return $this->quoteIdentifier($tableName); |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
/** |
454
|
|
|
* Custom alias method. |
455
|
|
|
* |
456
|
|
|
* Ignores some properties of identifier quoting, but since we use somehow |
457
|
|
|
* sane table and column names, ourselves, this is fine. |
458
|
|
|
* |
459
|
|
|
* This is an optimization and works around the ezcDB implementation. |
460
|
|
|
* |
461
|
|
|
* @param string $identifier |
|
|
|
|
462
|
|
|
* |
463
|
|
|
* @return string |
464
|
|
|
*/ |
465
|
|
|
public function alias($name, $alias) |
466
|
|
|
{ |
467
|
|
|
return $name . ' ' . $alias; |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
/** |
471
|
|
|
* Custom quote identifier method. |
472
|
|
|
* |
473
|
|
|
* Ignores some properties of identifier quoting, but since we use somehow |
474
|
|
|
* sane table and column names, ourselves, this is fine. |
475
|
|
|
* |
476
|
|
|
* This is an optimization and works around the ezcDB implementation. |
477
|
|
|
* |
478
|
|
|
* @param string $identifier |
479
|
|
|
* |
480
|
|
|
* @return string |
481
|
|
|
*/ |
482
|
|
|
public function quoteIdentifier($identifier) |
483
|
|
|
{ |
484
|
|
|
return '`' . $identifier . '`'; |
485
|
|
|
} |
486
|
|
|
|
487
|
|
|
/** |
488
|
|
|
* Get auto increment value. |
489
|
|
|
* |
490
|
|
|
* Returns the value used for autoincrement tables. Usually this will just |
491
|
|
|
* be null. In case for sequence based RDBMS this method can return a |
492
|
|
|
* proper value for the given column. |
493
|
|
|
* |
494
|
|
|
* @param string $table |
495
|
|
|
* @param string $column |
496
|
|
|
* |
497
|
|
|
* @return mixed |
498
|
|
|
*/ |
499
|
|
|
public function getAutoIncrementValue($table, $column) |
500
|
|
|
{ |
501
|
|
|
return 'null'; |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
/** |
505
|
|
|
* Returns the name of the affected sequence. |
506
|
|
|
* |
507
|
|
|
* @param string $table |
508
|
|
|
* @param string $column |
509
|
|
|
* |
510
|
|
|
* @return string |
511
|
|
|
*/ |
512
|
|
|
public function getSequenceName($table, $column) |
513
|
|
|
{ |
514
|
|
|
return null; |
515
|
|
|
} |
516
|
|
|
} |
517
|
|
|
|
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.