Skip to content

Commit 744b55c

Browse files
committed
Expose optional return types to userland
1 parent a29be3c commit 744b55c

13 files changed

+350
-5
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test that a notice is emitted when the return type/value of the overriding method is incompatible with the optional return type/value of the overridden method
3+
--FILE--
4+
<?php
5+
class Foo
6+
{
7+
#[OptionalReturnType]
8+
public function x(): string|false
9+
{
10+
return "x";
11+
}
12+
}
13+
14+
class Bar extends Foo
15+
{
16+
public function x(): array
17+
{
18+
return [];
19+
}
20+
}
21+
22+
$bar = new Bar();
23+
var_dump($bar->x());
24+
?>
25+
--EXPECTF--
26+
Strict Standards: Declaration of Bar::x(): array should be compatible with Foo::x(): string|false in %s on line %d
27+
array(0) {
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test that a notice is emitted when the return type/value of the overriding method is incompatible with the optional return type/value of the overridden method
3+
--FILE--
4+
<?php
5+
class Foo
6+
{
7+
#[OptionalReturnType]
8+
public function x(): string|false
9+
{
10+
return "x";
11+
}
12+
}
13+
14+
class Bar extends Foo
15+
{
16+
public function x()
17+
{
18+
return [];
19+
}
20+
}
21+
22+
$bar = new Bar();
23+
var_dump($bar->x());
24+
?>
25+
--EXPECTF--
26+
Strict Standards: Declaration of Bar::x() should be compatible with Foo::x(): string|false in %s on line %d
27+
array(0) {
28+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
Test that the OptionalReturnType attribute is not inherited
3+
--FILE--
4+
<?php
5+
class Foo
6+
{
7+
#[OptionalReturnType]
8+
public function x(): string|false
9+
{
10+
return "x";
11+
}
12+
}
13+
14+
class Bar extends Foo
15+
{
16+
public function x(): array
17+
{
18+
return [];
19+
}
20+
}
21+
22+
class Baz extends Bar
23+
{
24+
public function x()
25+
{
26+
return [];
27+
}
28+
}
29+
30+
?>
31+
--EXPECTF--
32+
Strict Standards: Declaration of Bar::x(): array should be compatible with Foo::x(): string|false in %s on line %d
33+
34+
Fatal error: Declaration of Baz::x() must be compatible with Bar::x(): array in %s on line %d
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Test that a compile error is emitted if the OptionalReturnType attribute is used when the method doesn't have a return type
3+
--FILE--
4+
<?php
5+
6+
class Foo
7+
{
8+
#[OptionalReturnType]
9+
public function x()
10+
{
11+
return "x";
12+
}
13+
}
14+
15+
?>
16+
--EXPECTF--
17+
Fatal error: OptionalReturnType attribute cannot be used when a method doesn't have any return type in %s on line %d
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test that the notice can be suppressed when the return type/value of the overriding method is incompatible with the optional return type/value of the overridden method
3+
--FILE--
4+
<?php
5+
class Foo
6+
{
7+
#[OptionalReturnType]
8+
public function x(): string|false
9+
{
10+
return "x";
11+
}
12+
}
13+
14+
class Bar extends Foo
15+
{
16+
#[SuppressOptionalReturnTypeNotice]
17+
public function x(): array
18+
{
19+
return [];
20+
}
21+
}
22+
23+
$bar = new Bar();
24+
var_dump($bar->x());
25+
?>
26+
--EXPECTF--
27+
array(0) {
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test that the notice can be suppressed when the return type/value of the overriding method is incompatible with the optional return type/value of the overridden method
3+
--FILE--
4+
<?php
5+
class Foo
6+
{
7+
#[OptionalReturnType]
8+
public function x(): string|false
9+
{
10+
return "x";
11+
}
12+
}
13+
14+
class Bar extends Foo
15+
{
16+
#[SuppressOptionalReturnTypeNotice]
17+
public function x()
18+
{
19+
return [];
20+
}
21+
}
22+
23+
$bar = new Bar();
24+
var_dump($bar->x());
25+
?>
26+
--EXPECTF--
27+
array(0) {
28+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
Test that the notice can be suppressed when the return type/value of the overriding method is incompatible with the optional return type/value of the overridden method
3+
--FILE--
4+
<?php
5+
class Foo
6+
{
7+
#[OptionalReturnType]
8+
public function bar(): string|false
9+
{
10+
return "x";
11+
}
12+
}
13+
14+
class Bar extends Foo
15+
{
16+
#[OptionalReturnType]
17+
#[SuppressOptionalReturnTypeNotice]
18+
public function x(): array
19+
{
20+
return [];
21+
}
22+
}
23+
24+
class Baz extends Bar
25+
{
26+
public function x(): array|false
27+
{
28+
return [];
29+
}
30+
}
31+
32+
$baz = new Baz();
33+
var_dump($baz->x());
34+
?>
35+
--EXPECTF--
36+
Strict Standards: Declaration of Baz::x(): array|false should be compatible with Bar::x(): array in %s on line %d
37+
array(0) {
38+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Test that the notice can be suppressed when the return type/value of the overriding method is incompatible with the optional return type/value of the overridden method
3+
--FILE--
4+
<?php
5+
class Foo
6+
{
7+
#[OptionalReturnType]
8+
public function x(): string|false
9+
{
10+
return "x";
11+
}
12+
}
13+
14+
class Bar extends Foo
15+
{
16+
public function x(): array
17+
{
18+
return [];
19+
}
20+
}
21+
22+
class Baz extends Bar
23+
{
24+
#[SuppressOptionalReturnTypeNotice]
25+
public function x(): array
26+
{
27+
return [];
28+
}
29+
}
30+
31+
$baz = new Baz();
32+
var_dump($baz->x());
33+
?>
34+
--EXPECTF--
35+
Strict Standards: Declaration of Bar::x(): array should be compatible with Foo::x(): string|false in %s on line %d
36+
array(0) {
37+
}

Zend/zend_attributes.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include "zend_smart_str.h"
2525

2626
ZEND_API zend_class_entry *zend_ce_attribute;
27+
ZEND_API zend_class_entry *zend_ce_optional_return_type_attribute;
28+
ZEND_API zend_class_entry *zend_ce_suppress_optional_return_type_notice_attribute;
2729

2830
static HashTable internal_attributes;
2931

@@ -55,6 +57,20 @@ void validate_attribute(zend_attribute *attr, uint32_t target, zend_class_entry
5557
}
5658
}
5759

60+
void validate_optional_return_type_attribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope)
61+
{
62+
if (target != ZEND_ATTRIBUTE_TARGET_METHOD) {
63+
zend_error(E_COMPILE_ERROR, "Only methods can be marked with #[OptionalReturnType]");
64+
}
65+
}
66+
67+
void validate_suppress_optional_return_type_notice_attribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope)
68+
{
69+
if (target != ZEND_ATTRIBUTE_TARGET_METHOD) {
70+
zend_error(E_COMPILE_ERROR, "Only methods can be marked with #[SuppressOptionalReturnTypeNotice]");
71+
}
72+
}
73+
5874
ZEND_METHOD(Attribute, __construct)
5975
{
6076
zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL;
@@ -67,6 +83,16 @@ ZEND_METHOD(Attribute, __construct)
6783
ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), flags);
6884
}
6985

