Skip to content

Commit b23a3b3

Browse files
committed
Helpers::normalizeRow() & detection moved to new class RowNormalizer
1 parent ccd13ce commit b23a3b3

File tree

6 files changed

+106
-73
lines changed

6 files changed

+106
-73
lines changed

src/Database/Connection.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class Connection
3030
private ?PDO $pdo = null;
3131

3232
/** @var callable(array, ResultSet): array */
33-
private $rowNormalizer = [Helpers::class, 'normalizeRow'];
33+
private $rowNormalizer;
3434
private ?string $sql = null;
3535
private int $transactionDepth = 0;
3636

@@ -43,6 +43,8 @@ public function __construct(
4343
private readonly ?string $password = null,
4444
private readonly array $options = [],
4545
) {
46+
$this->rowNormalizer = new RowNormalizer;
47+
4648
if (empty($options['lazy'])) {
4749
$this->connect();
4850
}

src/Database/Drivers/MySqlDriver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ public function getColumnTypes(\PDOStatement $statement): array
216216
$meta['native_type'] === 'NEWDECIMAL' && $meta['precision'] === 0 => Type::Integer,
217217
$meta['native_type'] === 'TINY' && $meta['len'] === 1 && $this->supportBooleans => Type::Bool,
218218
$meta['native_type'] === 'TIME' => Type::TimeInterval,
219-
default => Nette\Database\Helpers::detectType($meta['native_type']),
219+
default => Nette\Database\RowNormalizer::detectType($meta['native_type']),
220220
};
221221
}
222222
}

src/Database/Drivers/SqliteDriver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,9 @@ public function getColumnTypes(\PDOStatement $statement): array
235235
if (isset($meta['sqlite:decl_type'])) {
236236
$types[$meta['name']] = $this->fmtDateTime === 'U' && in_array($meta['sqlite:decl_type'], ['DATE', 'DATETIME'], strict: true)
237237
? Nette\Database\Type::UnixTimestamp
238-
: Nette\Database\Helpers::detectType($meta['sqlite:decl_type']);
238+
: Nette\Database\RowNormalizer::detectType($meta['sqlite:decl_type']);
239239
} elseif (isset($meta['native_type'])) {
240-
$types[$meta['name']] = Nette\Database\Helpers::detectType($meta['native_type']);
240+
$types[$meta['name']] = Nette\Database\RowNormalizer::detectType($meta['native_type']);
241241
}
242242
}
243243

src/Database/Drivers/SqlsrvDriver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,9 @@ public function getColumnTypes(\PDOStatement $statement): array
228228
isset($meta['sqlsrv:decl_type'])
229229
&& $meta['sqlsrv:decl_type'] !== 'timestamp'
230230
) { // timestamp does not mean time in sqlsrv
231-
$types[$meta['name']] = Nette\Database\Helpers::detectType($meta['sqlsrv:decl_type']);
231+
$types[$meta['name']] = Nette\Database\RowNormalizer::detectType($meta['sqlsrv:decl_type']);
232232
} elseif (isset($meta['native_type'])) {
233-
$types[$meta['name']] = Nette\Database\Helpers::detectType($meta['native_type']);
233+
$types[$meta['name']] = Nette\Database\RowNormalizer::detectType($meta['native_type']);
234234
}
235235
}
236236

src/Database/Helpers.php

Lines changed: 6 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,7 @@ class Helpers
2323

2424
/** maximum SQL length */
2525
public static int $maxLength = 100;
26-
27-
public static array $typePatterns = [
28-
'^_' => Type::Text, // PostgreSQL arrays
29-
'(TINY|SMALL|SHORT|MEDIUM|BIG|LONG)(INT)?|INT(EGER|\d+| IDENTITY| UNSIGNED)?|(SMALL|BIG|)SERIAL\d*|COUNTER|YEAR|BYTE|LONGLONG|UNSIGNED BIG INT' => Type::Integer,
30-
'(NEW)?DEC(IMAL)?(\(.*)?|NUMERIC|(SMALL)?MONEY|CURRENCY|NUMBER' => Type::Decimal,
31-
'REAL|DOUBLE( PRECISION)?|FLOAT\d*' => Type::Float,
32-
'BOOL(EAN)?' => Type::Bool,
33-
'TIME' => Type::Time,
34-
'DATE' => Type::Date,
35-
'(SMALL)?DATETIME(OFFSET)?\d*|TIME(STAMP.*)?' => Type::DateTime,
36-
'BYTEA|(TINY|MEDIUM|LONG|)BLOB|(LONG )?(VAR)?BINARY|IMAGE' => Type::Binary,
37-
];
26+
public static array $typePatterns = [];
3827

3928

4029
/**
@@ -174,75 +163,25 @@ public static function detectTypes(\PDOStatement $statement): array
174163
for ($col = 0; $col < $count; $col++) {
175164
$meta = $statement->getColumnMeta($col);
176165
if (isset($meta['native_type'])) {
177-
$types[$meta['name']] = self::detectType($meta['native_type']);
166+
$types[$meta['name']] = RowNormalizer::detectType($meta['native_type']);
178167
}
179168
}
180169

181170
return $types;
182171
}
183172

184173

185-
/**
186-
* Heuristic column type detection.
187-
* @return Type::*
188-
* @internal
189-
*/
174+
/** @deprecated use RowNormalizer::detectType() */
190175
public static function detectType(string $type): string
191176
{
192-
static $cache;
193-
if (!isset($cache[$type])) {
194-
$cache[$type] = 'string';
195-
foreach (self::$typePatterns as $s => $val) {
196-
if (preg_match("#^($s)$#i", $type)) {
197-
return $cache[$type] = $val;
198-
}
199-
}
200-
}
201-
202-
return $cache[$type];
177+
return RowNormalizer::detectType($type);
203178
}
204179

