Skip to content

Conversation

@elliota43
Copy link

Description

Fixes @json Blade directive incorrectly parsing complex expressions with nested structures. Replaces explode() with tokenizer-based parser that respects nesting depth.

Problem

The directive would split on commas inside nested arrays, closures, and method calls, causing parse errors.

Example (from original issue):

@json([
    'items' => $helpers['benefit']['getAll']()->map(fn($item) => [
        'icon' => $item->icon,
        'title' => (string)$item->title,
        'description' => (string)$item->description
    ]),
    'translation' => '%:booking.benefits%'
])

would incorrectly split on the comma inside the closure.

Solution

  • Added parseArguments() method using PHP's tokenizer
  • Tracks nesting depth (parentheses, brackets, braces)
  • Only splits on commas at top level (depth === 0)
  • Handles empty expressions (defaults to null)

Fully backwards compatible - defaults to same behavior as before.

Fixes #56331

Replaces `explode()` with tokenizer-based parser  that respects nested arrays, closures, and function calls.  Only splits on commas at the top level, preserving commas inside nested structure.

Example of previously broken case:
  @JSON([$items->map(fn($x) => ['a' => $x, 'b' => $x]), 'key' => 'value'])

This fix is backwards compatible - all existing @JSON usage continues
to work as before. The change only fixes previously broken cases with
complex nested structures.

Fixes laravel#56331
Comment on lines +22 to +31
$parts = $this->parseArguments($this->stripParentheses($expression));

$options = isset($parts[1]) ? trim($parts[1]) : $this->encodingOptions;

$depth = isset($parts[2]) ? trim($parts[2]) : 512;

return "<?php echo json_encode($parts[0], $options, $depth) ?>";
// Ensure we have at least one argument, default to null if empty
$data = $parts[0] ?? 'null';

return "<?php echo json_encode($data, $options, $depth) ?>";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can shorten this to

Suggested change
$parts = $this->parseArguments($this->stripParentheses($expression));
$options = isset($parts[1]) ? trim($parts[1]) : $this->encodingOptions;
$depth = isset($parts[2]) ? trim($parts[2]) : 512;
return "<?php echo json_encode($parts[0], $options, $depth) ?>";
// Ensure we have at least one argument, default to null if empty
$data = $parts[0] ?? 'null';
return "<?php echo json_encode($data, $options, $depth) ?>";
[$data, $options, $depth] = $this->parseArguments($this->stripParentheses($expression)) + ['null', $this->encodingOptions, 512];
return "<?php echo json_encode($data, trim($options), $depth) ?>";

if you want

* and other nested structures by using PHP's tokenizer.
*
* @param string $expression
* @return array
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can narrow this further down:

Suggested change
* @return array
* @return array{0?: string, 1?: string, 2?: string}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@json blade parsing error

2 participants