diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 23386942..c736f0a6 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -3356,15 +3356,17 @@ CODE; private function isParameterTypeOverrideCompatible(ArgInfo $childArg, ArgInfo $parentArg): bool { - if ($parentArg->typeCheck || $childArg->typeCheck) { - return $parentArg->typeStr === $childArg->typeStr; - } + // Child methods may omit parameter types (contravariance — accepting a + // wider set of inputs is always compatible with the parent contract). if ($childArg->undeclared || $childArg->type === self::TYPE_VAR) { return true; } if ($parentArg->undeclared || $parentArg->type === self::TYPE_VAR) { return false; } + if ($parentArg->typeCheck || $childArg->typeCheck) { + return $parentArg->typeStr === $childArg->typeStr; + } if ($childArg->type !== $parentArg->type) { return false; } diff --git a/tests/core/classes/class_implements_param_type.phpt b/tests/core/classes/class_implements_param_type.phpt new file mode 100644 index 00000000..55e76b17 --- /dev/null +++ b/tests/core/classes/class_implements_param_type.phpt @@ -0,0 +1,104 @@ +--TEST-- +Class implements interface: union type / composite type parameter compatibility +Tests parameter type contravariance for interface implementation with union types: + - Child method omitting the type should accept a wider set of inputs. + - Child method declaring the identical union type should also compile. + - nullable union (T1|T2|null) and three-way union (T1|T2|T3) are covered. +--FILE-- +single('hello'); + $a->union2('world'); + $a->union2(42); + $a->union3(1); + $a->union3('two'); + $a->union3(3.14); + $a->nullableUnion(100); + $a->nullableUnion(null); + + // ImplMirror — explicit matching union type + $b = new ImplMirror; + $b->mirror('ok'); + $b->mirror(99); + + // ImplMulti — multiple interfaces + $c = new ImplMulti; + $c->a('yes'); + $c->a(true); + $c->b(123); + $c->b(4.56); +} +?> +--EXPECT-- +string(5) "hello" +string(5) "world" +int(42) +int(1) +string(3) "two" +float(3.14) +int(100) +NULL +string(2) "ok" +int(99) +string(3) "yes" +bool(true) +int(123) +float(4.56)