Skip to content

Commit 26e1bb5

Browse files
committed
Improve detection of the type from a PHP variable
1 parent afb565e commit 26e1bb5

File tree

3 files changed

+74
-20
lines changed

3 files changed

+74
-20
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\ODM\MongoDB\Types;
6+
7+
use InvalidArgumentException;
8+
9+
use function sprintf;
10+
11+
final class InvalidTypeException extends InvalidArgumentException
12+
{
13+
public static function invalidTypeName(string $name): self
14+
{
15+
return new self(sprintf('Invalid type specified "%s".', $name));
16+
}
17+
}

lib/Doctrine/ODM/MongoDB/Types/Type.php

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@
44

55
namespace Doctrine\ODM\MongoDB\Types;
66

7+
use DateTimeImmutable;
78
use DateTimeInterface;
89
use Doctrine\ODM\MongoDB\Mapping\MappingException;
910
use Doctrine\ODM\MongoDB\Types;
10-
use InvalidArgumentException;
1111
use MongoDB\BSON\ObjectId;
1212

1313
use function end;
1414
use function explode;
1515
use function gettype;
1616
use function is_object;
17-
use function sprintf;
1817
use function str_replace;
1918

2019
/**
@@ -140,48 +139,52 @@ public static function registerType(string $name, string $class): void
140139
/**
141140
* Get a Type instance.
142141
*
143-
* @throws InvalidArgumentException
142+
* @throws InvalidTypeException
144143
*/
145144
public static function getType(string $type): Type
146145
{
147146
if (! isset(self::$typesMap[$type])) {
148-
throw new InvalidArgumentException(sprintf('Invalid type specified "%s".', $type));
147+
throw InvalidTypeException::invalidTypeName($type);
149148
}
150149

151-
if (! isset(self::$typeObjects[$type])) {
152-
$className = self::$typesMap[$type];
153-
self::$typeObjects[$type] = new $className();
154-
}
155-
156-
return self::$typeObjects[$type];
150+
return self::$typeObjects[$type] ??= new (self::$typesMap[$type]);
157151
}
158152

159153
/**
160154
* Get a Type instance based on the type of the passed php variable.
161155
*
162156
* @param mixed $variable
163-
*
164-
* @throws InvalidArgumentException
165157
*/
166158
public static function getTypeFromPHPVariable($variable): ?Type
167159
{
168160
if (is_object($variable)) {
161+
if ($variable instanceof DateTimeImmutable) {
162+
return self::getType(self::DATE_IMMUTABLE);
163+
}
164+
169165
if ($variable instanceof DateTimeInterface) {
170-
return self::getType('date');
166+
return self::getType(self::DATE);
171167
}
172168

173169
if ($variable instanceof ObjectId) {
174-
return self::getType('id');
170+
return self::getType(self::OBJECTID);
175171
}
176-
} else {
177-
$type = gettype($variable);
178-
switch ($type) {
179-
case 'integer':
180-
return self::getType('int');
172+
173+
// Try the variable class as type name
174+
try {
175+
return self::getType($variable::class);
176+
} catch (InvalidTypeException) {
177+
return null;
181178
}
182179
}
183180

184-
return null;
181+
return match (gettype($variable)) {
182+
'integer' => self::getType('int'),
183+
'boolean' => self::getType('bool'),
184+
'double' => self::getType('float'),
185+
'string' => self::getType('string'),
186+
default => null,
187+
};
185188
}
186189

187190
/**

tests/Doctrine/ODM/MongoDB/Tests/Types/TypeTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
use function get_debug_type;
2222
use function md5;
23+
use function sprintf;
2324
use function str_pad;
2425
use function str_repeat;
2526
use function time;
@@ -126,6 +127,39 @@ public function testConvertImmutableDate(): void
126127
self::assertInstanceOf(UTCDateTime::class, Type::convertPHPToDatabaseValue($date));
127128
}
128129

130+
#[DataProvider('provideTypeFromPHPVariable')]
131+
public function testGetTypeFromPHPVariable(?Type $expectedType, mixed $variable): void
132+
{
133+
$type = Type::getTypeFromPHPVariable($variable);
134+
135+
if ($expectedType === null) {
136+
self::assertNull($type);
137+
} elseif ($type === null) {
138+
self::fail(sprintf('Type is null, expected "%s"', $expectedType::class));
139+
} else {
140+
self::assertInstanceOf($expectedType::class, $type, $type::class);
141+
}
142+
}
143+
144+
public static function provideTypeFromPHPVariable(): array
145+
{
146+
return [
147+
'null' => [null, null],
148+
'bool' => [Type::getType(Type::BOOL), true],
149+
'int' => [Type::getType(Type::INT), 1],
150+
'float' => [Type::getType(Type::FLOAT), 3.14],
151+
'string' => [Type::getType(Type::STRING), 'ohai'],
152+
'DateTime' => [Type::getType(Type::DATE), new DateTime()],
153+
'DateTimeImmutable' => [Type::getType(Type::DATE_IMMUTABLE), new DateTimeImmutable()],
154+
'ObjectId' => [Type::getType(Type::OBJECTID), new ObjectId()],
155+
'unknown object' => [
156+
null,
157+
new class () {
158+
},
159+
],
160+
];
161+
}
162+
129163
private static function assertSameTypeAndValue(mixed $expected, mixed $actual): void
130164
{
131165
self::assertSame(get_debug_type($expected), get_debug_type($actual));

0 commit comments

Comments
 (0)