@@ -157,6 +157,7 @@ typedef struct _attribute_reference {
157157 zend_class_entry * scope ;
158158 zend_string * filename ;
159159 uint32_t target ;
160+ bool on_property_hook ;
160161} attribute_reference ;
161162
162163typedef enum {
@@ -1254,7 +1255,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c
12541255
12551256/* {{{ reflection_attribute_factory */
12561257static void reflection_attribute_factory (zval * object , HashTable * attributes , zend_attribute * data ,
1257- zend_class_entry * scope , uint32_t target , zend_string * filename )
1258+ zend_class_entry * scope , uint32_t target , zend_string * filename , bool on_property_hook )
12581259{
12591260 reflection_object * intern ;
12601261 attribute_reference * reference ;
@@ -1267,14 +1268,16 @@ static void reflection_attribute_factory(zval *object, HashTable *attributes, ze
12671268 reference -> scope = scope ;
12681269 reference -> filename = filename ? zend_string_copy (filename ) : NULL ;
12691270 reference -> target = target ;
1271+ reference -> on_property_hook = on_property_hook ;
12701272 intern -> ptr = reference ;
12711273 intern -> ref_type = REF_TYPE_ATTRIBUTE ;
12721274 ZVAL_STR_COPY (reflection_prop_name (object ), data -> name );
12731275}
12741276/* }}} */
12751277
12761278static int read_attributes (zval * ret , HashTable * attributes , zend_class_entry * scope ,
1277- uint32_t offset , uint32_t target , zend_string * name , zend_class_entry * base , zend_string * filename ) /* {{{ */
1279+ uint32_t offset , uint32_t target , zend_string * name , zend_class_entry * base , zend_string * filename ,
1280+ bool on_property_hook ) /* {{{ */
12781281{
12791282 ZEND_ASSERT (attributes != NULL );
12801283
@@ -1287,7 +1290,7 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s
12871290
12881291 ZEND_HASH_PACKED_FOREACH_PTR (attributes , attr ) {
12891292 if (attr -> offset == offset && zend_string_equals (attr -> lcname , filter )) {
1290- reflection_attribute_factory (& tmp , attributes , attr , scope , target , filename );
1293+ reflection_attribute_factory (& tmp , attributes , attr , scope , target , filename , on_property_hook );
12911294 add_next_index_zval (ret , & tmp );
12921295 }
12931296 } ZEND_HASH_FOREACH_END ();
@@ -1319,7 +1322,7 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s
13191322 }
13201323 }
13211324
1322- reflection_attribute_factory (& tmp , attributes , attr , scope , target , filename );
1325+ reflection_attribute_factory (& tmp , attributes , attr , scope , target , filename , on_property_hook );
13231326 add_next_index_zval (ret , & tmp );
13241327 } ZEND_HASH_FOREACH_END ();
13251328
@@ -1328,7 +1331,7 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s
13281331/* }}} */
13291332
13301333static void reflect_attributes (INTERNAL_FUNCTION_PARAMETERS , HashTable * attributes ,
1331- uint32_t offset , zend_class_entry * scope , uint32_t target , zend_string * filename ) /* {{{ */
1334+ uint32_t offset , zend_class_entry * scope , uint32_t target , zend_string * filename , bool on_property_hook ) /* {{{ */
13321335{
13331336 zend_string * name = NULL ;
13341337 zend_long flags = 0 ;
@@ -1361,7 +1364,7 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut
13611364
13621365 array_init (return_value );
13631366
1364- if (FAILURE == read_attributes (return_value , attributes , scope , offset , target , name , base , filename )) {
1367+ if (FAILURE == read_attributes (return_value , attributes , scope , offset , target , name , base , filename , on_property_hook )) {
13651368 RETURN_THROWS ();
13661369 }
13671370}
@@ -2066,15 +2069,21 @@ ZEND_METHOD(ReflectionFunctionAbstract, getAttributes)
20662069
20672070 GET_REFLECTION_OBJECT_PTR (fptr );
20682071
2072+ bool on_property_hook = false;
2073+
20692074 if (fptr -> common .scope && (fptr -> common .fn_flags & (ZEND_ACC_CLOSURE |ZEND_ACC_FAKE_CLOSURE )) != ZEND_ACC_CLOSURE ) {
20702075 target = ZEND_ATTRIBUTE_TARGET_METHOD ;
2076+ if (fptr -> common .prop_info != NULL ) {
2077+ on_property_hook = true;
2078+ }
20712079 } else {
20722080 target = ZEND_ATTRIBUTE_TARGET_FUNCTION ;
20732081 }
20742082
20752083 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
20762084 fptr -> common .attributes , 0 , fptr -> common .scope , target ,
2077- fptr -> type == ZEND_USER_FUNCTION ? fptr -> op_array .filename : NULL );
2085+ fptr -> type == ZEND_USER_FUNCTION ? fptr -> op_array .filename : NULL ,
2086+ on_property_hook );
20782087}
20792088/* }}} */
20802089
@@ -2916,7 +2925,8 @@ ZEND_METHOD(ReflectionParameter, getAttributes)
29162925
29172926 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
29182927 attributes , param -> offset + 1 , scope , ZEND_ATTRIBUTE_TARGET_PARAMETER ,
2919- param -> fptr -> type == ZEND_USER_FUNCTION ? param -> fptr -> op_array .filename : NULL );
2928+ param -> fptr -> type == ZEND_USER_FUNCTION ? param -> fptr -> op_array .filename : NULL ,
2929+ false);
29202930}
29212931
29222932/* {{{ Returns the index of the parameter, starting from 0 */
@@ -4038,7 +4048,8 @@ ZEND_METHOD(ReflectionClassConstant, getAttributes)
40384048
40394049 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
40404050 ref -> attributes , 0 , ref -> ce , ZEND_ATTRIBUTE_TARGET_CLASS_CONST ,
4041- ref -> ce -> type == ZEND_USER_CLASS ? ref -> ce -> info .user .filename : NULL );
4051+ ref -> ce -> type == ZEND_USER_CLASS ? ref -> ce -> info .user .filename : NULL ,
4052+ false);
40424053}
40434054/* }}} */
40444055
@@ -4443,7 +4454,8 @@ ZEND_METHOD(ReflectionClass, getAttributes)
44434454
44444455 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
44454456 ce -> attributes , 0 , ce , ZEND_ATTRIBUTE_TARGET_CLASS ,
4446- ce -> type == ZEND_USER_CLASS ? ce -> info .user .filename : NULL );
4457+ ce -> type == ZEND_USER_CLASS ? ce -> info .user .filename : NULL ,
4458+ false);
44474459}
44484460/* }}} */
44494461
@@ -6385,7 +6397,8 @@ ZEND_METHOD(ReflectionProperty, getAttributes)
63856397
63866398 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
63876399 ref -> prop -> attributes , 0 , ref -> prop -> ce , ZEND_ATTRIBUTE_TARGET_PROPERTY ,
6388- ref -> prop -> ce -> type == ZEND_USER_CLASS ? ref -> prop -> ce -> info .user .filename : NULL );
6400+ ref -> prop -> ce -> type == ZEND_USER_CLASS ? ref -> prop -> ce -> info .user .filename : NULL ,
6401+ false);
63896402}
63906403/* }}} */
63916404
@@ -7348,13 +7361,21 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
73487361 if (config != NULL && config -> validator != NULL ) {
73497362 config -> validator (
73507363 attr -> data ,
7351- flags | ZEND_ATTRIBUTE_DELAYED_TARGET_VALIDATION ,
7364+ attr -> target | ZEND_ATTRIBUTE_DELAYED_TARGET_VALIDATION ,
73527365 attr -> scope
73537366 );
73547367 if (EG (exception )) {
73557368 RETURN_THROWS ();
73567369 }
73577370 }
7371+ /* For #[NoDiscard], the attribute does not work on property hooks, but
7372+ * at runtime the validator has no way to access the method that an
7373+ * attribute is applied to, attr->scope is just the overall class entry
7374+ * that the method is a part of. */
7375+ if (ce == zend_ce_nodiscard && attr -> on_property_hook ) {
7376+ zend_throw_error (NULL , "#[\\NoDiscard] is not supported for property hooks" );;
7377+ RETURN_THROWS ();
7378+ }
73587379 }
73597380
73607381 /* Repetition validation is done even if #[DelayedTargetValidation] is used
@@ -7886,7 +7907,7 @@ ZEND_METHOD(ReflectionConstant, getAttributes)
78867907
78877908 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
78887909 const_ -> attributes , 0 , NULL , ZEND_ATTRIBUTE_TARGET_CONST ,
7889- const_ -> filename );
7910+ const_ -> filename , false );
78907911}
78917912
78927913ZEND_METHOD (ReflectionConstant , __toString )
0 commit comments