205180

206-
/** @internal */
181+
/** @deprecated use RowNormalizer */
207182
public static function normalizeRow(array $row, ResultSet $resultSet): array
208183
{
209-
foreach ($resultSet->getColumnTypes() as $key => $type) {
210-
$value = $row[$key];
211-
if ($value === null || $value === false || $type === Type::Text) {
212-
// do nothing
213-
} elseif ($type === Type::Integer) {
214-
$row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
215-
216-
} elseif ($type === Type::Float || $type === Type::Decimal) {
217-
if (is_string($value) && ($pos = strpos($value, '.')) !== false) {
218-
$value = rtrim(rtrim($pos === 0 ? "0$value" : $value, '0'), '.');
219-
}
220-
221-
$row[$key] = (float) $value;
222-
223-
} elseif ($type === Type::Bool) {
224-
$row[$key] = $value && $value !== 'f' && $value !== 'F';
225-
226-
} elseif ($type === Type::DateTime || $type === Type::Date) {
227-
$row[$key] = str_starts_with($value, '0000-00')
228-
? null
229-
: new DateTime($value);
230-
231-
} elseif ($type === Type::Time) {
232-
$row[$key] = (new DateTime($value))->setDate(1, 1, 1);
233-
234-
} elseif ($type === Type::TimeInterval) {
235-
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)(\.\d+)?$#D', $value, $m);
236-
$row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
237-
$row[$key]->f = isset($m[5]) ? (float) $m[5] : 0.0;
238-
$row[$key]->invert = (int) (bool) $m[1];
239-
240-
} elseif ($type === Type::UnixTimestamp) {
241-
$row[$key] = (new DateTime)->setTimestamp($value);
242-
}
243-
}
244-
245-
return $row;
184+
return (new RowNormalizer)($row, $resultSet);
246185
}
247186

248187

src/Database/RowNormalizer.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Nette\Database;
11+
12+
13+
/**
14+
* Normalizes fields in row.
15+
*/
16+
final class RowNormalizer
17+
{
18+
private const TypePatterns = [
19+
'^_' => Type::Text, // PostgreSQL arrays
20+
'(TINY|SMALL|SHORT|MEDIUM|BIG|LONG)(INT)?|INT(EGER|\d+| IDENTITY| UNSIGNED)?|(SMALL|BIG|)SERIAL\d*|COUNTER|YEAR|BYTE|LONGLONG|UNSIGNED BIG INT' => Type::Integer,
21+
'(NEW)?DEC(IMAL)?(\(.*)?|NUMERIC|(SMALL)?MONEY|CURRENCY|NUMBER' => Type::Decimal,
22+
'REAL|DOUBLE( PRECISION)?|FLOAT\d*' => Type::Float,
23+
'BOOL(EAN)?' => Type::Bool,
24+
'TIME' => Type::Time,
25+
'DATE' => Type::Date,
26+
'(SMALL)?DATETIME(OFFSET)?\d*|TIME(STAMP.*)?' => Type::DateTime,
27+
'BYTEA|(TINY|MEDIUM|LONG|)BLOB|(LONG )?(VAR)?BINARY|IMAGE' => Type::Binary,
28+
];
29+
30+
31+
/**
32+
* Heuristic column type detection.
33+
* @return Type::*
34+
* @internal
35+
*/
36+
public static function detectType(string $type): string
37+
{
38+
static $cache;
39+
if (!isset($cache[$type])) {
40+
$cache[$type] = 'string';
41+
foreach (self::TypePatterns as $s => $val) {
42+
if (preg_match("#^($s)$#i", $type)) {
43+
return $cache[$type] = $val;
44+
}
45+
}
46+
}
47+
48+
return $cache[$type];
49+
}
50+
51+
52+
public function __invoke(array $row, ResultSet $resultSet): array
53+
{
54+
foreach ($resultSet->getColumnTypes() as $key => $type) {
55+
$value = $row[$key];
56+
if ($value === null || $value === false || $type === IStructure::FIELD_TEXT) {
57+
// do nothing
58+
} elseif ($type === IStructure::FIELD_FLOAT || $type === IStructure::FIELD_DECIMAL) {
59+
$row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
60+
61+
} elseif ($type === IStructure::FIELD_FLOAT) {
62+
if (is_string($value) && ($pos = strpos($value, '.')) !== false) {
63+
$value = rtrim(rtrim($pos === 0 ? "0$value" : $value, '0'), '.');
64+
}
65+
66+
$row[$key] = (float) $value;
67+
68+
} elseif ($type === IStructure::FIELD_BOOL) {
69+
$row[$key] = $value && $value !== 'f' && $value !== 'F';
70+
71+
} elseif ($type === IStructure::FIELD_DATETIME || $type === IStructure::FIELD_DATE) {
72+
$row[$key] = str_starts_with($value, '0000-00')
73+
? null
74+
: new DateTime($value);
75+
76+
} elseif ($type === IStructure::FIELD_TIME) {
77+
$row[$key] = (new DateTime($value))->setDate(1, 1, 1);
78+
79+
} elseif ($type === IStructure::FIELD_TIME_INTERVAL) {
80+
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)(\.\d+)?$#D', $value, $m);
81+
$row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
82+
$row[$key]->f = isset($m[5]) ? (float) $m[5] : 0.0;
83+
$row[$key]->invert = (int) (bool) $m[1];
84+
85+
} elseif ($type === IStructure::FIELD_UNIX_TIMESTAMP) {
86+
$row[$key] = (new DateTime)->setTimestamp($value);
87+
}
88+
}
89+
90+
return $row;
91+
}
92+
}

0 commit comments

Comments
 (0)