Skip to content

Commit 49a3b03

Browse files
committed
Implement basic variance support
This is a minimal variance implementation: It does not support any cyclic type dependencies. Additionally the preloading requirements are much more restrictive than necessary. Hopefully we can relax these in the future.
1 parent afec3a9 commit 49a3b03

17 files changed

+418
-89
lines changed

Zend/tests/bug62441.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ namespace ns {
1616
}
1717
?>
1818
--EXPECTF--
19-
Fatal error: Declaration of ns\Foo::method(ns\stdClass $o) must be compatible with Iface::method(stdClass $o) in %s on line %d
19+
Fatal error: Could not check compatibility between ns\Foo::method(ns\stdClass $o) and Iface::method(stdClass $o), because class ns\stdClass is not available in %s on line %d

Zend/tests/return_types/008.phpt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class qux implements foo {
1414
}
1515

1616
$qux = new qux();
17-
var_dump($qux->bar());
18-
--EXPECTF--
19-
Fatal error: Declaration of qux::bar(): qux must be compatible with foo::bar(): foo in %s008.php on line 8
17+
echo get_class($qux->bar());
18+
19+
?>
20+
--EXPECT--
21+
qux

Zend/tests/return_types/generators003.phpt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class SomeCollection implements Collection {
1515
}
1616

1717
$some = new SomeCollection();
18-
var_dump($some->getIterator());
19-
--EXPECTF--
20-
Fatal error: Declaration of SomeCollection::getIterator(): Generator must be compatible with Collection::getIterator(): Iterator in %sgenerators003.php on line 7
18+
echo get_class($some->getIterator());
19+
20+
?>
21+
--EXPECT--
22+
Generator

Zend/tests/return_types/inheritance005.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,9 @@ class Bar extends Foo {
1313
return new Bar;
1414
}
1515
}
16-
--EXPECTF--
17-
Fatal error: Declaration of Bar::test(): Bar must be compatible with Foo::test(): Foo in %sinheritance005.php on line 9
16+
17+
echo get_class(Bar::test());
18+
19+
?>
20+
--EXPECT--
21+
Bar

Zend/tests/return_types/inheritance006.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,9 @@ class Bar extends Foo {
1717
return new B;
1818
}
1919
}
20-
--EXPECTF--
21-
Fatal error: Declaration of Bar::test(): B must be compatible with Foo::test(): A in %sinheritance006.php on line 11
20+
21+
echo get_class(Bar::test());
22+
23+
?>
24+
--EXPECT--
25+
B

Zend/tests/return_types/inheritance007.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,9 @@ class Bar extends Foo {
1515
return new ArrayObject([1, 2]);
1616
}
1717
}
18-
--EXPECTF--
19-
Fatal error: Declaration of Bar::test(): ArrayObject must be compatible with Foo::test(): Traversable in %sinheritance007.php on line 9
18+
19+
echo get_class(Bar::test());
20+
21+
?>
22+
--EXPECT--
23+
ArrayObject
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Returns are covariant, but we don't allow the code due to class ordering
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public function method() : B {}
8+
}
9+
class B extends A {
10+
public function method() : C {}
11+
}
12+
class C extends B {
13+
}
14+
15+
new C;
16+
17+
?>
18+
--EXPECTF--
19+
Fatal error: Could not check compatibility between B::method(): C and A::method(): B, because class C is not available in %s on line %d
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Returns are covariant, but we don't allow the code due to class ordering (autoload variation)
3+
--FILE--
4+
<?php
5+
6+
spl_autoload_register(function($class) {
7+
if ($class === 'A') {
8+
class A {
9+
public function method() : B {}
10+
}
11+
} else if ($class == 'B') {
12+
class B extends A {
13+
public function method() : C {}
14+
}
15+
} else {
16+
class C extends B {
17+
}
18+
}
19+
});
20+
21+
$c = new C;
22+
23+
?>
24+
--EXPECTF--
25+
Fatal error: Could not check compatibility between B::method(): C and A::method(): B, because class C is not available in %s on line %d
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Forward compatibility with types that look like classes but aren't
3+
--FILE--
4+
<?php
5+
6+
spl_autoload_register(function($class) {
7+
var_dump($class);
8+
if ($class === 'X') {
9+
class X {}
10+
} else {
11+
class Y {}
12+
}
13+
});
14+
15+
class A {
16+
public function method(X $param) : object {}
17+
}
18+
19+
class B extends A {
20+
public function method(object $param) : Y {}
21+
}
22+
23+
?>
24+
--EXPECT--
25+
string(1) "X"
26+
string(1) "Y"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Testing object's variance in inheritance
3+
--FILE--
4+
<?php
5+
6+
interface I1 {
7+
function method1(I1 $o): object;
8+
}
9+
interface I2 extends I1 {
10+
function method1(object $o): I1;
11+
}
12+
final class C1 implements I2 {
13+
function method1($o = null): self {
14+
return $this;
15+
}
16+
}
17+
18+
$o = new C1();
19+
echo get_class($o->method1());
20+
?>
21+
--EXPECT--
22+
C1
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
Use of parent inside a class that has / has no parent
3+
--FILE--
4+
<?php
5+
6+
// Illegal: A::parent is ill-defined
7+
class A {
8+
public function method(parent $x) {}
9+
}
10+
class B extends A {
11+
public function method(parent $x) {}
12+
}
13+
14+
// Legal: A2::parent == P2
15+
class P2 {}
16+
class A2 extends P2 {
17+
public function method(parent $x) {}
18+
}
19+
class B2 extends A2 {
20+
public function method(P2 $x) {}
21+
}
22+
23+
// Legal: B3::parent == A3 is subclass of A3::parent == P3 in covariant position
24+
class P3 {}
25+
class A3 extends P3 {
26+
public function method($x): parent {}
27+
}
28+
class B3 extends A3 {
29+
public function method($x): parent {}
30+
}
31+
32+
// Illegal: B4::parent == A4 is subclass of A4::parent == P4 in contravariant position
33+
class P4 {}
34+
class A4 extends P4 {
35+
public function method(parent $x) {}
36+
}
37+
class B4 extends A4 {
38+
public function method(parent $x) {}
39+
}
40+
41+
?>
42+
--EXPECTF--
43+
Warning: Declaration of B4::method(A4 $x) should be compatible with A4::method(P4 $x) in %s on line %d
44+
45+
Warning: Could not check compatibility between B::method(A $x) and A::method(parent $x), because class parent is not available in %s on line %d

0 commit comments

Comments
 (0)