Blade Parser

Blade Parser

Core Concepts

The heart of the Blade Parser library is its Document Parser. This parser is responsible for reading through a Blade template and extracting information from it.

It accomplishes this by looking for Blade constructs, such as directives, comments, echo statements, and related structures. Additionally, the Document Parser will parse and categorize any PHP tags embedded within the document.

The result of the Document Parser is a list of nodes representing the original template. If it encounters something that is not a Blade structure, or embedded PHP, the parser will produce a "literal" node with the parsed text. There are dedicated node types for all other types of constructs to help make decisions surrounding a Blade template.

Once the parser has produced a list of nodes, its role is mainly complete. We can use the resulting list of nodes in conjunction with other features provided by the library, such as the compiler or Document, Validator, and Workspace APIs.

#How the Parser Interacts with Other Features

The Document Parser provides a list of parsed nodes, or AST, to almost every other feature within the library. The following diagram gives a surface-level overview of how the library is structured:

Diagram of Blade Parser's Processes and Architecture.

The document parser receives a Blade template as input and produces the AST. The resulting AST is consumed directly by the validation system, document system, or compiler. Each of these individual systems may interact with other systems but are primarily decoupled from each other and can be used independently.

#Differences from the Core Laravel Parser

The document parser diverges from the core Laravel Blade parser in a few areas. These differences make it easier to consume the compiled templates directly or develop tooling to detect invalid PHP output.

#Ambiguous Character Sequences

The first difference is in how the parser parses structures, such as a simple echo containing ambiguous characters:

1{{ 'hello {{ world }}' }}

The previous example results in the following compiled output:

1<?php
2 
3// Laravel Compiler Output
4<?php echo e('hello {{ world); ?>' }}
5 
6// Blade Parser Compiler Output
7<?php echo e('hello {{ world }}'); ?>

#Directive Parsing and Whitespace Constraints

Another significant difference in the parser implementations is that directives do not require a leading space with the library (but require a trailing space if the next character is not a left parenthesis, line break or null).

1Leading@if('something) Trailing

Produces the following compiled documents:

1<?php
2 
3// Laravel Compiler Output
4Leading@if('something) Trailing
5 
6// Blade Parser Compiler Output
7Leading<?php if(): ?>('something) Trailing

Regarding directives, there are some situations in which the Laravel compiler may emit extraneous line-ending characters. The library's compiler will not emit these extra line-ending characters. It will only output line endings as the result of compilation or if they were present within the literal document text.

#Component Parameter Parsing

The component tag parser provided by the library accepts arbitrary whitespace between parameter names and their value, provided the value is enclosed within quotes:

1<x-alert
2 message
3 = "The message contents" />

While the parser supports it, it is still not recommended. It is supported to help tool authors detect possible improper spacing.

#The Raw PHP Directives

The last significant difference to bring attention to is how the library's parser and compiler handle Blade's PHP directive. The library's parser will not attempt to locate the @endphp directive if the opening @php directive contains arguments:

1@php ($args)
2 
3@endphp

The previous example produces the following output when using the Laravel compiler:

1<?php ($args)
2 
3?>

While the library's compiler would produce the following:

1<?php ($args); ?>

Both paired and un-pared @php directives may be used within a single template as long as they are correctly balanced, however:

1@php ($args)
2Literal
3@php
4 $variable++;
5@endphp

The Laravel compiler would produce the following output:

1<?php ($args)
2Literal
3@php
4 $variable++;
5?>

While this library's compiler would produce:

1<?php ($args); ?>
2Literal
3<?php $variable++; ?>

The parser implementation will greedily pair the @php and @endphp directives. What this means in practice is that the first @php directive in a series of directives will become the opening PHP tag, and the remainder within the pair will become literal text:

1@php @php
2$counter += 1;
3@endphp @php
4$counter += 2;
5@endphp @php @php @php @php $counter += 3; @endphp

produces the following:

1<?php @php
2$counter += 1; ?> <?php $counter += 2; ?> <?php @php @php @php $counter += 3; ?>

Some absolutely amazing
people

The following amazing people help support this site and my open source projects ♥️
If you're interesting in supporting my work and want to show up on this list, check out my GitHub Sponsors Profile.