View Parsing

The framework Parser in imspr/system/View/Parser.php transforms view templates using { and } delimiters, conditional tags, plugin tags, data placeholders, paired data blocks, and configured filters. It is separate from PDF document parsing and Kint inspection.

Services::parser() in imspr/system/Config/Services.php provides the construction boundary: the shared branch delegates to getSharedInstance('parser', $viewPath, $config). The non-shared branch supplies a default Config\View and view directory when needed, then constructs Parser with the view configuration, path, locator, CI_DEBUG, and logger.

Source-defined render flow

flowchart TD
    r["Parser::render(view, options, saveData)"] --> e["Append .php when view has no extension"]
    e --> c{"Cache option present and cache() returns a truthy value?"}
    c -->|Yes| cached["Return cached output"]
    c -->|No| local{"Local view file exists?"}
    local -->|Yes| read["Read resolved file"]
    local -->|No| locate["Views locator resolves the file"]
    locate --> empty{"Resolved file is empty?"}
    empty -->|Yes| invalid["Throw invalid-file ViewException"]
    empty -->|No| read
    read --> parse["Parser::parse(template, data, options)"]
    parse --> clear["Clear parser data when saveData is false"]
    clear --> save{"Cache option present?"}
    save -->|Yes| cache["Save parsed output"]
    save -->|No| result["Return output"]
    cache --> result

For a non-empty template, parse() applies transformations in this order:

flowchart LR
    p0["Input template"] --> p1["Replace PHP tags"]
    p1 --> p2["Remove comments"]
    p2 --> p3["Extract no-parse blocks"]
    p3 --> p4["Process conditionals"]
    p4 --> p5["Process plugins"]
    p5 --> p6["Replace data"]
    p6 --> p7["Restore no-parse blocks"]
    p7 --> p8["Parsed template"]

An empty template returns an empty string before these stages. renderString() uses the same parsing path for a supplied string and also clears stored data when saveData is false.

Template syntax contract

Construct Source-defined behavior
{key} Default scalar placeholder syntax. parseSingle() also recognizes optional ! no-escape markers and filter text after the key.
{variable}...{/variable} Paired data syntax. parsePair() repeats the inner template for each row, recursively processes nested arrays, and replaces the complete paired region.
{# ... #} parseComments() removes non-greedy comment regions.
{noparse}...{/noparse} extractNoparse() replaces matching regions with hash-based placeholders before other parsing; insertNoparse() restores them afterward.
{if ...}, {elseif ...}, {else}, {endif} parseConditionals() converts the tags to PHP control syntax and evaluates the generated template with the parser data.
{+plugin ...+}...{+/plugin+} Paired plugin syntax. The registered callback receives the content and parsed parameters.
{+plugin ...+} Unpaired plugin syntax. The registered callback receives the parsed parameters.
key|filter applyFilters() extracts parameters, normalizes the filter name, and invokes only filters present in $this->config->filters. Unknown filter names are skipped.

The default delimiters are declared as Parser::$leftDelimiter = '{' and Parser::$rightDelimiter = '}'. setDelimiters() can replace both delimiters.

Data and plugin handling

setData() merges supplied values into the parser’s existing data. When a context is supplied, it converts values through objectToArray() where applicable and records that context per key. Array values are handled as paired data during parsing; scalar values use scalar replacement.

Parser::__construct() copies $config->plugins into the parser’s plugin registry, defaulting to an empty array when the configuration property is absent. addPlugin() stores a callback under an alias; when $isPair is true, it stores the callback in a one-element array so parsePlugins() can distinguish paired tags. Plugin parameters support positional values and key=value values.

Replacement preparation splits chained filters on |. When a value is escapable and has no explicit filter, shouldAddEscaping() can add an esc({$context}) filter. A stored non-raw data context is used when present; a stored raw context suppresses automatic escaping; otherwise, when no context is stored, an unfiltered value defaults to HTML escaping. noescape suppresses automatic escaping, and an existing esc filter prevents another escaping filter from being added.

[!WARNING] The source establishes the parser implementation and service factory, but no application caller or observed rendering run proves that a particular application template path reaches it. A truthy result from cache() in render() returns before file resolution, file reading, or parsing; a falsey result falls through to those steps. If the view lookup remains empty, render() throws the invalid-file view exception instead of falling through to parsing. Conditional tags are an evaluation boundary because generated PHP is passed to eval; a resulting ParseError is translated to the view tag-syntax exception.

Updated