Skip to content

fix: Parser - Equal key name replace conflict #9246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions system/View/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,20 +254,39 @@ protected function parse(string $template, array $data = [], ?array $options = n
// it can potentially modify any template between its tags.
$template = $this->parsePlugins($template);

// loop over the data variables, replacing
// the content as we go.
// Parse stack for each parse type (Single and Pairs)
$replaceSingleStack = [];
$replacePairsStack = [];

// loop over the data variables, saving regex and data
// for later replacement.
foreach ($data as $key => $val) {
$escape = true;

if (is_array($val)) {
$escape = false;
$replace = $this->parsePair($key, $val, $template);
$escape = false;
$replacePairsStack[] = [
'replace' => $this->parsePair($key, $val, $template),
'escape' => $escape,
];
} else {
$replace = $this->parseSingle($key, (string) $val);
$replaceSingleStack[] = [
'replace' => $this->parseSingle($key, (string) $val),
'escape' => $escape,
];
}
}

// Merge both stacks, pairs first + single stacks
// This allows for nested data with the same key to be replaced properly
$replace = array_merge($replacePairsStack, $replaceSingleStack);

foreach ($replace as $pattern => $content) {
$template = $this->replaceSingle($pattern, $content, $template, $escape);
// Loop over each replace array item which
// holds all the data to be replaced
foreach ($replace as $replaceItem) {
// Loop over the actual data to be replaced
foreach ($replaceItem['replace'] as $pattern => $content) {
$template = $this->replaceSingle($pattern, $content, $template, $replaceItem['escape']);
}
}

Expand Down
40 changes: 40 additions & 0 deletions tests/system/View/ParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1061,4 +1061,44 @@ public function testChangeConditionalDelimitersWorkWithJavaScriptCode(): void
EOL;
$this->assertSame($expected, $this->parser->renderString($template));
}

/**
* @see https://github.com/codeigniter4/CodeIgniter4/issues/9245
*/
public function testParseSameArrayKeyName(): void
{
$data = [
'type' => 'Super Powers',
'powers' => [
[
'type' => 'invisibility',
],
],
];

$template = '{type} like {powers}{type}{/powers}';

$this->parser->setData($data);
$this->assertSame('Super Powers like invisibility', $this->parser->renderString($template));
}

public function testParseSameArrayKeyNameNested(): void
{
$data = [
'title' => 'My title',
'similar' => [
['items' => [
[
'title' => 'My similar title',
],
],
],
],
];

$template = '{title} with similar item {similar}{items}{title}{/items}{/similar}';

$this->parser->setData($data);
$this->assertSame('My title with similar item My similar title', $this->parser->renderString($template));
}
}
2 changes: 2 additions & 0 deletions user_guide_src/source/changelogs/v4.5.6.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Bugs Fixed

- **Validation:** Fixed the `getValidated()` method that did not return valid data when validation rules used multiple asterisks.

- **Parser:** Fixed bug that caused equal key names to be replaced by the key name defined first.

See the repo's
`CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_
for a complete list of bugs fixed.
Loading