@@ -468,17 +468,29 @@ private function castScalarValueForTypedProperty(mixed $value, array $types): mi
468468 private function getBuiltinPropertyTypesMap (array $ properties ): array
469469 {
470470 $ className = static ::class;
471+ $ requested = array_fill_keys ($ properties , true );
471472
472473 if (! isset (self ::$ propertyBuiltinTypesCache [$ className ])) {
473474 self ::$ propertyBuiltinTypesCache [$ className ] = [];
475+ }
476+
477+ // Fill only the properties requested by this call that are not cached yet.
478+ $ missing = array_diff_key ($ requested , self ::$ propertyBuiltinTypesCache [$ className ]);
474479
480+ if ($ missing !== []) {
475481 $ reflection = new ReflectionClass ($ className );
476482
477483 foreach ($ reflection ->getProperties () as $ property ) {
484+ $ propertyName = $ property ->getName ();
485+
486+ if (! isset ($ missing [$ propertyName ])) {
487+ continue ;
488+ }
489+
478490 $ type = $ property ->getType ();
479491
480492 if (! $ type instanceof ReflectionType) {
481- self ::$ propertyBuiltinTypesCache [$ className ][$ property -> getName () ] = [];
493+ self ::$ propertyBuiltinTypesCache [$ className ][$ propertyName ] = [];
482494
483495 continue ;
484496 }
@@ -498,7 +510,12 @@ private function getBuiltinPropertyTypesMap(array $properties): array
498510 $ builtinTypes [] = 'null ' ;
499511 }
500512
501- self ::$ propertyBuiltinTypesCache [$ className ][$ property ->getName ()] = $ builtinTypes ;
513+ self ::$ propertyBuiltinTypesCache [$ className ][$ propertyName ] = $ builtinTypes ;
514+ }
515+
516+ // Untyped or unresolved properties are cached as empty to avoid re-reflecting them.
517+ foreach (array_keys ($ missing ) as $ propertyName ) {
518+ self ::$ propertyBuiltinTypesCache [$ className ][$ propertyName ] ??= [];
502519 }
503520 }
504521
0 commit comments