Highlight Laravel Blade Templates with Highlight.php
This post was published 3 years ago. Some of the information might be outdated!
Before we begin, this blog posts assumes you already have Highlight.php setup in your application. If you don't already have it setup, consult the official documentation or look into alternatives such as commonmark-highlighter
that use this library under the hood.
The syntax
Highlight.php uses JSON files for syntax definitions, where as Highlight.js uses JavaScript files. This is just down to the fact that one is written in PHP where JavaScript evaluation is basically impossible and the other is a JavaScript library, so JavaScript can be used for configuration.
Here is the JSON file for the Blade syntax definition:
{
"case_insensitive": true,
"subLanguage": "xml",
"contains": [
{
"className": "comment",
"begin": "\\{\\{--",
"end": "--\\}\\}"
},
{
"className": "template-variable",
"begin": "\\{\\{",
"starts": {
"end": "\\}\\}",
"returnEnd": true,
"subLanguage": "php"
}
},
{
"className": "template-variable",
"begin": "\\}\\}"
},
{
"className": "template-variable",
"begin": "\\{!!",
"starts": {
"end": "!!\\}",
"returnEnd": true,
"subLanguage": "php"
}
},
{
"className": "template-variable",
"begin": "!!\\}"
},
{
"className": "template-tag",
"begin": "@php",
"starts": {
"end": "@endphp",
"returnEnd": true,
"subLanguage": "php"
},
"relevance": 10
},
{
"begin": "@[\\w]+",
"end": "[\\W]",
"excludeEnd": true,
"className": "template-tag"
}
]
}
At a very basic level, this JSON files uses RegEx patterns to describe starting and ending delimiters. Those patterns are used to match your code blocks against a particular type of token and then add a class to the wrapping HTML element.
As an example, the very first items inside of the contains
key is an object that stores information about comments. The className
describes which hljs-
class will be added to that text node. In this case a hljs-comment
class will be added when {{--
is encountered.
When that first pattern is matched, it will continue to match the text until the end
pattern matches. As a result, our Blade comments are rendered as:
{{-- This is a comment. --}}
Any objects that have a starts
key indicate that matching the begin
pattern should start a new sublanguage block. This is what lets you highlight text inside of {{ }}
as PHP, instead of a plain-text string.
The same applies to the @php
and @endphp
blocks. Everything that is found within those 2 directives will be highlighted as PHP.
{{ \Example::method() }}
@php
$example = 2 + 2;
echo $example;
@endphp
The @php
and @endphp
rules also have a relevance
key. The higher that value of this key, the more likely it is to be used to automatically match the current syntax.
If you don't specify which language you are currently rendering and your code contains an @php
directive, Highlight.php will use this relevance and assume you're trying to render Blade.
Storing our syntax
This is all down to personal preference, but I like to keep my blade.json
file in a resources/syntax
folder in my Laravel applications. You can of course place this anywhere you like, just be sure to change any references to it later on.
Registering the syntax
Once you have created the blade.json
file, we need to tell Highlight.php to use this new syntax definition. This is really simple and only requires a single line of code:
\Highlight\Highlighter::registerLanguage('blade', resource_path('syntax/blade.json'), true);
The first argument is the identifier for the language. In this case, I'll use blade
.
The second argument is where your blade.json
file is stored. I've stored mine in resources/syntax/blade.json
so I'm using the resource_path()
helper to get the correct path.
The third argument is optional, but it will forcefully overwrite any existing blade
definitions. At the time of writing, Highlight.php doesn't have its own (obviously, that's why you're here) so I'm going to say true
so that nothing unexpectedly breaks in the future.
Ensure that you execute this code before attempting to highlight anything. In a Laravel application, you could put it inside of a
ServiceProvider
.
Using the custom syntax
Once that's all done, you will be able to use blade
code in your blocks. Here's an example:
<h1>
@auth
Hello, {{ $user->name }}!
@else
Hello!
@endauth
</h1>
<livewire:user-profile user="{{ $user->name }}" />
Planned improvements
As I mentioned before, code inside of {{ }}
will try to be highlighted using the PHP syntax definitions. This is great, but when you try to right code inside of directives that expect expressions, it isn't highlighted correctly:
@if(2 + 2 === 5)
{{ 5 }}
@endif
This is a limitation of Highlight.js and, in turn, Highlight.php. Since the tokeniser relies on a beginning and ending pattern, it's quite hard to match all content between a pair of matching parentheses.
There might be a way to solve this, but my RegEx skills are nowhere near good enough. If you've got any ideas, I'd love to know on Twitter.
Another thing that I'd like to add in PHP highlighting inside of <x:
or <livewire:
attribute bindings. Since the syntax definition extends from XML, the attributes themselves can be highlighted but the content of those attributes is just rendered as a string.
This one is a bit easier to solve than the directive expressions, so I'll play around with the RegEx and update this article accordingly.
If you want a separate reference, I've put the slimmed down instructions into a GitHub Gist here.
Got any ideas or improvements? Comment on the Gist above and I'll reply as best as I can.