From 7a52bf21d358567980bc20a95cef730bc9e9f00d Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 9 Jan 2020 22:51:15 +0100 Subject: [PATCH] Add ReflectionProperty::getDefaultValue and ReflectionProperty::hasDefaultValue --- ext/reflection/php_reflection.c | 86 +++++++++++++++++++ ext/reflection/reflection.stub.php | 5 ++ ext/reflection/reflection_arginfo.h | 5 ++ .../ReflectionProperty_getDefaultValue.phpt | 73 ++++++++++++++++ .../ReflectionProperty_hasDefaultValue.phpt | 60 +++++++++++++ 5 files changed, 229 insertions(+) create mode 100644 ext/reflection/tests/ReflectionProperty_getDefaultValue.phpt create mode 100644 ext/reflection/tests/ReflectionProperty_hasDefaultValue.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index d53862fba75..e1c41e5ec5e 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -5686,6 +5686,90 @@ ZEND_METHOD(reflection_property, hasType) } /* }}} */ +/* {{{ proto public bool ReflectionProperty::hasDefaultValue() + Returns whether property has a default value */ +ZEND_METHOD(reflection_property, hasDefaultValue) +{ + reflection_object *intern; + property_reference *ref; + zend_property_info *prop_info; + zval *prop; + zend_class_entry *ce; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(ref); + + prop_info = ref->prop; + + if (prop_info == NULL) { + RETURN_FALSE; + } + + ce = prop_info->ce; + + if ((prop_info->flags & ZEND_ACC_STATIC) != 0) { + prop = &ce->default_static_members_table[prop_info->offset]; + ZVAL_DEINDIRECT(prop); + } else { + prop = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]; + } + + RETURN_BOOL(!Z_ISUNDEF_P(prop)); +} +/* }}} */ + +/* {{{ proto public mixed ReflectionProperty::getDefaultValue() + Returns the default value of a property */ +ZEND_METHOD(reflection_property, getDefaultValue) +{ + reflection_object *intern; + property_reference *ref; + zend_property_info *prop_info; + zval *prop; + zend_class_entry *ce; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(ref); + + prop_info = ref->prop; + + if (prop_info == NULL) { + return; // throw exception? + } + + ce = prop_info->ce; + + if ((prop_info->flags & ZEND_ACC_STATIC) != 0) { + prop = &ce->default_static_members_table[prop_info->offset]; + ZVAL_DEINDIRECT(prop); + } else { + prop = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]; + } + + if (Z_ISUNDEF_P(prop)) { + return; + } + + /* copy: enforce read only access */ + ZVAL_DEREF(prop); + ZVAL_COPY_OR_DUP(return_value, prop); + + /* this is necessary to make it able to work with default array + * properties, returned to user */ + if (Z_TYPE_P(return_value) == IS_CONSTANT_AST) { + if (UNEXPECTED(zval_update_constant_ex(return_value, ce) != SUCCESS)) { + RETURN_THROWS(); + } + } +} +/* }}} */ + /* {{{ proto public static mixed ReflectionExtension::export(string name [, bool return]) throws ReflectionException Exports a reflection object. Returns the output if TRUE is specified for return, printing it otherwise. */ ZEND_METHOD(reflection_extension, export) @@ -6461,6 +6545,8 @@ static const zend_function_entry reflection_property_functions[] = { ZEND_ME(reflection_property, setAccessible, arginfo_class_ReflectionProperty_setAccessible, 0) ZEND_ME(reflection_property, getType, arginfo_class_ReflectionProperty_getType, 0) ZEND_ME(reflection_property, hasType, arginfo_class_ReflectionProperty_hasType, 0) + ZEND_ME(reflection_property, hasDefaultValue, arginfo_class_ReflectionProperty_hasDefaultValue, 0) + ZEND_ME(reflection_property, getDefaultValue, arginfo_class_ReflectionProperty_getDefaultValue, 0) PHP_FE_END }; diff --git a/ext/reflection/reflection.stub.php b/ext/reflection/reflection.stub.php index 11bb6c64338..26e38ae37f0 100644 --- a/ext/reflection/reflection.stub.php +++ b/ext/reflection/reflection.stub.php @@ -425,6 +425,11 @@ class ReflectionProperty implements Reflector /** @return bool */ public function hasType() {} + + public function hasDefaultValue(): bool {} + + /** @return mixed */ + public function getDefaultValue() {} } class ReflectionClassConstant implements Reflector diff --git a/ext/reflection/reflection_arginfo.h b/ext/reflection/reflection_arginfo.h index 078a793c9b6..68d61026b0e 100644 --- a/ext/reflection/reflection_arginfo.h +++ b/ext/reflection/reflection_arginfo.h @@ -343,6 +343,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_hasType arginfo_class_Reflector___toString +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionProperty_hasDefaultValue, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionProperty_getDefaultValue arginfo_class_Reflector___toString + #define arginfo_class_ReflectionClassConstant___clone arginfo_class_Reflector___toString #define arginfo_class_ReflectionClassConstant_export arginfo_class_ReflectionMethod_export diff --git a/ext/reflection/tests/ReflectionProperty_getDefaultValue.phpt b/ext/reflection/tests/ReflectionProperty_getDefaultValue.phpt new file mode 100644 index 00000000000..26614f5c602 --- /dev/null +++ b/ext/reflection/tests/ReflectionProperty_getDefaultValue.phpt @@ -0,0 +1,73 @@ +--TEST-- +reflection: ReflectionProperty::getDefaultValue +--FILE-- +getDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'bar'); +var_dump($property->getDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'static1'); +var_dump($property->getDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'static2'); +var_dump($property->getDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'val1'); +var_dump($property->getDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'val2'); +var_dump($property->getDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'nullable'); +var_dump($property->getDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'nullable2'); +var_dump($property->getDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'constantAst'); +var_dump($property->getDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'constantRuntimeAst'); +var_dump($property->getDefaultValue()); + +$test = new TestClass; +$test->dynamic = null; +$property = new ReflectionProperty($test, 'dynamic'); +var_dump($property->getDefaultValue()); + +?> +--EXPECT-- +NULL +string(3) "baz" +NULL +int(1234) +NULL +int(1234) +NULL +NULL +int(4) +int(42) +NULL diff --git a/ext/reflection/tests/ReflectionProperty_hasDefaultValue.phpt b/ext/reflection/tests/ReflectionProperty_hasDefaultValue.phpt new file mode 100644 index 00000000000..57090003707 --- /dev/null +++ b/ext/reflection/tests/ReflectionProperty_hasDefaultValue.phpt @@ -0,0 +1,60 @@ +--TEST-- +reflection: ReflectionProperty::hasDefaultValue +--FILE-- +hasDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'bar'); +var_dump($property->hasDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'static1'); +var_dump($property->hasDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'static2'); +var_dump($property->hasDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'val1'); +var_dump($property->hasDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'val2'); +var_dump($property->hasDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'nullable'); +var_dump($property->hasDefaultValue()); + +$property = new ReflectionProperty(TestClass::class, 'nullable2'); +var_dump($property->hasDefaultValue()); + +$test = new TestClass; +$test->dynamic = null; +$property = new ReflectionProperty($test, 'dynamic'); +var_dump($property->hasDefaultValue()); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false)