86+
ZEND_METHOD(OptionalReturnType, __construct)
87+
{
88+
ZEND_PARSE_PARAMETERS_NONE();
89+
}
90+
91+
ZEND_METHOD(SuppressOptionalReturnTypeNotice, __construct)
92+
{
93+
ZEND_PARSE_PARAMETERS_NONE();
94+
}
95+
7096
static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
7197
{
7298
if (attributes) {
@@ -278,6 +304,14 @@ void zend_register_attribute_ce(void)
278304
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_PARAMETER"), ZEND_ATTRIBUTE_TARGET_PARAMETER);
279305
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_ALL"), ZEND_ATTRIBUTE_TARGET_ALL);
280306
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("IS_REPEATABLE"), ZEND_ATTRIBUTE_IS_REPEATABLE);
307+
308+
zend_ce_optional_return_type_attribute = register_class_OptionalReturnType();
309+
attr = zend_internal_attribute_register(zend_ce_optional_return_type_attribute, ZEND_ATTRIBUTE_TARGET_METHOD);
310+
attr->validator = validate_optional_return_type_attribute;
311+
312+
zend_ce_suppress_optional_return_type_notice_attribute = register_class_SuppressOptionalReturnTypeNotice();
313+
attr = zend_internal_attribute_register(zend_ce_suppress_optional_return_type_notice_attribute, ZEND_ATTRIBUTE_TARGET_METHOD);
314+
attr->validator = validate_suppress_optional_return_type_notice_attribute;
281315
}
282316

