Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pygments/lexers/php.py: 44%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

84 statements  

1""" 

2 pygments.lexers.php 

3 ~~~~~~~~~~~~~~~~~~~ 

4 

5 Lexers for PHP and related languages. 

6 

7 :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS. 

8 :license: BSD, see LICENSE for details. 

9""" 

10 

11import re 

12 

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 

18 

19__all__ = ['ZephirLexer', 'PsyshConsoleLexer', 'PhpLexer'] 

20 

21 

22class ZephirLexer(RegexLexer): 

23 """ 

24 For Zephir language source code. 

25 

26 Zephir is a compiled high level language aimed 

27 to the creation of C-extensions for PHP. 

28 """ 

29 

30 name = 'Zephir' 

31 url = 'http://zephir-lang.com/' 

32 aliases = ['zephir'] 

33 filenames = ['*.zep'] 

34 version_added = '2.0' 

35 

36 zephir_keywords = ['fetch', 'echo', 'isset', 'empty'] 

37 zephir_type = ['bit', 'bits', 'string'] 

38 

39 flags = re.DOTALL | re.MULTILINE 

40 

41 tokens = { 

42 'commentsandwhitespace': [ 

43 (r'\s+', Text), 

44 (r'//.*?\n', Comment.Single), 

45 (r'/\*.*?\*/', Comment.Multiline) 

46 ], 

47 'slashstartsregex': [ 

48 include('commentsandwhitespace'), 

49 (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/' 

50 r'([gim]+\b|\B)', String.Regex, '#pop'), 

51 (r'/', Operator, '#pop'), 

52 default('#pop') 

53 ], 

54 'badregex': [ 

55 (r'\n', Text, '#pop') 

56 ], 

57 'root': [ 

58 (r'^(?=\s|/)', Text, 'slashstartsregex'), 

59 include('commentsandwhitespace'), 

60 (r'\+\+|--|~|&&|\?|:|\|\||\\(?=\n)|' 

61 r'(<<|>>>?|==?|!=?|->|[-<>+*%&|^/])=?', Operator, 'slashstartsregex'), 

62 (r'[{(\[;,]', Punctuation, 'slashstartsregex'), 

63 (r'[})\].]', Punctuation), 

64 (r'(for|in|while|do|break|return|continue|switch|case|default|if|else|loop|' 

65 r'require|inline|throw|try|catch|finally|new|delete|typeof|instanceof|void|' 

66 r'namespace|use|extends|this|fetch|isset|unset|echo|fetch|likely|unlikely|' 

67 r'empty)\b', Keyword, 'slashstartsregex'), 

68 (r'(var|let|with|function)\b', Keyword.Declaration, 'slashstartsregex'), 

69 (r'(abstract|boolean|bool|char|class|const|double|enum|export|extends|final|' 

70 r'native|goto|implements|import|int|string|interface|long|ulong|char|uchar|' 

71 r'float|unsigned|private|protected|public|short|static|self|throws|reverse|' 

72 r'transient|volatile|readonly)\b', Keyword.Reserved), 

73 (r'(true|false|null|undefined)\b', Keyword.Constant), 

74 (r'(Array|Boolean|Date|_REQUEST|_COOKIE|_SESSION|' 

75 r'_GET|_POST|_SERVER|this|stdClass|range|count|iterator|' 

76 r'window)\b', Name.Builtin), 

77 (r'[$a-zA-Z_][\w\\]*', Name.Other), 

78 (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float), 

79 (r'0x[0-9a-fA-F]+', Number.Hex), 

80 (r'[0-9]+', Number.Integer), 

81 (r'"(\\\\|\\[^\\]|[^"\\])*"', String.Double), 

82 (r"'(\\\\|\\[^\\]|[^'\\])*'", String.Single), 

83 ] 

84 } 

85 

86 

87class PsyshConsoleLexer(Lexer): 

88 """ 

89 For PsySH console output, such as: 

90 

91 .. sourcecode:: psysh 

92 

93 >>> $greeting = function($name): string { 

94 ... return "Hello, {$name}"; 

95 ... }; 

96 => Closure($name): string {#2371 …3} 

97 >>> $greeting('World') 

98 => "Hello, World" 

99 """ 

100 name = 'PsySH console session for PHP' 

101 url = 'https://psysh.org/' 

102 aliases = ['psysh'] 

103 version_added = '2.7' 

104 

105 def __init__(self, **options): 

106 options['startinline'] = True 

107 Lexer.__init__(self, **options) 

108 

109 def get_tokens_unprocessed(self, text): 

110 phplexer = PhpLexer(**self.options) 

111 curcode = '' 

112 insertions = [] 

113 for match in line_re.finditer(text): 

114 line = match.group() 

115 if line.startswith('>>> ') or line.startswith('... '): 

116 insertions.append((len(curcode), 

117 [(0, Generic.Prompt, line[:4])])) 

