Repository navigation
Template: same escaping in partials, isolated scope, validated names - #12
Merged
Merged
Conversation
Three changes to how Template renders: - Partials loaded with template() or combine() now receive the shared and template variables exactly like a page does in parse(): strings are escaped unless they were set with setRaw(), and the original string is available as $_name_. Parameters handed to a partial are passed on as given, because they are values of the calling template, whose own variables are already escaped. - Escaping is explicit: htmlentities with ENT_QUOTES | ENT_SUBSTITUTE and UTF-8, so the result no longer depends on the PHP version (before 8.1 the default flags left single quotes alone and dropped strings with invalid UTF-8). - Templates are included from a method that has no parameters or locals of its own, so template variables can no longer shadow the variables the engine uses. A variable named "this" is dropped. - Template names are validated in the one place that looks them up: a name that is absolute, has a parent directory segment or contains a NUL byte is treated as not found. The "template not found" message escapes the name. Compatibility: a partial that escaped a template variable itself now escapes it twice and should print the variable as is (or use $_name_). Files rendered by one combine() call no longer share a variable scope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DqZUi2UiBJP6rZoCSRcdpE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes
All in
Neuron\Core\Template.Partials now receive the same escaping as pages.
template()andcombine()build their variables the same wayparse()does: a string set withset()orTemplate::share()is escaped, a value set withsetRaw()is left alone, and the original string is available as$_name_. Non-string values (objects, arrays, numbers) are untouched, as before.Parameters handed to a partial (
$this->template ('x.phpt', [ 'name' => $value ])) are passed on as given. They are values of the calling template, whose own variables are already escaped, so escaping them again would encode them twice.Escaping is explicit.
htmlentities ($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')instead of the default flags, so the output is the same on every supported PHP version. On PHP 8.1 and later this is what the defaults already were; on 7.4 and 8.0 single quotes are now encoded as'and a string with invalid UTF-8 gets a substitution character instead of becoming empty.Template variables can no longer shadow engine internals. Templates are included from a method that declares no parameters and no locals, so the template's scope holds the template variables and nothing else.
$thisis still theTemplate, so the helpers (template(),combine(),layout(),gettext(), ...) and closures defined in a template keep working. A variable namedthisis dropped.Template names are validated. A name that is absolute (leading slash or backslash, or a drive letter), has a
..segment, or contains a NUL byte is treated as not found, in the one function every lookup goes through (parse(),template(),combine(),hasTemplate()). The "Template not found" message escapes the name.Compatibility
$_name_.setRaw()for that variable, as a page already does.$_name_for the original.combine()call no longer share a variable scope: a variable assigned in the first file is not visible in the next.$template,$parameters,$ctlbtmpltfiles, ...) are no longer visible inside templates.${'...'}.Tests
tests/TemplateHardeningTest.php, written first and seen failing against the old engine. Each escaping case runs three times: as a page, as atemplate()partial and as acombine()partial.The full suite gives the same three errors as
master(MySQLErrorTest, on a PHP without mysqli) and nothing else. Also smoke-tested on PHP 7.4.Overlap with #8
#8 wraps the three
includestatements intry/catch; this PR replaces those same statements with$this->includeTemplate (...). Whichever is merged second gets three small conflicts inTemplate.php: keep thetry/catchfrom #8 and use theincludeTemplate()call from this PR inside it (and drop the variable loops this PR removes).🤖 Generated with Claude Code
https://claude.ai/code/session_01DqZUi2UiBJP6rZoCSRcdpE