@@ -1624,11 +1624,13 @@ public static function getAttribute(Environment $env, Source $source, $object, $
16241624 {
16251625 // array
16261626 if (Template::METHOD_CALL !== $ type ) {
1627- $ arrayItem = \is_bool ($ item ) || \is_float ($ item ) ? (int ) $ item : $ item ;
1627+ $ arrayItem = \is_bool ($ item ) || \is_float ($ item ) ? (int ) $ item : $ item = ( string ) $ item ;
16281628
1629- if (((\is_array ($ object ) || $ object instanceof \ArrayObject) && (isset ($ object [$ arrayItem ]) || \array_key_exists ($ arrayItem , (array ) $ object )))
1630- || ($ object instanceof \ArrayAccess && isset ($ object [$ arrayItem ]))
1631- ) {
1629+ if (match (true ) {
1630+ \is_array ($ object ) => \array_key_exists ($ arrayItem , $ object ),
1631+ $ object instanceof \ArrayAccess => $ object ->offsetExists ($ arrayItem ),
1632+ default => false ,
1633+ }) {
16321634 if ($ isDefinedTest ) {
16331635 return true ;
16341636 }
@@ -1671,6 +1673,8 @@ public static function getAttribute(Environment $env, Source $source, $object, $
16711673 }
16721674 }
16731675
1676+ $ item = (string ) $ item ;
1677+
16741678 if (!\is_object ($ object )) {
16751679 if ($ isDefinedTest ) {
16761680 return false ;
@@ -1697,7 +1701,23 @@ public static function getAttribute(Environment $env, Source $source, $object, $
16971701
16981702 // object property
16991703 if (Template::METHOD_CALL !== $ type ) {
1700- if (isset ($ object ->$ item ) || \array_key_exists ((string ) $ item , (array ) $ object )) {
1704+ static $ propertyCheckers = [];
1705+
1706+ if (isset ($ object ->$ item )
1707+ || ($ propertyCheckers [$ object ::class][$ item ] ??= self ::getPropertyChecker ($ object ::class, $ item ))($ object , $ item )
1708+ ) {
1709+ if ($ isDefinedTest ) {
1710+ return true ;
1711+ }
1712+
1713+ if ($ sandboxed ) {
1714+ $ env ->getExtension (SandboxExtension::class)->checkPropertyAllowed ($ object , $ item , $ lineno , $ source );
1715+ }
1716+
1717+ return $ object ->$ item ;
1718+ }
1719+
1720+ if ($ object instanceof \DateTimeInterface && \in_array ($ item , ['date ' , 'timezone ' , 'timezone_type ' ], true )) {
17011721 if ($ isDefinedTest ) {
17021722 return true ;
17031723 }
@@ -1706,7 +1726,7 @@ public static function getAttribute(Environment $env, Source $source, $object, $
17061726 $ env ->getExtension (SandboxExtension::class)->checkPropertyAllowed ($ object , $ item , $ lineno , $ source );
17071727 }
17081728
1709- return isset ( $ object -> $ item ) ? $ object -> $ item : (( array ) $ object )[( string ) $ item ];
1729+ return (( array ) $ object )[$ item ];
17101730 }
17111731
17121732 if (\defined ($ object ::class.':: ' .$ item )) {
@@ -2055,4 +2075,27 @@ public static function parseAttributeFunction(Parser $parser, Node $fakeNode, $a
20552075
20562076 return new GetAttrExpression ($ args [0 ], $ args [1 ], $ args [2 ] ?? null , Template::ANY_CALL , $ line );
20572077 }
2078+
2079+ private static function getPropertyChecker (string $ class , string $ property ): \Closure
2080+ {
2081+ static $ classReflectors = [];
2082+
2083+ $ class = $ classReflectors [$ class ] ??= new \ReflectionClass ($ class );
2084+
2085+ if (!$ class ->hasProperty ($ property )) {
2086+ static $ propertyExists ;
2087+
2088+ return $ propertyExists ??= \Closure::fromCallable ('property_exists ' );
2089+ }
2090+
2091+ $ property = $ class ->getProperty ($ property );
2092+
2093+ if (!$ property ->isPublic ()) {
2094+ static $ false ;
2095+
2096+ return $ false ??= static fn () => false ;
2097+ }
2098+
2099+ return static fn ($ object ) => $ property ->isInitialized ($ object );
2100+ }
20582101}
0 commit comments