Skip to content

Commit 9a620a8

Browse files
committed
Support default values of parameters of internal functions in the Reflection extension
1 parent f6f2e66 commit 9a620a8

6 files changed

+385
-63
lines changed

ext/reflection/php_reflection.c

Lines changed: 221 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131

3232
#include "zend.h"
3333
#include "zend_API.h"
34+
#include "zend_language_scanner.h"
35+
#include "zend_language_scanner_defs.h"
36+
#include "zend_language_parser.h"
3437
#include "zend_exceptions.h"
3538
#include "zend_operators.h"
3639
#include "zend_constants.h"
@@ -149,6 +152,8 @@ typedef struct {
149152
zend_object zo;
150153
} reflection_object;
151154

155+
static int _reflection_param_get_default_arg_info(zend_internal_arg_info *arg_info, zval *default_value_zval);
156+
152157
static inline reflection_object *reflection_object_from_obj(zend_object *obj) {
153158
return (reflection_object*)((char*)(obj) - XtOffsetOf(reflection_object, zo));
154159
}
@@ -571,6 +576,31 @@ static zend_op* _get_recv_op(zend_op_array *op_array, uint32_t offset)
571576
}
572577
/* }}} */
573578

579+
static void append_parameter_default_value(smart_str *str, zval zv)
580+
{
581+
if (Z_TYPE(zv) == IS_TRUE) {
582+
smart_str_appends(str, "true");
583+
} else if (Z_TYPE(zv) == IS_FALSE) {
584+
smart_str_appends(str, "false");
585+
} else if (Z_TYPE(zv) == IS_NULL) {
586+
smart_str_appends(str, "NULL");
587+
} else if (Z_TYPE(zv) == IS_STRING) {
588+
smart_str_appendc(str, '\'');
589+
smart_str_appendl(str, Z_STRVAL(zv), MIN(Z_STRLEN(zv), 15));
590+
if (Z_STRLEN(zv) > 15) {
591+
smart_str_appends(str, "...");
592+
}
593+
smart_str_appendc(str, '\'');
594+
} else if (Z_TYPE(zv) == IS_ARRAY) {
595+
smart_str_appends(str, "Array");
596+
} else {
597+
zend_string *tmp_zv_str;
598+
zend_string *zv_str = zval_get_tmp_string(&zv, &tmp_zv_str);
599+
smart_str_append(str, zv_str);
600+
zend_tmp_string_release(tmp_zv_str);
601+
}
602+
}
603+
574604
/* {{{ _parameter_string */
575605
static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_arg_info *arg_info, uint32_t offset, zend_bool required, char* indent)
576606
{
@@ -600,39 +630,38 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
600630
} else {
601631
smart_str_append_printf(str, "$param%d", offset);
602632
}
603-
if (fptr->type == ZEND_USER_FUNCTION && !required) {
604-
zend_op *precv = _get_recv_op((zend_op_array*)fptr, offset);
605-
if (precv && precv->opcode == ZEND_RECV_INIT && precv->op2_type != IS_UNUSED) {
606-
zval zv;
633+
if (!required) {
634+
zval zv;
607635

608-
smart_str_appends(str, " = ");
609-
ZVAL_COPY(&zv, RT_CONSTANT(precv, precv->op2));
610-
if (UNEXPECTED(zval_update_constant_ex(&zv, fptr->common.scope) == FAILURE)) {
611-
zval_ptr_dtor(&zv);
612-
return;
636+
if (fptr->type == ZEND_USER_FUNCTION) {
637+
zend_op *precv = _get_recv_op((zend_op_array*)fptr, offset);
638+
if (precv && precv->opcode == ZEND_RECV_INIT && precv->op2_type != IS_UNUSED) {
639+
ZVAL_COPY(&zv, RT_CONSTANT(precv, precv->op2));
640+
if (UNEXPECTED(zval_update_constant_ex(&zv, fptr->common.scope) == FAILURE)) {
641+
zval_ptr_dtor(&zv);
642+
return;
643+
}
644+
645+
smart_str_appends(str, " = ");
646+
append_parameter_default_value(str, zv);
647+
zval_ptr_dtor(&zv);
613648
}
614-
if (Z_TYPE(zv) == IS_TRUE) {
615-
smart_str_appends(str, "true");
616-
} else if (Z_TYPE(zv) == IS_FALSE) {
617-
smart_str_appends(str, "false");
618-
} else if (Z_TYPE(zv) == IS_NULL) {
619-
smart_str_appends(str, "NULL");
620-
} else if (Z_TYPE(zv) == IS_STRING) {
621-
smart_str_appendc(str, '\'');
622-
smart_str_appendl(str, Z_STRVAL(zv), MIN(Z_STRLEN(zv), 15));
623-
if (Z_STRLEN(zv) > 15) {
624-
smart_str_appends(str, "...");
649+
} else {
650+
if (((zend_internal_arg_info*) arg_info)->default_value != NULL) {
651+
if (_reflection_param_get_default_arg_info((zend_internal_arg_info *) arg_info, &zv) == FAILURE) {
652+
zval_ptr_dtor(&zv);
653+
return;
625654
}
626-
smart_str_appendc(str, '\'');
627-
} else if (Z_TYPE(zv) == IS_ARRAY) {
628-
smart_str_appends(str, "Array");
629-
} else {
630-
zend_string *tmp_zv_str;
631-
zend_string *zv_str = zval_get_tmp_string(&zv, &tmp_zv_str);
632-
smart_str_append(str, zv_str);
633-
zend_tmp_string_release(tmp_zv_str);
655+
656+
if (UNEXPECTED(zval_update_constant_ex(&zv, fptr->common.scope) == FAILURE)) {
657+
zval_ptr_dtor(&zv);
658+
return;
659+
}
660+
661+
smart_str_appends(str, " = ");
662+
append_parameter_default_value(str, zv);
663+
zval_ptr_dtor(&zv);
634664
}
635-
zval_ptr_dtor(&zv);
636665
}
637666
}
638667
smart_str_appends(str, " ]");
@@ -1351,10 +1380,6 @@ static parameter_reference *_reflection_param_get_default_param(INTERNAL_FUNCTIO
13511380
}
13521381

13531382
param = intern->ptr;
1354-
if (param->fptr->type != ZEND_USER_FUNCTION) {
1355-
zend_throw_exception_ex(reflection_exception_ptr, 0, "Cannot determine default value for internal functions");
1356-
return NULL;
1357-
}
13581383

13591384
return param;
13601385
}
@@ -1379,6 +1404,82 @@ static zend_op *_reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAMETERS
13791404
}
13801405
/* }}} */
13811406

1407+
/* {{{ _reflection_param_get_default_arg_info */
1408+
static int _reflection_param_get_default_arg_info(zend_internal_arg_info *arg_info, zval *default_value_zval)
1409+
{
1410+
const char *default_value = arg_info->default_value;
1411+
if (default_value == NULL) {
1412+
zend_throw_exception_ex(reflection_exception_ptr, 0, "Internal error: Failed to retrieve the default value");
1413+
return FAILURE;
1414+
}
1415+
1416+
zval code_zv;
1417+
zend_bool original_in_compilation;
1418+
uint32_t original_compiler_options;
1419+
zend_lex_state original_lex_state;
1420+
zend_ast *ast;
1421+
zend_arena *ast_arena;
1422+
int success = FAILURE;
1423+
1424+
size_t str1_length = strlen("<?php ");
1425+
size_t str2_length = strlen(default_value);
1426+
size_t str3_length = strlen(";");
1427+
size_t len = str1_length + str2_length + str3_length;
1428+
zend_string *code = zend_string_alloc(len, 0);
1429+
1430+
memcpy(ZSTR_VAL(code), "<?php ", str1_length);
1431+
memcpy(ZSTR_VAL(code) + str1_length, default_value, str2_length);
1432+
memcpy(ZSTR_VAL(code) + str1_length + str2_length, ";", str3_length);
1433+
ZSTR_VAL(code)[len] = '\0';
1434+
ZVAL_STR_COPY(&code_zv, code);
1435+
1436+
original_in_compilation = CG(in_compilation);
1437+
original_compiler_options = CG(compiler_options);
1438+
zend_save_lexical_state(&original_lex_state);
1439+
1440+
CG(in_compilation) = 1;
1441+
CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION;
1442+
1443+
if (zend_prepare_string_for_scanning(&code_zv, "") == SUCCESS) {
1444+
CG(ast) = NULL;
1445+
CG(ast_arena) = zend_arena_create(1024 * 32);
1446+
LANG_SCNG(yy_state) = yycINITIAL;
1447+
1448+
if (zendparse() != 0) {
1449+
zend_ast_destroy(CG(ast));
1450+
zend_arena_destroy(CG(ast_arena));
1451+
CG(ast) = NULL;
1452+
}
1453+
}
1454+
1455+
ast = CG(ast);
1456+
ast_arena = CG(ast_arena);
1457+
1458+
if (ast) {
1459+
zend_ast_list *statement_list = zend_ast_get_list(ast);
1460+
zend_ast *const_expression_ast = statement_list->child[0];
1461+
if (const_expression_ast) {
1462+
zend_const_expr_to_zval(default_value_zval, const_expression_ast);
1463+
success = SUCCESS;
1464+
}
1465+
}
1466+
1467+
zend_restore_lexical_state(&original_lex_state);
1468+
CG(compiler_options) = original_compiler_options;
1469+
CG(in_compilation) = original_in_compilation;
1470+
1471+
zval_dtor(&code_zv);
1472+
zend_string_free(code);
1473+
zend_ast_destroy(ast);
1474+
zend_arena_destroy(ast_arena);
1475+
1476+
if (success == FAILURE) {
1477+
zend_throw_exception_ex(reflection_exception_ptr, 0, "Internal error: Failed to retrieve the default value");
1478+
}
1479+
1480+
return success;
1481+
}
1482+
13821483
/* {{{ Preventing __clone from being called */
13831484
ZEND_METHOD(reflection, __clone)
13841485
{
@@ -2667,22 +2768,24 @@ ZEND_METHOD(reflection_parameter, isDefaultValueAvailable)
26672768
{
26682769
reflection_object *intern;
26692770
parameter_reference *param;
2670-
zend_op *precv;
26712771

26722772
if (zend_parse_parameters_none() == FAILURE) {
26732773
return;
26742774
}
26752775
GET_REFLECTION_OBJECT_PTR(param);
26762776

2677-
if (param->fptr->type != ZEND_USER_FUNCTION)
2777+
if (param->fptr->type == ZEND_USER_FUNCTION)
2778+
{
2779+
zend_op *precv;
2780+
precv = _get_recv_op((zend_op_array*)param->fptr, param->offset);
2781+
if (!precv || precv->opcode != ZEND_RECV_INIT || precv->op2_type == IS_UNUSED) {
2782+
RETURN_FALSE;
2783+
}
2784+
} else if (((zend_internal_arg_info*) param->arg_info)->default_value == NULL)
26782785
{
26792786
RETURN_FALSE;
26802787
}
26812788

2682-
precv = _get_recv_op((zend_op_array*)param->fptr, param->offset);
2683-
if (!precv || precv->opcode != ZEND_RECV_INIT || precv->op2_type == IS_UNUSED) {
2684-
RETURN_FALSE;
2685-
}
26862789
RETURN_TRUE;
26872790
}
26882791
/* }}} */
@@ -2692,7 +2795,6 @@ ZEND_METHOD(reflection_parameter, isDefaultValueAvailable)
26922795
ZEND_METHOD(reflection_parameter, getDefaultValue)
26932796
{
26942797
parameter_reference *param;
2695-
zend_op *precv;
26962798

26972799
if (zend_parse_parameters_none() == FAILURE) {
26982800
return;
@@ -2703,14 +2805,29 @@ ZEND_METHOD(reflection_parameter, getDefaultValue)
27032805
return;
27042806
}
27052807

2706-
precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param);
2707-
if (!precv) {
2708-
return;
2709-
}
2808+
if (param->fptr->type == ZEND_USER_FUNCTION) {
2809+
zend_op *precv;
2810+
precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param);
2811+
if (!precv) {
2812+
return;
2813+
}
27102814

2711-
ZVAL_COPY(return_value, RT_CONSTANT(precv, precv->op2));
2712-
if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) {
2713-
zval_update_constant_ex(return_value, param->fptr->common.scope);
2815+
ZVAL_COPY(return_value, RT_CONSTANT(precv, precv->op2));
2816+
if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) {
2817+
zval_update_constant_ex(return_value, param->fptr->common.scope);
2818+
}
2819+
} else {
2820+
zval default_value_zval;
2821+
if (_reflection_param_get_default_arg_info((zend_internal_arg_info *) param->arg_info, &default_value_zval) == FAILURE) {
2822+
return;
2823+
}
2824+
2825+
ZVAL_COPY(return_value, &default_value_zval);
2826+
if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) {
2827+
zval_update_constant_ex(return_value, param->fptr->common.scope);
2828+
}
2829+
2830+
zval_dtor(&default_value_zval);
27142831
}
27152832
}
27162833
/* }}} */
@@ -2719,7 +2836,6 @@ ZEND_METHOD(reflection_parameter, getDefaultValue)
27192836
Returns whether the default value of this parameter is constant */
27202837
ZEND_METHOD(reflection_parameter, isDefaultValueConstant)
27212838
{
2722-
zend_op *precv;
27232839
parameter_reference *param;
27242840

27252841
if (zend_parse_parameters_none() == FAILURE) {
@@ -2731,14 +2847,33 @@ ZEND_METHOD(reflection_parameter, isDefaultValueConstant)
27312847
RETURN_FALSE;
27322848
}
27332849

2734-
precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param);
2735-
if (precv && Z_TYPE_P(RT_CONSTANT(precv, precv->op2)) == IS_CONSTANT_AST) {
2736-
zend_ast *ast = Z_ASTVAL_P(RT_CONSTANT(precv, precv->op2));
2850+
if (param->fptr->type == ZEND_USER_FUNCTION) {
2851+
zend_op *precv;
2852+
precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param);
2853+
if (precv && Z_TYPE_P(RT_CONSTANT(precv, precv->op2)) == IS_CONSTANT_AST) {
2854+
zend_ast *ast = NULL;
2855+
ast = Z_ASTVAL_P(RT_CONSTANT(precv, precv->op2));
2856+
2857+
if (ast->kind == ZEND_AST_CONSTANT || ast->kind == ZEND_AST_CONSTANT_CLASS) {
2858+
zend_ast_destroy(ast);
2859+
RETURN_TRUE;
2860+
}
2861+
}
2862+
} else {
2863+
zval default_value_zval;
2864+
if (_reflection_param_get_default_arg_info((zend_internal_arg_info *) param->arg_info, &default_value_zval) == FAILURE) {
2865+
return;
2866+
}
27372867

2738-
if (ast->kind == ZEND_AST_CONSTANT
2739-
|| ast->kind == ZEND_AST_CONSTANT_CLASS) {
2740-
RETURN_TRUE;
2868+
if (Z_TYPE(default_value_zval) == IS_CONSTANT_AST) {
2869+
zend_ast *ast = Z_ASTVAL(default_value_zval);
2870+
if (ast->kind == ZEND_AST_CONSTANT) {
2871+
zval_dtor(&default_value_zval);
2872+
RETURN_TRUE;
2873+
}
27412874
}
2875+
2876+
zval_dtor(&default_value_zval);
27422877
}
27432878

27442879
RETURN_FALSE;
@@ -2749,7 +2884,6 @@ ZEND_METHOD(reflection_parameter, isDefaultValueConstant)
27492884
Returns the default value's constant name if default value is constant or null */
27502885
ZEND_METHOD(reflection_parameter, getDefaultValueConstantName)
27512886
{
2752-
zend_op *precv;
27532887
parameter_reference *param;
27542888

27552889
if (zend_parse_parameters_none() == FAILURE) {
@@ -2761,14 +2895,38 @@ ZEND_METHOD(reflection_parameter, getDefaultValueConstantName)
27612895
return;
27622896
}
27632897

2764-
precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param);
2765-
if (precv && Z_TYPE_P(RT_CONSTANT(precv, precv->op2)) == IS_CONSTANT_AST) {
2766-
zend_ast *ast = Z_ASTVAL_P(RT_CONSTANT(precv, precv->op2));
2898+
if (param->fptr->type == ZEND_USER_FUNCTION) {
2899+
zend_op *precv;
2900+
precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param);
2901+
if (precv && Z_TYPE_P(RT_CONSTANT(precv, precv->op2)) == IS_CONSTANT_AST) {
2902+
zend_ast *ast = Z_ASTVAL_P(RT_CONSTANT(precv, precv->op2));
2903+
2904+
if (ast->kind == ZEND_AST_CONSTANT) {
2905+
RETURN_STR_COPY(zend_ast_get_constant_name(ast));
2906+
} else if (ast->kind == ZEND_AST_CONSTANT_CLASS) {
2907+
RETURN_STRINGL("__CLASS__", sizeof("__CLASS__")-1);
2908+
}
2909+
}
2910+
} else {
2911+
zval default_value_zval;
2912+
if (_reflection_param_get_default_arg_info((zend_internal_arg_info *) param->arg_info, &default_value_zval) == FAILURE) {
2913+
return;
2914+
}
2915+
2916+
if (Z_TYPE(default_value_zval) == IS_CONSTANT_AST) {
2917+
zend_ast *ast = Z_ASTVAL(default_value_zval);
2918+
if (ast->kind == ZEND_AST_CONSTANT) {
2919+
zend_string *name = zend_ast_get_constant_name(ast);
2920+
zval_dtor(&default_value_zval);
2921+
zend_ast_destroy(ast);
2922+
2923+
RETURN_STR_COPY(name);
2924+
} else if (ast->kind == ZEND_AST_CONSTANT_CLASS) {
2925+
zval_dtor(&default_value_zval);
2926+
zend_ast_destroy(ast);
27672927

2768-
if (ast->kind == ZEND_AST_CONSTANT) {
2769-
RETURN_STR_COPY(zend_ast_get_constant_name(ast));
2770-
} else if (ast->kind == ZEND_AST_CONSTANT_CLASS) {
2771-
RETURN_STRINGL("__CLASS__", sizeof("__CLASS__")-1);
2928+
RETURN_STRINGL("__CLASS__", sizeof("__CLASS__")-1);
2929+
}
27722930
}
27732931
}
27742932
}
@@ -4281,7 +4439,7 @@ ZEND_METHOD(reflection_class, getProperties)
42814439
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l!", &filter, &filter_is_null) == FAILURE) {
42824440
return;
42834441
}
4284-
4442+
42854443
if (filter_is_null) {
42864444
filter = ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC;
42874445
}

0 commit comments

Comments
 (0)