118 curcode += line[4:] 

119 elif line.rstrip() == '...': 

120 insertions.append((len(curcode), 

121 [(0, Generic.Prompt, '...')])) 

122 curcode += line[3:] 

123 else: 

124 if curcode: 

125 yield from do_insertions( 

126 insertions, phplexer.get_tokens_unprocessed(curcode)) 

127 curcode = '' 

128 insertions = [] 

129 yield match.start(), Generic.Output, line 

130 if curcode: 

131 yield from do_insertions(insertions, 

132 phplexer.get_tokens_unprocessed(curcode)) 

133 

134 

135class PhpLexer(RegexLexer): 

136 """ 

137 For PHP source code. 

138 For PHP embedded in HTML, use the `HtmlPhpLexer`. 

139 

140 Additional options accepted: 

141 

142 `startinline` 

143 If given and ``True`` the lexer starts highlighting with 

144 php code (i.e.: no starting ``<?php`` required). The default 

145 is ``False``. 

146 `funcnamehighlighting` 

147 If given and ``True``, highlight builtin function names 

148 (default: ``True``). 

149 `disabledmodules` 

150 If given, must be a list of module names whose function names 

151 should not be highlighted. By default all modules are highlighted 

152 except the special ``'unknown'`` module that includes functions 

153 that are known to php but are undocumented. 

154 

155 To get a list of allowed modules have a look into the 

156 `_php_builtins` module: 

157 

158 .. sourcecode:: pycon 

159 

160 >>> from pygments.lexers._php_builtins import MODULES 

161 >>> MODULES.keys() 

162 ['PHP Options/Info', 'Zip', 'dba', ...] 

163 

164 In fact the names of those modules match the module names from 

165 the php documentation. 

166 """ 

167 

168 name = 'PHP' 

169 url = 'https://www.php.net/' 

170 aliases = ['php', 'php3', 'php4', 'php5'] 

171 filenames = ['*.php', '*.php[345]', '*.inc'] 

172 mimetypes = ['text/x-php'] 

173 version_added = '' 

174 

175 # Note that a backslash is included, PHP uses a backslash as a namespace 

176 # separator. 

177 _ident_inner = r'(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*' 

178 # But not inside strings. 

179 _ident_nons = r'(?:[_a-z]|[^\x00-\x7f])(?:\w|[^\x00-\x7f])*' 

180 

181 flags = re.IGNORECASE | re.DOTALL | re.MULTILINE 

