Skip to content

Commit a701c74

Browse files
Closes #33
1 parent 5af4922 commit a701c74

File tree

4 files changed

+56
-2
lines changed

4 files changed

+56
-2
lines changed

ChangeLog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
44

5+
## [5.1.1] - 2025-MM-DD
6+
7+
### Fixed
8+
9+
* [#33](https://github.com/sebastianbergmann/type/issues/33): `ReflectionMapper` does not handle unions that contain `iterable` correctly
10+
511
## [5.1.0] - 2024-09-17
612

713
### Added
@@ -171,6 +177,7 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt
171177

172178
* Initial release based on [code contributed by Michel Hartmann to PHPUnit](https://github.com/sebastianbergmann/phpunit/pull/3673)
173179

180+
[5.1.1]: https://github.com/sebastianbergmann/type/compare/5.1.0...5.1
174181
[5.1.0]: https://github.com/sebastianbergmann/type/compare/5.0.1...5.1.0
175182
[5.0.1]: https://github.com/sebastianbergmann/type/compare/5.0.0...5.0.1
176183
[5.0.0]: https://github.com/sebastianbergmann/type/compare/4.0...5.0.0

src/ReflectionMapper.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace SebastianBergmann\Type;
1111

1212
use function assert;
13+
use function count;
1314
use ReflectionFunction;
1415
use ReflectionIntersectionType;
1516
use ReflectionMethod;
@@ -156,18 +157,32 @@ private function mapNamedType(ReflectionNamedType $type, ReflectionFunction|Refl
156157

157158
private function mapUnionType(ReflectionUnionType $type, ReflectionFunction|ReflectionMethod|ReflectionProperty $reflector): Type
158159
{
159-
$types = [];
160+
$types = [];
161+
$objectType = false;
162+
$genericObjectType = false;
160163

161164
foreach ($type->getTypes() as $_type) {
162165
if ($_type instanceof ReflectionNamedType) {
163-
$types[] = $this->mapNamedType($_type, $reflector);
166+
$namedType = $this->mapNamedType($_type, $reflector);
167+
168+
if ($namedType instanceof GenericObjectType) {
169+
$genericObjectType = count($types);
170+
} elseif ($namedType instanceof ObjectType) {
171+
$objectType = true;
172+
}
173+
174+
$types[] = $namedType;
164175

165176
continue;
166177
}
167178

168179
$types[] = $this->mapIntersectionType($_type, $reflector);
169180
}
170181

182+
if ($objectType && $genericObjectType !== false) {
183+
unset($types[$genericObjectType]);
184+
}
185+
171186
return new UnionType(...$types);
172187
}
173188

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of sebastian/type.
4+
*
5+
* (c) Sebastian Bergmann <[email protected]>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace SebastianBergmann\Type\TestFixture;
11+
12+
interface InterfaceWithMethodThatReturnsObjectOrIterable
13+
{
14+
public function doSomething(): object|iterable;
15+
}

tests/unit/ReflectionMapperTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHPUnit\Framework\Attributes\DataProvider;
1414
use PHPUnit\Framework\Attributes\RequiresPhp;
1515
use PHPUnit\Framework\Attributes\Small;
16+
use PHPUnit\Framework\Attributes\Ticket;
1617
use PHPUnit\Framework\Attributes\UsesClass;
1718
use PHPUnit\Framework\TestCase;
1819
use ReflectionFunction;
@@ -35,6 +36,7 @@
3536
use SebastianBergmann\Type\TestFixture\ClassWithMethodThatDeclaresNullReturnType;
3637
use SebastianBergmann\Type\TestFixture\ClassWithMethodThatDeclaresTrueReturnType;
3738
use SebastianBergmann\Type\TestFixture\ClassWithProperties;
39+
use SebastianBergmann\Type\TestFixture\InterfaceWithMethodThatReturnsObjectOrIterable;
3840
use SebastianBergmann\Type\TestFixture\ParentClass;
3941

4042
#[CoversClass(ReflectionMapper::class)]
@@ -341,4 +343,19 @@ public function testMapsFromPropertyType(string $expectedTypeClass, string $expe
341343
$this->assertInstanceOf($expectedTypeClass, $type);
342344
$this->assertSame($expectedString, $type->asString());
343345
}
346+
347+
#[Ticket('https://github.com/sebastianbergmann/type/issues/33')]
348+
public function testGenericObjectTypeIsRemovedFromUnionWhenUnionContainsClassType(): void
349+
{
350+
$type = (new ReflectionMapper)->fromReturnType(
351+
new ReflectionMethod(
352+
InterfaceWithMethodThatReturnsObjectOrIterable::class,
353+
'doSomething',
354+
),
355+
);
356+
357+
$this->assertTrue($type->isUnion());
358+
$this->assertSame('Traversable|array', $type->name());
359+
$this->assertSame('Traversable|array', $type->asString());
360+
}
344361
}

0 commit comments

Comments
 (0)