Commit 289ddd9b3b34a63899edad8f523376295b758aa8

Michael Schmidt 2019-02-28T21:56:01

PHP: Fixed closing tag issue (#1652) This fixes that PHP's closing tag (`?>`) was detected inside strings and comments.

diff --git a/components/prism-php.js b/components/prism-php.js
index 8f3498d..fc10e36 100644
--- a/components/prism-php.js
+++ b/components/prism-php.js
@@ -35,11 +35,14 @@
 		}
 	});
 
-	Prism.languages.insertBefore('php', 'keyword', {
+	Prism.languages.insertBefore('php', 'comment', {
 		'delimiter': {
-			pattern: /\?>|<\?(?:php|=)?/i,
+			pattern: /\?>$|^<\?(?:php(?=\s)|=)?/i,
 			alias: 'important'
-		},
+		}
+	});
+
+	Prism.languages.insertBefore('php', 'keyword', {
 		'variable': /\$+(?:\w+\b|(?={))/i,
 		'package': {
 			pattern: /(\\|namespace\s+|use\s+)[\w\\]+/,
@@ -114,11 +117,11 @@
 	delete Prism.languages.php['string'];
 
 	Prism.hooks.add('before-tokenize', function(env) {
-		if (!/(?:<\?php|<\?)/ig.test(env.code)) {
+		if (!/<\?/.test(env.code)) {
 			return;
 		}
 
-		var phpPattern = /(?:<\?php|<\?)[\s\S]*?(?:\?>|$)/ig;
+		var phpPattern = /<\?(?:[^"'/#]|\/(?![*/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|(?:\/\/|#)(?:[^?\n\r]|\?(?!>))*|\/\*[\s\S]*?(?:\*\/|$))*?(?:\?>|$)/ig;
 		Prism.languages['markup-templating'].buildPlaceholders(env, 'php', phpPattern);
 	});
 
@@ -126,4 +129,4 @@
 		Prism.languages['markup-templating'].tokenizePlaceholders(env, 'php');
 	});
 
-}(Prism));
\ No newline at end of file
+}(Prism));
diff --git a/components/prism-php.min.js b/components/prism-php.min.js
index 999111b..cb0060e 100644
--- a/components/prism-php.min.js
+++ b/components/prism-php.min.js
@@ -1 +1 @@
-!function(e){e.languages.php=e.languages.extend("clike",{keyword:/\b(?:__halt_compiler|abstract|and|array|as|break|callable|case|catch|class|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|eval|exit|extends|final|finally|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|namespace|new|or|parent|print|private|protected|public|require|require_once|return|static|switch|throw|trait|try|unset|use|var|while|xor|yield)\b/i,"boolean":{pattern:/\b(?:false|true)\b/i,alias:"constant"},constant:[/\b[A-Z_][A-Z0-9_]*\b/,/\b(?:null)\b/i],comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0}}),e.languages.insertBefore("php","string",{"shell-comment":{pattern:/(^|[^\\])#.*/,lookbehind:!0,alias:"comment"}}),e.languages.insertBefore("php","keyword",{delimiter:{pattern:/\?>|<\?(?:php|=)?/i,alias:"important"},variable:/\$+(?:\w+\b|(?={))/i,"package":{pattern:/(\\|namespace\s+|use\s+)[\w\\]+/,lookbehind:!0,inside:{punctuation:/\\/}}}),e.languages.insertBefore("php","operator",{property:{pattern:/(->)[\w]+/,lookbehind:!0}});var n={pattern:/{\$(?:{(?:{[^{}]+}|[^{}]+)}|[^{}])+}|(^|[^\\{])\$+(?:\w+(?:\[.+?]|->\w+)*)/,lookbehind:!0,inside:{rest:e.languages.php}};e.languages.insertBefore("php","string",{"nowdoc-string":{pattern:/<<<'([^']+)'(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;/,greedy:!0,alias:"string",inside:{delimiter:{pattern:/^<<<'[^']+'|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<'?|[';]$/}}}},"heredoc-string":{pattern:/<<<(?:"([^"]+)"(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;|([a-z_]\w*)(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\2;)/i,greedy:!0,alias:"string",inside:{delimiter:{pattern:/^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<"?|[";]$/}},interpolation:n}},"single-quoted-string":{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0,alias:"string"},"double-quoted-string":{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,alias:"string",inside:{interpolation:n}}}),delete e.languages.php.string,e.hooks.add("before-tokenize",function(n){if(/(?:<\?php|<\?)/gi.test(n.code)){var t=/(?:<\?php|<\?)[\s\S]*?(?:\?>|$)/gi;e.languages["markup-templating"].buildPlaceholders(n,"php",t)}}),e.hooks.add("after-tokenize",function(n){e.languages["markup-templating"].tokenizePlaceholders(n,"php")})}(Prism);
\ No newline at end of file
+!function(e){e.languages.php=e.languages.extend("clike",{keyword:/\b(?:__halt_compiler|abstract|and|array|as|break|callable|case|catch|class|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|eval|exit|extends|final|finally|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|namespace|new|or|parent|print|private|protected|public|require|require_once|return|static|switch|throw|trait|try|unset|use|var|while|xor|yield)\b/i,"boolean":{pattern:/\b(?:false|true)\b/i,alias:"constant"},constant:[/\b[A-Z_][A-Z0-9_]*\b/,/\b(?:null)\b/i],comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0}}),e.languages.insertBefore("php","string",{"shell-comment":{pattern:/(^|[^\\])#.*/,lookbehind:!0,alias:"comment"}}),e.languages.insertBefore("php","comment",{delimiter:{pattern:/\?>$|^<\?(?:php(?=\s)|=)?/i,alias:"important"}}),e.languages.insertBefore("php","keyword",{variable:/\$+(?:\w+\b|(?={))/i,"package":{pattern:/(\\|namespace\s+|use\s+)[\w\\]+/,lookbehind:!0,inside:{punctuation:/\\/}}}),e.languages.insertBefore("php","operator",{property:{pattern:/(->)[\w]+/,lookbehind:!0}});var n={pattern:/{\$(?:{(?:{[^{}]+}|[^{}]+)}|[^{}])+}|(^|[^\\{])\$+(?:\w+(?:\[.+?]|->\w+)*)/,lookbehind:!0,inside:{rest:e.languages.php}};e.languages.insertBefore("php","string",{"nowdoc-string":{pattern:/<<<'([^']+)'(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;/,greedy:!0,alias:"string",inside:{delimiter:{pattern:/^<<<'[^']+'|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<'?|[';]$/}}}},"heredoc-string":{pattern:/<<<(?:"([^"]+)"(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;|([a-z_]\w*)(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\2;)/i,greedy:!0,alias:"string",inside:{delimiter:{pattern:/^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<"?|[";]$/}},interpolation:n}},"single-quoted-string":{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0,alias:"string"},"double-quoted-string":{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,alias:"string",inside:{interpolation:n}}}),delete e.languages.php.string,e.hooks.add("before-tokenize",function(n){if(/<\?/.test(n.code)){var t=/<\?(?:[^"'\/#]|\/(?![*\/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|(?:\/\/|#)(?:[^?\n\r]|\?(?!>))*|\/\*[\s\S]*?(?:\*\/|$))*?(?:\?>|$)/gi;e.languages["markup-templating"].buildPlaceholders(n,"php",t)}}),e.hooks.add("after-tokenize",function(n){e.languages["markup-templating"].tokenizePlaceholders(n,"php")})}(Prism);
\ No newline at end of file
diff --git a/tests/languages/markup+php/issue1582.test b/tests/languages/markup+php/issue1582.test
new file mode 100644
index 0000000..255f97e
--- /dev/null
+++ b/tests/languages/markup+php/issue1582.test
@@ -0,0 +1,23 @@
+<?php
+
+	echo '<?xml version="1.0" encoding="UTF-8"?><tracker />';
+	echo PHP_EOL;
+
+----------------------------------------------------
+
+[
+	["php", [
+		["delimiter", "<?php"],
+		["keyword", "echo"],
+		["single-quoted-string", "'<?xml version=\"1.0\" encoding=\"UTF-8\"?><tracker />'"],
+		["punctuation", ";"],
+		["keyword", "echo"],
+		["constant", "PHP_EOL"],
+		["punctuation", ";"]
+	]]
+]
+
+----------------------------------------------------
+
+Checks for PHP closing tags '?>' inside of strings.
+See #1582 for details.
diff --git a/tests/languages/markup+php/php_with_closing_tag_in_markup_feature.test b/tests/languages/markup+php/php_with_closing_tag_in_markup_feature.test
new file mode 100644
index 0000000..8067d7c
--- /dev/null
+++ b/tests/languages/markup+php/php_with_closing_tag_in_markup_feature.test
@@ -0,0 +1,44 @@
+<?php
+
+	// '
+	# "
+	//*
+	echo '?>' + "?>"; /* ?> */
+	echo PHP_EOL;
+	// */
+	// ?>
+
+<?php # ?>
+
+----------------------------------------------------
+
+[
+	["php", [
+		["delimiter", "<?php"],
+		["comment", "// '"],
+		["shell-comment", "# \""],
+		["comment", "//*"],
+		["keyword", "echo"],
+		["single-quoted-string", "'?>'"],
+		["operator", "+"],
+		["double-quoted-string", ["\"?>\""]],
+		["punctuation", ";"],
+		["comment", "/* ?> */"],
+		["keyword", "echo"],
+		["constant", "PHP_EOL"],
+		["punctuation", ";"],
+		["comment", "// */"],
+		["comment", "// "],
+		["delimiter", "?>"]
+	]],
+
+	["php", [
+		["delimiter", "<?php"],
+		["shell-comment", "# "],
+		["delimiter", "?>"]
+	]]
+]
+
+----------------------------------------------------
+
+Checks for PHP closing tags '?>' inside of strings and comments.
diff --git a/tests/languages/php/delimiter_feature.test b/tests/languages/php/delimiter_feature.test
index 15bd9a8..f7bd684 100644
--- a/tests/languages/php/delimiter_feature.test
+++ b/tests/languages/php/delimiter_feature.test
@@ -1,6 +1,6 @@
 <? ?>
 <?php ?>
-<?= ?>
+<?= // ?>
 
 ----------------------------------------------------
 
@@ -15,10 +15,11 @@
 	]],
 	["php", [
 		["delimiter", "<?="],
+		["comment", "// "],
 		["delimiter", "?>"]
 	]]
 ]
 
 ----------------------------------------------------
 
-Checks for delimiters.
\ No newline at end of file
+Checks for delimiters.