@@ -169,30 +169,33 @@ public static function lookupLength(mixed $base, bool $strict = false): mixed
169169 public static function createContext (mixed $ context , array $ options , array $ compiledPartials ): RuntimeContext
170170 {
171171 $ parentCx = self ::$ partialContext ;
172- $ root = ['root ' => $ context ];
173172
174173 if ($ parentCx !== null ) {
175174 // Partial context: reuse the parent's already-merged helpers and partials directly.
176- // PHP copy-on-write ensures inlinePartials is only copied if in() registers a new inline partial.
177- // Inherit the parent's current frame so @index, @key, etc. remain accessible inside partials.
178- // templateClosure will update frame['root'] to reference this partial's own data['root'].
175+ // PHP copy-on-write ensures inlinePartials is only copied if the partial registers a new {{#* inline}} partial.
176+ // Inherit the parent's current data so @index, @key, etc. remain accessible inside partials.
177+ // Unset 'root' first to break the reference established by `$in = &$cx->data['root']` in the
178+ // calling template; a direct assignment would write through it and corrupt the caller's $in.
179+ $ data = $ parentCx ->data ;
180+ unset($ data ['root ' ]);
181+ $ data ['root ' ] = $ context ;
179182 return new RuntimeContext (
180183 helpers: $ parentCx ->helpers ,
181184 partials: $ parentCx ->partials ,
182185 inlinePartials: $ parentCx ->inlinePartials ,
183186 depths: $ parentCx ->depths ,
184- data: $ root ,
185- frame: $ parentCx ->frame ,
187+ data: $ data ,
186188 partialBlock: $ parentCx ->partialBlock ,
187189 );
188190 }
189191
190192 $ data = $ options ['data ' ] ?? [];
193+ $ data ['root ' ] = $ data ['root ' ] ?? $ context ;
194+ $ extraHelpers = $ options ['helpers ' ] ?? [];
191195 return new RuntimeContext (
192- helpers: array_replace (Runtime::defaultHelpers (), $ options [ ' helpers ' ] ?? [] ),
196+ helpers: $ extraHelpers ? array_replace (Runtime::defaultHelpers (), $ extraHelpers ) : Runtime:: defaultHelpers ( ),
193197 partials: array_replace ($ compiledPartials , $ options ['partials ' ] ?? []),
194- data: ['root ' => $ data ['root ' ] ?? $ context ],
195- frame: $ data ,
198+ data: $ data ,
196199 );
197200 }
198201
@@ -251,8 +254,8 @@ public static function ifvar(mixed $v, bool $zero = false): bool
251254 && $ v !== false
252255 && ($ zero || ($ v !== 0 && $ v !== 0.0 ))
253256 && $ v !== ''
254- && (!$ v instanceof \Stringable || ( string ) $ v !== '' )
255- && (!is_array ( $ v ) || $ v );
257+ && (!is_array ( $ v ) || $ v )
258+ && (!$ v instanceof \Stringable || ( string ) $ v !== '' );
256259 }
257260
258261 /**
@@ -305,16 +308,17 @@ public static function raw(mixed $value): string
305308 */
306309 public static function sec (RuntimeContext $ cx , mixed $ value , mixed $ in , ?\Closure $ cb , ?\Closure $ else = null , ?string $ helperName = null ): string
307310 {
308- if ($ helperName !== null && isset ($ cx ->helpers [$ helperName ])) {
309- return static ::hbbch ($ cx , $ cx ->helpers [$ helperName ], $ helperName , [], [], $ in , $ cb , $ else );
311+ $ helper = $ helperName !== null ? ($ cx ->helpers [$ helperName ] ?? null ) : null ;
312+ if ($ helper !== null ) {
313+ return static ::hbbch ($ cx , $ helper , $ helperName , [], [], $ in , $ cb , $ else );
310314 }
311315
312316 // Lambda functions in block position: simple-path identifiers ($helperName set) receive
313317 // HelperOptions so they can render fn/inverse; complex paths ($helperName null) are called
314318 // with no arguments, mirroring HBS.js which does not treat them as helper calls.
315319 if ($ value instanceof \Closure) {
316320 $ result = $ helperName !== null
317- ? $ value (new HelperOptions (scope: $ in , data: $ cx ->frame , cx: $ cx , cb: $ cb , inv: $ else ))
321+ ? $ value (new HelperOptions (scope: $ in , data: $ cx ->data , cx: $ cx , cb: $ cb , inv: $ else ))
318322 : $ value ();
319323 return static ::resolveBlockResult ($ cx , $ result , $ in , $ cb , $ else );
320324 }
@@ -471,7 +475,7 @@ public static function hbch(RuntimeContext $cx, \Closure $helper, string $name,
471475 if ($ numParams === 0 || $ numParams > count ($ positional )) {
472476 $ positional [] = new HelperOptions (
473477 scope: $ _this ,
474- data: $ cx ->frame ,
478+ data: $ cx ->data ,
475479 cx: $ cx ,
476480 name: $ name ,
477481 hash: $ hash ,
@@ -495,7 +499,7 @@ public static function hbbch(RuntimeContext $cx, \Closure $helper, string $name,
495499 {
496500 $ positional [] = new HelperOptions (
497501 scope: $ _this ,
498- data: $ cx ->frame ,
502+ data: $ cx ->data ,
499503 cx: $ cx ,
500504 name: $ name ,
501505 hash: $ hash ,
0 commit comments