@@ -539,6 +539,7 @@ public static function getMethodProperties(File $phpcsFile, $stackPtr)
539
539
* 'scope_specified' => boolean, // TRUE if the scope was explicitly specified.
540
540
* 'is_static' => boolean, // TRUE if the static keyword was found.
541
541
* 'is_readonly' => boolean, // TRUE if the readonly keyword was found.
542
+ * 'is_final' => boolean, // TRUE if the final keyword was found.
542
543
* 'type' => string, // The type of the var (empty if no type specified).
543
544
* 'type_token' => integer|false, // The stack pointer to the start of the type
544
545
* // or FALSE if there is no type.
@@ -553,7 +554,7 @@ public static function getMethodProperties(File $phpcsFile, $stackPtr)
553
554
*
554
555
* Changelog for the PHPCS native function:
555
556
* - Introduced in PHPCS 0.0.5.
556
- * - The upstream method has received no significant updates since PHPCS 3.10.1.
557
+ * - PHPCS 3.12.0: report final properties
557
558
*
558
559
* @see \PHP_CodeSniffer\Files\File::getMemberProperties() Original source.
559
560
* @see \PHPCSUtils\Utils\Variables::getMemberProperties() PHPCSUtils native improved version.
@@ -572,7 +573,145 @@ public static function getMethodProperties(File $phpcsFile, $stackPtr)
572
573
*/
573
574
public static function getMemberProperties (File $ phpcsFile , $ stackPtr )
574
575
{
575
- return $ phpcsFile ->getMemberProperties ($ stackPtr );
576
+ $ tokens = $ phpcsFile ->getTokens ();
577
+ if ($ tokens [$ stackPtr ]['code ' ] !== T_VARIABLE ) {
578
+ throw new RuntimeException ('$stackPtr must be of type T_VARIABLE ' );
579
+ }
580
+
581
+ $ conditions = array_keys ($ tokens [$ stackPtr ]['conditions ' ]);
582
+ $ ptr = array_pop ($ conditions );
583
+ if (isset ($ tokens [$ ptr ]) === false
584
+ || ($ tokens [$ ptr ]['code ' ] !== T_CLASS
585
+ && $ tokens [$ ptr ]['code ' ] !== T_ANON_CLASS
586
+ && $ tokens [$ ptr ]['code ' ] !== T_TRAIT )
587
+ ) {
588
+ if (isset ($ tokens [$ ptr ]) === true
589
+ && ($ tokens [$ ptr ]['code ' ] === T_INTERFACE
590
+ || $ tokens [$ ptr ]['code ' ] === T_ENUM )
591
+ ) {
592
+ // T_VARIABLEs in interfaces/enums can actually be method arguments
593
+ // but they won't be seen as being inside the method because there
594
+ // are no scope openers and closers for abstract methods. If it is in
595
+ // parentheses, we can be pretty sure it is a method argument.
596
+ if (isset ($ tokens [$ stackPtr ]['nested_parenthesis ' ]) === false
597
+ || empty ($ tokens [$ stackPtr ]['nested_parenthesis ' ]) === true
598
+ ) {
599
+ $ error = 'Possible parse error: %ss may not include member vars ' ;
600
+ $ code = sprintf ('Internal.ParseError.%sHasMemberVar ' , ucfirst ($ tokens [$ ptr ]['content ' ]));
601
+ $ data = [strtolower ($ tokens [$ ptr ]['content ' ])];
602
+ $ phpcsFile ->addWarning ($ error , $ stackPtr , $ code , $ data );
603
+ return [];
604
+ }
605
+ } else {
606
+ throw new RuntimeException ('$stackPtr is not a class member var ' );
607
+ }
608
+ }
609
+
610
+ // Make sure it's not a method parameter.
611
+ if (empty ($ tokens [$ stackPtr ]['nested_parenthesis ' ]) === false ) {
612
+ $ parenthesis = array_keys ($ tokens [$ stackPtr ]['nested_parenthesis ' ]);
613
+ $ deepestOpen = array_pop ($ parenthesis );
614
+ if ($ deepestOpen > $ ptr
615
+ && isset ($ tokens [$ deepestOpen ]['parenthesis_owner ' ]) === true
616
+ && $ tokens [$ tokens [$ deepestOpen ]['parenthesis_owner ' ]]['code ' ] === T_FUNCTION
617
+ ) {
618
+ throw new RuntimeException ('$stackPtr is not a class member var ' );
619
+ }
620
+ }
621
+
622
+ $ valid = Collections::propertyModifierKeywords ();
623
+ $ valid += Tokens::$ emptyTokens ;
624
+
625
+ $ scope = 'public ' ;
626
+ $ scopeSpecified = false ;
627
+ $ isStatic = false ;
628
+ $ isReadonly = false ;
629
+ $ isFinal = false ;
630
+
631
+ $ startOfStatement = $ phpcsFile ->findPrevious (
632
+ [
633
+ T_SEMICOLON ,
634
+ T_OPEN_CURLY_BRACKET ,
635
+ T_CLOSE_CURLY_BRACKET ,
636
+ T_ATTRIBUTE_END ,
637
+ ],
638
+ ($ stackPtr - 1 )
639
+ );
640
+
641
+ for ($ i = ($ startOfStatement + 1 ); $ i < $ stackPtr ; $ i ++) {
642
+ if (isset ($ valid [$ tokens [$ i ]['code ' ]]) === false ) {
643
+ break ;
644
+ }
645
+
646
+ switch ($ tokens [$ i ]['code ' ]) {
647
+ case T_PUBLIC :
648
+ $ scope = 'public ' ;
649
+ $ scopeSpecified = true ;
650
+ break ;
651
+ case T_PRIVATE :
652
+ $ scope = 'private ' ;
653
+ $ scopeSpecified = true ;
654
+ break ;
655
+ case T_PROTECTED :
656
+ $ scope = 'protected ' ;
657
+ $ scopeSpecified = true ;
658
+ break ;
659
+ case T_STATIC :
660
+ $ isStatic = true ;
661
+ break ;
662
+ case T_READONLY :
663
+ $ isReadonly = true ;
664
+ break ;
665
+ case T_FINAL :
666
+ $ isFinal = true ;
667
+ break ;
668
+ }
669
+ }
670
+
671
+ $ type = '' ;
672
+ $ typeToken = false ;
673
+ $ typeEndToken = false ;
674
+ $ nullableType = false ;
675
+ $ propertyTypeTokens = Collections::propertyTypeTokens ();
676
+
677
+ if ($ i < $ stackPtr ) {
678
+ // We've found a type.
679
+ for ($ i ; $ i < $ stackPtr ; $ i ++) {
680
+ if ($ tokens [$ i ]['code ' ] === T_VARIABLE ) {
681
+ // Hit another variable in a group definition.
682
+ break ;
683
+ }
684
+
685
+ if ($ tokens [$ i ]['code ' ] === T_NULLABLE ) {
686
+ $ nullableType = true ;
687
+ }
688
+
689
+ if (isset ($ propertyTypeTokens [$ tokens [$ i ]['code ' ]]) === true ) {
690
+ $ typeEndToken = $ i ;
691
+ if ($ typeToken === false ) {
692
+ $ typeToken = $ i ;
693
+ }
694
+
695
+ $ type .= $ tokens [$ i ]['content ' ];
696
+ }
697
+ }
698
+
699
+ if ($ type !== '' && $ nullableType === true ) {
700
+ $ type = '? ' . $ type ;
701
+ }
702
+ }
703
+
704
+ return [
705
+ 'scope ' => $ scope ,
706
+ 'scope_specified ' => $ scopeSpecified ,
707
+ 'is_static ' => $ isStatic ,
708
+ 'is_readonly ' => $ isReadonly ,
709
+ 'is_final ' => $ isFinal ,
710
+ 'type ' => $ type ,
711
+ 'type_token ' => $ typeToken ,
712
+ 'type_end_token ' => $ typeEndToken ,
713
+ 'nullable_type ' => $ nullableType ,
714
+ ];
576
715
}
577
716
578
717
/**
0 commit comments