@@ -83,6 +83,12 @@ final class ClassReflection
8383 /** @var ExtendedPropertyReflection[] */
8484 private array $ properties = [];
8585
86+ /** @var ExtendedPropertyReflection[] */
87+ private array $ instanceProperties = [];
88+
89+ /** @var ExtendedPropertyReflection[] */
90+ private array $ staticProperties = [];
91+
8692 /** @var RealClassClassConstantReflection[] */
8793 private array $ constants = [];
8894
@@ -149,6 +155,12 @@ final class ClassReflection
149155 /** @var array<string, bool> */
150156 private array $ hasPropertyCache = [];
151157
158+ /** @var array<string, bool> */
159+ private array $ hasInstancePropertyCache = [];
160+
161+ /** @var array<string, bool> */
162+ private array $ hasStaticPropertyCache = [];
163+
152164 /**
153165 * @param PropertiesClassReflectionExtension[] $propertiesClassReflectionExtensions
154166 * @param MethodsClassReflectionExtension[] $methodsClassReflectionExtensions
@@ -463,6 +475,9 @@ private function allowsDynamicPropertiesExtensions(): bool
463475 return false ;
464476 }
465477
478+ /**
479+ * @deprecated Use hasInstanceProperty or hasStaticProperty instead
480+ */
466481 public function hasProperty (string $ propertyName ): bool
467482 {
468483 if (array_key_exists ($ propertyName , $ this ->hasPropertyCache )) {
@@ -482,13 +497,61 @@ public function hasProperty(string $propertyName): bool
482497 }
483498 }
484499
500+ // For BC purpose
501+ if ($ this ->getPhpExtension ()->hasStaticProperty ($ this , $ propertyName )) {
502+ return $ this ->hasPropertyCache [$ propertyName ] = true ;
503+ }
504+
485505 if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasProperty ($ this , $ propertyName )) {
486506 return $ this ->hasPropertyCache [$ propertyName ] = true ;
487507 }
488508
489509 return $ this ->hasPropertyCache [$ propertyName ] = false ;
490510 }
491511
512+ public function hasInstanceProperty (string $ propertyName ): bool
513+ {
514+ if (array_key_exists ($ propertyName , $ this ->hasInstancePropertyCache )) {
515+ return $ this ->hasInstancePropertyCache [$ propertyName ];
516+ }
517+
518+ if ($ this ->isEnum ()) {
519+ return $ this ->hasInstancePropertyCache [$ propertyName ] = $ this ->hasNativeProperty ($ propertyName );
520+ }
521+
522+ foreach ($ this ->propertiesClassReflectionExtensions as $ i => $ extension ) {
523+ if ($ i > 0 && !$ this ->allowsDynamicPropertiesExtensions ()) {
524+ break ;
525+ }
526+ if ($ extension ->hasProperty ($ this , $ propertyName )) {
527+ return $ this ->hasInstancePropertyCache [$ propertyName ] = true ;
528+ }
529+ }
530+
531+ if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasInstanceProperty ($ this , $ propertyName )) {
532+ return $ this ->hasPropertyCache [$ propertyName ] = true ;
533+ }
534+
535+ return $ this ->hasPropertyCache [$ propertyName ] = false ;
536+ }
537+
538+ public function hasStaticProperty (string $ propertyName ): bool
539+ {
540+ if (array_key_exists ($ propertyName , $ this ->hasStaticPropertyCache )) {
541+ return $ this ->hasStaticPropertyCache [$ propertyName ];
542+ }
543+
544+ if ($ this ->getPhpExtension ()->hasStaticProperty ($ this , $ propertyName )) {
545+ return $ this ->hasStaticPropertyCache [$ propertyName ] = true ;
546+ }
547+
548+ if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasStaticProperty ($ this , $ propertyName )) {
549+ return $ this ->hasStaticPropertyCache [$ propertyName ] = true ;
550+ }
551+
552+ return $ this ->hasStaticPropertyCache [$ propertyName ] = false ;
553+ }
554+
492555 public function hasMethod (string $ methodName ): bool
493556 {
494557 if (array_key_exists ($ methodName , $ this ->hasMethodCache )) {
@@ -633,6 +696,20 @@ public function evictPrivateSymbols(): void
633696
634697 unset($ this ->properties [$ name ]);
635698 }
699+ foreach ($ this ->instanceProperties as $ name => $ property ) {
700+ if (!$ property ->isPrivate ()) {
701+ continue ;
702+ }
703+
704+ unset($ this ->instanceProperties [$ name ]);
705+ }
706+ foreach ($ this ->staticProperties as $ name => $ property ) {
707+ if (!$ property ->isPrivate ()) {
708+ continue ;
709+ }
710+
711+ unset($ this ->staticProperties [$ name ]);
712+ }
636713 foreach ($ this ->methods as $ name => $ method ) {
637714 if (!$ method ->isPrivate ()) {
638715 continue ;
@@ -643,6 +720,7 @@ public function evictPrivateSymbols(): void
643720 $ this ->getPhpExtension ()->evictPrivateSymbols ($ this ->getCacheKey ());
644721 }
645722
723+ /** @deprecated Use getInstanceProperty or getStaticProperty */
646724 public function getProperty (string $ propertyName , ClassMemberAccessAnswerer $ scope ): ExtendedPropertyReflection
647725 {
648726 if ($ this ->isEnum ()) {
@@ -672,6 +750,15 @@ public function getProperty(string $propertyName, ClassMemberAccessAnswerer $sco
672750 }
673751 }
674752
753+ // For BC purpose
754+ if ($ this ->getPhpExtension ()->hasStaticProperty ($ this , $ propertyName )) {
755+ $ property = $ this ->wrapExtendedProperty ($ this ->getPhpExtension ()->getStaticProperty ($ this , $ propertyName ));
756+ if ($ scope ->canReadProperty ($ property )) {
757+ return $ this ->properties [$ key ] = $ property ;
758+ }
759+ $ this ->properties [$ key ] = $ property ;
760+ }
761+
675762 if (!isset ($ this ->properties [$ key ])) {
676763 if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasProperty ($ this , $ propertyName )) {
677764 $ property = $ this ->requireExtendsPropertiesClassReflectionExtension ->getProperty ($ this , $ propertyName );
@@ -686,9 +773,83 @@ public function getProperty(string $propertyName, ClassMemberAccessAnswerer $sco
686773 return $ this ->properties [$ key ];
687774 }
688775
776+ public function getInstanceProperty (string $ propertyName , ClassMemberAccessAnswerer $ scope ): ExtendedPropertyReflection
777+ {
778+ if ($ this ->isEnum ()) {
779+ return $ this ->getNativeProperty ($ propertyName );
780+ }
781+
782+ $ key = $ propertyName ;
783+ if ($ scope ->isInClass ()) {
784+ $ key = sprintf ('%s-%s ' , $ key , $ scope ->getClassReflection ()->getCacheKey ());
785+ }
786+
787+ if (!isset ($ this ->instanceProperties [$ key ])) {
788+ foreach ($ this ->propertiesClassReflectionExtensions as $ i => $ extension ) {
789+ if ($ i > 0 && !$ this ->allowsDynamicPropertiesExtensions ()) {
790+ break ;
791+ }
792+
793+ if (!$ extension ->hasProperty ($ this , $ propertyName )) {
794+ continue ;
795+ }
796+
797+ $ property = $ this ->wrapExtendedProperty ($ extension ->getProperty ($ this , $ propertyName ));
798+ if ($ scope ->canReadProperty ($ property )) {
799+ return $ this ->instanceProperties [$ key ] = $ property ;
800+ }
801+ $ this ->instanceProperties [$ key ] = $ property ;
802+ }
803+ }
804+
805+ if (!isset ($ this ->instanceProperties [$ key ])) {
806+ if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasInstanceProperty ($ this , $ propertyName )) {
807+ $ property = $ this ->requireExtendsPropertiesClassReflectionExtension ->getInstanceProperty ($ this , $ propertyName );
808+ $ this ->instanceProperties [$ key ] = $ property ;
809+ }
810+ }
811+
812+ if (!isset ($ this ->instanceProperties [$ key ])) {
813+ throw new MissingPropertyFromReflectionException ($ this ->getName (), $ propertyName );
814+ }
815+
816+ return $ this ->instanceProperties [$ key ];
817+ }
818+
819+ public function getStaticProperty (string $ propertyName , ClassMemberAccessAnswerer $ scope ): ExtendedPropertyReflection
820+ {
821+ $ key = $ propertyName ;
822+ if ($ scope ->isInClass ()) {
823+ $ key = sprintf ('%s-%s ' , $ key , $ scope ->getClassReflection ()->getCacheKey ());
824+ }
825+
826+ if (!isset ($ this ->staticProperties [$ key ])) {
827+ if ($ this ->getPhpExtension ()->hasStaticProperty ($ this , $ propertyName )) {
828+ $ property = $ this ->wrapExtendedProperty ($ this ->getPhpExtension ()->getStaticProperty ($ this , $ propertyName ));
829+ if ($ scope ->canReadProperty ($ property )) {
830+ return $ this ->staticProperties [$ key ] = $ property ;
831+ }
832+ $ this ->staticProperties [$ key ] = $ property ;
833+ }
834+ }
835+
836+ if (!isset ($ this ->staticProperties [$ key ])) {
837+ if ($ this ->requireExtendsPropertiesClassReflectionExtension ->hasStaticProperty ($ this , $ propertyName )) {
838+ $ property = $ this ->requireExtendsPropertiesClassReflectionExtension ->getStaticProperty ($ this , $ propertyName );
839+ $ this ->staticProperties [$ key ] = $ property ;
840+ }
841+ }
842+
843+ if (!isset ($ this ->staticProperties [$ key ])) {
844+ throw new MissingPropertyFromReflectionException ($ this ->getName (), $ propertyName );
845+ }
846+
847+ return $ this ->staticProperties [$ key ];
848+ }
849+
689850 public function hasNativeProperty (string $ propertyName ): bool
690851 {
691- return $ this ->getPhpExtension ()->hasProperty ($ this , $ propertyName );
852+ return $ this ->getPhpExtension ()->hasNativeProperty ($ this , $ propertyName );
692853 }
693854
694855 public function getNativeProperty (string $ propertyName ): PhpPropertyReflection
0 commit comments