Skip to content

Commit 971ca94

Browse files
committed
Display the default values of parameters of internal functions correctly
1 parent 878f8b0 commit 971ca94

11 files changed

+255
-78
lines changed

Zend/tests/closures/closure_from_callable_basic.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ $foo = new SubFoo;
8181
$fn = $foo->getSelfColonParentPublicInstanceMethod();
8282
echo $fn(" OK".PHP_EOL);
8383

84-
echo 'Access proteced instance method of parent object through "self::" to parent method';
84+
echo 'Access protected instance method of parent object through "self::" to parent method';
8585
$foo = new SubFoo;
8686
$fn = $foo->getSelfColonParentProtectedInstanceMethod();
8787
echo $fn(" OK".PHP_EOL);
@@ -115,7 +115,7 @@ Subclass closure over parent class static protected method OK
115115
Access public instance method of parent object through "parent::" OK
116116
Access public instance method of self object through "self::" OK
117117
Access public instance method of parent object through "self::" to parent method OK
118-
Access proteced instance method of parent object through "self::" to parent method OK
118+
Access protected instance method of parent object through "self::" to parent method OK
119119
MagicCall __call instance method __call,nonExistentMethod, OK
120120
MagicCall __callStatic static method __callStatic,nonExistentMethod, OK
121121
===DONE===
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
The default value is a class constant in the parent class method's signature.
3+
--FILE--
4+
<?php
5+
class MyDateTimeZone extends DateTimeZone
6+
{
7+
public static function listIdentifiers(int $what = DateTimeZone::ALL, bool $country = false)
8+
{
9+
}
10+
}
11+
echo "OK";
12+
--EXPECTF--
13+
Fatal error: Declaration of MyDateTimeZone::listIdentifiers(int $what = DateTimeZone::ALL, bool $country = false) must be compatible with DateTimeZone::listIdentifiers(int $what = DateTimeZone::ALL, ?string $country = NULL) in %s on line %d
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
The default value is a constant in the parent class method's signature.
3+
--FILE--
4+
<?php
5+
class MyDateTimeZone extends DateTimeZone
6+
{
7+
public function getTransitions(int $timestamp_begin = PHP_INT_MIN, bool $timestamp_end = false)
8+
{
9+
}
10+
}
11+
echo "OK";
12+
--EXPECTF--
13+
Fatal error: Declaration of MyDateTimeZone::getTransitions(int $timestamp_begin = PHP_INT_MIN, bool $timestamp_end = false) must be compatible with DateTimeZone::getTransitions(int $timestamp_begin = PHP_INT_MIN, int $timestamp_end = PHP_INT_MAX) in %s on line %d
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
The default value is false in the parent class method's signature.
3+
--FILE--
4+
<?php
5+
6+
interface MyDateTimeInterface extends DateTimeInterface
7+
{
8+
public function diff(DateTimeInterface $object, int $absolute = 0);
9+
}
10+
echo "OK";
11+
--EXPECTF--
12+
Fatal error: Declaration of MyDateTimeInterface::diff(DateTimeInterface $object, int $absolute = 0) must be compatible with DateTimeInterface::diff(DateTimeInterface $object, bool $absolute = false) in %s on line %d
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
The default value is an integer in the parent class method's signature.
3+
--FILE--
4+
<?php
5+
class MyDateTime extends DateTime
6+
{
7+
public function setTime(int $hour, int $minute, int $second = 0, bool $microseconds = false)
8+
{
9+
}
10+
}
11+
echo "OK";
12+
--EXPECTF--
13+
Fatal error: Declaration of MyDateTime::setTime(int $hour, int $minute, int $second = 0, bool $microseconds = false) must be compatible with DateTime::setTime(int $hour, int $minute, int $second = 0, int $microseconds = 0) in %s on line %d
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
The default value is null in the parent class method's signature.
3+
--FILE--
4+
<?php
5+
class MyDateTime extends DateTime
6+
{
7+
public static function createFromFormat(string $format, string $time, bool $timezone = false)
8+
{
9+
}
10+
}
11+
echo "OK";
12+
--EXPECTF--
13+
Fatal error: Declaration of MyDateTime::createFromFormat(string $format, string $time, bool $timezone = false) must be compatible with DateTime::createFromFormat(string $format, string $time, ?DateTimeZone $timezone = NULL) in %s on line %d

