1 /*! `php` grammar compiled for Highlight.js 11.11.1 */
3 var hljsGrammar
= (function () {
8 Author: Victor Karamzin <Victor.Karamzin@enterra-inc.com>
9 Contributors: Evgeny Stepanischev <imbolk@gmail.com>, Ivan Sagalaev <maniac@softwaremaniacs.org>
10 Website: https://www.php.net
15 * @param {HLJSApi} hljs
16 * @returns {LanguageDetail}
19 const regex
= hljs
.regex
;
20 // negative look-ahead tries to avoid matching patterns that are not
21 // Perl at all like $ident$, @ident@, etc.
22 const NOT_PERL_ETC
= /(?![A-Za-z0-9])(?![$])/;
23 const IDENT_RE
= regex
.concat(
24 /[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,
26 // Will not detect camelCase classes
27 const PASCAL_CASE_CLASS_NAME_RE
= regex
.concat(
28 /(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,
30 const UPCASE_NAME_RE
= regex
.concat(
35 match: '\\$+' + IDENT_RE
,
37 const PREPROCESSOR
= {
40 { begin: /<\?php/, relevance: 10 }, // boost for obvious PHP
42 // less relevant per PSR-1 which says not to use short-tags
43 { begin: /<\?/, relevance: 0.1 },
44 { begin: /\?>/ } // end php tag
57 const SINGLE_QUOTED
= hljs
.inherit(hljs
.APOS_STRING_MODE
, { illegal: null, });
58 const DOUBLE_QUOTED
= hljs
.inherit(hljs
.QUOTE_STRING_MODE
, {
60 contains: hljs
.QUOTE_STRING_MODE
.contains
.concat(SUBST
),
64 begin: /<<<[ \t]*(?:(\w
+)|"(\w+)")\n/,
66 contains: hljs
.QUOTE_STRING_MODE
.contains
.concat(SUBST
),
67 'on:begin': (m
, resp
) => { resp
.data
._beginMatch
= m
[1] || m
[2]; },
68 'on:end': (m
, resp
) => { if (resp
.data
._beginMatch
!== m
[1]) resp
.ignoreMatch(); },
71 const NOWDOC
= hljs
.END_SAME_AS_BEGIN({
72 begin: /<<<[ \t]*'(\w+)'\n/,
75 // list of valid whitespaces because non-breaking space might be part of a IDENT_RE
76 const WHITESPACE
= '[ \t\n]';
89 { begin: `\\b0[bB][01]+(?:_[01]+)*\\b` }, // Binary w/ underscore support
90 { begin: `\\b0[oO][0-7]+(?:_[0-7]+)*\\b` }, // Octals w/ underscore support
91 { begin: `\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b` }, // Hex w/ underscore support
92 // Decimals w/ underscore support, with optional fragments and scientific exponent (e) suffix.
93 { begin: `(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?` }
104 // <https://www.php.net/manual/en/language.constants.predefined.php>
109 "__COMPILER_HALT_OFFSET__",
114 // Function that look like language construct or language construct that look like function:
115 // List of keywords that may not require parenthesis
124 // These are not language construct (function) but operate on the currently-executing function and can access the current symbol table
125 // 'compact extract func_get_arg func_get_args func_num_args get_called_class get_parent_class ' +
127 // <https://www.php.net/manual/en/reserved.php>
128 // <https://www.php.net/manual/en/language.types.type-juggling.php>
205 // Standard PHP library:
206 // <https://www.php.net/manual/en/book.spl.php>
209 "ArgumentCountError",
214 "BadFunctionCallException",
215 "BadMethodCallException",
217 "CallbackFilterIterator",
221 "DivisionByZeroError",
226 "FilesystemIterator",
230 "InvalidArgumentException",
237 "OutOfBoundsException",
238 "OutOfRangeException",
244 "RecursiveArrayIterator",
245 "RecursiveCachingIterator",
246 "RecursiveCallbackFilterIterator",
247 "RecursiveDirectoryIterator",
248 "RecursiveFilterIterator",
250 "RecursiveIteratorIterator",
251 "RecursiveRegexIterator",
252 "RecursiveTreeIterator",
256 "SplDoublyLinkedList",
271 "UnderflowException",
272 "UnexpectedValueException",
273 "UnhandledMatchError",
274 // Reserved interfaces:
275 // <https://www.php.net/manual/en/reserved.interfaces.php>
291 // <https://www.php.net/manual/en/reserved.classes.php>
293 "__PHP_Incomplete_Class",
301 /** Dual-case keywords
304 * ["then", "THEN", "FILE", "file"]
306 * @param {string[]} items */
307 const dualCase
= (items
) => {
308 /** @type string[] */
310 items
.forEach(item
=> {
312 if (item
.toLowerCase() === item
) {
313 result
.push(item
.toUpperCase());
315 result
.push(item
.toLowerCase());
323 literal: dualCase(LITERALS
),
328 * @param {string[]} items */
329 const normalizeKeywords
= (items
) => {
330 return items
.map(item
=> {
331 return item
.replace(/\|\d+$/, "");
335 const CONSTRUCTOR_CALL
= { variants: [
339 regex
.concat(WHITESPACE
, "+"),
340 // to prevent built ins from being confused as the class constructor call
341 regex
.concat("(?!", normalizeKeywords(BUILT_INS
).join("\\b|"), "\\b)"),
342 PASCAL_CASE_CLASS_NAME_RE
,
351 const CONSTANT_REFERENCE
= regex
.concat(IDENT_RE
, "\\b(?!\\()");
353 const LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON
= { variants: [
358 regex
.lookahead(/(?!class\b)/)
362 scope: { 2: "variable.constant", },
369 scope: { 2: "variable.language", },
373 PASCAL_CASE_CLASS_NAME_RE
,
376 regex
.lookahead(/(?!class\b)/)
382 3: "variable.constant",
387 PASCAL_CASE_CLASS_NAME_RE
,
390 regex
.lookahead(/(?!class\b)/)
393 scope: { 1: "title.class", },
397 PASCAL_CASE_CLASS_NAME_RE
,
403 3: "variable.language",
408 const NAMED_ARGUMENT
= {
410 match: regex
.concat(IDENT_RE
, regex
.lookahead(':'), regex
.lookahead(/(?!::)/)),
412 const PARAMS_MODE
= {
420 LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON
,
421 hljs
.C_BLOCK_COMMENT_MODE
,
427 const FUNCTION_INVOKE
= {
431 // to prevent keywords from being confused as the function title
432 regex
.concat("(?!fn\\b|function\\b|", normalizeKeywords(KWS
).join("\\b|"), "|", normalizeKeywords(BUILT_INS
).join("\\b|"), "\\b)"),
434 regex
.concat(WHITESPACE
, "*"),
435 regex
.lookahead(/(?=\()/)
437 scope: { 3: "title.function.invoke", },
438 contains: [ PARAMS_MODE
]
440 PARAMS_MODE
.contains
.push(FUNCTION_INVOKE
);
442 const ATTRIBUTE_CONTAINS
= [
444 LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON
,
445 hljs
.C_BLOCK_COMMENT_MODE
,
452 begin: regex
.concat(/#\[\s*\\?/,
454 PASCAL_CASE_CLASS_NAME_RE
,
481 ...ATTRIBUTE_CONTAINS
,
484 ...ATTRIBUTE_CONTAINS
,
488 { match: PASCAL_CASE_CLASS_NAME_RE
},
489 { match: UPCASE_NAME_RE
}
496 case_insensitive: false,
500 hljs
.HASH_COMMENT_MODE
,
501 hljs
.COMMENT('//', '$'),
513 match: /__halt_compiler\(\);/,
514 keywords: '__halt_compiler',
517 end: hljs
.MATCH_NOTHING_RE
,
529 scope: 'variable.language',
534 LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON
,
543 3: "variable.constant",
550 beginKeywords: 'fn function',
555 { beginKeywords: 'use', },
556 hljs
.UNDERSCORE_TITLE_MODE
,
558 begin: '=>', // No markup, just a relevance booster
572 LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON
,
573 hljs
.C_BLOCK_COMMENT_MODE
,
584 beginKeywords: "enum",
588 beginKeywords: "class interface trait",
596 { beginKeywords: 'extends implements' },
597 hljs
.UNDERSCORE_TITLE_MODE
600 // both use and namespace still use "old style" rules (vs multi-match)
601 // because the namespace name can include `\` and we still want each
602 // element to be treated as its own *individual* title
604 beginKeywords: 'namespace',
608 contains: [ hljs
.inherit(hljs
.UNDERSCORE_TITLE_MODE
, { scope: "title.class" }) ]
611 beginKeywords: 'use',
615 // TODO: title.function vs title.class
617 match: /\b(as|const|function)\b/,
620 // TODO: could be title.class or title.function
621 hljs
.UNDERSCORE_TITLE_MODE
634 hljs
.registerLanguage('php', hljsGrammar
);