@@ -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 {
@@ -1236,7 +1237,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c
12361237
12371238/* {{{ reflection_attribute_factory */
12381239static void reflection_attribute_factory (zval * object , HashTable * attributes , zend_attribute * data ,
1239- zend_class_entry * scope , uint32_t target , zend_string * filename )
1240+ zend_class_entry * scope , uint32_t target , zend_string * filename , bool on_property_hook )
12401241{
12411242 reflection_object * intern ;
12421243 attribute_reference * reference ;
@@ -1249,14 +1250,16 @@ static void reflection_attribute_factory(zval *object, HashTable *attributes, ze
12491250 reference -> scope = scope ;
12501251 reference -> filename = filename ? zend_string_copy (filename ) : NULL ;
12511252 reference -> target = target ;
1253+ reference -> on_property_hook = on_property_hook ;
12521254 intern -> ptr = reference ;
12531255 intern -> ref_type = REF_TYPE_ATTRIBUTE ;
12541256 ZVAL_STR_COPY (reflection_prop_name (object ), data -> name );
12551257}
12561258/* }}} */
12571259
12581260static int read_attributes (zval * ret , HashTable * attributes , zend_class_entry * scope ,
1259- uint32_t offset , uint32_t target , zend_string * name , zend_class_entry * base , zend_string * filename ) /* {{{ */
1261+ uint32_t offset , uint32_t target , zend_string * name , zend_class_entry * base , zend_string * filename ,
1262+ bool on_property_hook ) /* {{{ */
12601263{
12611264 ZEND_ASSERT (attributes != NULL );
12621265
@@ -1269,7 +1272,7 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s
12691272
12701273 ZEND_HASH_PACKED_FOREACH_PTR (attributes , attr ) {
12711274 if (attr -> offset == offset && zend_string_equals (attr -> lcname , filter )) {
1272- reflection_attribute_factory (& tmp , attributes , attr , scope , target , filename );
1275+ reflection_attribute_factory (& tmp , attributes , attr , scope , target , filename , on_property_hook );
12731276 add_next_index_zval (ret , & tmp );
12741277 }
12751278 } ZEND_HASH_FOREACH_END ();
@@ -1301,7 +1304,7 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s
13011304 }
13021305 }
13031306
1304- reflection_attribute_factory (& tmp , attributes , attr , scope , target , filename );
1307+ reflection_attribute_factory (& tmp , attributes , attr , scope , target , filename , on_property_hook );
13051308 add_next_index_zval (ret , & tmp );
13061309 } ZEND_HASH_FOREACH_END ();
13071310
@@ -1310,7 +1313,7 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s
13101313/* }}} */
13111314
13121315static void reflect_attributes (INTERNAL_FUNCTION_PARAMETERS , HashTable * attributes ,
1313- uint32_t offset , zend_class_entry * scope , uint32_t target , zend_string * filename ) /* {{{ */
1316+ uint32_t offset , zend_class_entry * scope , uint32_t target , zend_string * filename , bool on_property_hook ) /* {{{ */
13141317{
13151318 zend_string * name = NULL ;
13161319 zend_long flags = 0 ;
@@ -1343,7 +1346,7 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut
13431346
13441347 array_init (return_value );
13451348
1346- if (FAILURE == read_attributes (return_value , attributes , scope , offset , target , name , base , filename )) {
1349+ if (FAILURE == read_attributes (return_value , attributes , scope , offset , target , name , base , filename , on_property_hook )) {
13471350 RETURN_THROWS ();
13481351 }
13491352}
@@ -2048,15 +2051,21 @@ ZEND_METHOD(ReflectionFunctionAbstract, getAttributes)
20482051
20492052 GET_REFLECTION_OBJECT_PTR (fptr );
20502053
2054+ bool on_property_hook = false;
2055+
20512056 if (fptr -> common .scope && (fptr -> common .fn_flags & (ZEND_ACC_CLOSURE |ZEND_ACC_FAKE_CLOSURE )) != ZEND_ACC_CLOSURE ) {
20522057 target = ZEND_ATTRIBUTE_TARGET_METHOD ;
2058+ if (fptr -> common .prop_info != NULL ) {
2059+ on_property_hook = true;
2060+ }
20532061 } else {
20542062 target = ZEND_ATTRIBUTE_TARGET_FUNCTION ;
20552063 }
20562064
20572065 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
20582066 fptr -> common .attributes , 0 , fptr -> common .scope , target ,
2059- fptr -> type == ZEND_USER_FUNCTION ? fptr -> op_array .filename : NULL );
2067+ fptr -> type == ZEND_USER_FUNCTION ? fptr -> op_array .filename : NULL ,
2068+ on_property_hook );
20602069}
20612070/* }}} */
20622071
@@ -2898,7 +2907,8 @@ ZEND_METHOD(ReflectionParameter, getAttributes)
28982907
28992908 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
29002909 attributes , param -> offset + 1 , scope , ZEND_ATTRIBUTE_TARGET_PARAMETER ,
2901- param -> fptr -> type == ZEND_USER_FUNCTION ? param -> fptr -> op_array .filename : NULL );
2910+ param -> fptr -> type == ZEND_USER_FUNCTION ? param -> fptr -> op_array .filename : NULL ,
2911+ false);
29022912}
29032913
29042914/* {{{ Returns the index of the parameter, starting from 0 */
@@ -4020,7 +4030,8 @@ ZEND_METHOD(ReflectionClassConstant, getAttributes)
40204030
40214031 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
40224032 ref -> attributes , 0 , ref -> ce , ZEND_ATTRIBUTE_TARGET_CLASS_CONST ,
4023- ref -> ce -> type == ZEND_USER_CLASS ? ref -> ce -> info .user .filename : NULL );
4033+ ref -> ce -> type == ZEND_USER_CLASS ? ref -> ce -> info .user .filename : NULL ,
4034+ false);
40244035}
40254036/* }}} */
40264037
@@ -4425,7 +4436,8 @@ ZEND_METHOD(ReflectionClass, getAttributes)
44254436
44264437 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
44274438 ce -> attributes , 0 , ce , ZEND_ATTRIBUTE_TARGET_CLASS ,
4428- ce -> type == ZEND_USER_CLASS ? ce -> info .user .filename : NULL );
4439+ ce -> type == ZEND_USER_CLASS ? ce -> info .user .filename : NULL ,
4440+ false);
44294441}
44304442/* }}} */
44314443
@@ -6353,7 +6365,8 @@ ZEND_METHOD(ReflectionProperty, getAttributes)
63536365
63546366 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
63556367 ref -> prop -> attributes , 0 , ref -> prop -> ce , ZEND_ATTRIBUTE_TARGET_PROPERTY ,
6356- ref -> prop -> ce -> type == ZEND_USER_CLASS ? ref -> prop -> ce -> info .user .filename : NULL );
6368+ ref -> prop -> ce -> type == ZEND_USER_CLASS ? ref -> prop -> ce -> info .user .filename : NULL ,
6369+ false);
63576370}
63586371/* }}} */
63596372
@@ -7319,13 +7332,21 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
73197332 if (config != NULL && config -> validator != NULL ) {
73207333 config -> validator (
73217334 attr -> data ,
7322- flags | ZEND_ATTRIBUTE_DELAYED_TARGET_VALIDATION ,
7335+ attr -> target | ZEND_ATTRIBUTE_DELAYED_TARGET_VALIDATION ,
73237336 attr -> scope
73247337 );
73257338 if (EG (exception )) {
73267339 RETURN_THROWS ();
73277340 }
73287341 }
7342+ /* For #[NoDiscard], the attribute does not work on property hooks, but
7343+ * at runtime the validator has no way to access the method that an
7344+ * attribute is applied to, attr->scope is just the overall class entry
7345+ * that the method is a part of. */
7346+ if (ce == zend_ce_nodiscard && attr -> on_property_hook ) {
7347+ zend_throw_error (NULL , "#[\\NoDiscard] is not supported for property hooks" );;
7348+ RETURN_THROWS ();
7349+ }
73297350 }
73307351
73317352 /* Repetition validation is done even if #[DelayedTargetValidation] is used
@@ -7857,7 +7878,7 @@ ZEND_METHOD(ReflectionConstant, getAttributes)
78577878
78587879 reflect_attributes (INTERNAL_FUNCTION_PARAM_PASSTHRU ,
78597880 const_ -> attributes , 0 , NULL , ZEND_ATTRIBUTE_TARGET_CONST ,
7860- const_ -> filename );
7881+ const_ -> filename , false );
78617882}
78627883
78637884ZEND_METHOD (ReflectionConstant , __toString )
0 commit comments