182 tokens = { 

183 'root': [ 

184 (r'<\?(php)?', Comment.Preproc, 'php'), 

185 (r'[^<]+', Other), 

186 (r'<', Other) 

187 ], 

188 'php': [ 

189 (r'\?>', Comment.Preproc, '#pop'), 

190 (r'(<<<)([\'"]?)(' + _ident_nons + r')(\2\n.*?\n\s*)(\3)(;?)(\n)', 

191 bygroups(String, String, String.Delimiter, String, String.Delimiter, 

192 Punctuation, Text)), 

193 (r'\s+', Text), 

194 (r'#\[', Punctuation, 'attribute'), 

195 (r'#.*?\n', Comment.Single), 

196 (r'//.*?\n', Comment.Single), 

197 # put the empty comment here, it is otherwise seen as 

198 # the start of a docstring 

199 (r'/\*\*/', Comment.Multiline), 

200 (r'/\*\*.*?\*/', String.Doc), 

201 (r'/\*.*?\*/', Comment.Multiline), 

202 (r'(->|::)(\s*)(' + _ident_nons + ')', 

203 bygroups(Operator, Text, Name.Attribute)), 

204 (r'[~!%^&*+=|:.<>/@-]+', Operator), 

205 (r'\?', Operator), # don't add to the charclass above! 

206 (r'[\[\]{}();,]+', Punctuation), 

207 (r'(new)(\s+)(class)\b', bygroups(Keyword, Text, Keyword)), 

208 (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'), 

209 (r'(function)(\s*)(?=\()', bygroups(Keyword, Text)), 

210 (r'(function)(\s+)(&?)(\s*)', 

211 bygroups(Keyword, Text, Operator, Text), 'functionname'), 

212 (r'(const)(\s+)(' + _ident_inner + ')', 

213 bygroups(Keyword, Text, Name.Constant)), 

214 (r'(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|' 

215 r'eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|' 

216 r'FALSE|print|for|require|continue|foreach|require_once|' 

217 r'declare|return|default|static|do|switch|die|stdClass|' 

218 r'echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|' 

219 r'virtual|endfor|include_once|while|endforeach|global|' 

220 r'endif|list|endswitch|new|endwhile|not|' 

221 r'array|E_ALL|NULL|final|php_user_filter|interface|' 

222 r'implements|public|private|protected|abstract|clone|try|' 

223 r'catch|throw|this|use|namespace|trait|yield|' 

224 r'finally|match)\b', Keyword), 

225 (r'(true|false|null)\b', Keyword.Constant), 

226 include('magicconstants'), 

227 (r'\$\{', Name.Variable, 'variablevariable'), 

228 (r'\$+' + _ident_inner, Name.Variable), 

229 (_ident_inner, Name.Other), 

230 (r'(\d+\.\d*|\d*\.\d+)(e[+-]?[0-9]+)?', Number.Float), 

231 (r'\d+e[+-]?[0-9]+', Number.Float), 

232 (r'0[0-7]+', Number.Oct), 

233 (r'0x[a-f0-9]+', Number.Hex), 

234 (r'\d+', Number.Integer), 

235 (r'0b[01]+', Number.Bin), 

236 (r"'([^'\\]*(?:\\.[^'\\]*)*)'", String.Single), 

237 (r'`([^`\\]*(?:\\.[^`\\]*)*)`', String.Backtick), 

238 (r'"', String.Double, 'string'), 

239 ], 

240 'variablevariable': [ 

241 (r'\}', Name.Variable, '#pop'), 

242 include('php') 

243 ], 

244 'magicfuncs': [ 

245 # source: http://php.net/manual/en/language.oop5.magic.php 

246 (words(( 

247 '__construct', '__destruct', '__call', '__callStatic', '__get', '__set', 

248 '__isset', '__unset', '__sleep', '__wakeup', '__toString', '__invoke', 

249 '__set_state', '__clone', '__debugInfo',), suffix=r'\b'), 

250 Name.Function.Magic), 

251 ], 

252 'magicconstants': [ 

253 # source: http://php.net/manual/en/language.constants.predefined.php 

254 (words(( 

255 '__LINE__', '__FILE__', '__DIR__', '__FUNCTION__', '__CLASS__', 

256 '__TRAIT__', '__METHOD__', '__NAMESPACE__',), 

257 suffix=r'\b'), 

258 Name.Constant), 

259 ], 

260 'classname': [ 

261 (_ident_inner, Name.Class, '#pop') 

262 ], 

263 'functionname': [ 

264 include('magicfuncs'), 

265 (_ident_inner, Name.Function, '#pop'), 

266 default('#pop') 

267 ], 

268 'string': [ 

269 (r'"', String.Double, '#pop'), 

270 (r'[^{$"\\]+', String.Double), 

271 (r'\\([nrt"$\\]|[0-7]{1,3}|x[0-9a-f]{1,2})', String.Escape), 

272 (r'\$' + _ident_nons + r'(\[\S+?\]|->' + _ident_nons + ')?', 

273 String.Interpol), 

274 (r'(\{\$\{)(.*?)(\}\})', 

275 bygroups(String.Interpol, using(this, _startinline=True), 

276 String.Interpol)), 

277 (r'(\{)(\$.*?)(\})', 

278 bygroups(String.Interpol, using(this, _startinline=True), 

279 String.Interpol)), 

280 (r'(\$\{)(\S+)(\})', 

281 bygroups(String.Interpol, Name.Variable, String.Interpol)), 

282 (r'[${\\]', String.Double) 

283 ], 

284 'attribute': [ 

285 (r'\]', Punctuation, '#pop'), 

286 (r'\(', Punctuation, 'attributeparams'), 

287 (_ident_inner, Name.Decorator), 

288 include('php') 

289 ], 

290 'attributeparams': [ 

291 (r'\)', Punctuation, '#pop'), 

292 include('php') 

293 ], 

294 } 

295 

296 def __init__(self, **options): 

297 self.funcnamehighlighting = get_bool_opt( 

298 options, 'funcnamehighlighting', True) 

299 self.disabledmodules = get_list_opt( 

300 options, 'disabledmodules', ['unknown']) 

301 self.startinline = get_bool_opt(options, 'startinline', False) 

302 

303 # private option argument for the lexer itself 

304 if '_startinline' in options: 

305 self.startinline = options.pop('_startinline') 

306 

307 # collect activated functions in a set 

308 self._functions = set() 

309 if self.funcnamehighlighting: 

310 from pygments.lexers._php_builtins import MODULES 

311 for key, value in MODULES.items(): 

312 if key not in self.disabledmodules: 

313 self._functions.update(value) 

314 RegexLexer.__init__(self, **options) 

315 

316 def get_tokens_unprocessed(self, text): 

317 stack = ['root'] 

318 if self.startinline: 

319 stack.append('php') 

320 for index, token, value in \ 

321 RegexLexer.get_tokens_unprocessed(self, text, stack): 

322 if token is Name.Other: 

323 if value in self._functions: 

324 yield index, Name.Builtin, value 

325 continue 

326 yield index, token, value 

327 

328 def analyse_text(text): 

329 if shebang_matches(text, r'php'): 

330 return True 

331 rv = 0.0 

332 if re.search(r'<\?(?!xml)', text): 

333 rv += 0.3 

334 return rv