Skip to content

Add covariance/contravariance to inherited methods #3732

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Zend/tests/bug76451.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php

class Foo {}
class_alias('Foo', 'Bar');
14 changes: 14 additions & 0 deletions Zend/tests/bug76451.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
Aliases during inheritance type checks (affected by opcache)
--FILE--
<?php
require __DIR__ . "/bug76451.inc";

class A {
public function test(Foo $foo) {}
}
class B extends A {
public function test(Bar $foo) {}
}
?>
--EXPECT--
7 changes: 4 additions & 3 deletions Zend/tests/return_types/008.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class qux implements foo {
}

$qux = new qux();
var_dump($qux->bar());
--EXPECTF--
Fatal error: Declaration of qux::bar(): qux must be compatible with foo::bar(): foo in %s008.php on line 7
echo get_class($qux->bar());

--EXPECT--
qux
7 changes: 4 additions & 3 deletions Zend/tests/return_types/generators003.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class SomeCollection implements Collection {
}

$some = new SomeCollection();
var_dump($some->getIterator());
--EXPECTF--
Fatal error: Declaration of SomeCollection::getIterator(): Generator must be compatible with Collection::getIterator(): Iterator in %sgenerators003.php on line 6
echo get_class($some->getIterator());

--EXPECT--
Generator
7 changes: 5 additions & 2 deletions Zend/tests/return_types/inheritance005.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ class Bar extends Foo {
return new Bar;
}
}
--EXPECTF--
Fatal error: Declaration of Bar::test(): Bar must be compatible with Foo::test(): Foo in %sinheritance005.php on line 12

echo get_class(Bar::test());

--EXPECT--
Bar
7 changes: 5 additions & 2 deletions Zend/tests/return_types/inheritance006.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,8 @@ class Bar extends Foo {
return new B;
}
}
--EXPECTF--
Fatal error: Declaration of Bar::test(): B must be compatible with Foo::test(): A in %sinheritance006.php on line 14

echo get_class(Bar::test());

--EXPECT--
B
7 changes: 5 additions & 2 deletions Zend/tests/return_types/inheritance007.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@ class Bar extends Foo {
return new ArrayObject([1, 2]);
}
}
--EXPECTF--
Fatal error: Declaration of Bar::test(): ArrayObject must be compatible with Foo::test(): Traversable in %sinheritance007.php on line 12

echo get_class(Bar::test());

--EXPECT--
ArrayObject
28 changes: 28 additions & 0 deletions Zend/tests/type_declarations/variance/across_namespaces_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--TEST--
Variance checks work across same-file namespaces (brace style)
--FILE--
<?php

namespace Foo {
class A {
function foo(B $x): A {
return new self();
}
}
}

namespace Bar {
class B extends \Foo\A {
function foo(\stdClass $x): B {
return new self();
}
}
}

namespace {
echo get_class((new \Bar\B())->foo(new \stdClass()));
}
?>
--EXPECTF--
Warning: Declaration of Bar\B::foo(stdClass $x): Bar\B should be compatible with Foo\A::foo(Foo\B $x): Foo\A in %s on line %d
Bar\B
26 changes: 26 additions & 0 deletions Zend/tests/type_declarations/variance/across_namespaces_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Variance checks work across same-file namespaces
--FILE--
<?php

namespace Foo;

class A {
function foo(B $x): A {
return new self();
}
}

namespace Bar;

class B extends \Foo\A {
function foo(\stdClass $x): B {
return new self();
}
}

echo get_class((new \Bar\B())->foo(new \stdClass()));
?>
--EXPECTF--
Warning: Declaration of Bar\B::foo(stdClass $x): Bar\B should be compatible with Foo\A::foo(Foo\B $x): Foo\A in %s on line %d
Bar\B
23 changes: 23 additions & 0 deletions Zend/tests/type_declarations/variance/conditional_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Delayed variance checks work with conditional statements
--FILE--
<?php

interface A {
static function m(Y $z): A;
}

