Skip to content

feat(parser): JSON properties, [[~context]] links, [[@if]] conditional blocks#16904

Open
Ibochkarev wants to merge 2 commits intomodxcms:3.xfrom
Ibochkarev:feat/15200-parser-json-conditional-context-link
Open

feat(parser): JSON properties, [[~context]] links, [[@if]] conditional blocks#16904
Ibochkarev wants to merge 2 commits intomodxcms:3.xfrom
Ibochkarev:feat/15200-parser-json-conditional-context-link

Conversation

@Ibochkarev
Copy link
Collaborator

What does it do?

  • JSON for element properties
    modParser::parsePropertyString() now accepts a JSON object or array as the property string (e.g. {"prop":"value","num":1} or ["a", "b"]). If the string starts with { or [, it is decoded with json_decode() and returned in the same format as the classic key=value&key2=value2 syntax (either values only or full property definitions with name/desc/type/options/value). Existing non-JSON property strings are unchanged.

  • Link tag by context key
    modLinkTag supports [[~contextKey]] (e.g. [[~web]]) in addition to [[~resourceId]]. When the link target is not numeric, it is treated as a context key: the parser loads that context, reads site_start, and builds the URL to that resource in the given context. If the context does not exist or has no site_start, a warning is logged and the tag outputs empty. Resolution is encapsulated in resolveContextLinkTarget().

  • Conditional blocks in content
    New processing step in processElementTags(): before collecting/processing other tags, processConditionalBlocks() runs. It finds [[@if (expr)]] … optional [[@else]][[@endif]] blocks, evaluates the expression (whitelist only: $modx->user->*, $modx->resource->*, empty(), isset(), comparisons with ==, !=, <, >, etc., no eval()), and replaces the block with the “then” or “else” branch. Nested [[@if]] blocks are supported. Expressions are parsed and compared via evaluateConditionalExpression() and parseConditionalComparisonValue().

  • Tests
    New/updated tests in modParserTest: testParsePropertiesJson, testConditionalBlocks, testConditionalBlocksNoElse, testLinkTagContextKey.

Why is it needed?

  • JSON properties: Allows chunks/snippets/elements to receive structured or many properties without long &prop=value strings; improves readability and tooling (editors, generators).
  • [[~contextKey]]: Enables “home link for this context” (e.g. [[~web]]) without hardcoding resource IDs; useful for multi-context sites and reusable templates.
  • [[@if]] blocks: Enables conditional output in content/templates without PHP or a separate snippet; keeps logic in the parser and avoids eval() for security.

How to test

1. Unit tests (parser and link tag)

From the project root, run the modParser test group (requires configured test DB and fixtures; if fixtures fail, you can run only the new tests by name):

# All modParser tests
./core/vendor/bin/phpunit _build/test/Tests/Model/modParserTest.php --group modParser

# Only the new tests
./core/vendor/bin/phpunit _build/test/Tests/Model/modParserTest.php --filter 'testParsePropertiesJson|testConditionalBlocks|testConditionalBlocksNoElse|testLinkTagContextKey'

Expected: testParsePropertiesJson passes (JSON {"a":1,"b":"two"} parsed to array); testConditionalBlocks / testConditionalBlocksNoElse pass (conditional output for $modx->user->id); testLinkTagContextKey passes (e.g. [[~web]] becomes a URL string when context web exists and has site_start, or remains unchanged/empty otherwise).

2. JSON properties in the manager

  • Create or edit a chunk/snippet that accepts properties.
  • In the property string (or default properties), use JSON, e.g. {"title":"Home","count":5}.
  • Save and use the element in a template; ensure the properties are available (e.g. [[$chunk? &title=Home &count=5]] or as default values).
  • Compare with the same values in classic form title=Home&count=5 — behavior should match.

3. Link by context key

  • Ensure you have a context (e.g. web) with site_start set to a resource ID.
  • In a template or chunk, put [[~web]].
  • Render the page; the tag should output the URL of the site_start resource in that context (e.g. / or friendly URL).
  • Use a non-existent context key (e.g. [[~nonexistent]]); expect empty output and a warning in the log.

4. Conditional blocks

  • In a chunk or template use:
    • [[@if ($modx->user->id == 1)]]Hello admin[[@else]]Hello guest[[@endif]] — output depends on current user id.
    • [[@if (empty($modx->resource->longtitle))]][[+pagetitle]][[@else]][[+longtitle]][[@endif]] — output depends on resource field.
  • Render as logged-in user with id 1 and as another user (or guest); verify “Hello admin” vs “Hello guest”.
  • Test without [[@else]]: [[@if ($modx->user->id == 99999)]]hidden[[@endif]] — should output nothing when condition is false.
  • Test nested: [[@if (1 == 1)]]a[[@if ($modx->user->id == 1)]]b[[@else]]c[[@endif]]d[[@endif]] — should render “abd” or “acd” depending on user.

5. PHPCS (optional)

./core/vendor/bin/phpcs core/src/Revolution/modLinkTag.php core/src/Revolution/modParser.php --standard=phpcs.xml

modLinkTag.php should report 0 errors; modParser.php may still report existing style issues outside the added code.

Related issue(s)/PR(s)

Resolves #15200

@Ibochkarev Ibochkarev marked this pull request as ready for review February 26, 2026 03:45
…l blocks

- modParser: parsePropertyString() accepts JSON object/array for element properties
- modParser: processConditionalBlocks() for [[@if (expr)]]...[[@else]]...[[@endif]]
- modLinkTag: [[~contextKey]] resolves to context site_start URL (e.g. [[~web]])
- Add tests: testParsePropertiesJson, testConditionalBlocks, testLinkTagContextKey

Resolves modxcms#15200
@Ibochkarev Ibochkarev force-pushed the feat/15200-parser-json-conditional-context-link branch from cf08f3a to 6a56d59 Compare February 26, 2026 03:47
- Updated the assertion for context links to check for the presence of a path or query string in the generated URL, enhancing the test's reliability.
@Ibochkarev Ibochkarev added feature Request about implementing a brand new function or possibility. needs-docs The issue requires adding or updating documentation after the pull request merged. labels Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Request about implementing a brand new function or possibility. needs-docs The issue requires adding or updating documentation after the pull request merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add new features to the MODX parser

1 participant