@@ -114,30 +114,64 @@ protected function processMemberVar(File $phpcsFile, $stackPtr)
114114 }//end if
115115 }//end if
116116
117- if ($ propertyInfo ['scope_specified ' ] === false ) {
117+ if ($ propertyInfo ['scope_specified ' ] === false && $ propertyInfo [ ' set_scope ' ] === false ) {
118118 $ error = 'Visibility must be declared on property "%s" ' ;
119119 $ data = [$ tokens [$ stackPtr ]['content ' ]];
120120 $ phpcsFile ->addError ($ error , $ stackPtr , 'ScopeMissing ' , $ data );
121121 }
122122
123123 /*
124- * Note: per PSR-PER section 4.6, the order should be:
124+ * Note: per PSR-PER section 4.6 v 2.1/3.0 , the order should be:
125125 * - Inheritance modifier: `abstract` or `final`.
126126 * - Visibility modifier: `public`, `protected`, or `private`.
127+ * - Set-visibility modifier: `public(set)`, `protected(set)`, or `private(set)`
127128 * - Scope modifier: `static`.
128129 * - Mutation modifier: `readonly`.
129130 * - Type declaration.
130131 * - Name.
131132 *
132- * Ref: https://www.php-fig.org/per/coding-style/#46-modifier-keywords
133+ * Ref:
134+ * - https://www.php-fig.org/per/coding-style/#46-modifier-keywords
135+ * - https://github.com/php-fig/per-coding-style/pull/99
133136 *
134137 * The `static` and `readonly` modifiers are mutually exclusive and cannot be used together.
135138 *
136139 * Based on that, the below modifier keyword order checks are sufficient (for now).
137140 */
138141
139- if ($ propertyInfo ['scope_specified ' ] === true && $ propertyInfo ['is_final ' ] === true ) {
140- $ scopePtr = $ phpcsFile ->findPrevious (Tokens::$ scopeModifiers , ($ stackPtr - 1 ));
142+ $ hasVisibilityModifier = ($ propertyInfo ['scope_specified ' ] === true || $ propertyInfo ['set_scope ' ] !== false );
143+ $ lastVisibilityModifier = $ phpcsFile ->findPrevious (Tokens::$ scopeModifiers , ($ stackPtr - 1 ));
144+ $ firstVisibilityModifier = $ lastVisibilityModifier ;
145+
146+ if ($ propertyInfo ['scope_specified ' ] === true && $ propertyInfo ['set_scope ' ] !== false ) {
147+ $ scopePtr = $ phpcsFile ->findPrevious ([T_PUBLIC , T_PROTECTED , T_PRIVATE ], ($ stackPtr - 1 ));
148+ $ setScopePtr = $ phpcsFile ->findPrevious ([T_PUBLIC_SET , T_PROTECTED_SET , T_PRIVATE_SET ], ($ stackPtr - 1 ));
149+ if ($ scopePtr > $ setScopePtr ) {
150+ $ error = 'The "read"-visibility must come before the "write"-visibility ' ;
151+ $ fix = $ phpcsFile ->addFixableError ($ error , $ stackPtr , 'AvizKeywordOrder ' );
152+ if ($ fix === true ) {
153+ $ phpcsFile ->fixer ->beginChangeset ();
154+
155+ for ($ i = ($ scopePtr + 1 ); $ scopePtr < $ stackPtr ; $ i ++) {
156+ if ($ tokens [$ i ]['code ' ] !== T_WHITESPACE ) {
157+ break ;
158+ }
159+
160+ $ phpcsFile ->fixer ->replaceToken ($ i , '' );
161+ }
162+
163+ $ phpcsFile ->fixer ->replaceToken ($ scopePtr , '' );
164+ $ phpcsFile ->fixer ->addContentBefore ($ setScopePtr , $ tokens [$ scopePtr ]['content ' ].' ' );
165+
166+ $ phpcsFile ->fixer ->endChangeset ();
167+ }
168+ }
169+
170+ $ firstVisibilityModifier = min ($ scopePtr , $ setScopePtr );
171+ }//end if
172+
173+ if ($ hasVisibilityModifier === true && $ propertyInfo ['is_final ' ] === true ) {
174+ $ scopePtr = $ firstVisibilityModifier ;
141175 $ finalPtr = $ phpcsFile ->findPrevious (T_FINAL , ($ stackPtr - 1 ));
142176 if ($ finalPtr > $ scopePtr ) {
143177 $ error = 'The final declaration must come before the visibility declaration ' ;
@@ -161,50 +195,50 @@ protected function processMemberVar(File $phpcsFile, $stackPtr)
161195 }
162196 }//end if
163197
164- if ($ propertyInfo [ ' scope_specified ' ] === true && $ propertyInfo ['is_static ' ] === true ) {
165- $ scopePtr = $ phpcsFile -> findPrevious (Tokens:: $ scopeModifiers , ( $ stackPtr - 1 )) ;
198+ if ($ hasVisibilityModifier === true && $ propertyInfo ['is_static ' ] === true ) {
199+ $ scopePtr = $ lastVisibilityModifier ;
166200 $ staticPtr = $ phpcsFile ->findPrevious (T_STATIC , ($ stackPtr - 1 ));
167201 if ($ scopePtr > $ staticPtr ) {
168202 $ error = 'The static declaration must come after the visibility declaration ' ;
169203 $ fix = $ phpcsFile ->addFixableError ($ error , $ stackPtr , 'StaticBeforeVisibility ' );
170204 if ($ fix === true ) {
171205 $ phpcsFile ->fixer ->beginChangeset ();
172206
173- for ($ i = ($ scopePtr + 1 ); $ scopePtr < $ stackPtr ; $ i ++) {
207+ for ($ i = ($ staticPtr + 1 ); $ staticPtr < $ stackPtr ; $ i ++) {
174208 if ($ tokens [$ i ]['code ' ] !== T_WHITESPACE ) {
175209 break ;
176210 }
177211
178212 $ phpcsFile ->fixer ->replaceToken ($ i , '' );
179213 }
180214
181- $ phpcsFile ->fixer ->replaceToken ($ scopePtr , '' );
182- $ phpcsFile ->fixer ->addContentBefore ( $ staticPtr , $ propertyInfo [ ' scope ' ]. ' ' );
215+ $ phpcsFile ->fixer ->replaceToken ($ staticPtr , '' );
216+ $ phpcsFile ->fixer ->addContent ( $ scopePtr , ' ' . $ tokens [ $ staticPtr ][ ' content ' ] );
183217
184218 $ phpcsFile ->fixer ->endChangeset ();
185219 }
186220 }
187221 }//end if
188222
189- if ($ propertyInfo [ ' scope_specified ' ] === true && $ propertyInfo ['is_readonly ' ] === true ) {
190- $ scopePtr = $ phpcsFile -> findPrevious (Tokens:: $ scopeModifiers , ( $ stackPtr - 1 )) ;
223+ if ($ hasVisibilityModifier === true && $ propertyInfo ['is_readonly ' ] === true ) {
224+ $ scopePtr = $ lastVisibilityModifier ;
191225 $ readonlyPtr = $ phpcsFile ->findPrevious (T_READONLY , ($ stackPtr - 1 ));
192226 if ($ scopePtr > $ readonlyPtr ) {
193227 $ error = 'The readonly declaration must come after the visibility declaration ' ;
194228 $ fix = $ phpcsFile ->addFixableError ($ error , $ stackPtr , 'ReadonlyBeforeVisibility ' );
195229 if ($ fix === true ) {
196230 $ phpcsFile ->fixer ->beginChangeset ();
197231
198- for ($ i = ($ scopePtr + 1 ); $ scopePtr < $ stackPtr ; $ i ++) {
232+ for ($ i = ($ readonlyPtr + 1 ); $ readonlyPtr < $ stackPtr ; $ i ++) {
199233 if ($ tokens [$ i ]['code ' ] !== T_WHITESPACE ) {
200234 break ;
201235 }
202236
203237 $ phpcsFile ->fixer ->replaceToken ($ i , '' );
204238 }
205239
206- $ phpcsFile ->fixer ->replaceToken ($ scopePtr , '' );
207- $ phpcsFile ->fixer ->addContentBefore ( $ readonlyPtr , $ propertyInfo [ ' scope ' ]. ' ' );
240+ $ phpcsFile ->fixer ->replaceToken ($ readonlyPtr , '' );
241+ $ phpcsFile ->fixer ->addContent ( $ scopePtr , ' ' . $ tokens [ $ readonlyPtr ][ ' content ' ] );
208242
209243 $ phpcsFile ->fixer ->endChangeset ();
210244 }
0 commit comments