Skip to content

Commit 33a73d7

Browse files
committed
uses TypeConverter to convert values to PHP
1 parent 1de6d06 commit 33a73d7

File tree

16 files changed

+191
-156
lines changed

16 files changed

+191
-156
lines changed

src/Database/Connection.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
use JetBrains\PhpStorm\Language;
1313
use Nette\Utils\Arrays;
14-
use Nette\Utils\DateTime;
1514
use PDOException;
1615
use function func_get_args, str_replace, ucfirst;
1716

@@ -30,6 +29,7 @@ class Connection
3029
'pdo-sqlite' => Drivers\PDO\SQLite\Driver::class,
3130
'pdo-sqlsrv' => Drivers\PDO\SQLSrv\Driver::class,
3231
];
32+
private const TypeConverterOptions = ['convertBoolean', 'newDateTime'];
3333

3434
/** @var array<callable(self): void> Occurs after connection is established */
3535
public array $onConnect = [];
@@ -40,6 +40,7 @@ class Connection
4040
private ?Drivers\Connection $connection = null;
4141
private Drivers\Engine $engine;
4242
private SqlPreprocessor $preprocessor;
43+
private TypeConverter $typeConverter;
4344

4445
/** @var ?\Closure(array<string, mixed>, Result): array<string, mixed> */
4546
private ?\Closure $rowNormalizer;
@@ -54,23 +55,22 @@ public function __construct(
5455
?string $password = null,
5556
array $options = [],
5657
) {
57-
$this->rowNormalizer = ($options['newDateTime'] ?? null) === false
58-
? fn(array $row, ResultSet $resultSet): array => Helpers::normalizeRow($row, $resultSet, DateTime::class)
59-
: Helpers::normalizeRow(...);
60-
6158
$driver = explode(':', $dsn)[0];
6259
$class = empty($options['driverClass'])
6360
? (self::Drivers['pdo-' . $driver] ?? throw new \LogicException("Unknown PDO driver '$driver'."))
6461
: $options['driverClass'];
6562
$args = compact('dsn', 'username', 'password', 'options');
66-
unset($options['lazy'], $options['newDateTime'], $options['driverClass']);
63+
unset($options['lazy'], $options['driverClass']);
6764
foreach ($options as $key => $value) {
6865
if (!is_int($key) && $value !== null) {
6966
$args[$key] = $value;
7067
unset($args['options'][$key]);
7168
}
7269
}
70+
$args = array_diff_key($args, array_flip(self::TypeConverterOptions));
7371
$this->driver = new $class(...$args);
72+
$this->typeConverter = new TypeConverter;
73+
array_map(fn($opt) => isset($options[$opt]) && ($this->typeConverter->$opt = (bool) $options[$opt]), self::TypeConverterOptions);
7474
}
7575

7676

@@ -159,6 +159,12 @@ public function getReflection(): Reflection
159159
}
160160

161161

162+
public function getTypeConverter(): TypeConverter
163+
{
164+
return $this->typeConverter;
165+
}
166+
167+
162168
/**
163169
* Sets callback for row preprocessing.
164170
*/

src/Database/Drivers/Engine.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace Nette\Database\Drivers;
1111

1212
use Nette\Database;
13+
use Nette\Database\TypeConverter;
1314

1415

1516
/**
@@ -35,6 +36,9 @@ function isSupported(string $feature): bool;
3536
*/
3637
function convertException(\PDOException $e): Database\DriverException;
3738

39+
/** Converts a value from the database to a PHP value. */
40+
function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed;
41+
3842
/********************* SQL utilities ****************d*g**/
3943

4044
/** Adds delimiters around database identifier. */
@@ -74,10 +78,4 @@ function getIndexes(string $table): array;
7478
* @return list<array{name: string, local: string, table: string, foreign: string}>
7579
*/
7680
function getForeignKeys(string $table): array;
77-
78-
/**
79-
* Returns associative array of detected types (IStructure::FIELD_*) in result set.
80-
* @return array<string, string>
81-
*/
82-
function getColumnTypes(\PDOStatement $statement): array;
8381
}

