highlightjs / highlight.js

JavaScript syntax highlighter with language auto-detection and zero dependencies.
https://highlightjs.org/
BSD 3-Clause "New" or "Revised" License
23.59k stars 3.58k forks source link

(PHP) Inline php code inside html quotes wrongly highlighted #4082

Closed samuelgfeller closed 2 months ago

samuelgfeller commented 2 months ago

Issue Inline PHP code inside html quotes is highlighted as string and everything that comes after it as well.
E.g. <p class="<?= $className ?>">This is not highlighted correctly</p>

Which language seems to have the issue? PHP

Are you using highlight or highlightAuto? Highlight, NOT auto.

Sample Code to Reproduce Code: see expected behaviour.

image

Expected behavior

<?php
echo 'This text is highlighted';
?>

<p class="<?= $className ?>">
    This text and everything that comes after it is treated as string
    and not highlighted
</p>

<?php
echo 'even this!';
?>
joshgoebel commented 2 months ago

To highlight a PHP template (mixed PHP & HTML) you'll need to use php-template grammar, not php.

samuelgfeller commented 2 months ago

@joshgoebel thank you for the quick answer. I'm writing code documentation in markdown which is then converted to html with parsedown.

php-template and phptemplate don't seem to be valid code block tags in markdown. The IDE doesn't recognize them and the highlight does not work on github.

What is the reason highlightjs separates php and php-template?
Is there any valid code block tag for php templates that markdown, github, the IDE and highlightjs understand?

If not, does php-tempate highlight php code exactly the same as php grammar? And is there an easy way to create a js addition so that the markdown php code block tag is always highlighted as php-template grammar?

Or maybe you have another suggestion on how to fix this issue but I'm really curious as to why highlightjs doesn't go the markdown/github way and combine php and php-templates. Mixed html and php is valid in a vanilla php file.

joshgoebel commented 2 months ago

I'm not involved with the parsedown project and can't offer support for that here. The names I provided are the name of our grammars you'd use them according to the documentation:

<pre><code class="language-php-template">
php template code here
</code></pre>

What is the reason highlightjs separates php and php-template?

Because they aren't the same contextually and it makes parsing simpler to have the author specify the context.

If not, does php-tempate highlight php code exactly the same as php grammar?

Not exactly, php-template allows HTML, <?php tags, etc... php does not.

And is there an easy way to create a js addition so that the markdown php code block tag is always highlighted as php-template grammar?

Don't think so, you can't just reregister the language since php-tempate references php internally.

Mixed html and php is valid in a vanilla php file.

Interesting, what would an example look like?

samuelgfeller commented 1 month ago

Claude.ai generated a good fix for Parsedownextra so that it detects if there is a php opening/closing syntax.

<?php

namespace App\Utility;

class ParsedownExtraExtended extends \ParsedownExtra
{

    /**
     * Highlight.js doesn't highlight PHP template code correctly with the
     * `php` language identifier. If the code block contains PHP closing/opening
     * syntax, it needs to be converted to `php-template`.
     * https://github.com/highlightjs/highlight.js/issues/4082
     * 
     * @param $Block
     * @return array|void
     */
    protected function blockFencedCodeComplete($Block)
    {
        $text = $Block['element']['text']['text'];

        // Check if the code contains <?php or <?=
        if (strpos($text, '<?php') !== false || strpos($text, '<?=') !== false) {
            // If it does, change the class to 'language-php-template'
            if (isset($Block['element']['text']['attributes']['class'])) {
                $Block['element']['text']['attributes']['class'] = 'language-php-template';
            }
        }

        $Block['element']['text']['text'] = $text;

        return $Block;
    }
}
joshgoebel commented 1 month ago

That's one way to go about it. :)