Skip to content
Merged
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
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ PHP 8.5 UPGRADE NOTES
the behavior of (bool) $object.
. The return value of gc_collect_cycles() no longer includes strings and
resources that were indirectly collected through cycles.
. It is now allowed to substitute static with self or the concrete class name
in final subclasses.

- Intl:
. The extension now requires at least ICU 57.1.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
--TEST--
Overriding static return types with self in final class
--FILE--
<?php

interface A
{
public function method1(): static;
}

abstract class B
{
abstract public function method2(): static;
}

trait C
{
abstract public function method3(): static;
}

final class Foo extends B implements A
{
use C;

public function method1(): self
{
return $this;
}

public function method2(): self
{
return $this;
}

public function method3(): self
{
return $this;
}
}

final class Bar extends B implements A
{
use C;

public function method1(): Bar
{
return $this;
}

public function method2(): Bar
{
return $this;
}

public function method3(): Bar
{
return $this;
}
}

$foo = new Foo();

var_dump($foo->method1());
var_dump($foo->method2());
var_dump($foo->method3());

$bar = new Bar();

var_dump($bar->method1());
var_dump($bar->method2());
var_dump($bar->method3());
?>
--EXPECTF--
object(Foo)#1 (0) {
}
object(Foo)#1 (0) {
}
object(Foo)#1 (0) {
}
object(Bar)#2 (0) {
}
object(Bar)#2 (0) {
}
object(Bar)#2 (0) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Overriding static return types with self in non-final class
--FILE--
<?php

interface A
{
public function method1(): static;
}

class Foo implements A
{
public function method1(): self
{
return $this;
}
}

$foo = new Foo();

var_dump($foo->method1());
?>
--EXPECTF--
Fatal error: Declaration of Foo::method1(): Foo must be compatible with A::method1(): static in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
--TEST--
Overriding static return types with self in final class with union types
--FILE--
<?php

interface A
{
public function methodScalar(): static|string;
public function methodIterable1(): static|iterable;
public function methodIterable2(): static|array;
public function methodObject1(): static|A;
public function methodObject2(): static|B;
public function methodObject3(): static|C;
public function methodObject4(): static|self;
public function methodNullable1(): ?static;
public function methodNullable2(): static|null;
}

final class B implements A
{
public function methodScalar(): self { return $this; }
public function methodIterable1(): self|iterable { return $this; }
public function methodIterable2(): array { return []; }
public function methodObject1(): self { return $this; }
public function methodObject2(): B { return $this; }
public function methodObject3(): C { return new C(); }
public function methodObject4(): self { return $this; }
public function methodNullable1(): ?static { return $this; }
public function methodNullable2(): ?static { return null; }
}

class C
{
}

$b = new B();
var_dump($b->methodScalar());
var_dump($b->methodIterable1());
var_dump($b->methodIterable2());
var_dump($b->methodObject1());
var_dump($b->methodObject2());
var_dump($b->methodObject3());
var_dump($b->methodObject4());
var_dump($b->methodNullable1());
var_dump($b->methodNullable2());
?>
--EXPECTF--
object(B)#1 (0) {
}
object(B)#1 (0) {
}
array(0) {
}
object(B)#1 (0) {
}
object(B)#1 (0) {
}
object(C)#2 (0) {
}
object(B)#1 (0) {
}
object(B)#1 (0) {
}
NULL
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
Overriding return type with type that is not in the interface in final class with union types
--FILE--
<?php

interface A
{
public function methodScalar1(): static|bool;
}

final class B implements A
{
public function methodScalar1(): array { return []; }
}

$b = new B();
var_dump($b->methodScalar1());
?>
--EXPECTF--
Fatal error: Declaration of B::methodScalar1(): array must be compatible with A::methodScalar1(): static|bool in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
Overriding static with self and add a type that is not in the interface in final class
--FILE--
<?php

interface A
{
public function methodScalar1(): static|bool;
}

final class B implements A
{
public function methodScalar1(): self|array { return []; }
}

$b = new B();
var_dump($b->methodScalar1());
?>
--EXPECTF--
Fatal error: Declaration of B::methodScalar1(): B|array must be compatible with A::methodScalar1(): static|bool in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Override static with another implementation of interface in final class
--FILE--
<?php

interface A
{
public function methodScalar1(): static|bool;
}

final class C implements A
{
public function methodScalar1(): self { return $this; }
}

final class B implements A
{
public function methodScalar1(): C { return new C(); }
}

$b = new B();
var_dump($b->methodScalar1());
?>
--EXPECTF--
Fatal error: Declaration of B::methodScalar1(): C must be compatible with A::methodScalar1(): static|bool in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Override static with another implementation of interface and add a type that is not in the interface in final class
--FILE--
<?php

interface A
{
public function methodScalar1(): static|bool;
}

final class C implements A
{
public function methodScalar1(): self { return $this; }
}

final class B implements A
{
public function methodScalar1(): C|array { return []; }
}

$b = new B();
var_dump($b->methodScalar1());
?>
--EXPECTF--
Fatal error: Declaration of B::methodScalar1(): C|array must be compatible with A::methodScalar1(): static|bool in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Override static with class that is even not an implementation of interface in final class
--FILE--
<?php

interface A
{
public function methodScalar1(): static|bool;
}

final class C
{
}

final class B implements A
{
public function methodScalar1(): C { return new C(); }
}

$b = new B();
var_dump($b->methodScalar1());
?>
--EXPECTF--
Fatal error: Declaration of B::methodScalar1(): C must be compatible with A::methodScalar1(): static|bool in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Override static with class that is even not an implementation of interface and add a type that is not in the interface in final class
--FILE--
<?php

interface A
{
public function methodScalar1(): static|bool;
}

final class C
{
}

final class B implements A
{
public function methodScalar1(): C|array { return []; }
}

$b = new B();
var_dump($b->methodScalar1());
?>
--EXPECTF--
Fatal error: Declaration of B::methodScalar1(): C|array must be compatible with A::methodScalar1(): static|bool in %s on line %d
11 changes: 11 additions & 0 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,17 @@ static inheritance_status zend_is_class_subtype_of_type(
}
}

/* If the parent has 'static' as a return type, then final classes could replace it with self */
if ((ZEND_TYPE_FULL_MASK(proto_type) & MAY_BE_STATIC) && (fe_scope->ce_flags & ZEND_ACC_FINAL)) {
if (!fe_ce) fe_ce = lookup_class(fe_scope, fe_class_name);
if (!fe_ce) {
have_unresolved = 1;
} else if (fe_ce == fe_scope) {
track_class_dependency(fe_ce, fe_class_name);
return INHERITANCE_SUCCESS;
}
}

zend_type *single_type;

/* Traverse the list of parent types and check if the current child (FE)
Expand Down
6 changes: 3 additions & 3 deletions ext/curl/tests/Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ respond /serverpush "main response"
respond /serverpush/pushed "pushed response"
push /serverpush /serverpush/pushed

basicauth /http-basic-auth {
# bcrypt password hash for "password", calculated with 'caddy hash-password'
user $2a$14$yUKl9SGqVTAAqPTzLup.DefsbXXx3kfreNnzpJOUHcIrKnr5lgef2
basic_auth /http-basic-auth {
# bcrypt password hash for "password", calculated with 'caddy hash-password'
user $2a$14$yUKl9SGqVTAAqPTzLup.DefsbXXx3kfreNnzpJOUHcIrKnr5lgef2
}
Loading