Zend/zend_API.h

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,32 +96,34 @@ typedef struct _zend_fcall_info_cache {
9696

9797
#define ZEND_FE_END { NULL, NULL, NULL, 0, 0 }
9898

99-
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 0},
100-
#define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, 0, pass_by_ref, 0},
101-
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 0 },
102-
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(IS_ARRAY, allow_null), pass_by_ref, 0 },
103-
#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(IS_CALLABLE, allow_null), pass_by_ref, 0 },
104-
#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(type_hint, allow_null), pass_by_ref, 0 },
105-
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 1 },
106-
#define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(type_hint, allow_null), pass_by_ref, 1 },
107-
#define ZEND_ARG_VARIADIC_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 1 },
99+
#define ZEND_ARG_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 0, NULL },
100+
#define ZEND_ARG_PASS_INFO(pass_by_ref) { NULL, 0, pass_by_ref, 0, NULL },
101+
#define ZEND_ARG_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 0, NULL },
102+
#define ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, classname, allow_null, default_value) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 0, default_value },
103+
#define ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(IS_ARRAY, allow_null), pass_by_ref, 0, NULL },
104+
#define ZEND_ARG_CALLABLE_INFO(pass_by_ref, name, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(IS_CALLABLE, allow_null), pass_by_ref, 0, NULL },
105+
#define ZEND_ARG_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(type_hint, allow_null), pass_by_ref, 0, NULL },
106+
#define ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(pass_by_ref, name, type_hint, allow_null, default_value) { #name, ZEND_TYPE_ENCODE_CODE(type_hint, allow_null), pass_by_ref, 0, default_value },
107+
#define ZEND_ARG_VARIADIC_INFO(pass_by_ref, name) { #name, 0, pass_by_ref, 1, NULL },
108+
#define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null) { #name, ZEND_TYPE_ENCODE_CODE(type_hint, allow_null), pass_by_ref, 1, NULL },
109+
#define ZEND_ARG_VARIADIC_OBJ_INFO(pass_by_ref, name, classname, allow_null) { #name, ZEND_TYPE_ENCODE_CLASS_CONST(#classname, allow_null), pass_by_ref, 1, NULL },
108110

