1010use PHPStan \DependencyInjection \AutowiredParameter ;
1111use PHPStan \DependencyInjection \AutowiredService ;
1212use PHPStan \Internal \SprintfHelper ;
13+ use PHPStan \Php \PhpVersion ;
1314use PHPStan \Reflection \ReflectionProvider ;
1415use PHPStan \Rules \ClassNameCheck ;
1516use PHPStan \Rules \ClassNameNodePair ;
@@ -40,6 +41,7 @@ public function __construct(
4041 private ReflectionProvider $ reflectionProvider ,
4142 private RuleLevelHelper $ ruleLevelHelper ,
4243 private ClassNameCheck $ classCheck ,
44+ private PhpVersion $ phpVersion ,
4345 #[AutowiredParameter(ref: '%tips.discoveringSymbols% ' )]
4446 private bool $ discoveringSymbolsTip ,
4547 )
@@ -49,7 +51,7 @@ public function __construct(
4951 /**
5052 * @return list<IdentifierRuleError>
5153 */
52- public function check (StaticPropertyFetch $ node , Scope $ scope ): array
54+ public function check (StaticPropertyFetch $ node , Scope $ scope, bool $ write ): array
5355 {
5456 if ($ node ->name instanceof Node \VarLikeIdentifier) {
5557 $ names = [$ node ->name ->name ];
@@ -59,7 +61,7 @@ public function check(StaticPropertyFetch $node, Scope $scope): array
5961
6062 $ errors = [];
6163 foreach ($ names as $ name ) {
62- $ errors = array_merge ($ errors , $ this ->processSingleProperty ($ scope , $ node , $ name ));
64+ $ errors = array_merge ($ errors , $ this ->processSingleProperty ($ scope , $ node , $ name, $ write ));
6365 }
6466
6567 return $ errors ;
@@ -68,7 +70,7 @@ public function check(StaticPropertyFetch $node, Scope $scope): array
6870 /**
6971 * @return list<IdentifierRuleError>
7072 */
71- private function processSingleProperty (Scope $ scope , StaticPropertyFetch $ node , string $ name ): array
73+ private function processSingleProperty (Scope $ scope , StaticPropertyFetch $ node , string $ name, bool $ write ): array
7274 {
7375 $ messages = [];
7476 if ($ node ->class instanceof Name) {
@@ -198,7 +200,11 @@ private function processSingleProperty(Scope $scope, StaticPropertyFetch $node,
198200
199201 while ($ parentClassReflection !== null ) {
200202 if ($ parentClassReflection ->hasStaticProperty ($ name )) {
201- if ($ scope ->canReadProperty ($ parentClassReflection ->getStaticProperty ($ name ))) {
203+ if ($ write ) {
204+ if ($ scope ->canWriteProperty ($ parentClassReflection ->getStaticProperty ($ name ))) {
205+ return [];
206+ }
207+ } elseif ($ scope ->canReadProperty ($ parentClassReflection ->getStaticProperty ($ name ))) {
202208 return [];
203209 }
204210 return [
@@ -241,7 +247,19 @@ private function processSingleProperty(Scope $scope, StaticPropertyFetch $node,
241247 }
242248
243249 $ property = $ classType ->getStaticProperty ($ name , $ scope );
244- if (!$ scope ->canReadProperty ($ property )) {
250+ if ($ write ) {
251+ if ($ scope ->canWriteProperty ($ property )) {
252+ return $ messages ;
253+ }
254+ } elseif ($ scope ->canReadProperty ($ property )) {
255+ return $ messages ;
256+ }
257+
258+ if (
259+ !$ this ->phpVersion ->supportsAsymmetricVisibilityForStaticProperties ()
260+ || !$ write
261+ || (!$ property ->isPrivateSet () && !$ property ->isProtectedSet ())
262+ ) {
245263 return array_merge ($ messages , [
246264 RuleErrorBuilder::message (sprintf (
247265 'Access to %s property $%s of class %s. ' ,
@@ -252,7 +270,14 @@ private function processSingleProperty(Scope $scope, StaticPropertyFetch $node,
252270 ]);
253271 }
254272
255- return $ messages ;
273+ return array_merge ($ messages , [
274+ RuleErrorBuilder::message (sprintf (
275+ 'Access to %s property $%s of class %s. ' ,
276+ $ property ->isPrivateSet () ? 'private(set) ' : 'protected(set) ' ,
277+ $ name ,
278+ $ property ->getDeclaringClass ()->getDisplayName (),
279+ ))->identifier (sprintf ('assign.staticProperty%s ' , $ property ->isPrivateSet () ? 'PrivateSet ' : 'ProtectedSet ' ))->build (),
280+ ]);
256281 }
257282
258283}
0 commit comments