src/Database/Drivers/Engines/MSSQLEngine.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Nette;
1313
use Nette\Database\Drivers\Connection;
1414
use Nette\Database\Drivers\Engine;
15+
use Nette\Database\TypeConverter;
1516
use function array_values, explode, preg_replace, str_replace, strtoupper;
1617

1718

@@ -233,8 +234,8 @@ public function getForeignKeys(string $table): array
233234
}
234235

235236

236-
public function getColumnTypes(\PDOStatement $statement): array
237+
public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed
237238
{
238-
return Nette\Database\Helpers::detectTypes($statement);
239+
return $converter->convertToPhp($value, $meta);
239240
}
240241
}

src/Database/Drivers/Engines/MySQLEngine.php

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@
2121
*/
2222
class MySQLEngine implements Engine
2323
{
24-
public bool $convertBoolean = true;
25-
26-
2724
public function __construct(
2825
private readonly Connection $connection,
2926
) {
@@ -186,22 +183,20 @@ public function getForeignKeys(string $table): array
186183
}
187184

188185

189-
public function getColumnTypes(\PDOStatement $statement): array
186+
public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed
190187
{
191-
$types = [];
192-
$count = $statement->columnCount();
193-
for ($col = 0; $col < $count; $col++) {
194-
$meta = $statement->getColumnMeta($col);
195-
if (isset($meta['native_type'])) {
196-
$types[$meta['name']] = match (true) {
197-
$meta['native_type'] === 'NEWDECIMAL' && $meta['precision'] === 0 => Nette\Database\IStructure::FIELD_INTEGER,
198-
$meta['native_type'] === 'TINY' && $meta['len'] === 1 && $this->convertBoolean => Nette\Database\IStructure::FIELD_BOOL,
199-
$meta['native_type'] === 'TIME' => Nette\Database\IStructure::FIELD_TIME_INTERVAL,
200-
default => TypeConverter::detectType($meta['native_type']),
201-
};
202-
}
203-
}
204-
205-
return $types;
188+
return match ($meta['nativeType']) {
189+
'NEWDECIMAL' => $meta['scale'] === 0
190+
? $converter->toInt($value)
191+
: $converter->toFloat($value),
192+
'TINY' => $meta['size'] === 1 && $converter->convertBoolean
193+
? $converter->toBool($value)
194+
: $converter->toInt($value),
195+
'TIME' => $converter->toInterval($value),
196+
'DATE', 'DATETIME', 'TIMESTAMP' => str_starts_with($value, '0000-00')
197+
? null
198+
: $converter->toDateTime($value),
199+
default => $converter->convertToPhp($value, $meta),
200+
};
206201
}
207202
}

src/Database/Drivers/Engines/ODBCEngine.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use Nette;
1313
use Nette\Database\Drivers\Engine;
14+
use Nette\Database\TypeConverter;
1415
use function preg_replace, str_replace;
1516

1617

@@ -96,8 +97,8 @@ public function getForeignKeys(string $table): array
9697
}
9798

9899

99-
public function getColumnTypes(\PDOStatement $statement): array
100+
public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed
100101
{
101-
return [];
102+
return $converter->convertToPhp($value, $meta);
102103
}
103104
}

src/Database/Drivers/Engines/OracleEngine.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Nette;
1313
use Nette\Database\Drivers\Connection;
1414
use Nette\Database\Drivers\Engine;
15+
use Nette\Database\TypeConverter;
1516
use function in_array, str_replace;
1617

1718

@@ -132,8 +133,8 @@ public function getForeignKeys(string $table): array
132133
}
133134

134135

135-
public function getColumnTypes(\PDOStatement $statement): array
136+
public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed
136137
{
137-
return [];
138+
return $converter->convertToPhp($value, $meta);
138139
}
139140
}

