@@ -32,13 +32,16 @@ class Caster
3232 public const EXCLUDE_EMPTY = 128 ;
3333 public const EXCLUDE_NOT_IMPORTANT = 256 ;
3434 public const EXCLUDE_STRICT = 512 ;
35+ public const EXCLUDE_UNINITIALIZED = 1024 ;
3536
3637 public const PREFIX_VIRTUAL = "\0~ \0" ;
3738 public const PREFIX_DYNAMIC = "\0+ \0" ;
3839 public const PREFIX_PROTECTED = "\0* \0" ;
3940 // usage: sprintf(Caster::PATTERN_PRIVATE, $class, $property)
4041 public const PATTERN_PRIVATE = "\0%s \0%s " ;
4142
43+ private static array $ classProperties = [];
44+
4245 /**
4346 * Casts objects to arrays and adds the dynamic property prefix.
4447 *
@@ -61,20 +64,17 @@ public static function castObject(object $obj, string $class, bool $hasDebugInfo
6164 return $ a ;
6265 }
6366
67+ $ classProperties = self ::$ classProperties [$ class ] ??= self ::getClassProperties (new \ReflectionClass ($ class ));
68+ $ a = array_replace ($ classProperties , $ a );
69+
6470 if ($ a ) {
65- static $ publicProperties = [];
6671 $ debugClass ??= get_debug_type ($ obj );
6772
6873 $ i = 0 ;
6974 $ prefixedKeys = [];
7075 foreach ($ a as $ k => $ v ) {
7176 if ("\0" !== ($ k [0 ] ?? '' )) {
72- if (!isset ($ publicProperties [$ class ])) {
73- foreach ((new \ReflectionClass ($ class ))->getProperties (\ReflectionProperty::IS_PUBLIC ) as $ prop ) {
74- $ publicProperties [$ class ][$ prop ->name ] = true ;
75- }
76- }
77- if (!isset ($ publicProperties [$ class ][$ k ])) {
77+ if (!isset ($ classProperties [$ k ])) {
7878 $ prefixedKeys [$ i ] = self ::PREFIX_DYNAMIC .$ k ;
7979 }
8080 } elseif ($ debugClass !== $ class && 1 === strpos ($ k , $ class )) {
@@ -131,6 +131,8 @@ public static function filter(array $a, int $filter, array $listedProperties = [
131131 $ type |= self ::EXCLUDE_EMPTY & $ filter ;
132132 } elseif (false === $ v || '' === $ v || '0 ' === $ v || 0 === $ v || 0.0 === $ v || [] === $ v ) {
133133 $ type |= self ::EXCLUDE_EMPTY & $ filter ;
134+ } elseif ($ v instanceof UninitializedStub) {
135+ $ type |= self ::EXCLUDE_UNINITIALIZED & $ filter ;
134136 }
135137 if ((self ::EXCLUDE_NOT_IMPORTANT & $ filter ) && !\in_array ($ k , $ listedProperties , true )) {
136138 $ type |= self ::EXCLUDE_NOT_IMPORTANT ;
@@ -169,4 +171,28 @@ public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array
169171
170172 return $ a ;
171173 }
174+
175+ private static function getClassProperties (\ReflectionClass $ class ): array
176+ {
177+ $ classProperties = [];
178+ $ className = $ class ->name ;
179+
180+ foreach ($ class ->getProperties () as $ p ) {
181+ if ($ p ->isStatic ()) {
182+ continue ;
183+ }
184+
185+ $ classProperties [match (true ) {
186+ $ p ->isPublic () => $ p ->name ,
187+ $ p ->isProtected () => self ::PREFIX_PROTECTED .$ p ->name ,
188+ default => "\0" .$ className ."\0" .$ p ->name ,
189+ }] = new UninitializedStub ($ p );
190+ }
191+
192+ if ($ parent = $ class ->getParentClass ()) {
193+ $ classProperties += self ::$ classProperties [$ parent ->name ] ??= self ::getClassProperties ($ parent );
194+ }
195+
196+ return $ classProperties ;
197+ }
172198}
0 commit comments