Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pygments/lexers/php.py: 61%
80 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-01 06:54 +0000
1"""
2 pygments.lexers.php
3 ~~~~~~~~~~~~~~~~~~~
5 Lexers for PHP and related languages.
7 :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
8 :license: BSD, see LICENSE for details.
9"""
11import re
13from pygments.lexer import Lexer, RegexLexer, include, bygroups, default, \
14 using, this, words, do_insertions, line_re
15from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
16 Number, Punctuation, Other, Generic
17from pygments.util import get_bool_opt, get_list_opt, shebang_matches
19__all__ = ['ZephirLexer', 'PsyshConsoleLexer', 'PhpLexer']
22class ZephirLexer(RegexLexer):
23 """
24 For Zephir language source code.
26 Zephir is a compiled high level language aimed
27 to the creation of C-extensions for PHP.
29 .. versionadded:: 2.0
30 """
32 name = 'Zephir'
33 url = 'http://zephir-lang.com/'
34 aliases = ['zephir']
35 filenames = ['*.zep']
37 zephir_keywords = ['fetch', 'echo', 'isset', 'empty']
38 zephir_type = ['bit', 'bits', 'string']
40 flags = re.DOTALL | re.MULTILINE
42 tokens = {
43 'commentsandwhitespace': [
44 (r'\s+', Text),
45 (r'//.*?\n', Comment.Single),
46 (r'/\*.*?\*/', Comment.Multiline)
47 ],
48 'slashstartsregex': [
49 include('commentsandwhitespace'),
50 (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/'
51 r'([gim]+\b|\B)', String.Regex, '#pop'),
52 (r'/', Operator, '#pop'),
53 default('#pop')
54 ],
55 'badregex': [
56 (r'\n', Text, '#pop')
57 ],
58 'root': [
59 (r'^(?=\s|/)', Text, 'slashstartsregex'),
60 include('commentsandwhitespace'),
61 (r'\+\+|--|~|&&|\?|:|\|\||\\(?=\n)|'
62 r'(<<|>>>?|==?|!=?|->|[-<>+*%&|^/])=?', Operator, 'slashstartsregex'),
63 (r'[{(\[;,]', Punctuation, 'slashstartsregex'),
64 (r'[})\].]', Punctuation),
65 (r'(for|in|while|do|break|return|continue|switch|case|default|if|else|loop|'
66 r'require|inline|throw|try|catch|finally|new|delete|typeof|instanceof|void|'
67 r'namespace|use|extends|this|fetch|isset|unset|echo|fetch|likely|unlikely|'
68 r'empty)\b', Keyword, 'slashstartsregex'),
69 (r'(var|let|with|function)\b', Keyword.Declaration, 'slashstartsregex'),
70 (r'(abstract|boolean|bool|char|class|const|double|enum|export|extends|final|'
71 r'native|goto|implements|import|int|string|interface|long|ulong|char|uchar|'
72 r'float|unsigned|private|protected|public|short|static|self|throws|reverse|'
73 r'transient|volatile|readonly)\b', Keyword.Reserved),
74 (r'(true|false|null|undefined)\b', Keyword.Constant),
75 (r'(Array|Boolean|Date|_REQUEST|_COOKIE|_SESSION|'
76 r'_GET|_POST|_SERVER|this|stdClass|range|count|iterator|'
77 r'window)\b', Name.Builtin),
78 (r'[$a-zA-Z_][\w\\]*', Name.Other),
79 (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float),
80 (r'0x[0-9a-fA-F]+', Number.Hex),
81 (r'[0-9]+', Number.Integer),
82 (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double),
83 (r"'(\\\\|\\[^\\]|[^'\\])*'", String.Single),
84 ]
85 }
88class PsyshConsoleLexer(Lexer):
89 """
90 For PsySH console output, such as:
92 .. sourcecode:: psysh
94 >>> $greeting = function($name): string {
95 ... return "Hello, {$name}";
96 ... };
97 => Closure($name): string {#2371 …3}
98 >>> $greeting('World')
99 => "Hello, World"
101 .. versionadded:: 2.7
102 """
103 name = 'PsySH console session for PHP'
104 url = 'https://psysh.org/'
105 aliases = ['psysh']
107 def __init__(self, **options):
108 options['startinline'] = True
109 Lexer.__init__(self, **options)
111 def get_tokens_unprocessed(self, text):
112 phplexer = PhpLexer(**self.options)
113 curcode = ''
114 insertions = []
115 for match in line_re.finditer(text):
116 line = match.group()
117 if line.startswith('>>> ') or line.startswith('... '):
118 insertions.append((len(curcode),
119 [(0, Generic.Prompt, line[:4])]))
120 curcode += line[4:]
121 elif line.rstrip() == '...':
122 insertions.append((len(curcode),
123 [(0, Generic.Prompt, '...')]))
124 curcode += line[3:]
125 else:
126 if curcode:
127 yield from do_insertions(
128 insertions, phplexer.get_tokens_unprocessed(curcode))
129 curcode = ''
130 insertions = []
131 yield match.start(), Generic.Output, line
132 if curcode:
133 yield from do_insertions(insertions,
134 phplexer.get_tokens_unprocessed(curcode))
137class PhpLexer(RegexLexer):
138 """
139 For PHP source code.
140 For PHP embedded in HTML, use the `HtmlPhpLexer`.
142 Additional options accepted:
144 `startinline`
145 If given and ``True`` the lexer starts highlighting with
146 php code (i.e.: no starting ``<?php`` required). The default
147 is ``False``.
148 `funcnamehighlighting`
149 If given and ``True``, highlight builtin function names
150 (default: ``True``).
151 `disabledmodules`
152 If given, must be a list of module names whose function names
153 should not be highlighted. By default all modules are highlighted
154 except the special ``'unknown'`` module that includes functions
155 that are known to php but are undocumented.
157 To get a list of allowed modules have a look into the
158 `_php_builtins` module:
160 .. sourcecode:: pycon
162 >>> from pygments.lexers._php_builtins import MODULES
163 >>> MODULES.keys()
164 ['PHP Options/Info', 'Zip', 'dba', ...]
166 In fact the names of those modules match the module names from
167 the php documentation.
168 """
170 name = 'PHP'
171 url = 'https://www.php.net/'
172 aliases = ['php', 'php3', 'php4', 'php5']
173 filenames = ['*.php', '*.php[345]', '*.inc']
174 mimetypes = ['text/x-php']
176 # Note that a backslash is included, PHP uses a backslash as a namespace
177 # separator.
178 _ident_inner = r'(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*'
179 # But not inside strings.
180 _ident_nons = r'(?:[_a-z]|[^\x00-\x7f])(?:\w|[^\x00-\x7f])*'
182 flags = re.IGNORECASE | re.DOTALL | re.MULTILINE
183 tokens = {
184 'root': [
185 (r'<\?(php)?', Comment.Preproc, 'php'),
186 (r'[^<]+', Other),
187 (r'<', Other)
188 ],
189 'php': [
190 (r'\?>', Comment.Preproc, '#pop'),
191 (r'(<<<)([\'"]?)(' + _ident_nons + r')(\2\n.*?\n\s*)(\3)(;?)(\n)',
192 bygroups(String, String, String.Delimiter, String, String.Delimiter,
193 Punctuation, Text)),
194 (r'\s+', Text),
195 (r'#\[', Punctuation, 'attribute'),
196 (r'#.*?\n', Comment.Single),
197 (r'//.*?\n', Comment.Single),
198 # put the empty comment here, it is otherwise seen as
199 # the start of a docstring
200 (r'/\*\*/', Comment.Multiline),
201 (r'/\*\*.*?\*/', String.Doc),
202 (r'/\*.*?\*/', Comment.Multiline),
203 (r'(->|::)(\s*)(' + _ident_nons + ')',
204 bygroups(Operator, Text, Name.Attribute)),
205 (r'[~!%^&*+=|:.<>/@-]+', Operator),
206 (r'\?', Operator), # don't add to the charclass above!
207 (r'[\[\]{}();,]+', Punctuation),
208 (r'(new)(\s+)(class)\b', bygroups(Keyword, Text, Keyword)),
209 (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'),
210 (r'(function)(\s*)(?=\()', bygroups(Keyword, Text)),
211 (r'(function)(\s+)(&?)(\s*)',
212 bygroups(Keyword, Text, Operator, Text), 'functionname'),
213 (r'(const)(\s+)(' + _ident_inner + ')',
214 bygroups(Keyword, Text, Name.Constant)),
215 (r'(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|'
216 r'eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|'
217 r'FALSE|print|for|require|continue|foreach|require_once|'
218 r'declare|return|default|static|do|switch|die|stdClass|'
219 r'echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|'
220 r'virtual|endfor|include_once|while|endforeach|global|'
221 r'endif|list|endswitch|new|endwhile|not|'
222 r'array|E_ALL|NULL|final|php_user_filter|interface|'
223 r'implements|public|private|protected|abstract|clone|try|'
224 r'catch|throw|this|use|namespace|trait|yield|'
225 r'finally|match)\b', Keyword),
226 (r'(true|false|null)\b', Keyword.Constant),
227 include('magicconstants'),
228 (r'\$\{', Name.Variable, 'variablevariable'),
229 (r'\$+' + _ident_inner, Name.Variable),
230 (_ident_inner, Name.Other),
231 (r'(\d+\.\d*|\d*\.\d+)(e[+-]?[0-9]+)?', Number.Float),
232 (r'\d+e[+-]?[0-9]+', Number.Float),
233 (r'0[0-7]+', Number.Oct),
234 (r'0x[a-f0-9]+', Number.Hex),
235 (r'\d+', Number.Integer),
236 (r'0b[01]+', Number.Bin),
237 (r"'([^'\\]*(?:\\.[^'\\]*)*)'", String.Single),
238 (r'`([^`\\]*(?:\\.[^`\\]*)*)`', String.Backtick),
239 (r'"', String.Double, 'string'),
240 ],
241 'variablevariable': [
242 (r'\}', Name.Variable, '#pop'),
243 include('php')
244 ],
245 'magicfuncs': [
246 # source: http://php.net/manual/en/language.oop5.magic.php
247 (words((
248 '__construct', '__destruct', '__call', '__callStatic', '__get', '__set',
249 '__isset', '__unset', '__sleep', '__wakeup', '__toString', '__invoke',
250 '__set_state', '__clone', '__debugInfo',), suffix=r'\b'),
251 Name.Function.Magic),
252 ],
253 'magicconstants': [
254 # source: http://php.net/manual/en/language.constants.predefined.php
255 (words((
256 '__LINE__', '__FILE__', '__DIR__', '__FUNCTION__', '__CLASS__',
257 '__TRAIT__', '__METHOD__', '__NAMESPACE__',),
258 suffix=r'\b'),
259 Name.Constant),
260 ],
261 'classname': [
262 (_ident_inner, Name.Class, '#pop')
263 ],
264 'functionname': [
265 include('magicfuncs'),
266 (_ident_inner, Name.Function, '#pop'),
267 default('#pop')
268 ],
269 'string': [
270 (r'"', String.Double, '#pop'),
271 (r'[^{$"\\]+', String.Double),
272 (r'\\([nrt"$\\]|[0-7]{1,3}|x[0-9a-f]{1,2})', String.Escape),
273 (r'\$' + _ident_nons + r'(\[\S+?\]|->' + _ident_nons + ')?',
274 String.Interpol),
275 (r'(\{\$\{)(.*?)(\}\})',
276 bygroups(String.Interpol, using(this, _startinline=True),
277 String.Interpol)),
278 (r'(\{)(\$.*?)(\})',
279 bygroups(String.Interpol, using(this, _startinline=True),
280 String.Interpol)),
281 (r'(\$\{)(\S+)(\})',
282 bygroups(String.Interpol, Name.Variable, String.Interpol)),
283 (r'[${\\]', String.Double)
284 ],
285 'attribute': [
286 (r'\]', Punctuation, '#pop'),
287 (r'\(', Punctuation, 'attributeparams'),
288 (_ident_inner, Name.Decorator),
289 include('php')
290 ],
291 'attributeparams': [
292 (r'\)', Punctuation, '#pop'),
293 include('php')
294 ],
295 }
297 def __init__(self, **options):
298 self.funcnamehighlighting = get_bool_opt(
299 options, 'funcnamehighlighting', True)
300 self.disabledmodules = get_list_opt(
301 options, 'disabledmodules', ['unknown'])
302 self.startinline = get_bool_opt(options, 'startinline', False)
304 # private option argument for the lexer itself
305 if '_startinline' in options:
306 self.startinline = options.pop('_startinline')
308 # collect activated functions in a set
309 self._functions = set()
310 if self.funcnamehighlighting:
311 from pygments.lexers._php_builtins import MODULES
312 for key, value in MODULES.items():
313 if key not in self.disabledmodules:
314 self._functions.update(value)
315 RegexLexer.__init__(self, **options)
317 def get_tokens_unprocessed(self, text):
318 stack = ['root']
319 if self.startinline:
320 stack.append('php')
321 for index, token, value in \
322 RegexLexer.get_tokens_unprocessed(self, text, stack):
323 if token is Name.Other:
324 if value in self._functions:
325 yield index, Name.Builtin, value
326 continue
327 yield index, token, value
329 def analyse_text(text):
330 if shebang_matches(text, r'php'):
331 return True
332 rv = 0.0
333 if re.search(r'<\?(?!xml)', text):
334 rv += 0.3
335 return rv