Check abstract method signatures coming from traits · php/php-src@f74e30c

12 files changed

lines changed

Original file line numberDiff line numberDiff line change

@@ -163,6 +163,21 @@ PHP 8.0 UPGRADE NOTES

163163

accepted, and func as assumed to refer to T1::func. Now it will generate a

164164

fatal error instead, and either T1::func or T2::func needs to be written

165165

explicitly.

166+

. The signature of abstract methods defined in traits is now checked against

167+

the implementing class method:

168+
169+

trait MyTrait {

170+

abstract private function neededByTrait(): string;

171+

}

172+
173+

class MyClass {

174+

use MyTrait;

175+
176+

// Error, because of return type mismatch.

177+

private function neededByTrait(): int { return 42; }

178+

}

179+
180+

RFC: https://wiki.php.net/rfc/abstract_trait_method_validation

166181
167182

- COM:

168183

. Removed the ability to import case-insensitive constants from type

@@ -428,8 +443,10 @@ PHP 8.0 UPGRADE NOTES

428443

. Some consistency fixes to variable syntax have been applied, for example

429444

writing `Foo::BAR::$baz` is now allowed.

430445

RFC: https://wiki.php.net/rfc/variable_syntax_tweaks

431-

. Added Stringable.

446+

. Added Stringable interface, which is automatically implemented if a class

447+

defines a __toString() method.

432448

RFC: https://wiki.php.net/rfc/stringable

449+

. Traits can now define abstract private methods.

433450
434451

- Date:

435452

. Added DateTime::createFromInterface() and

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,18 @@

1+

--TEST--

2+

Abstract method from trait enforced in class

3+

--FILE--

4+

<?php

5+
6+

trait T {

7+

abstract public function neededByTheTrait(int $a, string $b);

8+

}

9+
10+

class C {

11+

use T;

12+
13+

public function neededByTheTrait(array $a, object $b) {}

14+

}

15+
16+

?>

17+

--EXPECTF--

18+

Fatal error: Declaration of C::neededByTheTrait(array $a, object $b) must be compatible with T::neededByTheTrait(int $a, string $b) in %s on line %d

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,22 @@

1+

--TEST--

2+

Mutually incompatible methods from traits are fine as long as the final method is compatible

3+

--FILE--

4+

<?php

5+
6+

trait T1 {

7+

abstract public function test();

8+

}

9+

trait T2 {

10+

abstract public function test(): int;

11+

}

12+
13+

class C {

14+

use T1, T2;

15+
16+

public function test(): int {}

17+

}

18+
19+

?>

20+

===DONE===

21+

--EXPECT--

22+

===DONE===

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,18 @@

1+

--TEST--

2+

Private abstract method from trait enforced in class

3+

--FILE--

4+

<?php

5+
6+

trait T {

7+

abstract private function neededByTheTrait(int $a, string $b);

8+

}

9+
10+

class C {

11+

use T;

12+
13+

private function neededByTheTrait(array $a, object $b) {}

14+

}

15+
16+

?>

17+

--EXPECTF--

18+

Fatal error: Declaration of C::neededByTheTrait(array $a, object $b) must be compatible with T::neededByTheTrait(int $a, string $b) in %s on line %d

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,20 @@

1+

--TEST--

2+

Visibility enforcement on abstract trait methods

3+

--FILE--

4+

<?php

5+
6+

trait T {

7+

abstract public function method(int $a, string $b);

8+

}

9+
10+

class C {

11+

use T;

12+
13+

/* For backwards-compatibility reasons, visibility is not enforced here. */

14+

private function method(int $a, string $b) {}

15+

}

16+
17+

?>

18+

===DONE===

19+

--EXPECT--

20+

===DONE===

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,18 @@

1+

--TEST--

2+

Staticness enforcement on abstract trait methods

3+

--FILE--

4+

<?php

5+
6+

trait T {

7+

abstract static public function method(int $a, string $b);

8+

}

9+
10+

class C {

11+

use T;

12+
13+

public function method(int $a, string $b) {}

14+

}

15+
16+

?>

17+

--EXPECTF--

18+

Fatal error: Cannot make static method T::method() non static in class C in %s on line %d

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,20 @@

1+

--TEST--

2+

Abstract private trait method not implemented

3+

--FILE--

4+

<?php

5+
6+

trait T {

7+

abstract private function method(int $a, string $b);

8+

}

9+
10+

abstract class C {

11+

use T;

12+

}

13+
14+

class D extends C {

15+

private function method(int $a, string $b) {}

16+

}

17+
18+

?>

19+

--EXPECTF--

20+

Fatal error: Class C must implement 1 abstract private method (C::method) in %s on line %d

Original file line numberDiff line numberDiff line change

@@ -0,0 +1,23 @@

1+

--TEST--

2+

Abstract private trait method forwarded as abstract protected method

3+

--FILE--

4+

<?php

5+
6+

trait T {

7+

abstract private function method(int $a, string $b);

8+

}

9+
10+

abstract class C {

11+

use T;

12+
13+

abstract protected function method(int $a, string $b);

14+

}

15+
16+

class D extends C {

17+

protected function method(int $a, string $b) {}

18+

}

19+
20+

?>

21+

===DONE===

22+

--EXPECT--

23+

===DONE===

Original file line numberDiff line numberDiff line change

@@ -22,4 +22,4 @@ class CBroken {

2222

$o = new CBroken;

2323

$o->foo(1);

2424

--EXPECTF--

25-

Fatal error: Declaration of TBroken1::foo($a) must be compatible with TBroken2::foo($a, $b = 0) in %s

25+

Fatal error: Declaration of CBroken::foo($a) must be compatible with TBroken2::foo($a, $b = 0) in %s on line %d

Original file line numberDiff line numberDiff line change

@@ -22,4 +22,4 @@ class CBroken {

2222

$o = new CBroken;

2323

$o->foo(1);

2424

--EXPECTF--

25-

Fatal error: Declaration of TBroken2::foo($a) must be compatible with TBroken1::foo($a, $b = 0) in %s on line %d

25+

Fatal error: Declaration of CBroken::foo($a) must be compatible with TBroken1::foo($a, $b = 0) in %s on line %d