summaryrefslogtreecommitdiff
path: root/ext/reflection
diff options
context:
space:
mode:
Diffstat (limited to 'ext/reflection')
-rw-r--r--ext/reflection/php_reflection.c125
-rw-r--r--ext/reflection/tests/ReflectionClass_setStaticPropertyValue_002.phpt2
-rw-r--r--ext/reflection/tests/ReflectionClass_setStaticPropertyValue_003.phpt39
-rw-r--r--ext/reflection/tests/ReflectionProperty_isInitialized.phpt113
-rw-r--r--ext/reflection/tests/ReflectionProperty_typed_static.phpt51
-rw-r--r--ext/reflection/tests/ReflectionType_001.phpt37
6 files changed, 360 insertions, 7 deletions
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 105a87da8b..323a4d2fa4 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -2855,8 +2855,10 @@ ZEND_METHOD(reflection_type, isBuiltin)
/* {{{ reflection_type_name */
static zend_string *reflection_type_name(type_reference *param) {
- if (ZEND_TYPE_IS_CLASS(param->type)) {
+ if (ZEND_TYPE_IS_NAME(param->type)) {
return zend_string_copy(ZEND_TYPE_NAME(param->type));
+ } else if (ZEND_TYPE_IS_CE(param->type)) {
+ return zend_string_copy(ZEND_TYPE_CE(param->type)->name);
} else {
char *name = zend_get_type_by_const(ZEND_TYPE_CODE(param->type));
return zend_string_init(name, strlen(name), 0);
@@ -3809,7 +3811,7 @@ ZEND_METHOD(reflection_class, getStaticPropertyValue)
if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) {
return;
}
- prop = zend_std_get_static_property(ce, name, 1);
+ prop = zend_std_get_static_property(ce, name, BP_VAR_IS);
if (!prop) {
if (def_value) {
ZVAL_COPY(return_value, def_value);
@@ -3830,6 +3832,7 @@ ZEND_METHOD(reflection_class, setStaticPropertyValue)
{
reflection_object *intern;
zend_class_entry *ce;
+ zend_property_info *prop_info;
zend_string *name;
zval *variable_ptr, *value;
@@ -3842,15 +3845,30 @@ ZEND_METHOD(reflection_class, setStaticPropertyValue)
if (UNEXPECTED(zend_update_class_constants(ce) != SUCCESS)) {
return;
}
- variable_ptr = zend_std_get_static_property(ce, name, 1);
+ variable_ptr = zend_std_get_static_property_with_info(ce, name, BP_VAR_W, &prop_info);
if (!variable_ptr) {
+ zend_clear_exception();
zend_throw_exception_ex(reflection_exception_ptr, 0,
"Class %s does not have a property named %s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
return;
}
- ZVAL_DEREF(variable_ptr);
+
+ if (Z_ISREF_P(variable_ptr)) {
+ zend_reference *ref = Z_REF_P(variable_ptr);
+ variable_ptr = Z_REFVAL_P(variable_ptr);
+
+ if (!zend_verify_ref_assignable_zval(ref, value, 0)) {
+ return;
+ }
+ }
+
+ if (prop_info->type && !zend_verify_property_type(prop_info, value, 0)) {
+ return;
+ }
+
zval_ptr_dtor(variable_ptr);
ZVAL_COPY(variable_ptr, value);
+
}
/* }}} */
@@ -5463,6 +5481,55 @@ ZEND_METHOD(reflection_property, setValue)
}
/* }}} */
+/* {{{ proto public mixed ReflectionProperty::isInitialized([stdclass object])
+ Returns this property's value */
+ZEND_METHOD(reflection_property, isInitialized)
+{
+ reflection_object *intern;
+ property_reference *ref;
+ zval *object, *name;
+ zval *member_p = NULL;
+
+ GET_REFLECTION_OBJECT_PTR(ref);
+
+ if (!(ref->prop.flags & ZEND_ACC_PUBLIC) && intern->ignore_visibility == 0) {
+ name = _default_load_name(getThis());
+ zend_throw_exception_ex(reflection_exception_ptr, 0,
+ "Cannot access non-public member %s::$%s", ZSTR_VAL(intern->ce->name), Z_STRVAL_P(name));
+ return;
+ }
+
+ if (ref->prop.flags & ZEND_ACC_STATIC) {
+ member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, 1);
+ if (member_p) {
+ RETURN_BOOL(!Z_ISUNDEF_P(member_p))
+ }
+ RETURN_FALSE;
+ } else {
+ zval name_zv;
+ zend_class_entry *old_scope;
+ int retval;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &object) == FAILURE) {
+ return;
+ }
+
+ if (!instanceof_function(Z_OBJCE_P(object), ref->prop.ce)) {
+ _DO_THROW("Given object is not an instance of the class this property was declared in");
+ /* Returns from this function */
+ }
+
+ old_scope = EG(fake_scope);
+ EG(fake_scope) = intern->ce;
+ ZVAL_STR(&name_zv, ref->unmangled_name);
+ retval = Z_OBJ_HT_P(object)->has_property(object, &name_zv, ZEND_PROPERTY_EXISTS, NULL);
+ EG(fake_scope) = old_scope;
+
+ RETVAL_BOOL(retval);
+ }
+}
+/* }}} */
+
/* {{{ proto public ReflectionClass ReflectionProperty::getDeclaringClass()
Get the declaring class */
ZEND_METHOD(reflection_property, getDeclaringClass)
@@ -5530,6 +5597,44 @@ ZEND_METHOD(reflection_property, setAccessible)
}
/* }}} */
+/* {{{ proto public ReflectionType ReflectionProperty::getType()
+ Returns the type associated with the property */
+ZEND_METHOD(reflection_property, getType)
+{
+ reflection_object *intern;
+ property_reference *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ GET_REFLECTION_OBJECT_PTR(ref);
+
+ if (!ZEND_TYPE_IS_SET(ref->prop.type)) {
+ RETURN_NULL();
+ }
+
+ reflection_type_factory(ref->prop.type, return_value);
+}
+/* }}} */
+
+/* {{{ proto public bool ReflectionProperty::hasType()
+ Returns whether property has a type */
+ZEND_METHOD(reflection_property, hasType)
+{
+ reflection_object *intern;
+ property_reference *ref;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ GET_REFLECTION_OBJECT_PTR(ref);
+
+ RETVAL_BOOL(ZEND_TYPE_IS_SET(ref->prop.type));
+}
+/* }}} */
+
/* {{{ 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)
@@ -6367,6 +6472,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_property_setValue, 0, 0, 1)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_property_isInitialized, 0, 0, 0)
+ ZEND_ARG_INFO(0, object)
+ZEND_END_ARG_INFO()
+
ZEND_BEGIN_ARG_INFO(arginfo_reflection_property_setAccessible, 0)
ZEND_ARG_INFO(0, visible)
ZEND_END_ARG_INFO()
@@ -6378,6 +6487,7 @@ static const zend_function_entry reflection_property_functions[] = {
ZEND_ME(reflection_property, getName, arginfo_reflection__void, 0)
ZEND_ME(reflection_property, getValue, arginfo_reflection_property_getValue, 0)
ZEND_ME(reflection_property, setValue, arginfo_reflection_property_setValue, 0)
+ ZEND_ME(reflection_property, isInitialized, arginfo_reflection_property_isInitialized, 0)
ZEND_ME(reflection_property, isPublic, arginfo_reflection__void, 0)
ZEND_ME(reflection_property, isPrivate, arginfo_reflection__void, 0)
ZEND_ME(reflection_property, isProtected, arginfo_reflection__void, 0)
@@ -6387,6 +6497,8 @@ static const zend_function_entry reflection_property_functions[] = {
ZEND_ME(reflection_property, getDeclaringClass, arginfo_reflection__void, 0)
ZEND_ME(reflection_property, getDocComment, arginfo_reflection__void, 0)
ZEND_ME(reflection_property, setAccessible, arginfo_reflection_property_setAccessible, 0)
+ ZEND_ME(reflection_property, getType, arginfo_reflection__void, 0)
+ ZEND_ME(reflection_property, hasType, arginfo_reflection__void, 0)
PHP_FE_END
};
@@ -6516,7 +6628,7 @@ static const zend_function_entry reflection_ext_functions[] = { /* {{{ */
}; /* }}} */
/* {{{ _reflection_write_property */
-static void _reflection_write_property(zval *object, zval *member, zval *value, void **cache_slot)
+static zval *_reflection_write_property(zval *object, zval *member, zval *value, void **cache_slot)
{
if ((Z_TYPE_P(member) == IS_STRING)
&& zend_hash_exists(&Z_OBJCE_P(object)->properties_info, Z_STR_P(member))
@@ -6525,10 +6637,11 @@ static void _reflection_write_property(zval *object, zval *member, zval *value,
{
zend_throw_exception_ex(reflection_exception_ptr, 0,
"Cannot set read-only property %s::$%s", ZSTR_VAL(Z_OBJCE_P(object)->name), Z_STRVAL_P(member));
+ return &EG(uninitialized_zval);
}
else
{
- zend_std_write_property(object, member, value, cache_slot);
+ return zend_std_write_property(object, member, value, cache_slot);
}
}
/* }}} */
diff --git a/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_002.phpt b/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_002.phpt
index 14e8382e41..fb472681ca 100644
--- a/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_002.phpt
+++ b/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_002.phpt
@@ -1,5 +1,5 @@
--TEST--
-ReflectionClass::getStaticPropertyValue() - bad params
+ReflectionClass::setStaticPropertyValue() - bad params
--CREDITS--
Robin Fernandes <robinf@php.net>
Steve Seear <stevseea@php.net>
diff --git a/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_003.phpt b/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_003.phpt
new file mode 100644
index 0000000000..a83900a123
--- /dev/null
+++ b/ext/reflection/tests/ReflectionClass_setStaticPropertyValue_003.phpt
@@ -0,0 +1,39 @@
+--TEST--
+ReflectionClass::setStaticPropertyValue() - type constraints must be enforced
+--FILE--
+<?php
+
+class Test {
+ public static $x;
+ public static int $y = 2;
+}
+
+$rc = new ReflectionClass('Test');
+
+try {
+ $rc->setStaticPropertyValue("y", "foo");
+} catch (TypeError $e) { echo $e->getMessage(), "\n"; }
+var_dump(Test::$y);
+
+$rc->setStaticPropertyValue("y", "21");
+var_dump(Test::$y);
+
+
+Test::$x =& Test::$y;
+
+try {
+ $rc->setStaticPropertyValue("x", "foo");
+} catch (TypeError $e) { echo $e->getMessage(), "\n"; }
+var_dump(Test::$y);
+
+$rc->setStaticPropertyValue("x", "42");
+var_dump(Test::$y);
+
+?>
+--EXPECT--
+Typed property Test::$y must be int, string used
+int(2)
+int(21)
+Cannot assign string to reference held by property Test::$y of type int
+int(21)
+int(42)
diff --git a/ext/reflection/tests/ReflectionProperty_isInitialized.phpt b/ext/reflection/tests/ReflectionProperty_isInitialized.phpt
new file mode 100644
index 0000000000..f1f6e53ebd
--- /dev/null
+++ b/ext/reflection/tests/ReflectionProperty_isInitialized.phpt
@@ -0,0 +1,113 @@
+--TEST--
+Test ReflectionProperty::isInitialized()
+--FILE--
+<?php
+
+class A {
+ public static ?string $ssv = null;
+ public static ?string $ss;
+ public static $s;
+ public ?int $iv = null;
+ public ?int $i;
+ public $n;
+
+ private int $p;
+}
+
+class B extends A { }
+
+echo "Static properties:\n";
+var_dump((new ReflectionProperty(A::class, 'ssv'))->isInitialized());
+var_dump((new ReflectionProperty(A::class, 'ss'))->isInitialized());
+var_dump((new ReflectionProperty(A::class, 's'))->isInitialized());
+
+echo "Declared properties:\n";
+$a = new A;
+var_dump((new ReflectionProperty($a, 'iv'))->isInitialized($a));
+var_dump((new ReflectionProperty($a, 'i'))->isInitialized($a));
+var_dump((new ReflectionProperty($a, 'n'))->isInitialized($a));
+
+echo "Declared properties after unset:\n";
+unset($a->iv);
+unset($a->i);
+unset($a->n);
+var_dump((new ReflectionProperty($a, 'i'))->isInitialized($a));
+var_dump((new ReflectionProperty($a, 'iv'))->isInitialized($a));
+var_dump((new ReflectionProperty($a, 'n'))->isInitialized($a));
+
+echo "Dynamic properties:\n";
+$a->d = null;
+$rp = new ReflectionProperty($a, 'd');
+var_dump($rp->isInitialized($a));
+unset($a->d);
+var_dump($rp->isInitialized($a));
+
+echo "Visibility handling:\n";
+$rp = new ReflectionProperty('A', 'p');
+try {
+ var_dump($rp->isInitialized($a));
+} catch (ReflectionException $e) {
+ echo $e->getMessage(), "\n";
+}
+$rp->setAccessible(true);
+var_dump($rp->isInitialized($a));
+
+echo "Object type:\n";
+$rp = new ReflectionProperty('B', 'i');
+var_dump($rp->isInitialized($a));
+
+try {
+ var_dump($rp->isInitialized(new stdClass));
+} catch (ReflectionException $e) {
+ echo $e->getMessage(), "\n";
+}
+
+class WithMagic {
+ public $prop;
+ public int $intProp;
+
+ public function __isset($name) {
+ echo "__isset($name)\n";
+ return true;
+ }
+
+ public function __get($name) {
+ echo "__get($name)\n";
+ return "foobar";
+ }
+}
+
+echo "Class with __isset:\n";
+$obj = new WithMagic;
+unset($obj->prop);
+$rp = new ReflectionProperty('WithMagic', 'prop');
+var_dump($rp->isInitialized($obj));
+$rp = new ReflectionProperty('WithMagic', 'intProp');
+var_dump($rp->isInitialized($obj));
+
+?>
+--EXPECT--
+Static properties:
+bool(true)
+bool(false)
+bool(true)
+Declared properties:
+bool(true)
+bool(false)
+bool(true)
+Declared properties after unset:
+bool(false)
+bool(false)
+bool(false)
+Dynamic properties:
+bool(true)
+bool(false)
+Visibility handling:
+Cannot access non-public member A::$p
+bool(false)
+Object type:
+bool(false)
+Given object is not an instance of the class this property was declared in
+Class with __isset:
+bool(false)
+bool(false)
diff --git a/ext/reflection/tests/ReflectionProperty_typed_static.phpt b/ext/reflection/tests/ReflectionProperty_typed_static.phpt
new file mode 100644
index 0000000000..77f95d77ef
--- /dev/null
+++ b/ext/reflection/tests/ReflectionProperty_typed_static.phpt
@@ -0,0 +1,51 @@
+--TEST--
+ReflectionProperty::getValue() on typed static property
+--FILE--
+<?php
+
+class Test {
+ public static int $x = 42;
+ public static int $y;
+ public static $z;
+}
+
+$rp = new ReflectionProperty('Test', 'x');
+var_dump($rp->getValue());
+
+$rp = new ReflectionProperty('Test', 'y');
+try {
+ var_dump($rp->getValue());
+} catch (Error $e) {
+ echo $e->getMessage(), "\n";
+}
+
+$rp->setValue("24");
+var_dump($rp->getValue());
+
+try {
+ $rp->setValue("foo");
+} catch (TypeError $e) {
+ echo $e->getMessage(), "\n";
+}
+var_dump($rp->getValue());
+
+Test::$z =& Test::$y;
+
+$rp = new ReflectionProperty('Test', 'z');
+try {
+ $rp->setValue("foo");
+} catch (TypeError $e) {
+ echo $e->getMessage(), "\n";
+}
+var_dump($rp->getValue());
+
+
+?>
+--EXPECT--
+int(42)
+Typed static property Test::$y must not be accessed before initialization
+int(24)
+Typed property Test::$y must be int, string used
+int(24)
+Cannot assign string to reference held by property Test::$y of type int
+int(24)
diff --git a/ext/reflection/tests/ReflectionType_001.phpt b/ext/reflection/tests/ReflectionType_001.phpt
index d949e18107..e47a9615ba 100644
--- a/ext/reflection/tests/ReflectionType_001.phpt
+++ b/ext/reflection/tests/ReflectionType_001.phpt
@@ -74,6 +74,32 @@ foreach ([
}
}
+echo "\n*** property types\n";
+
+class PropTypeTest {
+ public int $int;
+ public string $string;
+ public array $arr;
+ public iterable $iterable;
+ public stdClass $std;
+ public OtherThing $other;
+ public $mixed;
+}
+
+$reflector = new ReflectionClass(PropTypeTest::class);
+
+foreach ($reflector->getProperties() as $name => $property) {
+ if ($property->hasType()) {
+ printf("public %s $%s;\n",
+ $property->getType(), $property->getName());
+ } else printf("public $%s;\n", $property->getName());
+}
+
+echo "*** resolved property types\n";
+$obj = new PropTypeTest;
+$obj->std = new stdClass;
+$r = (new ReflectionProperty($obj, 'std'))->getType();
+var_dump($r->getName());
?>
--EXPECT--
*** functions
@@ -185,3 +211,14 @@ bool(true)
bool(false)
bool(false)
string(4) "Test"
+
+*** property types
+public int $int;
+public string $string;
+public array $arr;
+public iterable $iterable;
+public stdClass $std;
+public OtherThing $other;
+public $mixed;
+*** resolved property types
+string(8) "stdClass"