]> luflow.net public git repositories - flow-web.git/blob - static/highlight/es/languages/php.js
Initial commit.
[flow-web.git] / static / highlight / es / languages / php.js
1 /*! `php` grammar compiled for Highlight.js 11.11.1 */
2 var hljsGrammar = (function () {
3 'use strict';
4
5 /*
6 Language: PHP
7 Author: Victor Karamzin <Victor.Karamzin@enterra-inc.com>
8 Contributors: Evgeny Stepanischev <imbolk@gmail.com>, Ivan Sagalaev <maniac@softwaremaniacs.org>
9 Website: https://www.php.net
10 Category: common
11 */
12
13 /**
14 * @param {HLJSApi} hljs
15 * @returns {LanguageDetail}
16 * */
17 function php(hljs) {
18 const regex = hljs.regex;
19 // negative look-ahead tries to avoid matching patterns that are not
20 // Perl at all like $ident$, @ident@, etc.
21 const NOT_PERL_ETC = /(?![A-Za-z0-9])(?![$])/;
22 const IDENT_RE = regex.concat(
23 /[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,
24 NOT_PERL_ETC);
25 // Will not detect camelCase classes
26 const PASCAL_CASE_CLASS_NAME_RE = regex.concat(
27 /(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,
28 NOT_PERL_ETC);
29 const UPCASE_NAME_RE = regex.concat(
30 /[A-Z]+/,
31 NOT_PERL_ETC);
32 const VARIABLE = {
33 scope: 'variable',
34 match: '\\$+' + IDENT_RE,
35 };
36 const PREPROCESSOR = {
37 scope: "meta",
38 variants: [
39 { begin: /<\?php/, relevance: 10 }, // boost for obvious PHP
40 { begin: /<\?=/ },
41 // less relevant per PSR-1 which says not to use short-tags
42 { begin: /<\?/, relevance: 0.1 },
43 { begin: /\?>/ } // end php tag
44 ]
45 };
46 const SUBST = {
47 scope: 'subst',
48 variants: [
49 { begin: /\$\w+/ },
50 {
51 begin: /\{\$/,
52 end: /\}/
53 }
54 ]
55 };
56 const SINGLE_QUOTED = hljs.inherit(hljs.APOS_STRING_MODE, { illegal: null, });
57 const DOUBLE_QUOTED = hljs.inherit(hljs.QUOTE_STRING_MODE, {
58 illegal: null,
59 contains: hljs.QUOTE_STRING_MODE.contains.concat(SUBST),
60 });
61
62 const HEREDOC = {
63 begin: /<<<[ \t]*(?:(\w+)|"(\w+)")\n/,
64 end: /[ \t]*(\w+)\b/,
65 contains: hljs.QUOTE_STRING_MODE.contains.concat(SUBST),
66 'on:begin': (m, resp) => { resp.data._beginMatch = m[1] || m[2]; },
67 'on:end': (m, resp) => { if (resp.data._beginMatch !== m[1]) resp.ignoreMatch(); },
68 };
69
70 const NOWDOC = hljs.END_SAME_AS_BEGIN({
71 begin: /<<<[ \t]*'(\w+)'\n/,
72 end: /[ \t]*(\w+)\b/,
73 });
74 // list of valid whitespaces because non-breaking space might be part of a IDENT_RE
75 const WHITESPACE = '[ \t\n]';
76 const STRING = {
77 scope: 'string',
78 variants: [
79 DOUBLE_QUOTED,
80 SINGLE_QUOTED,
81 HEREDOC,
82 NOWDOC
83 ]
84 };
85 const NUMBER = {
86 scope: 'number',
87 variants: [
88 { begin: `\\b0[bB][01]+(?:_[01]+)*\\b` }, // Binary w/ underscore support
89 { begin: `\\b0[oO][0-7]+(?:_[0-7]+)*\\b` }, // Octals w/ underscore support
90 { begin: `\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b` }, // Hex w/ underscore support
91 // Decimals w/ underscore support, with optional fragments and scientific exponent (e) suffix.
92 { begin: `(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?` }
93 ],
94 relevance: 0
95 };
96 const LITERALS = [
97 "false",
98 "null",
99 "true"
100 ];
101 const KWS = [
102 // Magic constants:
103 // <https://www.php.net/manual/en/language.constants.predefined.php>
104 "__CLASS__",
105 "__DIR__",
106 "__FILE__",
107 "__FUNCTION__",
108 "__COMPILER_HALT_OFFSET__",
109 "__LINE__",
110 "__METHOD__",
111 "__NAMESPACE__",
112 "__TRAIT__",
113 // Function that look like language construct or language construct that look like function:
114 // List of keywords that may not require parenthesis
115 "die",
116 "echo",
117 "exit",
118 "include",
119 "include_once",
120 "print",
121 "require",
122 "require_once",
123 // These are not language construct (function) but operate on the currently-executing function and can access the current symbol table
124 // 'compact extract func_get_arg func_get_args func_num_args get_called_class get_parent_class ' +
125 // Other keywords:
126 // <https://www.php.net/manual/en/reserved.php>
127 // <https://www.php.net/manual/en/language.types.type-juggling.php>
128 "array",
129 "abstract",
130 "and",
131 "as",
132 "binary",
133 "bool",
134 "boolean",
135 "break",
136 "callable",
137 "case",
138 "catch",
139 "class",
140 "clone",
141 "const",
142 "continue",
143 "declare",
144 "default",
145 "do",
146 "double",
147 "else",
148 "elseif",
149 "empty",
150 "enddeclare",
151 "endfor",
152 "endforeach",
153 "endif",
154 "endswitch",
155 "endwhile",
156 "enum",
157 "eval",
158 "extends",
159 "final",
160 "finally",
161 "float",
162 "for",
163 "foreach",
164 "from",
165 "global",
166 "goto",
167 "if",
168 "implements",
169 "instanceof",
170 "insteadof",
171 "int",
172 "integer",
173 "interface",
174 "isset",
175 "iterable",
176 "list",
177 "match|0",
178 "mixed",
179 "new",
180 "never",
181 "object",
182 "or",
183 "private",
184 "protected",
185 "public",
186 "readonly",
187 "real",
188 "return",
189 "string",
190 "switch",
191 "throw",
192 "trait",
193 "try",
194 "unset",
195 "use",
196 "var",
197 "void",
198 "while",
199 "xor",
200 "yield"
201 ];
202
203 const BUILT_INS = [
204 // Standard PHP library:
205 // <https://www.php.net/manual/en/book.spl.php>
206 "Error|0",
207 "AppendIterator",
208 "ArgumentCountError",
209 "ArithmeticError",
210 "ArrayIterator",
211 "ArrayObject",
212 "AssertionError",
213 "BadFunctionCallException",
214 "BadMethodCallException",
215 "CachingIterator",
216 "CallbackFilterIterator",
217 "CompileError",
218 "Countable",
219 "DirectoryIterator",
220 "DivisionByZeroError",
221 "DomainException",
222 "EmptyIterator",
223 "ErrorException",
224 "Exception",
225 "FilesystemIterator",
226 "FilterIterator",
227 "GlobIterator",
228 "InfiniteIterator",
229 "InvalidArgumentException",
230 "IteratorIterator",
231 "LengthException",
232 "LimitIterator",
233 "LogicException",
234 "MultipleIterator",
235 "NoRewindIterator",
236 "OutOfBoundsException",
237 "OutOfRangeException",
238 "OuterIterator",
239 "OverflowException",
240 "ParentIterator",
241 "ParseError",
242 "RangeException",
243 "RecursiveArrayIterator",
244 "RecursiveCachingIterator",
245 "RecursiveCallbackFilterIterator",
246 "RecursiveDirectoryIterator",
247 "RecursiveFilterIterator",
248 "RecursiveIterator",
249 "RecursiveIteratorIterator",
250 "RecursiveRegexIterator",
251 "RecursiveTreeIterator",
252 "RegexIterator",
253 "RuntimeException",
254 "SeekableIterator",
255 "SplDoublyLinkedList",
256 "SplFileInfo",
257 "SplFileObject",
258 "SplFixedArray",
259 "SplHeap",
260 "SplMaxHeap",
261 "SplMinHeap",
262 "SplObjectStorage",
263 "SplObserver",
264 "SplPriorityQueue",
265 "SplQueue",
266 "SplStack",
267 "SplSubject",
268 "SplTempFileObject",
269 "TypeError",
270 "UnderflowException",
271 "UnexpectedValueException",
272 "UnhandledMatchError",
273 // Reserved interfaces:
274 // <https://www.php.net/manual/en/reserved.interfaces.php>
275 "ArrayAccess",
276 "BackedEnum",
277 "Closure",
278 "Fiber",
279 "Generator",
280 "Iterator",
281 "IteratorAggregate",
282 "Serializable",
283 "Stringable",
284 "Throwable",
285 "Traversable",
286 "UnitEnum",
287 "WeakReference",
288 "WeakMap",
289 // Reserved classes:
290 // <https://www.php.net/manual/en/reserved.classes.php>
291 "Directory",
292 "__PHP_Incomplete_Class",
293 "parent",
294 "php_user_filter",
295 "self",
296 "static",
297 "stdClass"
298 ];
299
300 /** Dual-case keywords
301 *
302 * ["then","FILE"] =>
303 * ["then", "THEN", "FILE", "file"]
304 *
305 * @param {string[]} items */
306 const dualCase = (items) => {
307 /** @type string[] */
308 const result = [];
309 items.forEach(item => {
310 result.push(item);
311 if (item.toLowerCase() === item) {
312 result.push(item.toUpperCase());
313 } else {
314 result.push(item.toLowerCase());
315 }
316 });
317 return result;
318 };
319
320 const KEYWORDS = {
321 keyword: KWS,
322 literal: dualCase(LITERALS),
323 built_in: BUILT_INS,
324 };
325
326 /**
327 * @param {string[]} items */
328 const normalizeKeywords = (items) => {
329 return items.map(item => {
330 return item.replace(/\|\d+$/, "");
331 });
332 };
333
334 const CONSTRUCTOR_CALL = { variants: [
335 {
336 match: [
337 /new/,
338 regex.concat(WHITESPACE, "+"),
339 // to prevent built ins from being confused as the class constructor call
340 regex.concat("(?!", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"),
341 PASCAL_CASE_CLASS_NAME_RE,
342 ],
343 scope: {
344 1: "keyword",
345 4: "title.class",
346 },
347 }
348 ] };
349
350 const CONSTANT_REFERENCE = regex.concat(IDENT_RE, "\\b(?!\\()");
351
352 const LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON = { variants: [
353 {
354 match: [
355 regex.concat(
356 /::/,
357 regex.lookahead(/(?!class\b)/)
358 ),
359 CONSTANT_REFERENCE,
360 ],
361 scope: { 2: "variable.constant", },
362 },
363 {
364 match: [
365 /::/,
366 /class/,
367 ],
368 scope: { 2: "variable.language", },
369 },
370 {
371 match: [
372 PASCAL_CASE_CLASS_NAME_RE,
373 regex.concat(
374 /::/,
375 regex.lookahead(/(?!class\b)/)
376 ),
377 CONSTANT_REFERENCE,
378 ],
379 scope: {
380 1: "title.class",
381 3: "variable.constant",
382 },
383 },
384 {
385 match: [
386 PASCAL_CASE_CLASS_NAME_RE,
387 regex.concat(
388 "::",
389 regex.lookahead(/(?!class\b)/)
390 ),
391 ],
392 scope: { 1: "title.class", },
393 },
394 {
395 match: [
396 PASCAL_CASE_CLASS_NAME_RE,
397 /::/,
398 /class/,
399 ],
400 scope: {
401 1: "title.class",
402 3: "variable.language",
403 },
404 }
405 ] };
406
407 const NAMED_ARGUMENT = {
408 scope: 'attr',
409 match: regex.concat(IDENT_RE, regex.lookahead(':'), regex.lookahead(/(?!::)/)),
410 };
411 const PARAMS_MODE = {
412 relevance: 0,
413 begin: /\(/,
414 end: /\)/,
415 keywords: KEYWORDS,
416 contains: [
417 NAMED_ARGUMENT,
418 VARIABLE,
419 LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON,
420 hljs.C_BLOCK_COMMENT_MODE,
421 STRING,
422 NUMBER,
423 CONSTRUCTOR_CALL,
424 ],
425 };
426 const FUNCTION_INVOKE = {
427 relevance: 0,
428 match: [
429 /\b/,
430 // to prevent keywords from being confused as the function title
431 regex.concat("(?!fn\\b|function\\b|", normalizeKeywords(KWS).join("\\b|"), "|", normalizeKeywords(BUILT_INS).join("\\b|"), "\\b)"),
432 IDENT_RE,
433 regex.concat(WHITESPACE, "*"),
434 regex.lookahead(/(?=\()/)
435 ],
436 scope: { 3: "title.function.invoke", },
437 contains: [ PARAMS_MODE ]
438 };
439 PARAMS_MODE.contains.push(FUNCTION_INVOKE);
440
441 const ATTRIBUTE_CONTAINS = [
442 NAMED_ARGUMENT,
443 LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON,
444 hljs.C_BLOCK_COMMENT_MODE,
445 STRING,
446 NUMBER,
447 CONSTRUCTOR_CALL,
448 ];
449
450 const ATTRIBUTES = {
451 begin: regex.concat(/#\[\s*\\?/,
452 regex.either(
453 PASCAL_CASE_CLASS_NAME_RE,
454 UPCASE_NAME_RE
455 )
456 ),
457 beginScope: "meta",
458 end: /]/,
459 endScope: "meta",
460 keywords: {
461 literal: LITERALS,
462 keyword: [
463 'new',
464 'array',
465 ]
466 },
467 contains: [
468 {
469 begin: /\[/,
470 end: /]/,
471 keywords: {
472 literal: LITERALS,
473 keyword: [
474 'new',
475 'array',
476 ]
477 },
478 contains: [
479 'self',
480 ...ATTRIBUTE_CONTAINS,
481 ]
482 },
483 ...ATTRIBUTE_CONTAINS,
484 {
485 scope: 'meta',
486 variants: [
487 { match: PASCAL_CASE_CLASS_NAME_RE },
488 { match: UPCASE_NAME_RE }
489 ]
490 }
491 ]
492 };
493
494 return {
495 case_insensitive: false,
496 keywords: KEYWORDS,
497 contains: [
498 ATTRIBUTES,
499 hljs.HASH_COMMENT_MODE,
500 hljs.COMMENT('//', '$'),
501 hljs.COMMENT(
502 '/\\*',
503 '\\*/',
504 { contains: [
505 {
506 scope: 'doctag',
507 match: '@[A-Za-z]+'
508 }
509 ] }
510 ),
511 {
512 match: /__halt_compiler\(\);/,
513 keywords: '__halt_compiler',
514 starts: {
515 scope: "comment",
516 end: hljs.MATCH_NOTHING_RE,
517 contains: [
518 {
519 match: /\?>/,
520 scope: "meta",
521 endsParent: true
522 }
523 ]
524 }
525 },
526 PREPROCESSOR,
527 {
528 scope: 'variable.language',
529 match: /\$this\b/
530 },
531 VARIABLE,
532 FUNCTION_INVOKE,
533 LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON,
534 {
535 match: [
536 /const/,
537 /\s/,
538 IDENT_RE,
539 ],
540 scope: {
541 1: "keyword",
542 3: "variable.constant",
543 },
544 },
545 CONSTRUCTOR_CALL,
546 {
547 scope: 'function',
548 relevance: 0,
549 beginKeywords: 'fn function',
550 end: /[;{]/,
551 excludeEnd: true,
552 illegal: '[$%\\[]',
553 contains: [
554 { beginKeywords: 'use', },
555 hljs.UNDERSCORE_TITLE_MODE,
556 {
557 begin: '=>', // No markup, just a relevance booster
558 endsParent: true
559 },
560 {
561 scope: 'params',
562 begin: '\\(',
563 end: '\\)',
564 excludeBegin: true,
565 excludeEnd: true,
566 keywords: KEYWORDS,
567 contains: [
568 'self',
569 ATTRIBUTES,
570 VARIABLE,
571 LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON,
572 hljs.C_BLOCK_COMMENT_MODE,
573 STRING,
574 NUMBER
575 ]
576 },
577 ]
578 },
579 {
580 scope: 'class',
581 variants: [
582 {
583 beginKeywords: "enum",
584 illegal: /[($"]/
585 },
586 {
587 beginKeywords: "class interface trait",
588 illegal: /[:($"]/
589 }
590 ],
591 relevance: 0,
592 end: /\{/,
593 excludeEnd: true,
594 contains: [
595 { beginKeywords: 'extends implements' },
596 hljs.UNDERSCORE_TITLE_MODE
597 ]
598 },
599 // both use and namespace still use "old style" rules (vs multi-match)
600 // because the namespace name can include `\` and we still want each
601 // element to be treated as its own *individual* title
602 {
603 beginKeywords: 'namespace',
604 relevance: 0,
605 end: ';',
606 illegal: /[.']/,
607 contains: [ hljs.inherit(hljs.UNDERSCORE_TITLE_MODE, { scope: "title.class" }) ]
608 },
609 {
610 beginKeywords: 'use',
611 relevance: 0,
612 end: ';',
613 contains: [
614 // TODO: title.function vs title.class
615 {
616 match: /\b(as|const|function)\b/,
617 scope: "keyword"
618 },
619 // TODO: could be title.class or title.function
620 hljs.UNDERSCORE_TITLE_MODE
621 ]
622 },
623 STRING,
624 NUMBER,
625 ]
626 };
627 }
628
629 return php;
630
631 })();
632 ;
633 export default hljsGrammar;