@@ -82,6 +82,12 @@ final class ClassReflection
8282 /** @var ExtendedPropertyReflection[] */
8383 private array $ properties = [];
8484
85+ /** @var ExtendedPropertyReflection[] */
86+ private array $ instanceProperties = [];
87+
88+ /** @var ExtendedPropertyReflection[] */
89+ private array $ staticProperties = [];
90+
8591 /** @var RealClassClassConstantReflection[] */
8692 private array $ constants = [];
8793
@@ -148,6 +154,12 @@ final class ClassReflection
148154 /** @var array<string, bool> */
149155 private array $ hasPropertyCache = [];
150156
157+ /** @var array<string, bool> */
158+ private array $ hasInstancePropertyCache = [];
159+
160+ /** @var array<string, bool> */
161+ private array $ hasStaticPropertyCache = [];
162+
151163 /**
152164 * @param PropertiesClassReflectionExtension[] $propertiesClassReflectionExtensions
153165 * @param MethodsClassReflectionExtension[] $methodsClassReflectionExtensions
@@ -462,6 +474,9 @@ private function allowsDynamicPropertiesExtensions(): bool
462474 return false ;
463475 }
464476
477+ /**
478+ * @deprecated Use hasInstanceProperty or hasStaticProperty instead
479+ */
465480 public function hasProperty (string $ propertyName ): bool
466481 {
467482 if (array_key_exists ($ propertyName , $ this ->hasPropertyCache )) {
@@ -481,13 +496,61 @@ public function hasProperty(string $propertyName): bool
481496 }
482497 }
483498
499+ // For BC purpose
500+ if ($ this ->getPhpExtension ()->hasStaticProperty ($ this , $ propertyName )) {
501+ return $ this ->hasPropertyCache [$ propertyName ] = true ;
502+ }
503+
484504 if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasProperty ($ this , $ propertyName )) {
485505 return $ this ->hasPropertyCache [$ propertyName ] = true ;
486506 }
487507
488508 return $ this ->hasPropertyCache [$ propertyName ] = false ;
489509 }
490510
511+ public function hasInstanceProperty (string $ propertyName ): bool
512+ {
513+ if (array_key_exists ($ propertyName , $ this ->hasInstancePropertyCache )) {
514+ return $ this ->hasInstancePropertyCache [$ propertyName ];
515+ }
516+
517+ if ($ this ->isEnum ()) {
518+ return $ this ->hasInstancePropertyCache [$ propertyName ] = $ this ->hasNativeProperty ($ propertyName );
519+ }
520+
521+ foreach ($ this ->propertiesClassReflectionExtensions as $ i => $ extension ) {
522+ if ($ i > 0 && !$ this ->allowsDynamicPropertiesExtensions ()) {
523+ break ;
524+ }
525+ if ($ extension ->hasProperty ($ this , $ propertyName )) {
526+ return $ this ->hasInstancePropertyCache [$ propertyName ] = true ;
527+ }
528+ }
529+
530+ if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasInstanceProperty ($ this , $ propertyName )) {
531+ return $ this ->hasPropertyCache [$ propertyName ] = true ;
532+ }
533+
534+ return $ this ->hasPropertyCache [$ propertyName ] = false ;
535+ }
536+
537+ public function hasStaticProperty (string $ propertyName ): bool
538+ {
539+ if (array_key_exists ($ propertyName , $ this ->hasStaticPropertyCache )) {
540+ return $ this ->hasStaticPropertyCache [$ propertyName ];
541+ }
542+
543+ if ($ this ->getPhpExtension ()->hasStaticProperty ($ this , $ propertyName )) {
544+ return $ this ->hasStaticPropertyCache [$ propertyName ] = true ;
545+ }
546+
547+ if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasStaticProperty ($ this , $ propertyName )) {
548+ return $ this ->hasStaticPropertyCache [$ propertyName ] = true ;
549+ }
550+
551+ return $ this ->hasStaticPropertyCache [$ propertyName ] = false ;
552+ }
553+
491554 public function hasMethod (string $ methodName ): bool
492555 {
493556 if (array_key_exists ($ methodName , $ this ->hasMethodCache )) {
@@ -632,6 +695,20 @@ public function evictPrivateSymbols(): void
632695
633696 unset($ this ->properties [$ name ]);
634697 }
698+ foreach ($ this ->instanceProperties as $ name => $ property ) {
699+ if (!$ property ->isPrivate ()) {
700+ continue ;
701+ }
702+
703+ unset($ this ->instanceProperties [$ name ]);
704+ }
705+ foreach ($ this ->staticProperties as $ name => $ property ) {
706+ if (!$ property ->isPrivate ()) {
707+ continue ;
708+ }
709+
710+ unset($ this ->staticProperties [$ name ]);
711+ }
635712 foreach ($ this ->methods as $ name => $ method ) {
636713 if (!$ method ->isPrivate ()) {
637714 continue ;
@@ -642,6 +719,7 @@ public function evictPrivateSymbols(): void
642719 $ this ->getPhpExtension ()->evictPrivateSymbols ($ this ->getCacheKey ());
643720 }
644721
722+ /** @deprecated Use getInstanceProperty or getStaticProperty */
645723 public function getProperty (string $ propertyName , ClassMemberAccessAnswerer $ scope ): ExtendedPropertyReflection
646724 {
647725 if ($ this ->isEnum ()) {
@@ -671,6 +749,15 @@ public function getProperty(string $propertyName, ClassMemberAccessAnswerer $sco
671749 }
672750 }
673751
752+ // For BC purpose
753+ if ($ this ->getPhpExtension ()->hasStaticProperty ($ this , $ propertyName )) {
754+ $ property = $ this ->wrapExtendedProperty ($ this ->getPhpExtension ()->getStaticProperty ($ this , $ propertyName ));
755+ if ($ scope ->canReadProperty ($ property )) {
756+ return $ this ->properties [$ key ] = $ property ;
757+ }
758+ $ this ->properties [$ key ] = $ property ;
759+ }
760+
674761 if (!isset ($ this ->properties [$ key ])) {
675762 if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasProperty ($ this , $ propertyName )) {
676763 $ property = $ this ->requireExtendsPropertiesClassReflectionExtension ->getProperty ($ this , $ propertyName );
@@ -685,9 +772,83 @@ public function getProperty(string $propertyName, ClassMemberAccessAnswerer $sco
685772 return $ this ->properties [$ key ];
686773 }
687774
775+ public function getInstanceProperty (string $ propertyName , ClassMemberAccessAnswerer $ scope ): ExtendedPropertyReflection
776+ {
777+ if ($ this ->isEnum ()) {
778+ return $ this ->getNativeProperty ($ propertyName );
779+ }
780+
781+ $ key = $ propertyName ;
782+ if ($ scope ->isInClass ()) {
783+ $ key = sprintf ('%s-%s ' , $ key , $ scope ->getClassReflection ()->getCacheKey ());
784+ }
785+
786+ if (!isset ($ this ->instanceProperties [$ key ])) {
787+ foreach ($ this ->propertiesClassReflectionExtensions as $ i => $ extension ) {
788+ if ($ i > 0 && !$ this ->allowsDynamicPropertiesExtensions ()) {
789+ break ;
790+ }
791+
792+ if (!$ extension ->hasProperty ($ this , $ propertyName )) {
793+ continue ;
794+ }
795+
796+ $ property = $ this ->wrapExtendedProperty ($ extension ->getProperty ($ this , $ propertyName ));
797+ if ($ scope ->canReadProperty ($ property )) {
798+ return $ this ->instanceProperties [$ key ] = $ property ;
799+ }
800+ $ this ->instanceProperties [$ key ] = $ property ;
801+ }
802+ }
803+
804+ if (!isset ($ this ->instanceProperties [$ key ])) {
805+ if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasInstanceProperty ($ this , $ propertyName )) {
806+ $ property = $ this ->requireExtendsPropertiesClassReflectionExtension ->getInstanceProperty ($ this , $ propertyName );
807+ $ this ->instanceProperties [$ key ] = $ property ;
808+ }
809+ }
810+
811+ if (!isset ($ this ->instanceProperties [$ key ])) {
812+ throw new MissingPropertyFromReflectionException ($ this ->getName (), $ propertyName );
813+ }
814+
815+ return $ this ->instanceProperties [$ key ];
816+ }
817+
818+ public function getStaticProperty (string $ propertyName , ClassMemberAccessAnswerer $ scope ): ExtendedPropertyReflection
819+ {
820+ $ key = $ propertyName ;
821+ if ($ scope ->isInClass ()) {
822+ $ key = sprintf ('%s-%s ' , $ key , $ scope ->getClassReflection ()->getCacheKey ());
823+ }
824+
825+ if (!isset ($ this ->staticProperties [$ key ])) {
826+ if ($ this ->getPhpExtension ()->hasStaticProperty ($ this , $ propertyName )) {
827+ $ property = $ this ->wrapExtendedProperty ($ this ->getPhpExtension ()->getStaticProperty ($ this , $ propertyName ));
828+ if ($ scope ->canReadProperty ($ property )) {
829+ return $ this ->staticProperties [$ key ] = $ property ;
830+ }
831+ $ this ->staticProperties [$ key ] = $ property ;
832+ }
833+ }
834+
835+ if (!isset ($ this ->staticProperties [$ key ])) {
836+ if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasStaticProperty ($ this , $ propertyName )) {
837+ $ property = $ this ->requireExtendsPropertiesClassReflectionExtension ->getStaticProperty ($ this , $ propertyName );
838+ $ this ->staticProperties [$ key ] = $ property ;
839+ }
840+ }
841+
842+ if (!isset ($ this ->staticProperties [$ key ])) {
843+ throw new MissingPropertyFromReflectionException ($ this ->getName (), $ propertyName );
844+ }
845+
846+ return $ this ->staticProperties [$ key ];
847+ }
848+
688849 public function hasNativeProperty (string $ propertyName ): bool
689850 {
690- return $ this ->getPhpExtension ()->hasProperty ($ this , $ propertyName );
851+ return $ this ->getPhpExtension ()->hasNativeProperty ($ this , $ propertyName );
691852 }
692853
693854 public function getNativeProperty (string $ propertyName ): PhpPropertyReflection
0 commit comments