if (true) {
class B implements A {
static function m(X $z): B {
return new self();
}
}
class X {}
class Y extends X {}
}

echo get_class(B::m(new X()));
?>
--EXPECTF--
B
23 changes: 23 additions & 0 deletions Zend/tests/type_declarations/variance/conditional_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Delayed variance checks work with conditional statements
--FILE--
<?php

interface A {
static function m(Y $z): A;
}

if (true) {
class B implements A {
static function m(X $z): B {
return new self();
}
}
class X {}
//class Y extends X {}
}

echo get_class(B::m(new X()));
?>
--EXPECTF--
Fatal error: Declaration of B::m(X $z): B must be compatible with A::m(Y $z): A in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
non-consecutive type decls are still checked for variance
--FILE--
<?php

interface A {
function m(): A;
}

// "echo 'hi';" is not a declaration statement
echo 'hi';

class B implements A {
function m(): B { return $this; }
}

echo get_class((new B())->m());
?>
--EXPECT--
hiB
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
non-consecutive type decls are still checked for variance
--FILE--
<?php

interface A {
function m(): A;
}

// "echo 'hi';" is not a declaration statement
echo 'hi';

class B implements A {
function m(): stdClass { return $this; }
}

echo get_class((new B())->m());
?>
--EXPECTF--
hi
Fatal error: Declaration of B::m(): stdClass must be compatible with A::m(): A in %s on line %d
22 changes: 22 additions & 0 deletions Zend/tests/type_declarations/variance/object_variance.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Testing object's variance in inheritance
--FILE--
<?php

interface I1 {
function method1(I1 $o): object;
}
interface I2 extends I1 {
function method1(object $o): I1;
}
final class C1 implements I2 {
function method1($o = null): self {
return $this;
}
}

$o = new C1();
echo get_class($o->method1());
?>
--EXPECT--
C1
15 changes: 15 additions & 0 deletions Zend/tests/type_declarations/variance/undefined_type_01.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Undefined types during variance checks are warnings
--FILE--
<?php

class X {
function m(stdClass $z) {}
}

class Y extends X {
function m(UndefinedA $z) {}
}
?>
--EXPECTF--
Warning: Declaration of Y::m(UndefinedA $z) should be compatible with X::m(stdClass $z) in %s on line %d
15 changes: 15 additions & 0 deletions Zend/tests/type_declarations/variance/undefined_type_02.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Undefined types during variance checks are warnings
--FILE--
<?php

abstract class X {
function m(stdClass $z) {}
}

class Y extends X {
function m(UndefinedA $z) {}
}
?>
--EXPECTF--
Warning: Declaration of Y::m(UndefinedA $z) should be compatible with X::m(stdClass $z) in %s on line %d
15 changes: 15 additions & 0 deletions Zend/tests/type_declarations/variance/undefined_type_03.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Undefined types during variance checks are errors
--FILE--
<?php

abstract class X {
abstract function m(stdClass $z);
}

class Y extends X {
function m(UndefinedA $z) {}
}
?>
--EXPECTF--
Fatal error: Declaration of Y::m(UndefinedA $z) must be compatible with X::m(stdClass $z) in %s on line %d
15 changes: 15 additions & 0 deletions Zend/tests/type_declarations/variance/undefined_type_04.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Undefined types during variance checks are errors
--FILE--
<?php

interface X {
function m(stdClass $z);
}

class Y implements X {
function m(UndefinedA $z) {}
}
?>
--EXPECTF--
Fatal error: Declaration of Y::m(UndefinedA $z) must be compatible with X::m(stdClass $z) in %s on line %d
2 changes: 2 additions & 0 deletions Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,8 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
zend_hash_init_ex(compiler_globals->class_table, 64, NULL, ZEND_CLASS_DTOR, 1, 0);
zend_hash_copy(compiler_globals->class_table, global_class_table, zend_class_add_ref);

compiler_globals->unverified_types = NULL;

zend_set_default_compile_time_values();

compiler_globals->auto_globals = (HashTable *) malloc(sizeof(HashTable));
Expand Down
Loading