109111
#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, return_reference, required_num_args, class_name, allow_null) \
110112
static const zend_internal_arg_info name[] = { \
111-
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE_CLASS_CONST(#class_name, allow_null), return_reference, 0 },
113+
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE_CLASS_CONST(#class_name, allow_null), return_reference, 0, NULL },
112114

113115
#define ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO(name, class_name, allow_null) \
114116
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(name, 0, -1, class_name, allow_null)
115117

116118
#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, return_reference, required_num_args, type, allow_null) \
117119
static const zend_internal_arg_info name[] = { \
118-
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE_CODE(type, allow_null), return_reference, 0 },
120+
{ (const char*)(zend_uintptr_t)(required_num_args), ZEND_TYPE_ENCODE_CODE(type, allow_null), return_reference, 0, NULL },
119121
#define ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO(name, type, allow_null) \
120122
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(name, 0, -1, type, allow_null)
121123

122124
#define ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) \
123125
static const zend_internal_arg_info name[] = { \
124-
{ (const char*)(zend_uintptr_t)(required_num_args), 0, return_reference, 0 },
126+
{ (const char*)(zend_uintptr_t)(required_num_args), 0, return_reference, 0, NULL },
125127
#define ZEND_BEGIN_ARG_INFO(name, _unused) \
126128
ZEND_BEGIN_ARG_INFO_EX(name, 0, ZEND_RETURN_VALUE, -1)
127129
#define ZEND_END_ARG_INFO() };

Zend/zend_compile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ typedef struct _zend_internal_arg_info {
385385
zend_type type;
386386
zend_uchar pass_by_reference;
387387
zend_bool is_variadic;
388+
const char *default_value;
388389
} zend_internal_arg_info;
389390

390391
/* arg_info for user functions */
@@ -393,6 +394,7 @@ typedef struct _zend_arg_info {
393394
zend_type type;
394395
zend_uchar pass_by_reference;
395396
zend_bool is_variadic;
397+
const char *default_value;
396398
} zend_arg_info;
397399

398400
/* the following structure repeats the layout of zend_internal_arg_info,
@@ -405,6 +407,7 @@ typedef struct _zend_internal_function_info {
405407
zend_type type;
406408
zend_bool return_reference;
407409
zend_bool _is_variadic;
410+
const char *default_value;
408411
} zend_internal_function_info;
409412

410413
struct _zend_op_array {

Zend/zend_inheritance.c

Lines changed: 100 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919

2020
#include "zend.h"
2121
#include "zend_API.h"
22+
#include "zend_language_scanner.h"
23+
#include "zend_language_scanner_defs.h"
24+
#include "zend_language_parser.h"
2225
#include "zend_compile.h"
2326
#include "zend_execute.h"
2427
#include "zend_inheritance.h"
@@ -293,6 +296,37 @@ static zend_bool unlinked_instanceof(zend_class_entry *ce1, zend_class_entry *ce
293296
return 0;
294297
}
295298

299+
static void append_parameter_default_value(smart_str str, zval *zv) {
300+
if (Z_TYPE_P(zv) == IS_FALSE) {
301+
smart_str_appends(&str, "false");
302+
} else if (Z_TYPE_P(zv) == IS_TRUE) {
303+
smart_str_appends(&str, "true");
304+
} else if (Z_TYPE_P(zv) == IS_NULL) {
305+
smart_str_appends(&str, "NULL");
306+
} else if (Z_TYPE_P(zv) == IS_STRING) {
307+
smart_str_appendc(&str, '\'');
308+
smart_str_appendl(&str, Z_STRVAL_P(zv), MIN(Z_STRLEN_P(zv), 10));
309+
if (Z_STRLEN_P(zv) > 10) {
310+
smart_str_appends(&str, "...");
311+
}
312+
smart_str_appendc(&str, '\'');
313+
} else if (Z_TYPE_P(zv) == IS_ARRAY) {
314+
smart_str_appends(&str, "Array");
315+
} else if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
316+
zend_ast *ast = Z_ASTVAL_P(zv);
317+
if (ast->kind == ZEND_AST_CONSTANT) {
318+
smart_str_append(&str, zend_ast_get_constant_name(ast));
319+
} else {
320+
smart_str_appends(&str, "<expression>");
321+
}
322+
} else {
323+
zend_string *tmp_zv_str;
324+
zend_string *zv_str = zval_get_tmp_string(zv, &tmp_zv_str);
325+
smart_str_append(&str, zv_str);
326+
zend_tmp_string_release(tmp_zv_str);
327+
}
328+
}
329+
296330
/* Unresolved means that class declarations that are currently not available are needed to
297331
* determine whether the inheritance is valid or not. At runtime UNRESOLVED should be treated
298332
* as an ERROR. */
@@ -594,38 +628,78 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function
594628
}
595629
if (precv && precv->opcode == ZEND_RECV_INIT && precv->op2_type != IS_UNUSED) {
596630
zval *zv = RT_CONSTANT(precv, precv->op2);
631+
append_parameter_default_value(str, zv);
632+
}
633+
} else {
634+
zend_internal_arg_info *internal_arginfo = (zend_internal_arg_info*) arg_info;
635+
if (internal_arginfo->default_value) {
636+
zval code_zv;
637+
zend_bool original_in_compilation;
638+
uint32_t original_compiler_options;
639+
zend_lex_state original_lex_state;
640+
zend_ast *ast;
641+
zend_arena *ast_arena;
642+
643+
size_t str1_length = strlen("<?php ");
644+
size_t str2_length = strlen(internal_arginfo->default_value);
645+
size_t str3_length = strlen(";");
646+
size_t len = str1_length + str2_length + str3_length;
647+
zend_string *code = zend_string_alloc(len, 0);
648+
649+
memcpy(ZSTR_VAL(code), "<?php ", str1_length);
650+
memcpy(ZSTR_VAL(code) + str1_length, internal_arginfo->default_value, str2_length);
651+
memcpy(ZSTR_VAL(code) + str1_length + str2_length, ";", str3_length);
652+
ZSTR_VAL(code)[len] = '\0';
653+
ZVAL_STR_COPY(&code_zv, code);
654+
655+
original_in_compilation = CG(in_compilation);
656+
original_compiler_options = CG(compiler_options);
657+
zend_save_lexical_state(&original_lex_state);
658+
659+
CG(in_compilation) = 1;
660+
CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION;
661+
662+
if (zend_prepare_string_for_scanning(&code_zv, "") == SUCCESS) {
663+
CG(ast) = NULL;
664+
CG(ast_arena) = zend_arena_create(1024 * 32);
665+
LANG_SCNG(yy_state) = yycINITIAL;
666+
667+
if (zendparse() != 0) {
668+
zend_ast_destroy(CG(ast));
669+
zend_arena_destroy(CG(ast_arena));
670+
CG(ast) = NULL;
671+
}
672+
}
597673

598-
if (Z_TYPE_P(zv) == IS_FALSE) {
599-
smart_str_appends(&str, "false");
600-
} else if (Z_TYPE_P(zv) == IS_TRUE) {
601-
smart_str_appends(&str, "true");
602-
} else if (Z_TYPE_P(zv) == IS_NULL) {
603-
smart_str_appends(&str, "NULL");
604-
} else if (Z_TYPE_P(zv) == IS_STRING) {
605-
smart_str_appendc(&str, '\'');
606-
smart_str_appendl(&str, Z_STRVAL_P(zv), MIN(Z_STRLEN_P(zv), 10));
607-
if (Z_STRLEN_P(zv) > 10) {
608-
smart_str_appends(&str, "...");
609-
}
610-
smart_str_appendc(&str, '\'');
611-
} else if (Z_TYPE_P(zv) == IS_ARRAY) {
612-
smart_str_appends(&str, "Array");
613-
} else if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
614-
zend_ast *ast = Z_ASTVAL_P(zv);
615-
if (ast->kind == ZEND_AST_CONSTANT) {
616-
smart_str_append(&str, zend_ast_get_constant_name(ast));
674+
ast = CG(ast);
675+
ast_arena = CG(ast_arena);
676+
677+
if (ast) {
678+
zend_ast_list *statement_list = zend_ast_get_list(ast);
679+
zend_ast *const_expression_ast = statement_list->child[0];
680+
if (const_expression_ast) {
681+
zval zv;
682+
zend_const_expr_to_zval(&zv, const_expression_ast);
683+
append_parameter_default_value(str, &zv);
684+
zval_dtor(&zv);
617685
} else {
618-
smart_str_appends(&str, "<expression>");
686+
smart_str_appends(&str, "<default>");
619687
}
620688
} else {
621-
zend_string *tmp_zv_str;
622-
zend_string *zv_str = zval_get_tmp_string(zv, &tmp_zv_str);
623-
smart_str_append(&str, zv_str);
624-
zend_tmp_string_release(tmp_zv_str);
689+
smart_str_appends(&str, "<default>");
625690
}
691+
692+
zend_restore_lexical_state(&original_lex_state);
693+
CG(compiler_options) = original_compiler_options;
694+
CG(in_compilation) = original_in_compilation;
695+
696+
zval_dtor(&code_zv);
697+
zend_string_free(code);
698+
zend_ast_destroy(ast);
699+
zend_arena_destroy(ast_arena);
700+
} else {
701+
smart_str_appends(&str, "<default>");
626702
}
627-
} else {
628-
smart_str_appends(&str, "NULL");
629703
}
630704
}
631705

0 commit comments

Comments
 (0)