283317
void zend_attributes_shutdown(void)

Zend/zend_attributes.stub.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,13 @@ final class Attribute
88

99
public function __construct(int $flags = Attribute::TARGET_ALL) {}
1010
}
11+
12+
final class OptionalReturnType
13+
{
14+
public function __construct() {}
15+
}
16+
17+
final class SuppressOptionalReturnTypeNotice
18+
{
19+
public function __construct() {}
20+
}

Zend/zend_attributes_arginfo.h

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,38 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 0183e750e66999862a7688ecb251017110d06d1f */
2+
* Stub hash: cf3fa04933ca9509925d14fe5e994932b39dd757 */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0)
55
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL")
66
ZEND_END_ARG_INFO()
77

8+
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_OptionalReturnType___construct, 0, 0, 0)
9+
ZEND_END_ARG_INFO()
10+
11+
#define arginfo_class_SuppressOptionalReturnTypeNotice___construct arginfo_class_OptionalReturnType___construct
12+
813

914
ZEND_METHOD(Attribute, __construct);
15+
ZEND_METHOD(OptionalReturnType, __construct);
16+
ZEND_METHOD(SuppressOptionalReturnTypeNotice, __construct);
1017

1118

1219
static const zend_function_entry class_Attribute_methods[] = {
1320
ZEND_ME(Attribute, __construct, arginfo_class_Attribute___construct, ZEND_ACC_PUBLIC)
1421
ZEND_FE_END
1522
};
1623

24+
25+
static const zend_function_entry class_OptionalReturnType_methods[] = {
26+
ZEND_ME(OptionalReturnType, __construct, arginfo_class_OptionalReturnType___construct, ZEND_ACC_PUBLIC)
27+
ZEND_FE_END
28+
};
29+
30+
31+
static const zend_function_entry class_SuppressOptionalReturnTypeNotice_methods[] = {
32+
ZEND_ME(SuppressOptionalReturnTypeNotice, __construct, arginfo_class_SuppressOptionalReturnTypeNotice___construct, ZEND_ACC_PUBLIC)
33+
ZEND_FE_END
34+
};
35+
1736
static zend_class_entry *register_class_Attribute(void)
1837
{
1938
zend_class_entry ce, *class_entry;
@@ -30,3 +49,25 @@ static zend_class_entry *register_class_Attribute(void)
3049

3150
return class_entry;
3251
}
52+
53+
static zend_class_entry *register_class_OptionalReturnType(void)
54+
{
55+
zend_class_entry ce, *class_entry;
56+
57+
INIT_CLASS_ENTRY(ce, "OptionalReturnType", class_OptionalReturnType_methods);
58+
class_entry = zend_register_internal_class_ex(&ce, NULL);
59+
class_entry->ce_flags |= ZEND_ACC_FINAL;
60+
61+
return class_entry;
62+
}
63+
64+
static zend_class_entry *register_class_SuppressOptionalReturnTypeNotice(void)
65+
{
66+
zend_class_entry ce, *class_entry;
67+
68+
INIT_CLASS_ENTRY(ce, "SuppressOptionalReturnTypeNotice", class_SuppressOptionalReturnTypeNotice_methods);
69+
class_entry = zend_register_internal_class_ex(&ce, NULL);
70+
class_entry->ce_flags |= ZEND_ACC_FINAL;
71+
72+
return class_entry;
73+
}

0 commit comments

Comments
 (0)