Skip to content

Commit f167095

Browse files
committed
drivers: escape*() methods moved to Engine [WIP]
1 parent e994444 commit f167095

23 files changed

Lines changed: 479 additions & 644 deletions

phpstan.neon

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ parameters:
2626
-
2727
identifier: ternary.alwaysTrue
2828
path: src/Dibi/Helpers.php
29-
-
30-
identifier: method.nameCase
31-
path: src/Dibi/Helpers.php
3229

3330
# Translator: switch-case type narrowing in formatValue()
3431
-

src/Dibi/Connection.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class Connection
4343
/** @var array<string, ?string> Type constant => format string */
4444
private array $formats;
4545
private ?Drivers\Connection $driver = null;
46+
private Drivers\Engine $engine;
4647
private ?Translator $translator = null;
4748

4849
/** @var array<string, callable(object): Expression | null> */
@@ -692,6 +693,17 @@ public function loadFile(string $file, ?callable $onProgress = null): int
692693
}
693694

694695

696+
public function getDatabaseEngine(): Drivers\Engine
697+
{
698+
if (!$this->driver) {
699+
$this->connect();
700+
}
701+
702+
assert($this->driver !== null);
703+
return $this->engine ??= $this->driver->getReflector();
704+
}
705+
706+
695707
/**
696708
* Gets a information about the current database.
697709
*/
@@ -702,7 +714,7 @@ public function getDatabaseInfo(): Reflection\Database
702714
assert($this->driver !== null);
703715
}
704716

705-
return new Reflection\Database($this->driver->getReflector(), $this->config['database'] ?? null);
717+
return new Reflection\Database($this->getDatabaseEngine(), $this->config['database'] ?? null);
706718
}
707719

708720

src/Dibi/DataSource.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class DataSource implements IDataSource
3939
public function __construct(string $sql, Connection $connection)
4040
{
4141
$this->sql = strpbrk($sql, " \t\r\n") === false
42-
? $connection->getDriver()->escapeIdentifier($sql) // table name
42+
? $connection->getDatabaseEngine()->escapeIdentifier($sql) // table name
4343
: '(' . $sql . ') t'; // SQL command
4444
$this->connection = $connection;
4545
}

src/Dibi/Drivers/Connection.php

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,24 +72,4 @@ function getReflector(): Engine;
7272
function escapeText(string $value): string;
7373

7474
function escapeBinary(string $value): string;
75-
76-
function escapeIdentifier(string $value): string;
77-
78-
function escapeBool(bool $value): string;
79-
80-
function escapeDate(\DateTimeInterface $value): string;
81-
82-
function escapeDateTime(\DateTimeInterface $value): string;
83-
84-
function escapeDateInterval(\DateInterval $value): string;
85-
86-
/**
87-
* Encodes string for use in a LIKE statement.
88-
*/
89-
function escapeLike(string $value, int $pos): string;
90-
91-
/**
92-
* Injects LIMIT/OFFSET to the SQL query.
93-
*/
94-
function applyLimit(string &$sql, ?int $limit, ?int $offset): void;
9575
}

