@@ -324,14 +324,37 @@ private function addPropertyToClass(ClassLike $class, Node\Stmt\Property $node):
324324 foreach ($ node ->props as $ item ) {
325325 $ prop = $ class ->addProperty ($ item ->name ->toString ());
326326 $ prop ->setStatic ($ node ->isStatic ());
327- $ prop ->setVisibility ($ this ->toVisibility ($ node ->flags ));
327+ $ prop ->setVisibility ($ this ->toVisibility ($ node ->flags ), $ this -> toSetterVisibility ( $ node -> flags ) );
328328 $ prop ->setType ($ node ->type ? $ this ->toPhp ($ node ->type ) : null );
329329 if ($ item ->default ) {
330330 $ prop ->setValue ($ this ->toValue ($ item ->default ));
331331 }
332332
333333 $ prop ->setReadOnly ((method_exists ($ node , 'isReadonly ' ) && $ node ->isReadonly ()) || ($ class instanceof ClassType && $ class ->isReadOnly ()));
334334 $ this ->addCommentAndAttributes ($ prop , $ node );
335+
336+ $ prop ->setAbstract ((bool ) ($ node ->flags & Modifiers::ABSTRACT ));
337+ $ prop ->setFinal ((bool ) ($ node ->flags & Modifiers::FINAL ));
338+ $ this ->addHooksToProperty ($ prop , $ node );
339+ }
340+ }
341+
342+
343+ private function addHooksToProperty (Property |PromotedParameter $ prop , Node \Stmt \Property |Node \Param $ node ): void
344+ {
345+ if (!class_exists (Node \PropertyHook::class)) {
346+ return ;
347+ }
348+
349+ foreach ($ node ->hooks as $ hookNode ) {
350+ $ hook = $ prop ->addHook ($ hookNode ->name ->toString ());
351+ $ hook ->setFinal ((bool ) ($ hookNode ->flags & Modifiers::FINAL ));
352+ $ this ->setupFunction ($ hook , $ hookNode );
353+ if ($ hookNode ->body === null ) {
354+ $ hook ->setAbstract ();
355+ } elseif (!is_array ($ hookNode ->body )) {
356+ $ hook ->setBody ($ this ->getReformattedContents ([$ hookNode ->body ], 1 ), short: true );
357+ }
335358 }
336359 }
337360
@@ -381,7 +404,7 @@ private function addFunctionToFile(PhpFile $phpFile, Node\Stmt\Function_ $node):
381404
382405
383406 private function addCommentAndAttributes (
384- PhpFile |ClassLike |Constant |Property |GlobalFunction |Method |Parameter |EnumCase |TraitUse $ element ,
407+ PhpFile |ClassLike |Constant |Property |GlobalFunction |Method |Parameter |EnumCase |TraitUse | PropertyHook $ element ,
385408 Node $ node ,
386409 ): void
387410 {
@@ -409,18 +432,29 @@ private function addCommentAndAttributes(
409432 }
410433
411434
412- private function setupFunction (GlobalFunction |Method $ function , Node \FunctionLike $ node ): void
435+ private function setupFunction (GlobalFunction |Method | PropertyHook $ function , Node \FunctionLike $ node ): void
413436 {
414437 $ function ->setReturnReference ($ node ->returnsByRef ());
415- $ function ->setReturnType ($ node ->getReturnType () ? $ this ->toPhp ($ node ->getReturnType ()) : null );
438+ if (!$ function instanceof PropertyHook) {
439+ $ function ->setReturnType ($ node ->getReturnType () ? $ this ->toPhp ($ node ->getReturnType ()) : null );
440+ }
441+
416442 foreach ($ node ->getParams () as $ item ) {
417- $ visibility = $ this ->toVisibility ($ item ->flags );
418- $ param = $ visibility
419- ? $ function ->addPromotedParameter ($ item ->var ->name )->setVisibility ($ visibility )->setReadonly ($ item ->isReadonly ())
420- : $ function ->addParameter ($ item ->var ->name );
443+ $ getVisibility = $ this ->toVisibility ($ item ->flags );
444+ $ setVisibility = $ this ->toSetterVisibility ($ item ->flags );
445+ if ($ getVisibility || $ setVisibility ) {
446+ $ param = $ function ->addPromotedParameter ($ item ->var ->name )
447+ ->setVisibility ($ getVisibility , $ setVisibility )
448+ ->setReadonly ($ item ->isReadonly ());
449+ $ this ->addHooksToProperty ($ param , $ item );
450+ } else {
451+ $ param = $ function ->addParameter ($ item ->var ->name );
452+ }
421453 $ param ->setType ($ item ->type ? $ this ->toPhp ($ item ->type ) : null );
422454 $ param ->setReference ($ item ->byRef );
423- $ function ->setVariadic ($ item ->variadic );
455+ if (!$ function instanceof PropertyHook) {
456+ $ function ->setVariadic ($ item ->variadic );
457+ }
424458 if ($ item ->default ) {
425459 $ param ->setDefaultValue ($ this ->toValue ($ item ->default ));
426460 }
@@ -491,6 +525,18 @@ private function toVisibility(int $flags): ?string
491525 }
492526
493527
528+ private function toSetterVisibility (int $ flags ): ?string
529+ {
530+ return match (true ) {
531+ !class_exists (Node \PropertyHook::class) => null ,
532+ (bool ) ($ flags & Modifiers::PUBLIC_SET ) => ClassType::VisibilityPublic,
533+ (bool ) ($ flags & Modifiers::PROTECTED_SET ) => ClassType::VisibilityProtected,
534+ (bool ) ($ flags & Modifiers::PRIVATE_SET ) => ClassType::VisibilityPrivate,
535+ default => null ,
536+ };
537+ }
538+
539+
494540 private function toPhp (Node $ value ): string
495541 {
496542 $ dolly = clone $ value ;
0 commit comments