src/Database/Drivers/Engines/PostgreSQLEngine.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Nette;
1313
use Nette\Database\Drivers\Connection;
1414
use Nette\Database\Drivers\Engine;
15+
use Nette\Database\TypeConverter;
1516
use function array_map, array_values, explode, implode, str_contains, str_replace;
1617

1718

@@ -239,12 +240,11 @@ public function getForeignKeys(string $table): array
239240
}
240241

241242

242-
public function getColumnTypes(\PDOStatement $statement): array
243+
public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed
243244
{
244-
static $cache;
245-
$item = &$cache[$statement->queryString];
246-
$item ??= Nette\Database\Helpers::detectTypes($statement);
247-
return $item;
245+
return $meta['nativeType'] === 'bool'
246+
? ($value && $value !== 'f' && $value !== 'F')
247+
: $converter->convertToPhp($value, $meta);
248248
}
249249

250250

src/Database/Drivers/Engines/SQLServerEngine.php

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -229,22 +229,11 @@ public function getForeignKeys(string $table): array
229229
}
230230

231231

232-
public function getColumnTypes(\PDOStatement $statement): array
232+
public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed
233233
{
234-
$types = [];
235-
$count = $statement->columnCount();
236-
for ($col = 0; $col < $count; $col++) {
237-
$meta = $statement->getColumnMeta($col);
238-
if (
239-
isset($meta['sqlsrv:decl_type'])
240-
&& $meta['sqlsrv:decl_type'] !== 'timestamp'
241-
) { // timestamp does not mean time in sqlsrv
242-
$types[$meta['name']] = TypeConverter::detectType($meta['sqlsrv:decl_type']);
243-
} elseif (isset($meta['native_type'])) {
244-
$types[$meta['name']] = TypeConverter::detectType($meta['native_type']);
245-
}
246-
}
247-
248-
return $types;
234+
return match ($meta['nativeType']) {
235+
'timestamp' => $value, // timestamp does not mean time in sqlsrv
236+
default => $converter->convertToPhp($value, $meta),
237+
};
249238
}
250239
}

src/Database/Drivers/Engines/SQLiteEngine.php

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace Nette\Database\Drivers\Engines;
1111

1212
use Nette;
13+
use Nette\Database\DateTime;
1314
use Nette\Database\Drivers\Connection;
1415
use Nette\Database\Drivers\Engine;
1516
use Nette\Database\TypeConverter;
@@ -230,21 +231,10 @@ public function getForeignKeys(string $table): array
230231
}
231232

232233

233-
public function getColumnTypes(\PDOStatement $statement): array
234+
public function convertToPhp(mixed $value, array $meta, TypeConverter $converter): mixed
234235
{
235-
$types = [];
236-
$count = $statement->columnCount();
237-
for ($col = 0; $col < $count; $col++) {
238-
$meta = $statement->getColumnMeta($col);
239-
if (isset($meta['sqlite:decl_type'])) {
240-
$types[$meta['name']] = $this->formatDateTime === 'U' && in_array($meta['sqlite:decl_type'], ['DATE', 'DATETIME'], strict: true)
241-
? Nette\Database\IStructure::FIELD_UNIX_TIMESTAMP
242-
: TypeConverter::detectType($meta['sqlite:decl_type']);
243-
} elseif (isset($meta['native_type'])) {
244-
$types[$meta['name']] = TypeConverter::detectType($meta['native_type']);
245-
}
246-
}
247-
248-
return $types;
236+
return in_array($meta['nativeType'], ['DATE', 'DATETIME'], strict: true)
237+
? (is_int($value) ? (new DateTime)->setTimestamp($value) : new DateTime($value))
238+
: $converter->convertToPhp($value, $meta);
249239
}
250240
}

src/Database/Drivers/PDO/Connection.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
class Connection implements Drivers\Connection
1717
{
18+
public string $metaTypeKey = 'native_type';
19+
20+
1821
public function __construct(
1922
protected readonly PDO $pdo,
2023
) {

0 commit comments

Comments
 (0)