Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F9125993
php.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
13 KB
Subscribers
None
php.js
View Options
/*
Language: PHP
Author: Victor Karamzin <Victor.Karamzin@enterra-inc.com>
Contributors: Evgeny Stepanischev <imbolk@gmail.com>, Ivan Sagalaev <maniac@softwaremaniacs.org>
Website: https://www.php.net
Category: common
*/
/**
* @param {HLJSApi} hljs
* @returns {LanguageDetail}
* */
function
php
(
hljs
)
{
const
regex
=
hljs
.
regex
;
// negative look-ahead tries to avoid matching patterns that are not
// Perl at all like $ident$, @ident@, etc.
const
NOT_PERL_ETC
=
/(?![A-Za-z0-9])(?![$])/
;
const
IDENT_RE
=
regex
.
concat
(
/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/
,
NOT_PERL_ETC
);
// Will not detect camelCase classes
const
PASCAL_CASE_CLASS_NAME_RE
=
regex
.
concat
(
/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/
,
NOT_PERL_ETC
);
const
VARIABLE
=
{
scope
:
'variable'
,
match
:
'\\$+'
+
IDENT_RE
,
};
const
PREPROCESSOR
=
{
scope
:
'meta'
,
variants
:
[
{
begin
:
/<\?php/
,
relevance
:
10
},
// boost for obvious PHP
{
begin
:
/<\?=/
},
// less relevant per PSR-1 which says not to use short-tags
{
begin
:
/<\?/
,
relevance
:
0.1
},
{
begin
:
/\?>/
}
// end php tag
]
};
const
SUBST
=
{
scope
:
'subst'
,
variants
:
[
{
begin
:
/\$\w+/
},
{
begin
:
/\{\$/
,
end
:
/\}/
}
]
};
const
SINGLE_QUOTED
=
hljs
.
inherit
(
hljs
.
APOS_STRING_MODE
,
{
illegal
:
null
,
});
const
DOUBLE_QUOTED
=
hljs
.
inherit
(
hljs
.
QUOTE_STRING_MODE
,
{
illegal
:
null
,
contains
:
hljs
.
QUOTE_STRING_MODE
.
contains
.
concat
(
SUBST
),
});
const
HEREDOC
=
hljs
.
END_SAME_AS_BEGIN
({
begin
:
/<<<[ \t]*(\w+)\n/
,
end
:
/[ \t]*(\w+)\b/
,
contains
:
hljs
.
QUOTE_STRING_MODE
.
contains
.
concat
(
SUBST
),
});
// list of valid whitespaces because non-breaking space might be part of a IDENT_RE
const
WHITESPACE
=
'[ \t\n]'
;
const
STRING
=
{
scope
:
'string'
,
variants
:
[
DOUBLE_QUOTED
,
SINGLE_QUOTED
,
HEREDOC
]
};
const
NUMBER
=
{
scope
:
'number'
,
variants
:
[
{
begin
:
`\\b0[bB][01]+(?:_[01]+)*\\b`
},
// Binary w/ underscore support
{
begin
:
`\\b0[oO][0-7]+(?:_[0-7]+)*\\b`
},
// Octals w/ underscore support
{
begin
:
`\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b`
},
// Hex w/ underscore support
// Decimals w/ underscore support, with optional fragments and scientific exponent (e) suffix.
{
begin
:
`(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?`
}
],
relevance
:
0
};
const
LITERALS
=
[
"false"
,
"null"
,
"true"
];
const
KWS
=
[
// Magic constants:
// <https://www.php.net/manual/en/language.constants.predefined.php>
"__CLASS__"
,
"__DIR__"
,
"__FILE__"
,
"__FUNCTION__"
,
"__COMPILER_HALT_OFFSET__"
,
"__LINE__"
,
"__METHOD__"
,
"__NAMESPACE__"
,
"__TRAIT__"
,
// Function that look like language construct or language construct that look like function:
// List of keywords that may not require parenthesis
"die"
,
"echo"
,
"exit"
,
"include"
,
"include_once"
,
"print"
,
"require"
,
"require_once"
,
// These are not language construct (function) but operate on the currently-executing function and can access the current symbol table
// 'compact extract func_get_arg func_get_args func_num_args get_called_class get_parent_class ' +
// Other keywords:
// <https://www.php.net/manual/en/reserved.php>
// <https://www.php.net/manual/en/language.types.type-juggling.php>
"array"
,
"abstract"
,
"and"
,
"as"
,
"binary"
,
"bool"
,
"boolean"
,
"break"
,
"callable"
,
"case"
,
"catch"
,
"class"
,
"clone"
,
"const"
,
"continue"
,
"declare"
,
"default"
,
"do"
,
"double"
,
"else"
,
"elseif"
,
"empty"
,
"enddeclare"
,
"endfor"
,
"endforeach"
,
"endif"
,
"endswitch"
,
"endwhile"
,
"enum"
,
"eval"
,
"extends"
,
"final"
,
"finally"
,
"float"
,
"for"
,
"foreach"
,
"from"
,
"global"
,
"goto"
,
"if"
,
"implements"
,
"instanceof"
,
"insteadof"
,
"int"
,
"integer"
,
"interface"
,
"isset"
,
"iterable"
,
"list"
,
"match|0"
,
"mixed"
,
"new"
,
"never"
,
"object"
,
"or"
,
"private"
,
"protected"
,
"public"
,
"readonly"
,
"real"
,
"return"
,
"string"
,
"switch"
,
"throw"
,
"trait"
,
"try"
,
"unset"
,
"use"
,
"var"
,
"void"
,
"while"
,
"xor"
,
"yield"
];
const
BUILT_INS
=
[
// Standard PHP library:
// <https://www.php.net/manual/en/book.spl.php>
"Error|0"
,
"AppendIterator"
,
"ArgumentCountError"
,
"ArithmeticError"
,
"ArrayIterator"
,
"ArrayObject"
,
"AssertionError"
,
"BadFunctionCallException"
,
"BadMethodCallException"
,
"CachingIterator"
,
"CallbackFilterIterator"
,
"CompileError"
,
"Countable"
,
"DirectoryIterator"
,
"DivisionByZeroError"
,
"DomainException"
,
"EmptyIterator"
,
"ErrorException"
,
"Exception"
,
"FilesystemIterator"
,
"FilterIterator"
,
"GlobIterator"
,
"InfiniteIterator"
,
"InvalidArgumentException"
,
"IteratorIterator"
,
"LengthException"
,
"LimitIterator"
,
"LogicException"
,
"MultipleIterator"
,
"NoRewindIterator"
,
"OutOfBoundsException"
,
"OutOfRangeException"
,
"OuterIterator"
,
"OverflowException"
,
"ParentIterator"
,
"ParseError"
,
"RangeException"
,
"RecursiveArrayIterator"
,
"RecursiveCachingIterator"
,
"RecursiveCallbackFilterIterator"
,
"RecursiveDirectoryIterator"
,
"RecursiveFilterIterator"
,
"RecursiveIterator"
,
"RecursiveIteratorIterator"
,
"RecursiveRegexIterator"
,
"RecursiveTreeIterator"
,
"RegexIterator"
,
"RuntimeException"
,
"SeekableIterator"
,
"SplDoublyLinkedList"
,
"SplFileInfo"
,
"SplFileObject"
,
"SplFixedArray"
,
"SplHeap"
,
"SplMaxHeap"
,
"SplMinHeap"
,
"SplObjectStorage"
,
"SplObserver"
,
"SplPriorityQueue"
,
"SplQueue"
,
"SplStack"
,
"SplSubject"
,
"SplTempFileObject"
,
"TypeError"
,
"UnderflowException"
,
"UnexpectedValueException"
,
"UnhandledMatchError"
,
// Reserved interfaces:
// <https://www.php.net/manual/en/reserved.interfaces.php>
"ArrayAccess"
,
"BackedEnum"
,
"Closure"
,
"Fiber"
,
"Generator"
,
"Iterator"
,
"IteratorAggregate"
,
"Serializable"
,
"Stringable"
,
"Throwable"
,
"Traversable"
,
"UnitEnum"
,
"WeakReference"
,
"WeakMap"
,
// Reserved classes:
// <https://www.php.net/manual/en/reserved.classes.php>
"Directory"
,
"__PHP_Incomplete_Class"
,
"parent"
,
"php_user_filter"
,
"self"
,
"static"
,
"stdClass"
];
/** Dual-case keywords
*
* ["then","FILE"] =>
* ["then", "THEN", "FILE", "file"]
*
* @param {string[]} items */
const
dualCase
=
(
items
)
=>
{
/** @type string[] */
const
result
=
[];
items
.
forEach
(
item
=>
{
result
.
push
(
item
);
if
(
item
.
toLowerCase
()
===
item
)
{
result
.
push
(
item
.
toUpperCase
());
}
else
{
result
.
push
(
item
.
toLowerCase
());
}
});
return
result
;
};
const
KEYWORDS
=
{
keyword
:
KWS
,
literal
:
dualCase
(
LITERALS
),
built_in
:
BUILT_INS
,
};
/**
* @param {string[]} items */
const
normalizeKeywords
=
(
items
)
=>
{
return
items
.
map
(
item
=>
{
return
item
.
replace
(
/\|\d+$/
,
""
);
});
};
const
CONSTRUCTOR_CALL
=
{
variants
:
[
{
match
:
[
/new/
,
regex
.
concat
(
WHITESPACE
,
"+"
),
// to prevent built ins from being confused as the class constructor call
regex
.
concat
(
"(?!"
,
normalizeKeywords
(
BUILT_INS
).
join
(
"\\b|"
),
"\\b)"
),
PASCAL_CASE_CLASS_NAME_RE
,
],
scope
:
{
1
:
"keyword"
,
4
:
"title.class"
,
},
}
]
};
const
CONSTANT_REFERENCE
=
regex
.
concat
(
IDENT_RE
,
"\\b(?!\\()"
);
const
LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON
=
{
variants
:
[
{
match
:
[
regex
.
concat
(
/::/
,
regex
.
lookahead
(
/(?!class\b)/
)
),
CONSTANT_REFERENCE
,
],
scope
:
{
2
:
"variable.constant"
,
},
},
{
match
:
[
/::/
,
/class/
,
],
scope
:
{
2
:
"variable.language"
,
},
},
{
match
:
[
PASCAL_CASE_CLASS_NAME_RE
,
regex
.
concat
(
/::/
,
regex
.
lookahead
(
/(?!class\b)/
)
),
CONSTANT_REFERENCE
,
],
scope
:
{
1
:
"title.class"
,
3
:
"variable.constant"
,
},
},
{
match
:
[
PASCAL_CASE_CLASS_NAME_RE
,
regex
.
concat
(
"::"
,
regex
.
lookahead
(
/(?!class\b)/
)
),
],
scope
:
{
1
:
"title.class"
,
},
},
{
match
:
[
PASCAL_CASE_CLASS_NAME_RE
,
/::/
,
/class/
,
],
scope
:
{
1
:
"title.class"
,
3
:
"variable.language"
,
},
}
]
};
const
NAMED_ARGUMENT
=
{
scope
:
'attr'
,
match
:
regex
.
concat
(
IDENT_RE
,
regex
.
lookahead
(
':'
),
regex
.
lookahead
(
/(?!::)/
)),
};
const
PARAMS_MODE
=
{
relevance
:
0
,
begin
:
/\(/
,
end
:
/\)/
,
keywords
:
KEYWORDS
,
contains
:
[
NAMED_ARGUMENT
,
VARIABLE
,
LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON
,
hljs
.
C_BLOCK_COMMENT_MODE
,
STRING
,
NUMBER
,
CONSTRUCTOR_CALL
,
],
};
const
FUNCTION_INVOKE
=
{
relevance
:
0
,
match
:
[
/\b/
,
// to prevent keywords from being confused as the function title
regex
.
concat
(
"(?!fn\\b|function\\b|"
,
normalizeKeywords
(
KWS
).
join
(
"\\b|"
),
"|"
,
normalizeKeywords
(
BUILT_INS
).
join
(
"\\b|"
),
"\\b)"
),
IDENT_RE
,
regex
.
concat
(
WHITESPACE
,
"*"
),
regex
.
lookahead
(
/(?=\()/
)
],
scope
:
{
3
:
"title.function.invoke"
,
},
contains
:
[
PARAMS_MODE
]
};
PARAMS_MODE
.
contains
.
push
(
FUNCTION_INVOKE
);
const
ATTRIBUTE_CONTAINS
=
[
NAMED_ARGUMENT
,
LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON
,
hljs
.
C_BLOCK_COMMENT_MODE
,
STRING
,
NUMBER
,
CONSTRUCTOR_CALL
,
];
const
ATTRIBUTES
=
{
begin
:
regex
.
concat
(
/#\[\s*/
,
PASCAL_CASE_CLASS_NAME_RE
),
beginScope
:
"meta"
,
end
:
/]/
,
endScope
:
"meta"
,
keywords
:
{
literal
:
LITERALS
,
keyword
:
[
'new'
,
'array'
,
]
},
contains
:
[
{
begin
:
/\[/
,
end
:
/]/
,
keywords
:
{
literal
:
LITERALS
,
keyword
:
[
'new'
,
'array'
,
]
},
contains
:
[
'self'
,
...
ATTRIBUTE_CONTAINS
,
]
},
...
ATTRIBUTE_CONTAINS
,
{
scope
:
'meta'
,
match
:
PASCAL_CASE_CLASS_NAME_RE
}
]
};
return
{
case_insensitive
:
false
,
keywords
:
KEYWORDS
,
contains
:
[
ATTRIBUTES
,
hljs
.
HASH_COMMENT_MODE
,
hljs
.
COMMENT
(
'//'
,
'$'
),
hljs
.
COMMENT
(
'/\\*'
,
'\\*/'
,
{
contains
:
[
{
scope
:
'doctag'
,
match
:
'@[A-Za-z]+'
}
]
}
),
{
match
:
/__halt_compiler\(\);/
,
keywords
:
'__halt_compiler'
,
starts
:
{
scope
:
"comment"
,
end
:
hljs
.
MATCH_NOTHING_RE
,
contains
:
[
{
match
:
/\?>/
,
scope
:
"meta"
,
endsParent
:
true
}
]
}
},
PREPROCESSOR
,
{
scope
:
'variable.language'
,
match
:
/\$this\b/
},
VARIABLE
,
FUNCTION_INVOKE
,
LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON
,
{
match
:
[
/const/
,
/\s/
,
IDENT_RE
,
],
scope
:
{
1
:
"keyword"
,
3
:
"variable.constant"
,
},
},
CONSTRUCTOR_CALL
,
{
scope
:
'function'
,
relevance
:
0
,
beginKeywords
:
'fn function'
,
end
:
/[;{]/
,
excludeEnd
:
true
,
illegal
:
'[$%\\[]'
,
contains
:
[
{
beginKeywords
:
'use'
,
},
hljs
.
UNDERSCORE_TITLE_MODE
,
{
begin
:
'=>'
,
// No markup, just a relevance booster
endsParent
:
true
},
{
scope
:
'params'
,
begin
:
'\\('
,
end
:
'\\)'
,
excludeBegin
:
true
,
excludeEnd
:
true
,
keywords
:
KEYWORDS
,
contains
:
[
'self'
,
VARIABLE
,
LEFT_AND_RIGHT_SIDE_OF_DOUBLE_COLON
,
hljs
.
C_BLOCK_COMMENT_MODE
,
STRING
,
NUMBER
]
},
]
},
{
scope
:
'class'
,
variants
:
[
{
beginKeywords
:
"enum"
,
illegal
:
/[($"]/
},
{
beginKeywords
:
"class interface trait"
,
illegal
:
/[:($"]/
}
],
relevance
:
0
,
end
:
/\{/
,
excludeEnd
:
true
,
contains
:
[
{
beginKeywords
:
'extends implements'
},
hljs
.
UNDERSCORE_TITLE_MODE
]
},
// both use and namespace still use "old style" rules (vs multi-match)
// because the namespace name can include `\` and we still want each
// element to be treated as its own *individual* title
{
beginKeywords
:
'namespace'
,
relevance
:
0
,
end
:
';'
,
illegal
:
/[.']/
,
contains
:
[
hljs
.
inherit
(
hljs
.
UNDERSCORE_TITLE_MODE
,
{
scope
:
"title.class"
})
]
},
{
beginKeywords
:
'use'
,
relevance
:
0
,
end
:
';'
,
contains
:
[
// TODO: title.function vs title.class
{
match
:
/\b(as|const|function)\b/
,
scope
:
"keyword"
},
// TODO: could be title.class or title.function
hljs
.
UNDERSCORE_TITLE_MODE
]
},
STRING
,
NUMBER
,
]
};
}
module
.
exports
=
php
;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Jun 21 2025, 9:34 PM (4 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3333809
Attached To
rDWAPPS Web applications
Event Timeline
Log In to Comment