@@ -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,8 +573,171 @@ 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
- }
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
+ }//end if
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 = [
623
+ T_PUBLIC => T_PUBLIC ,
624
+ T_PRIVATE => T_PRIVATE ,
625
+ T_PROTECTED => T_PROTECTED ,
626
+ T_STATIC => T_STATIC ,
627
+ T_VAR => T_VAR ,
628
+ T_READONLY => T_READONLY ,
629
+ T_FINAL => T_FINAL ,
630
+ ];
631
+
632
+ $ valid += Tokens::$ emptyTokens ;
633
+
634
+ $ scope = 'public ' ;
635
+ $ scopeSpecified = false ;
636
+ $ isStatic = false ;
637
+ $ isReadonly = false ;
638
+ $ isFinal = false ;
639
+
640
+ $ startOfStatement = $ phpcsFile ->findPrevious (
641
+ [
642
+ T_SEMICOLON ,
643
+ T_OPEN_CURLY_BRACKET ,
644
+ T_CLOSE_CURLY_BRACKET ,
645
+ T_ATTRIBUTE_END ,
646
+ ],
647
+ ($ stackPtr - 1 )
648
+ );
649
+
650
+ for ($ i = ($ startOfStatement + 1 ); $ i < $ stackPtr ; $ i ++) {
651
+ if (isset ($ valid [$ tokens [$ i ]['code ' ]]) === false ) {
652
+ break ;
653
+ }
654
+
655
+ switch ($ tokens [$ i ]['code ' ]) {
656
+ case T_PUBLIC :
657
+ $ scope = 'public ' ;
658
+ $ scopeSpecified = true ;
659
+ break ;
660
+ case T_PRIVATE :
661
+ $ scope = 'private ' ;
662
+ $ scopeSpecified = true ;
663
+ break ;
664
+ case T_PROTECTED :
665
+ $ scope = 'protected ' ;
666
+ $ scopeSpecified = true ;
667
+ break ;
668
+ case T_STATIC :
669
+ $ isStatic = true ;
670
+ break ;
671
+ case T_READONLY :
672
+ $ isReadonly = true ;
673
+ break ;
674
+ case T_FINAL :
675
+ $ isFinal = true ;
676
+ break ;
677
+ }//end switch
678
+ }//end for
679
+
680
+ $ type = '' ;
681
+ $ typeToken = false ;
682
+ $ typeEndToken = false ;
683
+ $ nullableType = false ;
684
+
685
+ if ($ i < $ stackPtr ) {
686
+ // We've found a type.
687
+ $ valid = [
688
+ T_STRING => T_STRING ,
689
+ T_CALLABLE => T_CALLABLE ,
690
+ T_SELF => T_SELF ,
691
+ T_PARENT => T_PARENT ,
692
+ T_FALSE => T_FALSE ,
693
+ T_TRUE => T_TRUE ,
694
+ T_NULL => T_NULL ,
695
+ T_NAMESPACE => T_NAMESPACE ,
696
+ T_NS_SEPARATOR => T_NS_SEPARATOR ,
697
+ T_TYPE_UNION => T_TYPE_UNION ,
698
+ T_TYPE_INTERSECTION => T_TYPE_INTERSECTION ,
699
+ T_TYPE_OPEN_PARENTHESIS => T_TYPE_OPEN_PARENTHESIS ,
700
+ T_TYPE_CLOSE_PARENTHESIS => T_TYPE_CLOSE_PARENTHESIS ,
701
+ ];
702
+
703
+ for ($ i ; $ i < $ stackPtr ; $ i ++) {
704
+ if ($ tokens [$ i ]['code ' ] === T_VARIABLE ) {
705
+ // Hit another variable in a group definition.
706
+ break ;
707
+ }
708
+
709
+ if ($ tokens [$ i ]['code ' ] === T_NULLABLE ) {
710
+ $ nullableType = true ;
711
+ }
712
+
713
+ if (isset ($ valid [$ tokens [$ i ]['code ' ]]) === true ) {
714
+ $ typeEndToken = $ i ;
715
+ if ($ typeToken === false ) {
716
+ $ typeToken = $ i ;
717
+ }
718
+
719
+ $ type .= $ tokens [$ i ]['content ' ];
720
+ }
721
+ }
722
+
723
+ if ($ type !== '' && $ nullableType === true ) {
724
+ $ type = '? ' .$ type ;
725
+ }
726
+ }//end if
727
+
728
+ return [
729
+ 'scope ' => $ scope ,
730
+ 'scope_specified ' => $ scopeSpecified ,
731
+ 'is_static ' => $ isStatic ,
732
+ 'is_readonly ' => $ isReadonly ,
733
+ 'is_final ' => $ isFinal ,
734
+ 'type ' => $ type ,
735
+ 'type_token ' => $ typeToken ,
736
+ 'type_end_token ' => $ typeEndToken ,
737
+ 'nullable_type ' => $ nullableType ,
738
+ ];
739
+
740
+ }//end getMemberProperties()
577
741
578
742
/**
579
743
* Returns the implementation properties of a class.
0 commit comments