src/Dibi/Drivers/Engine.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,26 @@
1313
*/
1414
interface Engine
1515
{
16+
function escapeIdentifier(string $value): string;
17+
18+
function escapeBool(bool $value): string;
19+
20+
function escapeDate(\DateTimeInterface $value): string;
21+
22+
function escapeDateTime(\DateTimeInterface $value): string;
23+
24+
function escapeDateInterval(\DateInterval $value): string;
25+
26+
/**
27+
* Encodes string for use in a LIKE statement.
28+
*/
29+
function escapeLike(string $value, int $pos): string;
30+
31+
/**
32+
* Injects LIMIT/OFFSET to the SQL query.
33+
*/
34+
function applyLimit(string &$sql, ?int $limit, ?int $offset): void;
35+
1636
/**
1737
* Returns list of tables.
1838
* @return list<array{name: string, view: bool}>

src/Dibi/Drivers/Engines/FirebirdEngine.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace Dibi\Drivers\Engines;
99

10+
use Dibi;
1011
use Dibi\Drivers\Connection;
1112
use Dibi\Drivers\Engine;
1213

@@ -22,6 +23,58 @@ public function __construct(
2223
}
2324

2425

26+
public function escapeIdentifier(string $value): string
27+
{
28+
return '"' . str_replace('"', '""', $value) . '"';
29+
}
30+
31+
32+
public function escapeBool(bool $value): string
33+
{
34+
return $value ? '1' : '0';
35+
}
36+
37+
38+
public function escapeDate(\DateTimeInterface $value): string
39+
{
40+
return $value->format("'Y-m-d'");
41+
}
42+
43+
44+
public function escapeDateTime(\DateTimeInterface $value): string
45+
{
46+
return "'" . substr($value->format('Y-m-d H:i:s.u'), 0, -2) . "'";
47+
}
48+
49+
50+
public function escapeDateInterval(\DateInterval $value): string
51+
{
52+
throw new Dibi\NotImplementedException;
53+
}
54+
55+
56+
/**
57+
* Encodes string for use in a LIKE statement.
58+
*/
59+
public function escapeLike(string $value, int $pos): string
60+
{
61+
$value = addcslashes(str_replace("'", "''", $value), '%_\\');
62+
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'") . " ESCAPE '\\'";
63+
}
64+
65+
66+
/**
67+
* Injects LIMIT/OFFSET to the SQL query.
68+
*/
69+
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
70+
{
71+
if ($limit > 0 || $offset > 0) {
72+
// http://www.firebirdsql.org/refdocs/langrefupd20-select.html
73+
$sql = 'SELECT ' . ($limit > 0 ? 'FIRST ' . $limit : '') . ($offset > 0 ? ' SKIP ' . $offset : '') . ' * FROM (' . $sql . ')';
74+
}
75+
}
76+
77+
2578
/**
2679
* Returns list of tables.
2780
*/

src/Dibi/Drivers/Engines/MySQLEngine.php

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,66 @@ public function __construct(
2424
}
2525

2626

27+
public function escapeIdentifier(string $value): string
28+
{
29+
return '`' . str_replace('`', '``', $value) . '`';
30+
}
31+
32+
33+
public function escapeBool(bool $value): string
34+
{
35+
return $value ? '1' : '0';
36+
}
37+
38+
39+
public function escapeDate(\DateTimeInterface $value): string
40+
{
41+
return $value->format("'Y-m-d'");
42+
}
43+
44+
45+
public function escapeDateTime(\DateTimeInterface $value): string
46+
{
47+
return $value->format("'Y-m-d H:i:s.u'");
48+
}
49+
50+
51+
public function escapeDateInterval(\DateInterval $value): string
52+
{
53+
if ($value->y || $value->m || $value->d) {
54+
throw new Dibi\NotSupportedException('Only time interval is supported.');
55+
}
56+
57+
return $value->format("'%r%H:%I:%S.%f'");
58+
}
59+
60+
61+
/**
62+
* Encodes string for use in a LIKE statement.
63+
*/
64+
public function escapeLike(string $value, int $pos): string
65+
{
66+
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
67+
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
68+
}
69+
70+
71+
/**
72+
* Injects LIMIT/OFFSET to the SQL query.
73+
*/
74+
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
75+
{
76+
if ($limit < 0 || $offset < 0) {
77+
throw new Dibi\NotSupportedException('Negative offset or limit.');
78+
79+
} elseif ($limit !== null || $offset) {
80+
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
81+
$sql .= ' LIMIT ' . ($limit ?? '18446744073709551615')
82+
. ($offset ? ' OFFSET ' . $offset : '');
83+
}
84+
}
85+
86+
2787
/**
2888
* Returns list of tables.
2989
*/
@@ -47,7 +107,7 @@ public function getTables(): array
47107
*/
48108
public function getColumns(string $table): array
49109
{
50-
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escapeIdentifier($table)}")
110+
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->escapeIdentifier($table)}")
51111
?? throw new \LogicException('Unexpected null result.');
52112
$columns = [];
53113
while ($row = $res->fetch(true)) {
@@ -73,7 +133,7 @@ public function getColumns(string $table): array
73133
*/
74134
public function getIndexes(string $table): array
75135
{
76-
$res = $this->driver->query("SHOW INDEX FROM {$this->driver->escapeIdentifier($table)}")
136+
$res = $this->driver->query("SHOW INDEX FROM {$this->escapeIdentifier($table)}")
77137
?? throw new \LogicException('Unexpected null result.');
78138
$indexes = [];
79139
while ($row = $res->fetch(true)) {

src/Dibi/Drivers/Engines/ODBCEngine.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,63 @@ public function __construct(
2323
}
2424

2525

26+
public function escapeIdentifier(string $value): string
27+
{
28+
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
29+
}
30+
31+
32+
public function escapeBool(bool $value): string
33+
{
34+
return $value ? '1' : '0';
35+
}
36+
37+
38+
public function escapeDate(\DateTimeInterface $value): string
39+
{
40+
return $value->format('#m/d/Y#');
41+
}
42+
43+
44+
public function escapeDateTime(\DateTimeInterface $value): string
45+
{
46+
return $value->format('#m/d/Y H:i:s.u#'); // TODO
47+
}
48+
49+
50+
public function escapeDateInterval(\DateInterval $value): string
51+
{
52+
throw new Dibi\NotImplementedException;
53+
}
54+
55+
56+
/**
57+
* Encodes string for use in a LIKE statement.
58+
*/
59+
public function escapeLike(string $value, int $pos): string
60+
{
61+
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
62+
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
63+
}
64+
65+
66+
/**
67+
* Injects LIMIT/OFFSET to the SQL query.
68+
*/
69+
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
70+
{
71+
if ($offset) {
72+
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
73+
74+
} elseif ($limit < 0) {
75+
throw new Dibi\NotSupportedException('Negative offset or limit.');
76+
77+
} elseif ($limit !== null) {
78+
$sql = 'SELECT TOP ' . $limit . ' * FROM (' . $sql . ') t';
79+
}
80+
}
81+
82+
2683
/**
2784
* Returns list of tables.
2885
*/

src/Dibi/Drivers/Engines/OracleEngine.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,68 @@ public function __construct(
2323
}
2424

2525

26+
public function escapeIdentifier(string $value): string
27+
{
28+
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
29+
return '"' . str_replace('"', '""', $value) . '"';
30+
}
31+
32+
33+
public function escapeBool(bool $value): string
34+
{
35+
return $value ? '1' : '0';
36+
}
37+
38+
39+
public function escapeDate(\DateTimeInterface $value): string
40+
{
41+
return "to_date('" . $value->format('Y-m-d') . "', 'YYYY-mm-dd')"; // TODO
42+
}
43+
44+
45+
public function escapeDateTime(\DateTimeInterface $value): string
46+
{
47+
return "to_date('" . $value->format('Y-m-d G:i:s') . "', 'YYYY-mm-dd hh24:mi:ss')"; // TODO
48+
}
49+
50+
51+
public function escapeDateInterval(\DateInterval $value): string
52+
{
53+
throw new Dibi\NotImplementedException;
54+
}
55+
56+
57+
/**
58+
* Encodes string for use in a LIKE statement.
59+
*/
60+
public function escapeLike(string $value, int $pos): string
61+
{
62+
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
63+
$value = str_replace("'", "''", $value);
64+
return ($pos & 1 ? "'%" : "'") . $value . ($pos & 2 ? "%'" : "'");
65+
}
66+
67+
68+
/**
69+
* Injects LIMIT/OFFSET to the SQL query.
70+
*/
71+
public function applyLimit(string &$sql, ?int $limit, ?int $offset): void
72+
{
73+
if ($limit < 0 || $offset < 0) {
74+
throw new Dibi\NotSupportedException('Negative offset or limit.');
75+
76+
} elseif ($offset) {
77+
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
78+
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
79+
. ($limit !== null ? 'WHERE ROWNUM <= ' . ($offset + $limit) : '')
80+
. ') WHERE "__rnum" > ' . $offset;
81+
82+
} elseif ($limit !== null) {
83+
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . $limit;
84+
}
85+
}
86+
87+
2688
/**
2789
* Returns list of tables.
2890
*/

0 commit comments

Comments
 (0)