Let MixedArgumentOrganiser deal with union types
MODIFY
src/Behat/Testwork/Argument/MixedArgumentOrganiser.php
src/Behat/Testwork/Argument/MixedArgumentOrganiser.php
@@ -134,8 +134,10 @@ final class MixedArgumentOrganiser implements ArgumentOrganiser
*/
private function isValueMatchesTypehintedParameter($value, ReflectionParameter $parameter) : bool
{
- if ($typehintRefl = $this->getReflectionClassFromParameter($parameter)) {
- return $typehintRefl->isInstance($value);
+ foreach($this->getReflectionClassesFromParameter($parameter) as $typehintRefl) {
+ if($typehintRefl->isInstance($value)) {
+ return true;
+ }
}
return false;
@@ -219,31 +221,54 @@ final class MixedArgumentOrganiser implements ArgumentOrganiser
return array_filter($parameters,
function($parameter, $num) {
return !$this->isArgumentDefined($num)
- && $this->getReflectionClassFromParameter($parameter);
+ && $this->getReflectionClassesFromParameter($parameter);
},
ARRAY_FILTER_USE_BOTH
);
}
- private function getReflectionClassFromParameter(\ReflectionParameter $parameter) : ?ReflectionClass
+ /**
+ * @return ReflectionClass[]
+ */
+ private function getReflectionClassesFromParameter(\ReflectionParameter $parameter): array
{
- try {
+ $classes = [];
+
if (!$parameter->hasType()) {
- return null;
+ return $classes;
+ }
+
+ $type = $parameter->getType();
+
+ if ($type instanceof \ReflectionNamedType) {
+ $types = [$type];
}
- $type = $parameter->getType()->getName();
+ elseif ($parameter->getType() instanceof \ReflectionUnionType) {
+ $types = $type->getTypes();
+ }
+ else {
+ $types = [];
+ }
+
+ foreach ($types as $type) {
- if ($type == 'self') {
- $type = $parameter->getDeclaringClass();
- } elseif ($type == 'parent') {
- $type = $parameter->getDeclaringClass()->getParentClass();
+ $typeString = $type->getName();
+
+ if ($typeString == 'self') {
+ $typeString = $parameter->getDeclaringClass();
+ } elseif ($typeString == 'parent') {
+ $typeString = $parameter->getDeclaringClass()->getParentClass();
}
- return new ReflectionClass($type); // will throw
+ try {
+ $classes[] = new ReflectionClass($typeString);
} catch (ReflectionException $e) {
- return null;
+ continue;
+ }
}
+
+ return $classes;
}
/**
@@ -287,7 +312,8 @@ final class MixedArgumentOrganiser implements ArgumentOrganiser
$predicate
) {
foreach ($candidates as $candidateIndex => $candidate) {
- if (($class = $this->getReflectionClassFromParameter($parameter)) && $predicate($class, $candidate)) {
+ foreach($this->getReflectionClassesFromParameter($parameter) as $class) {
+ if ($predicate($class, $candidate)) {
$num = $parameter->getPosition();
$arguments[$num] = $candidate;
@@ -299,6 +325,7 @@ final class MixedArgumentOrganiser implements ArgumentOrganiser
return true;
}
}
+ }
return false;
}

MODIFY
tests/Behat/Tests/Testwork/Argument/MixedArgumentOrganiserTest.php
tests/Behat/Tests/Testwork/Argument/MixedArgumentOrganiserTest.php
@@ -92,4 +92,27 @@ final class MixedArgumentOrganiserTest extends TestCase
$this->assertSame([1, 'date' => $date], $organised);
}
+
+ /** @test */
+ function it_matches_union_types()
+ {
+ if (\PHP_VERSION_ID < 80000) {
+ $this->markTestSkipped('Union types are not supported');
+ }
+
+ $r = eval(<<<CODE
+ return new \ReflectionFunction(
+ function(int|\DateTimeInterface \$a) {}
+ );
+CODE
+ );
+ $args = [
+ 'date' => $date = new \DateTime(),
+ 'x' => 1
+ ];
+
+ $organised = $this->organiser->organiseArguments($r, $args);
+
+ $this->assertSame([$date], $organised);
+ }
}

Let MixedArgumentOrganiser deal with union types