summaryrefslogtreecommitdiff
path: root/ext/reflection/php_reflection.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/reflection/php_reflection.c')
-rw-r--r--ext/reflection/php_reflection.c252
1 files changed, 168 insertions, 84 deletions
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 6b9ca5b595..5f7f5270a1 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -558,8 +558,7 @@ static void _class_const_string(smart_str *str, char *name, zend_class_constant
}
/* }}} */
-/* {{{ _get_recv_opcode */
-static zend_op* _get_recv_op(zend_op_array *op_array, uint32_t offset)
+static zend_op *get_recv_op(zend_op_array *op_array, uint32_t offset)
{
zend_op *op = op_array->opcodes;
zend_op *end = op + op_array->last;
@@ -573,9 +572,18 @@ static zend_op* _get_recv_op(zend_op_array *op_array, uint32_t offset)
}
++op;
}
+ ZEND_ASSERT(0 && "Failed to find op");
return NULL;
}
-/* }}} */
+
+static zval *get_default_from_recv(zend_op_array *op_array, uint32_t offset) {
+ zend_op *recv = get_recv_op(op_array, offset);
+ if (!recv || recv->opcode != ZEND_RECV_INIT) {
+ return NULL;
+ }
+
+ return RT_CONSTANT(recv, recv->op2);
+}
static int format_default_value(smart_str *str, zval *value, zend_class_entry *scope) {
zval zv;
@@ -639,12 +647,21 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
smart_str_append_printf(str, "$%s", has_internal_arg_info(fptr)
? ((zend_internal_arg_info*)arg_info)->name : ZSTR_VAL(arg_info->name));
- if (fptr->type == ZEND_USER_FUNCTION && !required) {
- zend_op *precv = _get_recv_op((zend_op_array*)fptr, offset);
- if (precv && precv->opcode == ZEND_RECV_INIT && precv->op2_type != IS_UNUSED) {
+ if (!required) {
+ if (fptr->type == ZEND_INTERNAL_FUNCTION) {
smart_str_appends(str, " = ");
- if (format_default_value(str, RT_CONSTANT(precv, precv->op2), fptr->common.scope) == FAILURE) {
- return;
+ if (((zend_internal_arg_info*)arg_info)->default_value) {
+ smart_str_appends(str, ((zend_internal_arg_info*)arg_info)->default_value);
+ } else {
+ smart_str_appends(str, "<default>");
+ }
+ } else {
+ zval *default_value = get_default_from_recv((zend_op_array*)fptr, offset);
+ if (default_value) {
+ smart_str_appends(str, " = ");
+ if (format_default_value(str, default_value, fptr->common.scope) == FAILURE) {
+ return;
+ }
}
}
}
@@ -1275,49 +1292,116 @@ static void reflection_class_constant_factory(zend_string *name_str, zend_class_
}
/* }}} */
-/* {{{ _reflection_param_get_default_param */
-static parameter_reference *_reflection_param_get_default_param(INTERNAL_FUNCTION_PARAMETERS)
-{
- reflection_object *intern;
- parameter_reference *param;
+static int get_default_via_ast(zval *default_value_zval, const char *default_value) {
+ zend_ast *ast;
+ zend_arena *ast_arena;
- intern = Z_REFLECTION_P(ZEND_THIS);
- if (intern->ptr == NULL) {
- if (EG(exception) && EG(exception)->ce == reflection_exception_ptr) {
- return NULL;
- }
- zend_throw_error(NULL, "Internal error: Failed to retrieve the reflection object");
- return NULL;
+ smart_str code = {0};
+ smart_str_appends(&code, "<?php ");
+ smart_str_appends(&code, default_value);
+ smart_str_appendc(&code, ';');
+ smart_str_0(&code);
+
+ ast = zend_compile_string_to_ast(code.s, &ast_arena, "");
+ smart_str_free(&code);
+
+ if (!ast) {
+ return FAILURE;
}
- param = intern->ptr;
- if (param->fptr->type != ZEND_USER_FUNCTION) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Cannot determine default value for internal functions");
- return NULL;
+ zend_ast_list *statement_list = zend_ast_get_list(ast);
+ zend_ast *const_expression_ast = statement_list->child[0];
+
+ zend_arena *original_ast_arena = CG(ast_arena);
+ uint32_t original_compiler_options = CG(compiler_options);
+ zend_file_context original_file_context;
+ CG(ast_arena) = ast_arena;
+ /* Disable constant substitution, to make getDefaultValueConstant() work. */
+ CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION;
+ zend_file_context_begin(&original_file_context);
+ zend_const_expr_to_zval(default_value_zval, const_expression_ast);
+ CG(ast_arena) = original_ast_arena;
+ CG(compiler_options) = original_compiler_options;
+ zend_file_context_end(&original_file_context);
+
+ zend_ast_destroy(ast);
+ zend_arena_destroy(ast_arena);
+
+ return SUCCESS;
+}
+
+static zend_string *try_parse_string(const char *str, size_t len, char quote) {
+ if (len == 0) {
+ return ZSTR_EMPTY_ALLOC();
}
- return param;
+ for (size_t i = 0; i < len; i++) {
+ if (str[i] == '\\' || str[i] == quote) {
+ return NULL;
+ }
+ }
+ return zend_string_init(str, len, 0);
}
-/* }}} */
-/* {{{ _reflection_param_get_default_precv */
-static zend_op *_reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAMETERS, parameter_reference *param)
+static int get_default_from_arg_info(zval *default_value_zval, zend_internal_arg_info *arg_info)
{
- zend_op *precv;
-
- if (param == NULL) {
- return NULL;
+ const char *default_value = arg_info->default_value;
+ if (!default_value) {
+ return FAILURE;
}
- precv = _get_recv_op((zend_op_array*)param->fptr, param->offset);
- if (!precv || precv->opcode != ZEND_RECV_INIT || precv->op2_type == IS_UNUSED) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Internal error: Failed to retrieve the default value");
- return NULL;
+ /* Avoid going through the full AST machinery for some simple and common cases. */
+ size_t default_value_len = strlen(default_value);
+ zend_ulong lval;
+ if (default_value_len == sizeof("null")-1
+ && !memcmp(default_value, "null", sizeof("null")-1)) {
+ ZVAL_NULL(default_value_zval);
+ return SUCCESS;
+ } else if (default_value_len == sizeof("true")-1
+ && !memcmp(default_value, "true", sizeof("true")-1)) {
+ ZVAL_TRUE(default_value_zval);
+ return SUCCESS;
+ } else if (default_value_len == sizeof("false")-1
+ && !memcmp(default_value, "false", sizeof("false")-1)) {
+ ZVAL_FALSE(default_value_zval);
+ return SUCCESS;
+ } else if (default_value_len >= 2
+ && (default_value[0] == '\'' || default_value[0] == '"')
+ && default_value[default_value_len - 1] == default_value[0]) {
+ zend_string *str = try_parse_string(
+ default_value + 1, default_value_len - 2, default_value[0]);
+ if (str) {
+ ZVAL_STR(default_value_zval, str);
+ return SUCCESS;
+ }
+ } else if (default_value_len == sizeof("[]")-1
+ && !memcmp(default_value, "[]", sizeof("[]")-1)) {
+ ZVAL_EMPTY_ARRAY(default_value_zval);
+ return SUCCESS;
+ } else if (ZEND_HANDLE_NUMERIC_STR(default_value, default_value_len, lval)) {
+ ZVAL_LONG(default_value_zval, lval);
+ return SUCCESS;
}
- return precv;
+#if 0
+ fprintf(stderr, "Evaluating %s via AST\n", default_value);
+#endif
+ return get_default_via_ast(default_value_zval, default_value);
+}
+
+static int get_parameter_default(zval *result, parameter_reference *param) {
+ if (param->fptr->type == ZEND_INTERNAL_FUNCTION) {
+ return get_default_from_arg_info(result, (zend_internal_arg_info*) param->arg_info);
+ } else {
+ zval *default_value = get_default_from_recv((zend_op_array *) param->fptr, param->offset);
+ if (!default_value) {
+ return FAILURE;
+ }
+
+ ZVAL_COPY(result, default_value);
+ return SUCCESS;
+ }
}
-/* }}} */
/* {{{ Preventing __clone from being called */
ZEND_METHOD(reflection, __clone)
@@ -2612,23 +2696,19 @@ ZEND_METHOD(reflection_parameter, isDefaultValueAvailable)
{
reflection_object *intern;
parameter_reference *param;
- zend_op *precv;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
- GET_REFLECTION_OBJECT_PTR(param);
- if (param->fptr->type != ZEND_USER_FUNCTION)
- {
- RETURN_FALSE;
- }
+ GET_REFLECTION_OBJECT_PTR(param);
- precv = _get_recv_op((zend_op_array*)param->fptr, param->offset);
- if (!precv || precv->opcode != ZEND_RECV_INIT || precv->op2_type == IS_UNUSED) {
- RETURN_FALSE;
+ if (param->fptr->type == ZEND_INTERNAL_FUNCTION) {
+ RETURN_BOOL(((zend_internal_arg_info*) (param->arg_info))->default_value);
+ } else {
+ zval *default_value = get_default_from_recv((zend_op_array *)param->fptr, param->offset);
+ RETURN_BOOL(default_value != NULL);
}
- RETURN_TRUE;
}
/* }}} */
@@ -2636,24 +2716,21 @@ ZEND_METHOD(reflection_parameter, isDefaultValueAvailable)
Returns the default value of this parameter or throws an exception */
ZEND_METHOD(reflection_parameter, getDefaultValue)
{
+ reflection_object *intern;
parameter_reference *param;
- zend_op *precv;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
- param = _reflection_param_get_default_param(INTERNAL_FUNCTION_PARAM_PASSTHRU);
- if (!param) {
- return;
- }
+ GET_REFLECTION_OBJECT_PTR(param);
- precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param);
- if (!precv) {
- return;
+ if (get_parameter_default(return_value, param) == FAILURE) {
+ zend_throw_exception_ex(reflection_exception_ptr, 0,
+ "Internal error: Failed to retrieve the default value");
+ RETURN_THROWS();
}
- ZVAL_COPY(return_value, RT_CONSTANT(precv, precv->op2));
if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) {
zval_update_constant_ex(return_value, param->fptr->common.scope);
}
@@ -2664,29 +2741,29 @@ ZEND_METHOD(reflection_parameter, getDefaultValue)
Returns whether the default value of this parameter is constant */
ZEND_METHOD(reflection_parameter, isDefaultValueConstant)
{
- zend_op *precv;
+ reflection_object *intern;
parameter_reference *param;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
- param = _reflection_param_get_default_param(INTERNAL_FUNCTION_PARAM_PASSTHRU);
- if (!param) {
- RETURN_FALSE;
- }
-
- precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param);
- if (precv && Z_TYPE_P(RT_CONSTANT(precv, precv->op2)) == IS_CONSTANT_AST) {
- zend_ast *ast = Z_ASTVAL_P(RT_CONSTANT(precv, precv->op2));
+ GET_REFLECTION_OBJECT_PTR(param);
- if (ast->kind == ZEND_AST_CONSTANT
- || ast->kind == ZEND_AST_CONSTANT_CLASS) {
- RETURN_TRUE;
- }
+ zval default_value;
+ if (get_parameter_default(&default_value, param) == FAILURE) {
+ zend_throw_exception_ex(reflection_exception_ptr, 0,
+ "Internal error: Failed to retrieve the default value");
+ RETURN_THROWS();
}
- RETURN_FALSE;
+ if (Z_TYPE(default_value) == IS_CONSTANT_AST) {
+ zend_ast *ast = Z_ASTVAL(default_value);
+ RETVAL_BOOL(ast->kind == ZEND_AST_CONSTANT || ast->kind == ZEND_AST_CONSTANT_CLASS);
+ zval_ptr_dtor_nogc(&default_value);
+ } else {
+ RETURN_FALSE;
+ }
}
/* }}} */
@@ -2694,30 +2771,37 @@ ZEND_METHOD(reflection_parameter, isDefaultValueConstant)
Returns the default value's constant name if default value is constant or null */
ZEND_METHOD(reflection_parameter, getDefaultValueConstantName)
{
- zend_op *precv;
+ reflection_object *intern;
parameter_reference *param;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
- param = _reflection_param_get_default_param(INTERNAL_FUNCTION_PARAM_PASSTHRU);
- if (!param) {
- return;
+ GET_REFLECTION_OBJECT_PTR(param);
+
+ zval default_value;
+ if (get_parameter_default(&default_value, param) == FAILURE) {
+ zend_throw_exception_ex(reflection_exception_ptr, 0,
+ "Internal error: Failed to retrieve the default value");
+ RETURN_THROWS();
}
- precv = _reflection_param_get_default_precv(INTERNAL_FUNCTION_PARAM_PASSTHRU, param);
- if (precv && Z_TYPE_P(RT_CONSTANT(precv, precv->op2)) == IS_CONSTANT_AST) {
- zend_ast *ast = Z_ASTVAL_P(RT_CONSTANT(precv, precv->op2));
+ if (Z_TYPE(default_value) != IS_CONSTANT_AST) {
+ zval_ptr_dtor_nogc(&default_value);
+ RETURN_NULL();
+ }
- if (ast->kind == ZEND_AST_CONSTANT) {
- RETURN_STR_COPY(zend_ast_get_constant_name(ast));
- } else if (ast->kind == ZEND_AST_CONSTANT_CLASS) {
- RETURN_STRINGL("__CLASS__", sizeof("__CLASS__")-1);
- }
+ zend_ast *ast = Z_ASTVAL(default_value);
+ if (ast->kind == ZEND_AST_CONSTANT) {
+ RETVAL_STR_COPY(zend_ast_get_constant_name(ast));
+ } else if (ast->kind == ZEND_AST_CONSTANT_CLASS) {
+ RETVAL_STRINGL("__CLASS__", sizeof("__CLASS__")-1);
+ } else {
+ RETVAL_NULL();
}
+ zval_ptr_dtor_nogc(&default_value);
}
-/* }}} */
/* {{{ proto public bool ReflectionParameter::isVariadic()
Returns whether this parameter is a variadic parameter */