Skip to content

Commit f73a301

Browse files
committed
feature symfony#54679 [TypeInfo] Proxies methods to non-nullable and fail gracefully (mtarld)
This PR was merged into the 7.2 branch. Discussion ---------- [TypeInfo] Proxies methods to non-nullable and fail gracefully | Q | A | ------------- | --- | Branch? | 7.1 | Bug fix? | no | New feature? | no | Deprecations? | no | Issues | | License | MIT - Proxies methods of "nullable union types" (ie: string|null) on non-nullable related type. - Gracefully fail with a `LogicException` on invalid method call. This allows for example to call `getClassName` on every `Type` within a try/catch block, without having to retrieve the very precise type holding the class name. Commits ------- 0053750 [TypeInfo] Proxies methods to non-nullable and fail gracefully
2 parents 77ed125 + 0053750 commit f73a301

File tree

6 files changed

+68
-0
lines changed

6 files changed

+68
-0
lines changed

src/Symfony/Component/TypeInfo/Tests/Type/CollectionTypeTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,10 @@ public function testIsA()
108108
$this->assertTrue($type->isA(TypeIdentifier::OBJECT));
109109
$this->assertTrue($type->isA(self::class));
110110
}
111+
112+
public function testProxiesMethodsToBaseType()
113+
{
114+
$type = new CollectionType(Type::generic(Type::builtin(TypeIdentifier::ARRAY), Type::string(), Type::bool()));
115+
$this->assertEquals([Type::string(), Type::bool()], $type->getVariableTypes());
116+
}
111117
}

src/Symfony/Component/TypeInfo/Tests/Type/GenericTypeTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,10 @@ public function testIsA()
6262
$this->assertFalse($type->isA(TypeIdentifier::STRING));
6363
$this->assertTrue($type->isA(self::class));
6464
}
65+
66+
public function testProxiesMethodsToBaseType()
67+
{
68+
$type = new GenericType(Type::object(self::class), Type::float());
69+
$this->assertSame(self::class, $type->getClassName());
70+
}
6571
}

src/Symfony/Component/TypeInfo/Tests/Type/UnionTypeTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,23 @@ public function testIsA()
123123
$type = new UnionType(Type::string(), Type::intersection(Type::int(), Type::int()));
124124
$this->assertTrue($type->isA(TypeIdentifier::INT));
125125
}
126+
127+
public function testProxiesMethodsToNonNullableType()
128+
{
129+
$this->assertEquals(Type::string(), (new UnionType(Type::list(Type::string()), Type::null()))->getCollectionValueType());
130+
131+
try {
132+
(new UnionType(Type::int(), Type::null()))->getCollectionValueType();
133+
$this->fail();
134+
} catch (LogicException) {
135+
$this->addToAssertionCount(1);
136+
}
137+
138+
try {
139+
(new UnionType(Type::list(Type::string()), Type::string()))->getCollectionValueType();
140+
$this->fail();
141+
} catch (LogicException) {
142+
$this->addToAssertionCount(1);
143+
}
144+
}
126145
}

src/Symfony/Component/TypeInfo/Tests/TypeTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,10 @@ public function testCannotGetBaseTypeOnCompoundType()
5151
$this->expectException(LogicException::class);
5252
Type::union(Type::int(), Type::string())->getBaseType();
5353
}
54+
55+
public function testThrowsOnUnexistingMethod()
56+
{
57+
$this->expectException(LogicException::class);
58+
Type::int()->unexistingMethod();
59+
}
5460
}

src/Symfony/Component/TypeInfo/Type.php

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

1212
namespace Symfony\Component\TypeInfo;
1313

14+
use Symfony\Component\TypeInfo\Exception\LogicException;
1415
use Symfony\Component\TypeInfo\Type\BuiltinType;
1516
use Symfony\Component\TypeInfo\Type\ObjectType;
1617

@@ -45,4 +46,14 @@ public function isNullable(): bool
4546
{
4647
return $this->is(fn (Type $t): bool => $t->isA(TypeIdentifier::NULL) || $t->isA(TypeIdentifier::MIXED));
4748
}
49+
50+
/**
51+
* Graceful fallback for unexisting methods.
52+
*
53+
* @param list<mixed> $arguments
54+
*/
55+
public function __call(string $method, array $arguments): mixed
56+
{
57+
throw new LogicException(sprintf('Cannot call "%s" on "%s" type.', $method, $this));
58+
}
4859
}

src/Symfony/Component/TypeInfo/Type/UnionType.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,24 @@ public function __toString(): string
7878

7979
return $string;
8080
}
81+
82+
/**
83+
* Proxies all method calls to the original non-nullable type.
84+
*
85+
* @param list<mixed> $arguments
86+
*/
87+
public function __call(string $method, array $arguments): mixed
88+
{
89+
$nonNullableType = $this->asNonNullable();
90+
91+
if (!$nonNullableType instanceof self) {
92+
if (!method_exists($nonNullableType, $method)) {
93+
throw new LogicException(sprintf('Method "%s" doesn\'t exist on "%s" type.', $method, $nonNullableType));
94+
}
95+
96+
return $nonNullableType->{$method}(...$arguments);
97+
}
98+
99+
throw new LogicException(sprintf('Cannot call "%s" on "%s" compound type.', $method, $this));
100+
}
81101
}

0 commit comments

Comments
 (0)