Skip to content

Commit 2b59df1

Browse files
committed
Add support for tentative return types of internal methods
RFC: https://wiki.php.net/rfc/internal_method_return_types
1 parent 32aff25 commit 2b59df1

36 files changed

+631
-293
lines changed

Zend/Optimizer/zend_inference.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4003,7 +4003,7 @@ uint32_t zend_get_return_info_from_signature_only(
40034003
const zend_function *func, const zend_script *script,
40044004
zend_class_entry **ce, bool *ce_is_instanceof) {
40054005
uint32_t type;
4006-
if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
4006+
if (func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && !ZEND_ARG_TYPE_IS_TENTATIVE(func->common.arg_info - 1)) {
40074007
zend_arg_info *ret_info = func->common.arg_info - 1;
40084008
type = zend_fetch_arg_info_type(script, ret_info, ce);
40094009
*ce_is_instanceof = ce != NULL;
@@ -4025,15 +4025,16 @@ uint32_t zend_get_return_info_from_signature_only(
40254025
ZEND_API void zend_init_func_return_info(
40264026
const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret)
40274027
{
4028-
if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
4029-
zend_ssa_range tmp_range = {0, 0, 0, 0};
4030-
bool is_instanceof = false;
4031-
ret->type = zend_get_return_info_from_signature_only(
4032-
(zend_function *) op_array, script, &ret->ce, &is_instanceof);
4033-
ret->is_instanceof = is_instanceof;
4034-
ret->range = tmp_range;
4035-
ret->has_range = 0;
4036-
}
4028+
ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE));
4029+
ZEND_ASSERT(!ZEND_ARG_TYPE_IS_TENTATIVE(&((zend_function *) op_array)->common.arg_info[-1]));
4030+
4031+
zend_ssa_range tmp_range = {0, 0, 0, 0};
4032+
bool is_instanceof = false;
4033+
ret->type = zend_get_return_info_from_signature_only(
4034+
(zend_function *) op_array, script, &ret->ce, &is_instanceof);
4035+
ret->is_instanceof = is_instanceof;
4036+
ret->range = tmp_range;
4037+
ret->has_range = 0;
40374038
}
40384039

40394040
void zend_func_return_info(const zend_op_array *op_array,

Zend/Optimizer/zend_optimizer.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1446,7 +1446,9 @@ ZEND_API int zend_optimize_script(zend_script *script, zend_long optimization_le
14461446
func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
14471447
if (func_info) {
14481448
func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
1449-
if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
1449+
if ((call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) &&
1450+
!ZEND_ARG_TYPE_IS_TENTATIVE(&((zend_function *) call_graph.op_arrays[i])->common.arg_info[-1])
1451+
) {
14501452
zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
14511453
}
14521454
}

Zend/tests/parameter_default_values/internal_declaration_error_class_const.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ The default value is a class constant in the parent class method's signature.
44
<?php
55
class MyDateTimeZone extends DateTimeZone
66
{
7-
public static function listIdentifiers()
7+
public static function listIdentifiers(): array
88
{
99
}
1010
}
1111
?>
1212
--EXPECTF--
13-
Fatal error: Declaration of MyDateTimeZone::listIdentifiers() must be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null) in %s on line %d
13+
Fatal error: Declaration of MyDateTimeZone::listIdentifiers(): array must be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d

Zend/tests/parameter_default_values/internal_declaration_error_const.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ The default value is a constant in the parent class method's signature.
44
<?php
55
class MyDateTimeZone extends DateTimeZone
66
{
7-
public function getTransitions()
7+
public function getTransitions(): array|false
88
{
99
}
1010
}
1111
?>
1212
--EXPECTF--
13-
Fatal error: Declaration of MyDateTimeZone::getTransitions() must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX) in %s on line %d
13+
Fatal error: Declaration of MyDateTimeZone::getTransitions(): array|false must be compatible with DateTimeZone::getTransitions(int $timestampBegin = PHP_INT_MIN, int $timestampEnd = PHP_INT_MAX): array|false in %s on line %d

Zend/tests/parameter_default_values/internal_declaration_error_false.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ The default value is false in the parent class method's signature.
55

66
interface MyDateTimeInterface extends DateTimeInterface
77
{
8-
public function diff();
8+
public function diff(): DateInterval;
99
}
1010
?>
1111
--EXPECTF--
12-
Fatal error: Declaration of MyDateTimeInterface::diff() must be compatible with DateTimeInterface::diff(DateTimeInterface $targetObject, bool $absolute = false) in %s on line %d
12+
Fatal error: Declaration of MyDateTimeInterface::diff(): DateInterval must be compatible with DateTimeInterface::diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval in %s on line %d

Zend/tests/parameter_default_values/internal_declaration_error_int.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ The default value is an integer in the parent class method's signature.
44
<?php
55
class MyDateTime extends DateTime
66
{
7-
public function setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false)
7+
public function setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false): DateTime
88
{
99
}
1010
}
1111
?>
1212
--EXPECTF--
13-
Fatal error: Declaration of MyDateTime::setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false) must be compatible with DateTime::setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0) in %s on line %d
13+
Fatal error: Declaration of MyDateTime::setTime(int $hour, int $minute, int $second = 0, bool $microsecond = false): DateTime must be compatible with DateTime::setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTime in %s on line %d

Zend/tests/parameter_default_values/internal_declaration_error_null.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ The default value is null in the parent class method's signature.
44
<?php
55
class MyDateTime extends DateTime
66
{
7-
public static function createFromFormat()
7+
public static function createFromFormat(): DateTime|false
88
{
99
}
1010
}
1111
?>
1212
--EXPECTF--
13-
Fatal error: Declaration of MyDateTime::createFromFormat() must be compatible with DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null) in %s on line %d
13+
Fatal error: Declaration of MyDateTime::createFromFormat(): DateTime|false must be compatible with DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false 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 no notice is emitted when the return type/value of the overriding method is compatible with the tentative return type/value of the overridden method
3+
--FILE--
4+
<?php
5+
class MyDateTimeZone extends DateTimeZone
6+
{
7+
public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array
8+
{
9+
return [];
10+
}
11+
}
12+
13+
var_dump(MyDateTimeZone::listIdentifiers());
14+
?>
15+
--EXPECT--
16+
array(0) {
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Test that a notice is emitted when the return type/value of the overriding method is incompatible with the tentative return type/value of the overridden method
3+
--FILE--
4+
<?php
5+
class MyDateTimeZone extends DateTimeZone
6+
{
7+
public static function listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): string
8+
{
9+
return "";
10+
}
11+
}
12+
13+
var_dump(MyDateTimeZone::listIdentifiers());
14+
?>
15+
--EXPECTF--
16+
Deprecated: Declaration of MyDateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): string should be compatible with DateTimeZone::listIdentifiers(int $timezoneGroup = DateTimeZone::ALL, ?string $countryCode = null): array in %s on line %d
17+
string(0) ""
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Internal class as parent
3+
--FILE--
4+
<?php
5+
6+
class Test extends DateTime {
7+
public static function createFromFormat($format, $datetime, Wrong $timezone = null): DateTime|false {}
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Could not check compatibility between Test::createFromFormat($format, $datetime, ?Wrong $timezone = null): DateTime|false and DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false, because class Wrong is not available in %s on line %d

0 commit comments

Comments
 (0)