phpLanguage top-level constant
Grammar
const phpLanguage
PHP 8.
Code is PHP by default, so snippets without <?php highlight correctly.
?> switches to HTML until the next <?php or <?=, and a file that
starts with markup is HTML until its first PHP tag.
Implementation
const phpLanguage = Grammar(
name: 'php',
aliases: ['php8', 'phtml'],
fileExtensions: ['php', 'phtml', 'php8', 'php7'],
shebangs: ['php'],
signatures: [
r'<\?php\b',
r'\$this->\w+',
r'^\s*(?:namespace|use)\s+[A-Z]\w*(?:\\\w+)+;',
r'\bfunction\s+\w+\s*\([^)]*\$\w+',
r'\b(?:echo|require_once)\s',
],
rules: [
TokenRule(r'^#!.*$', scope: Scopes.meta),
// A file that starts with markup is HTML until the first PHP tag.
RegionRule(
begin: r'(?<![\s\S])(?=[ \t\r\n]*<(?!\?))',
end: r'<\?(?:php\b|=)?|(?![\s\S])',
endScope: Scopes.meta,
embed: htmlLanguage,
),
// `?>` leaves PHP; the text up to the next open tag is HTML.
RegionRule(
begin: r'\?>',
end: r'<\?(?:php\b|=)?|(?![\s\S])',
beginScope: Scopes.meta,
endScope: Scopes.meta,
embed: htmlLanguage,
),
TokenRule(r'<\?(?:php\b|=)?', scope: Scopes.meta),
IncludeRule('code'),
],
repository: {
'code': [
BlockComment('/**', '*/', scope: Scopes.commentDoc),
BlockComment('/*', '*/'),
// Line comments end at `?>`, which leaves PHP even inside a comment.
TokenRule(r'(?://|#(?!\[))(?:[^?\n]|\?(?!>))*', scope: Scopes.comment),
// Attributes: `#[Route('/x')]`.
RegionRule(
begin: r'#\[',
end: r'\]',
scope: Scopes.meta,
rules: [IncludeRule('strings'), IncludeRule('brackets')],
),
IncludeRule('heredocs'),
IncludeRule('strings'),
TokenRule(r'\$this\b', scope: Scopes.variableLanguage),
TokenRule(r'\$+[A-Za-z_]\w*', scope: Scopes.variable),
Numbers(),
// Members and static calls: `->name(`, `?->name`, `::name(`.
TokenRule(r'(?<=->|::)[A-Za-z_]\w*(?=[ \t]*\()', scope: Scopes.function),
TokenRule(r'(?<=->)[A-Za-z_]\w*'),
KeywordRule(
{
Scopes.keyword: [
'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', 'enum', 'eval', 'exit', 'extends', 'final',
'finally', 'fn', 'for', 'foreach', 'from', 'function', 'global',
'goto', 'if', 'implements', 'include', 'include_once',
'instanceof', 'insteadof', 'interface', 'isset', 'list', 'match',
'namespace', 'new', 'or', 'print', 'private', 'protected',
'public', 'readonly', 'require', 'require_once', 'return',
'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use',
'var', 'while', 'xor', 'yield',
],
Scopes.literal: [
'true', 'false', 'null', 'TRUE', 'FALSE', 'NULL', 'True', //
'False', 'Null', '__CLASS__', '__DIR__', '__FILE__',
'__FUNCTION__', '__LINE__', '__METHOD__', '__NAMESPACE__',
'__TRAIT__', 'PHP_EOL', 'PHP_VERSION',
],
Scopes.variableLanguage: ['self', 'parent'],
Scopes.typeBuiltin: [
'bool', 'float', 'int', 'iterable', 'mixed', 'never', 'object', //
'string', 'void',
],
},
otherwise: [CommonRules.capitalizedType, _call],
),
Operators('+-*/%=<>!&|^~?:.@\\'),
],
'brackets': [
RegionRule(
begin: r'\[',
end: r'\]',
rules: [IncludeRule('strings'), IncludeRule('brackets')],
),
],
'strings': [
QuotedString(
"'",
multiline: true,
escape: null,
rules: [TokenRule(r"\\[\\']", scope: Scopes.stringEscape)],
),
QuotedString('"', multiline: true, rules: [IncludeRule('interpolated')]),
QuotedString('`', multiline: true, rules: [IncludeRule('interpolated')]),
],
// `$name`, `$obj->prop`, `$arr['k']`, `{$expr}` and `${name}`.
'interpolated': [
RegionRule(
begin: r'\{(?=\$)',
end: r'\}',
scope: Scopes.interpolation,
rules: [IncludeRule('code')],
),
TokenRule(r'\$\{[^}\n]*\}', scope: Scopes.variable),
TokenRule(
r'\$[A-Za-z_]\w*(?:->[A-Za-z_]\w*|\[[^\]\n]*\])?',
scope: Scopes.variable,
),
],
// Heredoc `<<<EOT` / `<<<"EOT"` and nowdoc `<<<'EOT'`. The closing
// marker may be indented and followed by `;`, `,` or `)`.
'heredocs': [
RegionRule(
begin: r"(<<<)[ \t]*'([A-Za-z_]\w*)'",
end: r'^[ \t]*\2\b',
beginCaptures: {1: Scopes.operator, 2: Scopes.meta},
endScope: Scopes.meta,
endReferencesBegin: true,
rules: [TokenRule(r'[^\n]+', scope: Scopes.string)],
),
RegionRule(
begin: r'(<<<)[ \t]*"?([A-Za-z_]\w*)"?',
end: r'^[ \t]*\2\b',
beginCaptures: {1: Scopes.operator, 2: Scopes.meta},
endScope: Scopes.meta,
endReferencesBegin: true,
rules: [
TokenRule(r'\\[\s\S]', scope: Scopes.stringEscape),
IncludeRule('interpolated'),
TokenRule(r'[^$\\{\n]+|[${]', scope: Scopes.string),
],
),
],
},
);