@@ -33,40 +33,39 @@ final class ComponentReflection extends \ReflectionClass
3333
3434
3535 /**
36- * Returns array of classes persistent parameters. They have public visibility,
37- * are non-static and have annotation @persistent.
36+ * Returns array of persistent properties. They are public and have attribute #[Persistent] or annotation @persistent.
3837 */
39- public function getPersistentParams (? string $ class = null ): array
38+ public function getPersistentParams (): array
4039 {
41- $ class = $ class ?? $ this ->getName ();
42- $ params = &self ::$ ppCache [$ class ];
40+ $ params = &self ::$ ppCache [$ this ->getName ()];
4341 if ($ params !== null ) {
4442 return $ params ;
4543 }
4644
4745 $ params = [];
48- if (is_subclass_of ($ class , Component::class)) {
49- $ isPresenter = is_subclass_of ($ class , Presenter::class);
50- $ defaults = get_class_vars ($ class );
51- foreach ($ defaults as $ name => $ default ) {
52- $ rp = new \ReflectionProperty ($ class , $ name );
53- if (!$ rp ->isStatic ()
54- && ((PHP_VERSION_ID >= 80000 && $ rp ->getAttributes (Nette \Application \Attributes \Persistent::class))
55- || self ::parseAnnotation ($ rp , 'persistent ' ))
56- ) {
57- $ params [$ name ] = [
58- 'def ' => $ default ,
59- 'type ' => self ::getPropertyType ($ rp , $ default ),
60- 'since ' => $ isPresenter ? Nette \Utils \Reflection::getPropertyDeclaringClass ($ rp )->getName () : null ,
61- ];
62- }
46+ $ isPresenter = $ this ->isSubclassOf (Presenter::class);
47+ $ defaults = $ this ->getDefaultProperties ();
48+ foreach ($ this ->getProperties (\ReflectionProperty::IS_PUBLIC ) as $ prop ) {
49+ if (!$ prop ->isStatic ()
50+ && (self ::parseAnnotation ($ prop , 'persistent ' )
51+ || (PHP_VERSION_ID >= 80000 && $ prop ->getAttributes (Nette \Application \Attributes \Persistent::class)))
52+ ) {
53+ $ default = $ defaults [$ prop ->getName ()] ?? null ;
54+ $ params [$ prop ->getName ()] = [
55+ 'def ' => $ default ,
56+ 'type ' => self ::getPropertyType ($ prop , $ default ),
57+ 'since ' => $ isPresenter ? Nette \Utils \Reflection::getPropertyDeclaringClass ($ prop )->getName () : null ,
58+ ];
6359 }
60+ }
6461
65- foreach ($ this ->getPersistentParams (get_parent_class ($ class )) as $ name => $ param ) {
62+ if ($ this ->getParentClass ()->isSubclassOf (Component::class)) {
63+ $ parent = new self ($ this ->getParentClass ()->getName ());
64+ foreach ($ parent ->getPersistentParams () as $ name => $ meta ) {
6665 if (isset ($ params [$ name ])) {
67- $ params [$ name ]['since ' ] = $ param ['since ' ];
66+ $ params [$ name ]['since ' ] = $ meta ['since ' ];
6867 } else {
69- $ params [$ name ] = $ param ;
68+ $ params [$ name ] = $ meta ;
7069 }
7170 }
7271 }
@@ -75,25 +74,22 @@ public function getPersistentParams(?string $class = null): array
7574 }
7675
7776
78- public function getPersistentComponents (? string $ class = null ): array
77+ public function getPersistentComponents (): array
7978 {
80- $ class = $ class ?? $ this ->getName ();
79+ $ class = $ this ->getName ();
8180 $ components = &self ::$ pcCache [$ class ];
8281 if ($ components !== null ) {
8382 return $ components ;
8483 }
8584
8685 $ components = [];
87- if (is_subclass_of ( $ class , Presenter::class)) {
86+ if ($ this -> isSubclassOf ( Presenter::class)) {
8887 foreach ($ class ::getPersistentComponents () as $ name => $ meta ) {
89- if (is_string ($ meta )) {
90- $ name = $ meta ;
91- }
92-
93- $ components [$ name ] = ['since ' => $ class ];
88+ $ components [is_string ($ meta ) ? $ meta : $ name ] = ['since ' => $ class ];
9489 }
9590
96- $ components = $ this ->getPersistentComponents (get_parent_class ($ class )) + $ components ;
91+ $ parent = new self ($ this ->getParentClass ()->getName ());
92+ $ components = $ parent ->getPersistentComponents () + $ components ;
9793 }
9894
9995 return $ components ;
@@ -197,12 +193,28 @@ public static function combineArgs(\ReflectionFunctionAbstract $method, array $a
197193
198194
199195 /**
200- * Non data-loss type conversion.
196+ * Lossless type conversion.
201197 */
202198 public static function convertType (&$ val , string $ types ): bool
203199 {
200+ $ scalars = ['string ' => 1 , 'int ' => 1 , 'float ' => 1 , 'bool ' => 1 , 'boolean ' => 1 , 'double ' => 1 , 'integer ' => 1 ];
201+ $ testable = ['iterable ' => 1 , 'object ' => 1 , 'array ' => 1 , 'null ' => 1 ];
202+
204203 foreach (explode ('| ' , $ types ) as $ type ) {
205- if (self ::convertSingleType ($ val , $ type )) {
204+ if (isset ($ scalars [$ type ])) {
205+ $ ok = self ::castScalar ($ val , $ type );
206+ } elseif (isset ($ testable [$ type ])) {
207+ $ ok = "is_ $ type " ($ val );
208+ } elseif ($ type === 'scalar ' ) { // special type due to historical reasons
209+ $ ok = !is_array ($ val );
210+ } elseif ($ type === 'mixed ' ) {
211+ $ ok = true ;
212+ } elseif ($ type === 'callable ' ) { // intentionally disabled for security reasons
213+ $ ok = false ;
214+ } else {
215+ $ ok = $ val instanceof $ type ;
216+ }
217+ if ($ ok ) {
206218 return true ;
207219 }
208220 }
@@ -212,52 +224,26 @@ public static function convertType(&$val, string $types): bool
212224
213225
214226 /**
215- * Non data-loss type conversion .
227+ * Lossless type casting .
216228 */
217- private static function convertSingleType (&$ val , string $ type ): bool
229+ private static function castScalar (&$ val , string $ type ): bool
218230 {
219- $ builtin = [
220- 'string ' => 1 , 'int ' => 1 , 'float ' => 1 , 'bool ' => 1 , 'array ' => 1 , 'object ' => 1 ,
221- 'callable ' => 1 , 'iterable ' => 1 , 'void ' => 1 , 'null ' => 1 , 'mixed ' => 1 ,
222- 'boolean ' => 1 , 'integer ' => 1 , 'double ' => 1 , 'scalar ' => 1 ,
223- ];
224-
225- if (empty ($ builtin [$ type ])) {
226- return $ val instanceof $ type ;
227-
228- } elseif ($ type === 'object ' ) {
229- return is_object ($ val );
230-
231- } elseif ($ type === 'callable ' ) {
232- return false ;
233-
234- } elseif ($ type === 'scalar ' ) {
235- return !is_array ($ val );
236-
237- } elseif ($ type === 'array ' || $ type === 'iterable ' ) {
238- return is_array ($ val );
239-
240- } elseif ($ type === 'mixed ' ) {
241- return true ;
242-
243- } elseif (!is_scalar ($ val )) { // array, resource, null, etc.
231+ if (!is_scalar ($ val )) {
244232 return false ;
233+ }
245234
246- } else {
247- $ tmp = ($ val === false ? '0 ' : (string ) $ val );
248- if ($ type === 'double ' || $ type === 'float ' ) {
249- $ tmp = preg_replace ('#\.0*$#D ' , '' , $ tmp );
250- }
251-
252- $ orig = $ tmp ;
253- settype ($ tmp , $ type );
254- if ($ orig !== ($ tmp === false ? '0 ' : (string ) $ tmp )) {
255- return false ; // data-loss occurs
256- }
235+ $ tmp = ($ val === false ? '0 ' : (string ) $ val );
236+ if ($ type === 'double ' || $ type === 'float ' ) {
237+ $ tmp = preg_replace ('#\.0*$#D ' , '' , $ tmp );
238+ }
257239
258- $ val = $ tmp ;
240+ $ orig = $ tmp ;
241+ settype ($ tmp , $ type );
242+ if ($ orig !== ($ tmp === false ? '0 ' : (string ) $ tmp )) {
243+ return false ; // data-loss occurs
259244 }
260245
246+ $ val = $ tmp ;
261247 return true ;
262248 }
263249
0 commit comments