@@ -61,6 +61,7 @@ public function testLog(): void
6161 #[DataProvider("ifElseProvider " )]
6262 #[DataProvider("sectionProvider " )]
6363 #[DataProvider("contextProvider " )]
64+ #[DataProvider("objectProvider " )]
6465 #[DataProvider("arrayLengthProvider " )]
6566 #[DataProvider("dataClosuresProvider " )]
6667 #[DataProvider("missingDataProvider " )]
@@ -2228,6 +2229,81 @@ public static function contextProvider(): array
22282229 ];
22292230 }
22302231
2232+ /** @return array<string, RegIssue> */
2233+ public static function objectProvider (): array
2234+ {
2235+ return [
2236+ 'object: single-property root context ' => [
2237+ 'template ' => '{{name}} ' ,
2238+ 'data ' => (object ) ['name ' => 'Alice ' ],
2239+ 'expected ' => 'Alice ' ,
2240+ ],
2241+ 'object: nested property lookup ' => [
2242+ 'template ' => '{{user.name}} ' ,
2243+ 'data ' => ['user ' => (object ) ['name ' => 'Bob ' ]],
2244+ 'expected ' => 'Bob ' ,
2245+ ],
2246+ 'object: deeply nested object properties ' => [
2247+ 'template ' => '{{a.b.c}} ' ,
2248+ 'data ' => ['a ' => (object ) ['b ' => (object ) ['c ' => 'deep ' ]]],
2249+ 'expected ' => 'deep ' ,
2250+ ],
2251+ 'object: missing property renders empty ' => [
2252+ 'template ' => '{{user.missing}} ' ,
2253+ 'data ' => ['user ' => (object ) ['name ' => 'Carol ' ]],
2254+ 'expected ' => '' ,
2255+ ],
2256+ 'object: {{#with}} scopes to object ' => [
2257+ 'template ' => '{{#with user}}{{name}}{{/with}} ' ,
2258+ 'data ' => ['user ' => (object ) ['name ' => 'Dan ' ]],
2259+ 'expected ' => 'Dan ' ,
2260+ ],
2261+ 'object: {{#each}} iterates public properties ' => [
2262+ 'template ' => '{{#each obj}}{{this}}{{/each}} ' ,
2263+ 'data ' => ['obj ' => (object ) ['x ' => '1 ' , 'y ' => '2 ' ]],
2264+ 'expected ' => '12 ' ,
2265+ ],
2266+ 'object: block section treats object as new scope ' => [
2267+ 'template ' => '{{#obj}}{{name}}{{/obj}} ' ,
2268+ 'data ' => ['obj ' => (object ) ['name ' => 'Eve ' ]],
2269+ 'expected ' => 'Eve ' ,
2270+ ],
2271+ 'object: ../path traversal from inside object scope ' => [
2272+ 'template ' => '{{#with user}}{{../title}}{{/with}} ' ,
2273+ 'data ' => ['title ' => 'Hello ' , 'user ' => (object ) ['name ' => 'Frank ' ]],
2274+ 'expected ' => 'Hello ' ,
2275+ ],
2276+ 'object: block params resolve properties on object items ' => [
2277+ 'template ' => '{{#each items as |item|}}{{item.name}}{{/each}} ' ,
2278+ 'data ' => ['items ' => [(object ) ['name ' => 'Grace ' ]]],
2279+ 'expected ' => 'Grace ' ,
2280+ ],
2281+ 'object: strict mode allows null-valued property ' => [
2282+ 'template ' => '{{user.name}} ' ,
2283+ 'options ' => new Options (strict: true ),
2284+ 'data ' => ['user ' => (object ) ['name ' => null ]],
2285+ 'expected ' => '' ,
2286+ ],
2287+ 'object: assumeObjects mode property access ' => [
2288+ 'template ' => '{{user.name}} ' ,
2289+ 'options ' => new Options (assumeObjects: true ),
2290+ 'data ' => ['user ' => (object ) ['name ' => 'Henry ' ]],
2291+ 'expected ' => 'Henry ' ,
2292+ ],
2293+ 'object: explicit length property ' => [
2294+ 'template ' => '{{obj.length}} ' ,
2295+ 'data ' => ['obj ' => (object ) ['length ' => 42 ]],
2296+ 'expected ' => '42 ' ,
2297+ ],
2298+ 'object: compat mode falls through to parent when property absent from object ' => [
2299+ 'template ' => '{{#each items}}{{name}}{{/each}} ' ,
2300+ 'options ' => new Options (compat: true ),
2301+ 'data ' => ['name ' => 'parent ' , 'items ' => [(object ) []]],
2302+ 'expected ' => 'parent ' ,
2303+ ],
2304+ ];
2305+ }
2306+
22312307 /** @return array<string, RegIssue> */
22322308 public static function arrayLengthProvider (): array
22332309 {
0 commit comments