Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1754 lines
67 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. ;;; php-mode.el --- Major mode for editing PHP code
  2. ;; Copyright (C) 2020 Friends of Emacs-PHP development
  3. ;; Copyright (C) 1999, 2000, 2001, 2003, 2004 Turadg Aleahmad
  4. ;; 2008 Aaron S. Hawley
  5. ;; 2011, 2012, 2013, 2014, 2015, 2016, 2017 Eric James Michael Ritz
  6. ;; Author: Eric James Michael Ritz
  7. ;; Maintainer: USAMI Kenta <tadsan@zonu.me>
  8. ;; URL: https://github.com/emacs-php/php-mode
  9. ;; Keywords: languages php
  10. ;; Version: 1.24.0
  11. ;; Package-Requires: ((emacs "25.2"))
  12. ;; License: GPL-3.0-or-later
  13. (defconst php-mode-version-number "1.24.0"
  14. "PHP Mode version number.")
  15. ;; This program is free software; you can redistribute it and/or modify
  16. ;; it under the terms of the GNU General Public License as published by
  17. ;; the Free Software Foundation, either version 3 of the License, or
  18. ;; (at your option) any later version.
  19. ;; This program is distributed in the hope that it will be useful,
  20. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. ;; GNU General Public License for more details.
  23. ;; You should have received a copy of the GNU General Public License
  24. ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
  25. ;;; Commentary:
  26. ;; PHP Mode is a major mode for editing PHP script. It's an extension
  27. ;; of CC mode; thus it inherits all C mode's navigation functionality.
  28. ;; But it colors according to the PHP syntax and indents according to the
  29. ;; PSR-2 coding guidelines. It also includes a couple handy IDE-type
  30. ;; features such as documentation search and a source and class browser.
  31. ;; Please read the manual for setting items compatible with CC Mode.
  32. ;; https://www.gnu.org/software/emacs/manual/html_mono/ccmode.html
  33. ;; This mode is designed for PHP scripts consisting of a single <?php block.
  34. ;; We recommend the introduction of Web Mode for HTML and Blade templates combined with PHP.
  35. ;; http://web-mode.org/
  36. ;; Modern PHP Mode can be set on a project basis by .dir-locals.el.
  37. ;; Please read php-project.el for details of directory local variables.
  38. ;; If you are using a package manager, you do not need (require 'php-mode) in
  39. ;; your ~/.emacs.d/init.el. Read the README for installation instructions.
  40. ;; https://github.com/emacs-php/php-mode
  41. ;;; Code:
  42. (require 'php)
  43. (require 'php-face)
  44. (require 'cc-mode)
  45. (require 'cc-langs)
  46. (eval-when-compile
  47. (require 'cc-fonts))
  48. ;; Boilerplate from other `cc-mode' derived modes. See
  49. ;; http://cc-mode.sourceforge.net/derived-mode-ex.el for details on how this all
  50. ;; fits together.
  51. (eval-and-compile
  52. (c-add-language 'php-mode 'java-mode))
  53. (require 'font-lock)
  54. (require 'custom)
  55. (require 'etags)
  56. (require 'speedbar)
  57. (require 'imenu)
  58. (require 'nadvice nil t)
  59. (require 'cl-lib)
  60. (require 'mode-local)
  61. (require 'php-project)
  62. (eval-when-compile
  63. (require 'regexp-opt)
  64. (defvar add-log-current-defun-header-regexp)
  65. (defvar add-log-current-defun-function)
  66. (defvar c-vsemi-status-unknown-p)
  67. (defvar syntax-propertize-via-font-lock))
  68. ;; Work around emacs bug#18845, cc-mode expects cl to be loaded
  69. ;; while php-mode only uses cl-lib (without compatibility aliases)
  70. (eval-and-compile
  71. (when (and (= emacs-major-version 24) (>= emacs-minor-version 4))
  72. (require 'cl)))
  73. ;; Work around https://github.com/emacs-php/php-mode/issues/310.
  74. ;;
  75. ;; In emacs 24.4 and 24.5, lines after functions with a return type
  76. ;; are incorrectly analyzed as member-init-cont.
  77. ;;
  78. ;; Before emacs 24.4, c member initializers are not supported this
  79. ;; way. Starting from emacs 25.1, cc-mode only detects member
  80. ;; initializers when the major mode is c++-mode.
  81. (eval-and-compile
  82. (if (and (= emacs-major-version 24) (or (= emacs-minor-version 4)
  83. (= emacs-minor-version 5)))
  84. (defun c-back-over-member-initializers ()
  85. ;; Override of cc-engine.el, cc-mode in emacs 24.4 and 24.5 are too
  86. ;; optimistic in recognizing c member initializers. Since we don't
  87. ;; need it in php-mode, just return nil.
  88. nil)))
  89. (autoload 'php-mode-debug "php-mode-debug"
  90. "Display informations useful for debugging PHP Mode." t)
  91. ;; Local variables
  92. ;;;###autoload
  93. (defgroup php-mode nil
  94. "Major mode for editing PHP code."
  95. :tag "PHP Mode"
  96. :prefix "php-mode-"
  97. :group 'languages
  98. :group 'php
  99. :link '(url-link :tag "Official Site" "https://github.com/emacs-php/php-mode")
  100. :link '(url-link :tag "PHP Mode Wiki" "https://github.com/emacs-php/php-mode/wiki"))
  101. (define-obsolete-variable-alias 'php-default-face 'php-mode-default-face "1.20.0")
  102. (defcustom php-mode-default-face 'default
  103. "Default face in `php-mode' buffers."
  104. :group 'php-mode
  105. :tag "PHP Mode Default Face"
  106. :type 'face)
  107. (define-obsolete-variable-alias 'php-speedbar-config 'php-mode-speedbar-config "1.20.0")
  108. (defcustom php-mode-speedbar-config t
  109. "When set to true automatically configures Speedbar to observe PHP files.
  110. Ignores php-file patterns option; fixed to expression \"\\.\\(inc\\|php[s345]?\\)\""
  111. :group 'php-mode
  112. :tag "PHP Mode Speedbar Config"
  113. :type 'boolean
  114. :set (lambda (sym val)
  115. (set-default sym val)
  116. (when val
  117. (speedbar-add-supported-extension
  118. "\\.\\(inc\\|php[s345]?\\|phtml\\)"))))
  119. (defcustom php-mode-speedbar-open nil
  120. "Normally `php-mode' starts with the speedbar closed.
  121. Turning this on will open it whenever `php-mode' is loaded."
  122. :group 'php-mode
  123. :tag "PHP Mode Speedbar Open"
  124. :type 'boolean
  125. :set (lambda (sym val)
  126. (set-default sym val)
  127. (when val
  128. (speedbar 1))))
  129. (define-obsolete-variable-alias 'php-template-compatibility 'php-mode-template-compatibility "1.20.0")
  130. (defcustom php-mode-template-compatibility t
  131. "Should detect presence of html tags."
  132. :group 'php-mode
  133. :tag "PHP Mode Template Compatibility"
  134. :type 'boolean)
  135. (define-obsolete-variable-alias 'php-lineup-cascaded-calls 'php-mode-lineup-cascaded-calls "1.20.0")
  136. (defcustom php-mode-lineup-cascaded-calls nil
  137. "Indent chained method calls to the previous line."
  138. :group 'php-mode
  139. :tag "PHP Mode Lineup Cascaded Calls"
  140. :type 'boolean)
  141. (defcustom php-mode-page-delimiter
  142. (eval-when-compile
  143. (rx symbol-start
  144. (or "namespace" "function" "class" "trait" "interface")
  145. symbol-end))
  146. "Regexp describing line-beginnings that PHP declaration statements."
  147. :group 'php-mode
  148. :tag "PHP Mode Page Delimiter"
  149. :type 'regexp)
  150. (define-obsolete-variable-alias 'php-do-not-use-semantic-imenu 'php-mode-do-not-use-semantic-imenu "1.20.0")
  151. (defcustom php-mode-do-not-use-semantic-imenu t
  152. "Customize `imenu-create-index-function' for `php-mode'.
  153. If using function `semantic-mode' `imenu-create-index-function' will be
  154. set to `semantic-create-imenu-index' due to `c-mode' being its
  155. parent. Set this variable to t if you want to use
  156. `imenu-default-create-index-function' even with `semantic-mode'
  157. enabled."
  158. :group 'php-mode
  159. :tag "PHP Mode Do Not Use Semantic Imenu"
  160. :type 'boolean)
  161. (defcustom php-completion-file ""
  162. "Path to the file which contains the function names known to PHP."
  163. :type 'string)
  164. (defcustom php-manual-path ""
  165. "Path to the directory which contains the PHP manual."
  166. :type 'string)
  167. ;;;###autoload
  168. (if (version< emacs-version "24.4")
  169. (dolist (i '("php" "php5" "php7"))
  170. (add-to-list 'interpreter-mode-alist (cons i 'php-mode)))
  171. (add-to-list 'interpreter-mode-alist
  172. ;; Match php, php-3, php5, php7, php5.5, php-7.0.1, etc.
  173. (cons "php\\(?:-?[3457]\\(?:\\.[0-9]+\\)*\\)?" 'php-mode)))
  174. (defcustom php-mode-hook nil
  175. "List of functions to be executed on entry to `php-mode'."
  176. :group 'php-mode
  177. :tag "PHP Mode Hook"
  178. :type 'hook)
  179. (defcustom php-mode-pear-hook nil
  180. "Hook called when a PHP PEAR file is opened with `php-mode'."
  181. :group 'php-mode
  182. :tag "PHP Mode Pear Hook"
  183. :type 'hook)
  184. (defcustom php-mode-drupal-hook nil
  185. "Hook called when a Drupal file is opened with `php-mode'."
  186. :group 'php-mode
  187. :tag "PHP Mode Drupal Hook"
  188. :type 'hook)
  189. (defcustom php-mode-wordpress-hook nil
  190. "Hook called when a WordPress file is opened with `php-mode'."
  191. :group 'php-mode
  192. :tag "PHP Mode WordPress Hook"
  193. :type 'hook)
  194. (defcustom php-mode-symfony2-hook nil
  195. "Hook called when a Symfony2 file is opened with `php-mode'."
  196. :group 'php-mode
  197. :tag "PHP Mode Symfony2 Hook"
  198. :type 'hook)
  199. (defcustom php-mode-psr2-hook nil
  200. "Hook called when a PSR-2 file is opened with `php-mode'."
  201. :group 'php-mode
  202. :tag "PHP Mode PSR-2 Hook"
  203. :type 'hook)
  204. (defcustom php-mode-force-pear nil
  205. "Normally PEAR coding rules are enforced only when the filename contains \"PEAR.\"
  206. Turning this on will force PEAR rules on all PHP files."
  207. :group 'php-mode
  208. :tag "PHP Mode Force Pear"
  209. :type 'boolean)
  210. (defcustom php-mode-warn-if-mumamo-off t
  211. "Warn once per buffer if you try to indent a buffer without
  212. mumamo-mode turned on. Detects if there are any HTML tags in the
  213. buffer before warning, but this is is not very smart; e.g. if you
  214. have any tags inside a PHP string, it will be fooled."
  215. :group 'php-mode
  216. :tag "PHP Mode Warn If MuMaMo Off"
  217. :type '(choice (const :tag "Warn" t) (const "Don't warn" nil)))
  218. (defcustom php-mode-coding-style 'pear
  219. "Select default coding style to use with php-mode.
  220. This variable can take one of the following symbol values:
  221. `Default' - use a reasonable default style for PHP.
  222. `PSR-2' - use PSR standards (PSR-2, PSR-12).
  223. `PEAR' - use coding styles preferred for PEAR code and modules.
  224. `Drupal' - use coding styles preferred for working with Drupal projects.
  225. `WordPress' - use coding styles preferred for working with WordPress projects.
  226. `Symfony2' - use coding styles preferred for working with Symfony2 projects."
  227. :group 'php-mode
  228. :tag "PHP Mode Coding Style"
  229. :type '(choice (const :tag "Default" php)
  230. (const :tag "PEAR" pear)
  231. (const :tag "Drupal" drupal)
  232. (const :tag "WordPress" wordpress)
  233. (const :tag "Symfony2" symfony2)
  234. (const :tag "PSR-2" psr2))
  235. :initialize 'custom-initialize-default)
  236. ;; Since this function has a bad influence on the environment of many users,
  237. ;; temporarily disable it
  238. (defcustom php-mode-enable-project-coding-style nil
  239. "When set to true override php-mode-coding-style by php-project-coding-style.
  240. If you want to suppress styles from being overwritten by directory / file
  241. local variables, set NIL."
  242. :group 'php-mode
  243. :tag "PHP Mode Enable Project Coding Style"
  244. :type 'boolean)
  245. (defcustom php-mode-enable-backup-style-variables t
  246. "When set to `T', back up values set by hook and buffer local variables.
  247. This function may interfere with other hooks and other behaviors.
  248. In that case set to `NIL'."
  249. :group 'php-mode
  250. :tag "PHP Mode Enable Backup Style Variables"
  251. :type 'boolean)
  252. (defcustom php-mode-disable-c-auto-align-backslashes t
  253. "When set to non-NIL, override `c-auto-align-backslashes' to NIL."
  254. :group 'php-mode
  255. :tag "PHP Mode Disable c-auto-align-backslashes"
  256. :type 'boolean)
  257. (define-obsolete-variable-alias 'php-mode-disable-parent-mode-hooks 'php-mode-disable-c-mode-hook "1.21.0")
  258. (defcustom php-mode-disable-c-mode-hook t
  259. "When set to `T', do not run hooks of parent modes (`java-mode', `c-mode')."
  260. :group 'php-mode
  261. :tag "PHP Mode Disable C Mode Hook"
  262. :type 'boolean)
  263. (defcustom php-mode-enable-project-local-variable t
  264. "When set to `T', apply project local variable to buffer local variable."
  265. :group 'php-mode
  266. :tag "PHP Mode Enable Project Local Variable"
  267. :type 'boolean)
  268. (defconst php-mode-cc-vertion
  269. (eval-when-compile c-version))
  270. (defun php-mode-version ()
  271. "Display string describing the version of PHP Mode."
  272. (interactive)
  273. (let ((fmt
  274. (eval-when-compile
  275. (let ((id "$Id$"))
  276. (concat "PHP Mode %s"
  277. (if (string= id (concat [?$ ?I ?d ?$]))
  278. ""
  279. (concat " " id)))))))
  280. (funcall
  281. (if (called-interactively-p 'interactive) #'message #'format)
  282. fmt php-mode-version-number)))
  283. ;;;###autoload
  284. (define-obsolete-variable-alias 'php-available-project-root-files 'php-project-available-root-files "1.19.0")
  285. (defvar php-mode-map
  286. (let ((map (make-sparse-keymap "PHP Mode")))
  287. ;; Remove menu item for c-mode
  288. (define-key map [menu-bar C] nil)
  289. ;; By default PHP Mode binds C-M-h to c-mark-function, which it
  290. ;; inherits from cc-mode. But there are situations where
  291. ;; c-mark-function fails to properly mark a function. For
  292. ;; example, if we use c-mark-function within a method definition
  293. ;; then the region will expand beyond the method and into the
  294. ;; class definition itself.
  295. ;;
  296. ;; Changing the default to mark-defun provides behavior that users
  297. ;; are more likely to expect.
  298. (define-key map (kbd "C-M-h") 'mark-defun)
  299. ;; Many packages based on cc-mode provide the 'C-c C-w' binding
  300. ;; to toggle Subword Mode. See the page
  301. ;;
  302. ;; https://www.gnu.org/software/emacs/manual/html_node/ccmode/Subword-Movement.html
  303. ;;
  304. ;; for more information about Subword mode.
  305. (define-key map (kbd "C-c C-w") 'subword-mode)
  306. ;; We inherit c-beginning-of-defun and c-end-of-defun from CC Mode
  307. ;; but we have two replacement functions specifically for PHP. We
  308. ;; remap the commands themselves and not their default
  309. ;; key-bindings so that our PHP-specific versions will work even
  310. ;; if the user has reconfigured their keys, e.g. if they rebind
  311. ;; c-end-of-defun to something other than C-M-e.
  312. (define-key map [remap c-beginning-of-defun] 'php-beginning-of-defun)
  313. (define-key map [remap c-end-of-defun] 'php-end-of-defun)
  314. (define-key map [remap c-set-style] 'php-set-style)
  315. (define-key map [(control c) (control f)] 'php-search-documentation)
  316. (define-key map [(meta tab)] 'php-complete-function)
  317. (define-key map [(control c) (control m)] 'php-browse-manual)
  318. (define-key map [(control .)] 'php-show-arglist)
  319. (define-key map [(control c) (control r)] 'php-send-region)
  320. ;; Use the Emacs standard indentation binding. This may upset c-mode
  321. ;; which does not follow this at the moment, but I see no better
  322. ;; choice.
  323. (define-key map [tab] 'indent-for-tab-command)
  324. map)
  325. "Keymap for `php-mode'.")
  326. (c-lang-defconst c-mode-menu
  327. php (append '(["Complete function name" php-complete-function t]
  328. ["Browse manual" php-browse-manual t]
  329. ["Search documentation" php-search-documentation t]
  330. ["----" t])
  331. (c-lang-const c-mode-menu)))
  332. (c-lang-defconst c-at-vsemi-p-fn
  333. php 'php-c-at-vsemi-p)
  334. (c-lang-defconst c-vsemi-status-unknown-p-fn
  335. php 'php-c-vsemi-status-unknown-p)
  336. (c-lang-defconst c-get-state-before-change-functions
  337. php nil)
  338. (c-lang-defconst c-before-font-lock-functions
  339. php (c-get-lang-constant 'c-before-font-lock-functions nil t))
  340. ;; Make php-mode recognize opening tags as preprocessor macro's.
  341. ;;
  342. ;; This is a workaround, the tags must be recognized as something
  343. ;; in order for the syntactic guesses of code below the tag
  344. ;; to be correct and as a result not break indentation.
  345. ;;
  346. ;; Note that submatches or \\| here are not expected by cc-mode.
  347. (c-lang-defconst c-opt-cpp-prefix
  348. php "\\s-*<\\?")
  349. (c-lang-defconst c-anchored-cpp-prefix
  350. php "\\s-*\\(<\\?(=\\|\\sw+)\\)")
  351. (c-lang-defconst c-identifier-ops
  352. php '(
  353. (left-assoc "\\" "::" "->")
  354. (prefix "\\" "::")))
  355. (c-lang-defconst c-operators
  356. php `((prefix "new" "clone")
  357. ,@(c-lang-const c-identifier-ops)
  358. (postfix "->")
  359. (postfix "++" "--" "[" "]" "(" ")")
  360. (right-assoc "**")
  361. (prefix "++" "--" "+" "-" "~" "(" ")" "@")
  362. (prefix "instanceof")
  363. (prefix "!")
  364. (left-assoc "*" "/" "%")
  365. (left-assoc "+" "-" ".")
  366. (left-assoc "<<" ">>")
  367. (left-assoc "<" ">" "<=" ">=")
  368. (left-assoc "==" "!=" "===" "!==" "<>" "<=>")
  369. (left-assoc "&")
  370. (left-assoc "^")
  371. (left-assoc "|")
  372. (left-assoc "&&")
  373. (left-assoc "||")
  374. (right-assoc "??")
  375. (left-assoc "?:")
  376. (right-assoc-sequence "?" ":")
  377. (right-assoc ,@(c-lang-const c-assignment-operators))
  378. (left-assoc "and")
  379. (left-assoc "xor")
  380. (left-assoc "or")
  381. (left-assoc ",")))
  382. ;; Allow '\' when scanning from open brace back to defining
  383. ;; construct like class
  384. (c-lang-defconst c-block-prefix-disallowed-chars
  385. php (cl-set-difference (c-lang-const c-block-prefix-disallowed-chars)
  386. '(?\\)))
  387. ;; Allow $ so variables are recognized in cc-mode and remove @. This
  388. ;; makes cc-mode highlight variables and their type hints in arglists.
  389. (c-lang-defconst c-symbol-start
  390. php (concat "[" c-alpha "_$]"))
  391. ;; All string literals can possibly span multiple lines
  392. (c-lang-defconst c-multiline-string-start-char
  393. php t)
  394. (c-lang-defconst c-assignment-operators
  395. php '("=" "*=" "/=" "%=" "+=" "-=" ">>=" "<<=" "&=" "^=" "|=" ".=" "??="))
  396. (c-lang-defconst beginning-of-defun-function
  397. php 'php-beginning-of-defun)
  398. (c-lang-defconst end-of-defun-function
  399. php 'php-end-of-defun)
  400. (c-lang-defconst c-primitive-type-kwds
  401. php '("int" "integer" "bool" "boolean" "float" "double" "real"
  402. "string" "object" "void" "mixed"))
  403. (c-lang-defconst c-class-decl-kwds
  404. "Keywords introducing declarations where the following block (if any)
  405. contains another declaration level that should be considered a class."
  406. php '("class" "trait" "interface" "enum"))
  407. (c-lang-defconst c-brace-list-decl-kwds
  408. "Keywords introducing declarations where the following block (if
  409. any) is a brace list.
  410. PHP does not have an \"enum\"-like keyword."
  411. php nil)
  412. (c-lang-defconst c-typeless-decl-kwds
  413. php (append (c-lang-const c-class-decl-kwds) '("function")))
  414. (c-lang-defconst c-modifier-kwds
  415. php '("abstract" "const" "final" "static" "case"))
  416. (c-lang-defconst c-protection-kwds
  417. "Access protection label keywords in classes."
  418. php '("private" "protected" "public"))
  419. (c-lang-defconst c-postfix-decl-spec-kwds
  420. php '("implements" "extends"))
  421. (c-lang-defconst c-type-list-kwds
  422. php '("@new" ;; @new is *NOT* language construct, it's workaround for coloring.
  423. "new" "use" "implements" "extends" "namespace" "instanceof" "insteadof"))
  424. (c-lang-defconst c-ref-list-kwds
  425. php nil)
  426. (c-lang-defconst c-block-stmt-2-kwds
  427. php '("catch" "declare" "elseif" "for" "foreach" "if" "switch" "while"))
  428. (c-lang-defconst c-simple-stmt-kwds
  429. php '("break" "continue" "die" "echo" "exit" "goto" "return" "throw"
  430. "include" "include_once" "print" "require" "require_once"))
  431. (c-lang-defconst c-constant-kwds
  432. php '("true" "false" "null"))
  433. (c-lang-defconst c-lambda-kwds
  434. php '("function" "use"))
  435. (c-lang-defconst c-inexpr-block-kwds
  436. php '("match"))
  437. (c-lang-defconst c-other-block-decl-kwds
  438. php '("namespace"))
  439. (c-lang-defconst c-other-kwds
  440. "Keywords not accounted for by any other `*-kwds' language constant."
  441. php
  442. '(
  443. "__halt_compiler"
  444. "and"
  445. "array"
  446. "as"
  447. "break"
  448. "catch"
  449. "clone"
  450. "default"
  451. "empty"
  452. "enddeclare"
  453. "endfor"
  454. "endforeach"
  455. "endif"
  456. "endswitch"
  457. "endwhile"
  458. "eval"
  459. "fn" ;; NOT c-lambda-kwds
  460. "global"
  461. "isset"
  462. "list"
  463. "or"
  464. "parent"
  465. "static"
  466. "unset"
  467. "var"
  468. "xor"
  469. "yield"
  470. "yield from"
  471. ;; Below keywords are technically not reserved keywords, but
  472. ;; threated no differently by php-mode from actual reserved
  473. ;; keywords
  474. ;;
  475. ;;; declare directives:
  476. "encoding"
  477. "ticks"
  478. "strict_types"
  479. ;;; self for static references:
  480. "self"))
  481. ;; PHP does not have <> templates/generics
  482. (c-lang-defconst c-recognize-<>-arglists
  483. php nil)
  484. (c-lang-defconst c-<>-type-kwds
  485. php nil)
  486. (c-lang-defconst c-inside-<>-type-kwds
  487. php nil)
  488. (c-lang-defconst c-enums-contain-decls
  489. php nil)
  490. (c-lang-defconst c-nonlabel-token-key
  491. "Regexp matching things that can't occur in generic colon labels.
  492. This overrides cc-mode `c-nonlabel-token-key' to support switching on
  493. double quoted strings and true/false/null.
  494. Note: this regexp is also applied to goto-labels, a future improvement
  495. might be to handle switch and goto labels differently."
  496. php (concat
  497. ;; All keywords except `c-label-kwds' and `c-constant-kwds'.
  498. (c-make-keywords-re t
  499. (cl-set-difference (c-lang-const c-keywords)
  500. (append (c-lang-const c-label-kwds)
  501. (c-lang-const c-constant-kwds))
  502. :test 'string-equal))))
  503. (c-lang-defconst c-basic-matchers-before
  504. php (cl-remove-if (lambda (elm) (and (listp elm) (equal (car elm) "\\s|")))
  505. (c-lang-const c-basic-matchers-before php)))
  506. (c-lang-defconst c-basic-matchers-after
  507. php (cl-remove-if (lambda (elm) (and (listp elm) (memq 'c-annotation-face elm)))
  508. (c-lang-const c-basic-matchers-after php)))
  509. (c-lang-defconst c-opt-<>-sexp-key
  510. php nil)
  511. (defconst php-mode--re-return-typed-closure
  512. (eval-when-compile
  513. (rx symbol-start "function" symbol-end
  514. (* (syntax whitespace))
  515. "(" (* (not (any "("))) ")"
  516. (* (syntax whitespace))
  517. (? symbol-start "use" symbol-end
  518. (* (syntax whitespace))
  519. "(" (* (not (any "("))) ")"
  520. (* (syntax whitespace)))
  521. ":" (+ (not (any "{}")))
  522. (group "{"))))
  523. (defun php-c-lineup-arglist (langelem)
  524. "Line up the current argument line under the first argument using `c-lineup-arglist' LANGELEM."
  525. (let (in-return-typed-closure)
  526. (when (and (consp langelem)
  527. (eq 'arglist-cont-nonempty (car langelem)))
  528. (save-excursion
  529. (save-match-data
  530. (when (re-search-backward php-mode--re-return-typed-closure (cdr langelem) t)
  531. (goto-char (match-beginning 1))
  532. (when (not (php-in-string-or-comment-p))
  533. (setq in-return-typed-closure t))))))
  534. (unless in-return-typed-closure
  535. (c-lineup-arglist langelem))))
  536. (defun php-lineup-cascaded-calls (langelem)
  537. "Line up chained methods using `c-lineup-cascaded-calls',
  538. but only if the setting is enabled"
  539. (if php-mode-lineup-cascaded-calls
  540. (c-lineup-cascaded-calls langelem)
  541. (save-excursion
  542. (beginning-of-line)
  543. (if (looking-at-p "\\s-*->") '+ nil))))
  544. (defun php-c-looking-at-or-maybe-in-bracelist (&optional containing-sexp lim)
  545. "Replace `c-looking-at-or-maybe-in-bracelist'.
  546. CONTAINING-SEXP is the position of the brace/paren/bracket enclosing
  547. POINT, or nil if there is no such position, or we do not know it. LIM is
  548. a backward search limit."
  549. (cond
  550. ((looking-at-p "{")
  551. (save-excursion
  552. (c-backward-token-2 2 t lim)
  553. ;; PHP 8.0 match expression
  554. ;; echo match ($var) |{
  555. ;; ↑ matches ↑ initial position
  556. (when (looking-at-p (eval-when-compile (rx symbol-start "match" symbol-end)))
  557. (cons (point) t))))
  558. (t nil)))
  559. (c-add-style
  560. "php"
  561. `((c-basic-offset . 4)
  562. (c-offsets-alist . ((arglist-close . php-lineup-arglist-close)
  563. (arglist-cont . (first php-lineup-cascaded-calls 0))
  564. (arglist-cont-nonempty . (first php-lineup-cascaded-calls php-c-lineup-arglist))
  565. (arglist-intro . php-lineup-arglist-intro)
  566. (case-label . +)
  567. (class-open . 0)
  568. (comment-intro . 0)
  569. (inexpr-class . 0)
  570. (inlambda . 0)
  571. (inline-open . 0)
  572. (namespace-open . 0)
  573. (lambda-intro-cont . +)
  574. (label . +)
  575. (statement-cont . (first php-lineup-cascaded-calls php-lineup-string-cont +))
  576. (substatement-open . 0)
  577. (topmost-intro-cont . (first php-lineup-cascaded-calls +))))
  578. (indent-tabs-mode . nil)
  579. (tab-width . ,(default-value 'tab-width))
  580. (fill-column . ,(default-value 'fill-column))
  581. (show-trailing-whitespace . ,(default-value 'show-trailing-whitespace))
  582. (php-mode-lineup-cascaded-calls . t)
  583. (php-style-delete-trailing-whitespace . nil)))
  584. (defun php-enable-default-coding-style ()
  585. "Set PHP Mode to use reasonable default formatting."
  586. (interactive)
  587. (php-set-style "php"))
  588. (c-add-style
  589. "pear"
  590. '("php"
  591. (c-basic-offset . 4)
  592. (c-offsets-alist . ((case-label . 0)))
  593. (tab-width . 4)))
  594. (defun php-enable-pear-coding-style ()
  595. "Set up php-mode to use the coding styles preferred for PEAR code and modules."
  596. (interactive)
  597. (php-set-style "pear"))
  598. (c-add-style
  599. "drupal"
  600. '("php"
  601. (c-basic-offset . 2)
  602. (tab-width . 2)
  603. (fill-column . 78)
  604. (show-trailing-whitespace . t)
  605. (php-mode-lineup-cascaded-calls . nil)
  606. (php-style-delete-trailing-whitespace . t)))
  607. (defun php-enable-drupal-coding-style ()
  608. "Make php-mode use coding styles that are preferable for working with Drupal."
  609. (interactive)
  610. (php-set-style "drupal"))
  611. (c-add-style
  612. "wordpress"
  613. '("php"
  614. (c-basic-offset . 4)
  615. (c-indent-comments-syntactically-p t)
  616. (indent-tabs-mode . t)
  617. (tab-width . 4)
  618. (fill-column . 78)))
  619. (defun php-enable-wordpress-coding-style ()
  620. "Make php-mode use coding styles that are preferable for working with Wordpress."
  621. (interactive)
  622. (php-set-style "wordpress"))
  623. (c-add-style
  624. "symfony2"
  625. '("php"
  626. (c-offsets-alist . ((statement-cont . php-lineup-hanging-semicolon)))
  627. (c-indent-comments-syntactically-p . t)
  628. (php-mode-lineup-cascaded-calls . nil)
  629. (fill-column . 78)))
  630. (defun php-enable-symfony2-coding-style ()
  631. "Make php-mode use coding styles that are preferable for working with Symfony2."
  632. (interactive)
  633. (php-set-style "symfony2"))
  634. (c-add-style
  635. "psr2" ; PSR-2 / PSR-12
  636. '("php"
  637. (c-offsets-alist . ((statement-cont . +)))
  638. (c-indent-comments-syntactically-p . t)
  639. (fill-column . 78)
  640. (show-trailing-whitespace . t)
  641. (php-mode-lineup-cascaded-calls . nil)
  642. (php-style-delete-trailing-whitespace . t)))
  643. (defun php-enable-psr2-coding-style ()
  644. "Make php-mode comply to the PSR-2 coding style."
  645. (interactive)
  646. (php-set-style "psr2"))
  647. (defun php-beginning-of-defun (&optional arg)
  648. "Move to the beginning of the ARGth PHP function from point.
  649. Implements PHP version of `beginning-of-defun-function'."
  650. (interactive "p")
  651. (let (found-p (arg (or arg 1)))
  652. (while (> arg 0)
  653. (setq found-p (re-search-backward php-beginning-of-defun-regexp
  654. nil 'noerror))
  655. (setq arg (1- arg)))
  656. (while (< arg 0)
  657. (end-of-line 1)
  658. (let ((opoint (point)))
  659. (beginning-of-defun 1)
  660. (forward-list 2)
  661. (forward-line 1)
  662. (if (eq opoint (point))
  663. (setq found-p (re-search-forward php-beginning-of-defun-regexp
  664. nil 'noerror)))
  665. (setq arg (1+ arg))))
  666. (not (null found-p))))
  667. (defun php-end-of-defun (&optional arg)
  668. "Move the end of the ARGth PHP function from point.
  669. Implements PHP version of `end-of-defun-function'
  670. See `php-beginning-of-defun'."
  671. (interactive "p")
  672. (php-beginning-of-defun (- (or arg 1))))
  673. (defvar php-warned-bad-indent nil)
  674. ;; Do it but tell it is not good if html tags in buffer.
  675. (defun php-check-html-for-indentation ()
  676. (let ((html-tag-re "^\\s-*</?\\sw+.*?>")
  677. (here (point)))
  678. (goto-char (line-beginning-position))
  679. (if (or (when (boundp 'mumamo-multi-major-mode) mumamo-multi-major-mode)
  680. ;; Fix-me: no idea how to check for mmm or multi-mode
  681. (save-match-data
  682. (not (or (re-search-forward html-tag-re (line-end-position) t)
  683. (re-search-backward html-tag-re (line-beginning-position) t)))))
  684. (progn
  685. (goto-char here)
  686. t)
  687. (goto-char here)
  688. (setq php-warned-bad-indent t)
  689. (let* ((known-multi-libs '(("mumamo" mumamo (lambda () (nxhtml-mumamo)))
  690. ("mmm-mode" mmm-mode (lambda () (mmm-mode 1)))
  691. ("multi-mode" multi-mode (lambda () (multi-mode 1)))
  692. ("web-mode" web-mode (lambda () (web-mode)))))
  693. (known-names (mapcar (lambda (lib) (car lib)) known-multi-libs))
  694. (available-multi-libs (delq nil
  695. (mapcar
  696. (lambda (lib)
  697. (when (locate-library (car lib)) lib))
  698. known-multi-libs)))
  699. (available-names (mapcar (lambda (lib) (car lib)) available-multi-libs))
  700. (base-msg
  701. (concat
  702. "Indentation fails badly with mixed HTML/PHP in the HTML part in
  703. plain `php-mode'. To get indentation to work you must use an
  704. Emacs library that supports 'multiple major modes' in a buffer.
  705. Parts of the buffer will then be in `php-mode' and parts in for
  706. example `html-mode'. Known such libraries are:\n\t"
  707. (mapconcat 'identity known-names ", ")
  708. "\n"
  709. (if available-multi-libs
  710. (concat
  711. "You have these available in your `load-path':\n\t"
  712. (mapconcat 'identity available-names ", ")
  713. "\n\n"
  714. "Do you want to turn any of those on? ")
  715. "You do not have any of those in your `load-path'.")))
  716. (is-using-multi
  717. (catch 'is-using
  718. (dolist (lib available-multi-libs)
  719. (when (and (boundp (cadr lib))
  720. (symbol-value (cadr lib)))
  721. (throw 'is-using t))))))
  722. (unless is-using-multi
  723. (if available-multi-libs
  724. (if (not (y-or-n-p base-msg))
  725. (message "Did not do indentation, but you can try again now if you want")
  726. (let* ((name
  727. (if (= 1 (length available-multi-libs))
  728. (car available-names)
  729. ;; Minibuffer window is more than one line, fix that first:
  730. (message "")
  731. (completing-read "Choose multiple major mode support library: "
  732. available-names nil t
  733. (car available-names)
  734. '(available-names . 1)
  735. )))
  736. (mode (when name
  737. (cl-caddr (assoc name available-multi-libs)))))
  738. (when mode
  739. ;; Minibuffer window is more than one line, fix that first:
  740. (message "")
  741. (load name)
  742. (funcall mode))))
  743. (lwarn 'php-indent :warning base-msg)))
  744. nil))))
  745. (defun php-cautious-indent-region (start end &optional quiet)
  746. "Carefully indent region `START' `END' in contexts other than HTML templates.
  747. If the optional argument `QUIET' is non-nil then no syntactic errors are
  748. reported, even if `c-report-syntactic-errors' is non-nil."
  749. (if (or (not php-mode-warn-if-mumamo-off)
  750. (not (php-in-poly-php-html-mode))
  751. php-warned-bad-indent
  752. (php-check-html-for-indentation))
  753. (funcall 'c-indent-region start end quiet)))
  754. (defun php-cautious-indent-line ()
  755. "Carefully indent lines in contexts other than HTML templates."
  756. (if (or (not php-mode-warn-if-mumamo-off)
  757. (not (php-in-poly-php-html-mode))
  758. php-warned-bad-indent
  759. (php-check-html-for-indentation))
  760. (let ((here (point))
  761. (c-auto-align-backslashes
  762. (unless php-mode-disable-c-auto-align-backslashes
  763. c-auto-align-backslashes))
  764. doit)
  765. (move-beginning-of-line nil)
  766. ;; Don't indent heredoc end mark
  767. (save-match-data
  768. (unless (and (looking-at "[a-zA-Z0-9_]+;\n")
  769. (php-in-string-p))
  770. (setq doit t)))
  771. (goto-char here)
  772. (when doit
  773. (funcall 'c-indent-line)))))
  774. (defun php-c-at-vsemi-p (&optional pos)
  775. "Return T on HTML lines (including php tag) or PHP8 Attribute, otherwise NIL.
  776. POS is a position on the line in question.
  777. This is was done due to the problem reported here:
  778. URL `https://answers.launchpad.net/nxhtml/+question/43320'"
  779. ;; If this function could call c-beginning-of-statement-1, change php-c-vsemi-status-unknown-p.
  780. (save-excursion
  781. (if pos
  782. (goto-char pos)
  783. (setq pos (point)))
  784. (unless (php-in-string-or-comment-p)
  785. (or
  786. ;; Detect PHP8 attribute: <<Attribute()>>
  787. (when (and (< 2 pos) (< 2 (- pos (c-point 'bol))))
  788. (backward-char 2)
  789. (looking-at-p ">>\\s-*\\(?:<<\\|$\\)"))
  790. ;; Detect HTML/XML tag and PHP tag (<?php, <?=, ?>)
  791. (when php-mode-template-compatibility
  792. (beginning-of-line)
  793. (looking-at-p
  794. (eval-when-compile
  795. (rx (or (: bol (0+ space) "<" (in "a-z\\?"))
  796. (: (0+ not-newline) (in "a-z\\?") ">" (0+ space) eol))))))))))
  797. (defun php-c-vsemi-status-unknown-p ()
  798. "Always return NIL. See `c-vsemi-status-unknown-p'."
  799. ;; Current implementation of php-c-at-vsemi-p never calls c-beginning-of-statement-1
  800. nil)
  801. (defun php-lineup-string-cont (langelem)
  802. "Line up string toward equal sign or dot.
  803. e.g.
  804. $str = 'some'
  805. . 'string';
  806. this ^ lineup"
  807. (save-excursion
  808. (goto-char (cdr langelem))
  809. (let (ret finish)
  810. (while (and (not finish) (re-search-forward "[=.]" (line-end-position) t))
  811. (unless (php-in-string-or-comment-p)
  812. (setq finish t
  813. ret (vector (1- (current-column))))))
  814. ret)))
  815. (defun php-lineup-arglist-intro (langelem)
  816. (save-excursion
  817. (goto-char (cdr langelem))
  818. (vector (+ (current-column) c-basic-offset))))
  819. (defun php-lineup-arglist-close (langelem)
  820. (save-excursion
  821. (goto-char (cdr langelem))
  822. (vector (current-column))))
  823. (defun php-lineup-arglist (_langelem)
  824. (save-excursion
  825. (beginning-of-line)
  826. (if (looking-at-p "\\s-*->") '+ 0)))
  827. (defun php-lineup-hanging-semicolon (_langelem)
  828. (save-excursion
  829. (beginning-of-line)
  830. (if (looking-at-p "\\s-*;\\s-*$") 0 '+)))
  831. (eval-and-compile
  832. (defconst php-heredoc-start-re
  833. "<<<[ \t]*\\(?:\\_<.+?\\_>\\|'\\_<.+?\\_>'\\|\"\\_<.+?\\_>\"\\)$"
  834. "Regular expression for the start of a PHP heredoc."))
  835. (defun php-heredoc-end-re (heredoc-start)
  836. "Build a regular expression for the end of a heredoc started by the string HEREDOC-START."
  837. ;; Extract just the identifier without <<< and quotes.
  838. (string-match "\\_<.+?\\_>" heredoc-start)
  839. (concat "^\\s-*\\(" (match-string 0 heredoc-start) "\\)\\W"))
  840. (defun php-syntax-propertize-function (start end)
  841. "Apply propertize rules from START to END."
  842. (goto-char start)
  843. (while (and (< (point) end)
  844. (re-search-forward php-heredoc-start-re end t))
  845. (php-heredoc-syntax))
  846. (goto-char start)
  847. (while (re-search-forward "['\"]" end t)
  848. (when (php-in-comment-p)
  849. (c-put-char-property (match-beginning 0)
  850. 'syntax-table (string-to-syntax "_")))))
  851. (defun php-heredoc-syntax ()
  852. "Mark the boundaries of searched heredoc."
  853. (goto-char (match-beginning 0))
  854. (c-put-char-property (point) 'syntax-table (string-to-syntax "|"))
  855. (if (re-search-forward (php-heredoc-end-re (match-string 0)) nil t)
  856. (goto-char (match-end 1))
  857. ;; Did not find the delimiter so go to the end of the buffer.
  858. (goto-char (point-max)))
  859. (c-put-char-property (1- (point)) 'syntax-table (string-to-syntax "|")))
  860. (defvar-local php-mode--propertize-extend-region-current nil
  861. "Prevent undesirable recursion in PHP-SYNTAX-PROPERTIZE-EXTEND-REGION")
  862. (defun php-syntax-propertize-extend-region (start end)
  863. "Extend the propertize region if START or END falls inside a PHP heredoc."
  864. (let ((pair (cons start end)))
  865. (when (not (member pair php-mode--propertize-extend-region-current))
  866. ;; re-search functions may trigger
  867. ;; syntax-propertize-extend-region-functions to be called again, which in
  868. ;; turn call this to be called again.
  869. (push pair php-mode--propertize-extend-region-current)
  870. (unwind-protect
  871. (let ((new-start)
  872. (new-end))
  873. (goto-char start)
  874. (when (re-search-backward php-heredoc-start-re nil t)
  875. (let ((maybe (point)))
  876. (when (and (re-search-forward (php-heredoc-end-re (match-string 0)) nil t)
  877. (> (point) start))
  878. (setq new-start maybe))))
  879. (goto-char end)
  880. (when (re-search-backward php-heredoc-start-re nil t)
  881. (if (re-search-forward (php-heredoc-end-re (match-string 0)) nil t)
  882. (when (> (point) end)
  883. (setq new-end (point)))
  884. (setq new-end (point-max))))
  885. (when (or new-start new-end)
  886. (cons (or new-start start) (or new-end end))))
  887. ;; Cleanup
  888. (setq php-mode--propertize-extend-region-current
  889. (delete pair php-mode--propertize-extend-region-current))))))
  890. (easy-menu-define php-mode-menu php-mode-map "PHP Mode Commands"
  891. (cons "PHP" (c-lang-const c-mode-menu php)))
  892. (defun php-mode-get-style-alist ()
  893. "Return an alist consisting of `php' style and styles that inherit it."
  894. (cl-loop for l in c-style-alist
  895. if (or (string= (car l) "php")
  896. (equal (cadr l) "php"))
  897. collect l))
  898. (defvar php-mode-set-style-history nil)
  899. (defvar-local php-mode--delayed-set-style nil)
  900. (defvar-local php-style-delete-trailing-whitespace nil)
  901. (defun php-set-style (stylename &optional dont-override)
  902. "Set the current `php-mode' buffer to use the style STYLENAME.
  903. STYLENAME is one of the names selectable in `php-mode-coding-style'.
  904. Borrow the `interactive-form' from `c-set-style' and use the original
  905. `c-set-style' function to set all declared stylevars.
  906. For compatibility with `c-set-style' pass DONT-OVERRIDE to it.
  907. After setting the stylevars run hooks according to STYLENAME
  908. \"pear\" `php-mode-pear-hook'
  909. \"drupal\" `php-mode-drupal-hook'
  910. \"wordpress\" `php-mode-wordpress-hook'
  911. \"symfony2\" `php-mode-symfony2-hook'
  912. \"psr2\" `php-mode-psr2-hook'"
  913. (interactive
  914. (list (let ((completion-ignore-case t)
  915. (prompt (format "Which %s indentation style? "
  916. mode-name)))
  917. (completing-read prompt
  918. (php-mode-get-style-alist)
  919. nil t nil
  920. 'php-mode-set-style-history
  921. c-indentation-style))))
  922. (php-mode--disable-delay-set-style)
  923. ;; Back up manually set variables
  924. (let* (value
  925. (backup-vars
  926. (and php-mode-enable-backup-style-variables
  927. (cl-loop for name in c-style-variables
  928. do (setq value (symbol-value name))
  929. if (and value (not (eq 'set-from-style value)))
  930. collect (cons name value)))))
  931. (c-set-style stylename dont-override)
  932. ;; Restore variables
  933. (cl-loop for (name . value) in backup-vars
  934. do (set (make-local-variable name) value)))
  935. (if (eq (symbol-value 'php-style-delete-trailing-whitespace) t)
  936. (add-hook 'before-save-hook 'delete-trailing-whitespace nil t)
  937. (remove-hook 'before-save-hook 'delete-trailing-whitespace t))
  938. (cond ((equal stylename "pear") (run-hooks 'php-mode-pear-hook))
  939. ((equal stylename "drupal") (run-hooks 'php-mode-drupal-hook))
  940. ((equal stylename "wordpress") (run-hooks 'php-mode-wordpress-hook))
  941. ((equal stylename "symfony2") (run-hooks 'php-mode-symfony2-hook))
  942. ((equal stylename "psr2") (run-hooks 'php-mode-psr2-hook))))
  943. (defun php-mode--disable-delay-set-style (&rest args)
  944. "Disable php-mode-set-style-delay on after hook. `ARGS' be ignore."
  945. (setq php-mode--delayed-set-style nil)
  946. (when (fboundp 'advice-remove)
  947. (advice-remove #'php-mode--disable-delay-set-style #'c-set-style)))
  948. (defun php-mode-set-style-delay ()
  949. "Set the current `php-mode' buffer to use the style by custom or local variables."
  950. (when php-mode--delayed-set-style
  951. (let ((coding-style (or (and (boundp 'php-project-coding-style) php-project-coding-style)
  952. php-mode-coding-style)))
  953. (prog1 (when coding-style
  954. (php-set-style (symbol-name coding-style)))
  955. (remove-hook 'hack-local-variables-hook #'php-mode-set-style-delay)))))
  956. (defun php-mode-set-local-variable-delay ()
  957. "Set local variable from php-project."
  958. (php-project-apply-local-variables)
  959. (remove-hook 'hack-local-variables-hook #'php-mode-set-local-variable-delay))
  960. (defvar php-mode-syntax-table
  961. (let ((table (make-syntax-table)))
  962. (c-populate-syntax-table table)
  963. (modify-syntax-entry ?_ "_" table)
  964. (modify-syntax-entry ?` "\"" table)
  965. (modify-syntax-entry ?\" "\"" table)
  966. (modify-syntax-entry ?# "< b" table)
  967. (modify-syntax-entry ?\n "> b" table)
  968. (modify-syntax-entry ?$ "_" table)
  969. table))
  970. ;;;###autoload
  971. (define-derived-mode php-mode c-mode "PHP"
  972. "Major mode for editing PHP code.
  973. \\{php-mode-map}"
  974. :syntax-table php-mode-syntax-table
  975. ;; :after-hook (c-update-modeline)
  976. ;; (setq abbrev-mode t)
  977. (unless (string= php-mode-cc-vertion c-version)
  978. (user-error "CC Mode has been updated. %s"
  979. (if (package-installed-p 'php-mode)
  980. "Please run `M-x package-reinstall php-mode' command."
  981. "Please byte recompile PHP Mode files.")))
  982. (when php-mode-disable-c-mode-hook
  983. (setq-local c-mode-hook nil)
  984. (setq-local java-mode-hook nil))
  985. (c-initialize-cc-mode t)
  986. (c-init-language-vars php-mode)
  987. (c-common-init 'php-mode)
  988. (setq-local comment-start "// ")
  989. (setq-local comment-start-skip
  990. (eval-when-compile
  991. (rx (group (or (: "#")
  992. (: "/" (+ "/"))
  993. (: "/*")))
  994. (* (syntax whitespace)))))
  995. (setq-local comment-end "")
  996. (setq-local page-delimiter php-mode-page-delimiter)
  997. (setq-local font-lock-string-face 'php-string)
  998. (setq-local font-lock-keyword-face 'php-keyword)
  999. (setq-local font-lock-builtin-face 'php-builtin)
  1000. (setq-local c-preprocessor-face-name 'php-php-tag)
  1001. (setq-local font-lock-function-name-face 'php-function-name)
  1002. (setq-local font-lock-variable-name-face 'php-variable-name)
  1003. (setq-local font-lock-constant-face 'php-constant)
  1004. (setq-local syntax-propertize-function #'php-syntax-propertize-function)
  1005. (add-hook 'syntax-propertize-extend-region-functions
  1006. #'php-syntax-propertize-extend-region t t)
  1007. (setq imenu-generic-expression php-imenu-generic-expression)
  1008. ;; PHP vars are case-sensitive
  1009. (setq case-fold-search t)
  1010. (when php-mode-enable-project-local-variable
  1011. (add-hook 'hack-local-variables-hook #'php-mode-set-local-variable-delay t t))
  1012. ;; When php-mode-enable-project-coding-style is set, it is delayed by hook.
  1013. ;; Since it depends on the timing at which the file local variable is set.
  1014. ;; File local variables are set after initialization of major mode except `run-hook' is complete.
  1015. (if php-mode-enable-project-coding-style
  1016. (progn
  1017. (add-hook 'hack-local-variables-hook #'php-mode-set-style-delay t t)
  1018. (setq php-mode--delayed-set-style t)
  1019. (when (fboundp 'advice-add)
  1020. (advice-add #'c-set-style :after #'php-mode--disable-delay-set-style '(local))))
  1021. (let ((php-mode-enable-backup-style-variables nil))
  1022. (php-set-style (symbol-name php-mode-coding-style))))
  1023. (when (or php-mode-force-pear
  1024. (and (stringp buffer-file-name)
  1025. (string-match "PEAR\\|pear" buffer-file-name)
  1026. (string-match "\\.php\\'" buffer-file-name)))
  1027. (php-set-style "pear"))
  1028. (setq indent-line-function 'php-cautious-indent-line)
  1029. (setq indent-region-function 'php-cautious-indent-region)
  1030. (setq c-at-vsemi-p-fn #'php-c-at-vsemi-p)
  1031. (setq c-vsemi-status-unknown-p-fn #'php-c-vsemi-status-unknown-p)
  1032. ;; We map the php-{beginning,end}-of-defun functions so that they
  1033. ;; replace the similar commands that we inherit from CC Mode.
  1034. ;; Because of our remapping we may not actually need to keep the
  1035. ;; following two local variables, but we keep them for now until we
  1036. ;; are completely sure their removal will not break any current
  1037. ;; behavior or backwards compatibility.
  1038. (setq-local beginning-of-defun-function 'php-beginning-of-defun)
  1039. (setq-local end-of-defun-function 'php-end-of-defun)
  1040. (setq-local open-paren-in-column-0-is-defun-start nil)
  1041. (setq-local defun-prompt-regexp
  1042. "^\\s-*function\\s-+&?\\s-*\\(\\(\\sw\\|\\s_\\)+\\)\\s-*")
  1043. (setq-local add-log-current-defun-function nil)
  1044. (setq-local add-log-current-defun-header-regexp php-beginning-of-defun-regexp)
  1045. (when (fboundp 'c-looking-at-or-maybe-in-bracelist)
  1046. (advice-add #'c-looking-at-or-maybe-in-bracelist
  1047. :override 'php-c-looking-at-or-maybe-in-bracelist))
  1048. (when (>= emacs-major-version 25)
  1049. (with-silent-modifications
  1050. (save-excursion
  1051. (let* ((start (point-min))
  1052. (end (min (point-max)
  1053. (+ start syntax-propertize-chunk-size))))
  1054. (php-syntax-propertize-function start end))))))
  1055. (declare-function semantic-create-imenu-index "semantic/imenu" (&optional stream))
  1056. (defvar-mode-local php-mode imenu-create-index-function
  1057. (if php-do-not-use-semantic-imenu
  1058. #'imenu-default-create-index-function
  1059. (require 'semantic/imenu)
  1060. #'semantic-create-imenu-index)
  1061. "Imenu index function for PHP.")
  1062. ;; Define function name completion function
  1063. (defvar php-completion-table nil
  1064. "Obarray of tag names defined in current tags table and functions known to PHP.")
  1065. (defun php-complete-function ()
  1066. "Perform function completion on the text around point.
  1067. Completes to the set of names listed in the current tags table
  1068. and the standard php functions.
  1069. The string to complete is chosen in the same way as the default
  1070. for \\[find-tag] (which see)."
  1071. (interactive)
  1072. (let ((pattern (php-get-pattern))
  1073. beg
  1074. completion
  1075. (php-functions (php-completion-table)))
  1076. (if (not pattern) (message "Nothing to complete")
  1077. (if (not (search-backward pattern nil t))
  1078. (message "Can't complete here")
  1079. (setq beg (point))
  1080. (forward-char (length pattern))
  1081. (setq completion (try-completion pattern php-functions nil))
  1082. (cond ((eq completion t))
  1083. ((null completion)
  1084. (message "Can't find completion for \"%s\"" pattern)
  1085. (ding))
  1086. ((not (string= pattern completion))
  1087. (delete-region beg (point))
  1088. (insert completion))
  1089. (t
  1090. (let ((selected (completing-read
  1091. "Select completion: "
  1092. (all-completions pattern php-functions)
  1093. nil t pattern)))
  1094. (delete-region beg (point))
  1095. (insert selected))))))))
  1096. (defun php-completion-table ()
  1097. "Build variable `php-completion-table' on demand.
  1098. The table includes the PHP functions and the tags from the
  1099. current `tags-file-name'."
  1100. (or (and tags-file-name
  1101. (save-excursion (tags-verify-table tags-file-name))
  1102. php-completion-table)
  1103. (let ((tags-table
  1104. (when tags-file-name
  1105. (with-current-buffer (get-file-buffer tags-file-name)
  1106. (etags-tags-completion-table))))
  1107. (php-table
  1108. (cond ((and (not (string= "" php-completion-file))
  1109. (file-readable-p php-completion-file))
  1110. (php-build-table-from-file php-completion-file))
  1111. ((and (not (string= "" php-manual-path))
  1112. (file-directory-p php-manual-path))
  1113. (php-build-table-from-path php-manual-path))
  1114. (t nil))))
  1115. (unless (or php-table tags-table)
  1116. (error
  1117. (concat "No TAGS file active nor are "
  1118. "`php-completion-file' or `php-manual-path' set")))
  1119. (when tags-table
  1120. ;; Combine the tables.
  1121. (if (obarrayp tags-table)
  1122. (mapatoms (lambda (sym) (intern (symbol-name sym) php-table))
  1123. tags-table)
  1124. (setq php-table (append tags-table php-table))))
  1125. (setq php-completion-table php-table))))
  1126. (defun php-build-table-from-file (filename)
  1127. (let ((table (make-vector 1022 0))
  1128. (buf (find-file-noselect filename)))
  1129. (with-current-buffer buf
  1130. (goto-char (point-min))
  1131. (while (re-search-forward
  1132. "^\\([-a-zA-Z0-9_.]+\\)\n"
  1133. nil t)
  1134. (intern (buffer-substring (match-beginning 1) (match-end 1))
  1135. table)))
  1136. (kill-buffer buf)
  1137. table))
  1138. (defun php-build-table-from-path (path)
  1139. "Return list of PHP function name from `PATH' directory."
  1140. (cl-loop for file in (directory-files path nil "^function\\..+\\.html$")
  1141. if (string-match "\\.\\([-a-zA-Z_0-9]+\\)\\.html$" file)
  1142. collect (replace-regexp-in-string
  1143. "-" "_" (substring file (match-beginning 1) (match-end 1)) t)))
  1144. ;; Find the pattern we want to complete
  1145. ;; find-tag-default from GNU Emacs etags.el
  1146. (defun php-get-pattern ()
  1147. (save-excursion
  1148. (while (looking-at "\\sw\\|\\s_")
  1149. (forward-char 1))
  1150. (if (or (re-search-backward "\\sw\\|\\s_"
  1151. (save-excursion (beginning-of-line) (point))
  1152. t)
  1153. (re-search-forward "\\(\\sw\\|\\s_\\)+"
  1154. (save-excursion (end-of-line) (point))
  1155. t))
  1156. (progn (goto-char (match-end 0))
  1157. (buffer-substring-no-properties
  1158. (point)
  1159. (progn (forward-sexp -1)
  1160. (while (looking-at "\\s'")
  1161. (forward-char 1))
  1162. (point))))
  1163. nil)))
  1164. (defun php-show-arglist ()
  1165. "Show function arguments at cursor position."
  1166. (interactive)
  1167. (let* ((tagname (php-get-pattern))
  1168. (buf (find-tag-noselect tagname nil nil))
  1169. arglist)
  1170. (with-current-buffer buf
  1171. (save-excursion
  1172. (goto-char (point-min))
  1173. (when (re-search-forward
  1174. (format "function\\s-+%s\\s-*(\\([^{]*\\))" tagname)
  1175. nil t)
  1176. (setq arglist (buffer-substring-no-properties
  1177. (match-beginning 1) (match-end 1))))))
  1178. (if arglist
  1179. (message "Arglist for %s: %s" tagname arglist)
  1180. (message "Unknown function: %s" tagname))))
  1181. (defcustom php-search-documentation-browser-function nil
  1182. "Function to display PHP documentation in a WWW browser.
  1183. If non-nil, this shadows the value of `browse-url-browser-function' when
  1184. calling `php-search-documentation' or `php-search-local-documentation'."
  1185. :group 'php
  1186. :tag "PHP Search Documentation Browser Function"
  1187. :type '(choice (const :tag "default" nil) function)
  1188. :link '(variable-link browse-url-browser-function))
  1189. (defun php-browse-documentation-url (url)
  1190. "Browse a documentation URL using the configured browser function.
  1191. See `php-search-documentation-browser-function'."
  1192. (let ((browse-url-browser-function
  1193. (or php-search-documentation-browser-function
  1194. browse-url-browser-function)))
  1195. (browse-url url)))
  1196. (defvar php-search-local-documentation-types
  1197. (list "function" "control-structures" "class" "book")
  1198. ;; "intro" and "ref" also look interesting, but for all practical purposes
  1199. ;; their terms are sub-sets of the "book" terms (with the few exceptions
  1200. ;; being very unlikely search terms).
  1201. "The set (and priority sequence) of documentation file prefixes
  1202. under which to search for files in the local documentation directory.")
  1203. (defvar php-search-local-documentation-words-cache nil)
  1204. (defun php--search-documentation-read-arg ()
  1205. "Obtain interactive argument for searching documentation."
  1206. ;; Cache the list of documentation words available for completion,
  1207. ;; based on the defined types-of-interest.
  1208. (let ((types-list php-search-local-documentation-types)
  1209. (words-cache php-search-local-documentation-words-cache)
  1210. (local-manual (and (stringp php-manual-path)
  1211. (not (string= php-manual-path "")))))
  1212. (when (and local-manual
  1213. (not (assq types-list words-cache)))
  1214. ;; Generate the cache on the first run, or if the types changed.
  1215. ;; We read the filenames matching our types list in the local
  1216. ;; documentation directory, and extract the 'middle' component
  1217. ;; of each. e.g. "function.array-map.html" => "array_map".
  1218. (let* ((types-opt (regexp-opt types-list))
  1219. (pattern (concat "\\`" types-opt "\\.\\(.+\\)\\.html\\'"))
  1220. (collection
  1221. (mapcar (lambda (filename) (subst-char-in-string
  1222. ?- ?_ (replace-regexp-in-string
  1223. pattern "\\1" filename)))
  1224. (directory-files php-manual-path nil pattern))))
  1225. ;; Replace the entire cache. If the types changed, we don't need
  1226. ;; to retain the collection for the previous value.
  1227. (setq words-cache (list (cons types-list collection)))
  1228. (setq php-search-local-documentation-words-cache words-cache)))
  1229. ;; By default we search for (current-word) immediately, without prompting.
  1230. ;; With a prefix argument, or if there is no (current-word), we perform a
  1231. ;; completing read for a word from the cached collection.
  1232. (let* ((default (current-word))
  1233. (prompt (if default
  1234. (format "Search PHP docs (%s): " default)
  1235. "Search PHP docs: "))
  1236. (collection (and local-manual
  1237. (cdr (assq types-list words-cache))))
  1238. (word (if (or current-prefix-arg (not default))
  1239. (completing-read prompt collection nil nil nil nil default)
  1240. default)))
  1241. ;; Return interactive argument list.
  1242. (list word))))
  1243. (defun php-search-local-documentation (word)
  1244. "Search the local PHP documentation (i.e. in `php-manual-path') for
  1245. the word at point. The function returns t if the requested documentation
  1246. exists, and nil otherwise.
  1247. With a prefix argument, prompt (with completion) for a word to search for."
  1248. (interactive (php--search-documentation-read-arg))
  1249. (let ((file (catch 'found
  1250. (cl-loop for type in php-search-local-documentation-types do
  1251. (let* ((doc-html (format "%s.%s.html"
  1252. type
  1253. (replace-regexp-in-string
  1254. "_" "-" (downcase word))))
  1255. (file (expand-file-name doc-html php-manual-path)))
  1256. (when (file-exists-p file)
  1257. (throw 'found file)))))))
  1258. (when file
  1259. (let ((file-url (if (string-prefix-p "file://" file)
  1260. file
  1261. (concat "file://" file))))
  1262. (php-browse-documentation-url file-url))
  1263. t)))
  1264. (defsubst php-search-web-documentation (word)
  1265. "Return URL to search PHP manual search by `WORD'."
  1266. (php-browse-documentation-url (concat (or php-search-url php-site-url) word)))
  1267. ;; Define function documentation function
  1268. (defun php-search-documentation (word)
  1269. "Search PHP documentation for the `WORD' at point.
  1270. If `php-manual-path' has a non-empty string value then the command
  1271. will first try searching the local documentation. If the requested
  1272. documentation does not exist it will fallback to searching the PHP
  1273. website.
  1274. With a prefix argument, prompt for a documentation word to search
  1275. for. If the local documentation is available, it is used to build
  1276. a completion list."
  1277. (interactive (php--search-documentation-read-arg))
  1278. (if (and (stringp php-manual-path)
  1279. (not (string= php-manual-path "")))
  1280. (or (php-search-local-documentation word)
  1281. (php-search-web-documentation word))
  1282. (php-search-web-documentation word)))
  1283. ;; Define function for browsing manual
  1284. (defun php-browse-manual ()
  1285. "Bring up manual for PHP."
  1286. (interactive)
  1287. (browse-url (if (stringp php-manual-url)
  1288. php-manual-url
  1289. (format "%smanual/%s/" php-site-url php-manual-url))))
  1290. ;; Font Lock
  1291. (defconst php-phpdoc-type-keywords
  1292. (list "string" "integer" "int" "boolean" "bool" "float"
  1293. "double" "object" "mixed" "array" "resource"
  1294. "void" "null" "false" "true" "self" "static"
  1295. "callable" "iterable" "number"))
  1296. (defconst php-phpdoc-type-tags
  1297. (list "package" "param" "property" "property-read" "property-write"
  1298. "return" "throws" "var"))
  1299. (defconst php-phpdoc-font-lock-doc-comments
  1300. `(("{@[-[:alpha:]]+\\s-*\\([^}]*\\)}" ; "{@foo ...}" markup.
  1301. (0 'php-doc-annotation-tag prepend nil)
  1302. (1 'php-string prepend nil))
  1303. (,(rx (group "$") (group (in "A-Za-z_") (* (in "0-9A-Za-z_"))))
  1304. (1 'php-doc-variable-sigil prepend nil)
  1305. (2 'php-variable-name prepend nil))
  1306. ("\\(\\$\\)\\(this\\)\\>" (1 'php-doc-$this-sigil prepend nil) (2 'php-doc-$this prepend nil))
  1307. (,(concat "\\s-@" (regexp-opt php-phpdoc-type-tags) "\\s-+"
  1308. "\\(" (rx (+ (? "?") (? "\\") (+ (in "0-9A-Z_a-z")) (? "[]") (? "|"))) "\\)+")
  1309. 1 'php-string prepend nil)
  1310. (,(concat "\\(?:|\\|\\?\\|\\s-\\)\\("
  1311. (regexp-opt php-phpdoc-type-keywords 'words)
  1312. "\\)")
  1313. 1 font-lock-type-face prepend nil)
  1314. ("https?://[^\n\t ]+"
  1315. 0 'link prepend nil)
  1316. ("^\\(?:/\\*\\)?\\(?:\\s \\|\\*\\)*\\(@[[:alpha:]][-[:alpha:]\\]*\\)" ; "@foo ..." markup.
  1317. 1 'php-doc-annotation-tag prepend nil)))
  1318. (defvar php-phpdoc-font-lock-keywords
  1319. `((,(lambda (limit)
  1320. (c-font-lock-doc-comments "/\\*\\*" limit
  1321. php-phpdoc-font-lock-doc-comments)))))
  1322. (defconst php-font-lock-keywords-1 (c-lang-const c-matchers-1 php)
  1323. "Basic highlighting for PHP Mode.")
  1324. (defconst php-font-lock-keywords-2 (c-lang-const c-matchers-2 php)
  1325. "Medium level highlighting for PHP Mode.")
  1326. (defconst php-font-lock-keywords-3
  1327. (append
  1328. php-phpdoc-font-lock-keywords
  1329. ;; php-mode patterns *before* cc-mode:
  1330. ;; only add patterns here if you want to prevent cc-mode from applying
  1331. ;; a different face.
  1332. `(
  1333. ;; Class declaration specification keywords (implements, extends)
  1334. ("\\_<\\(?:implements\\|extends\\)\\_>" . 'php-class-declaration-spec)
  1335. ;; Namespace declaration
  1336. ("\\_<namespace\\_>" . 'php-namespace-declaration)
  1337. ;; import statement
  1338. ("\\_<use\\_>" . 'php-import-declaration)
  1339. ;; Class modifiers (abstract, final)
  1340. ("\\_<\\(abstract\\|final\\)\\_>\\s-+\\_<class\\>" 1 'php-class-modifier)
  1341. ;; Highlight variables, e.g. 'var' in '$var' and '$obj->var', but
  1342. ;; not in $obj->var()
  1343. ("\\(->\\)\\(\\sw+\\)\\s-*(" (1 'php-object-op) (2 'php-method-call))
  1344. ("\\<\\(const\\)\\s-+\\(\\_<.+?\\_>\\)" (1 'php-keyword) (2 'php-constant-assign))
  1345. ;; Logical operator (!)
  1346. ("\\(!\\)[^=]" 1 'php-logical-op)
  1347. ;; Highlight special variables
  1348. ("\\(\\$\\)\\(this\\)\\>" (1 'php-$this-sigil) (2 'php-$this))
  1349. ("\\(\\$+\\)\\(\\sw+\\)" (1 'php-variable-sigil) (2 'php-variable-name))
  1350. ("\\(->\\)\\([a-zA-Z0-9_]+\\)" (1 'php-object-op) (2 'php-property-name))
  1351. ;; Highlight function/method names
  1352. ("\\<function\\s-+&?\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*(" 1 'php-function-name)
  1353. ;; 'array' and 'callable' are keywords, except in the following situations:
  1354. ;; - when used as a type hint
  1355. ;; - when used as a return type
  1356. ("\\b\\(array\\|callable\\)\\s-+&?\\$" 1 font-lock-type-face)
  1357. (")\\s-*:\\s-*\\??\\(array\\|callable\\)\\b" 1 font-lock-type-face)
  1358. ;; For 'array', there is an additional situation:
  1359. ;; - when used as cast, so that (int) and (array) look the same
  1360. ("(\\(array\\))" 1 font-lock-type-face)
  1361. (,(regexp-opt php-magical-constants 'symbols) (1 'php-magical-constant))
  1362. ;; namespaces
  1363. ("\\(\\([a-zA-Z0-9_]+\\\\\\)+[a-zA-Z0-9_]+\\|\\(\\\\[a-zA-Z0-9_]+\\)+\\)[^:a-zA-Z0-9_\\\\]" 1 'font-lock-type-face)
  1364. ("\\(\\([a-zA-Z0-9_]+\\\\\\)+[a-zA-Z0-9_]+\\|\\(\\\\[a-zA-Z0-9_]+\\)+\\)::" 1 'php-constant)
  1365. (,(eval-when-compile
  1366. (rx bol (* (syntax whitespace))
  1367. (or "private" "protected" "public")
  1368. (+ (syntax whitespace))
  1369. (group (? "?") (+ (or "\\" (syntax word) (syntax symbol))))
  1370. (+ (syntax whitespace))
  1371. (: "$" (+ (or (syntax word) (syntax symbol))))))
  1372. 1 'php-class)
  1373. ;; Support the ::class constant in PHP5.6
  1374. ("\\sw+\\(::\\)\\(class\\)\\b" (1 'php-paamayim-nekudotayim) (2 'php-magical-constant))
  1375. ;; Class declaration keywords (class, trait, interface)
  1376. ("\\_<\\(class\\|trait\\|interface\\)\\_>" . 'php-class-declaration)
  1377. ;; Highlight static method calls as such. This is necessary for method
  1378. ;; names which are identical to keywords to be highlighted correctly.
  1379. ("\\sw+::\\(\\sw+\\)(" 1 'php-static-method-call)
  1380. ;; Multiple catch (FooException | BarException $e)
  1381. (,(rx symbol-start "catch" symbol-end
  1382. (* (syntax whitespace)) "(" (* (syntax whitespace))
  1383. (group (+ (or (syntax word) (syntax symbol)))))
  1384. (1 font-lock-type-face)
  1385. (,(rx (* (syntax whitespace)) "|" (* (syntax whitespace))
  1386. (group (+ (or (syntax word) (syntax symbol))) symbol-end))
  1387. nil nil (1 font-lock-type-face)))
  1388. ;; While c-opt-cpp-* highlights the <?php opening tags, it is not
  1389. ;; possible to make it highlight short open tags and closing tags
  1390. ;; as well. So we force the correct face on all cases that
  1391. ;; c-opt-cpp-* lacks for this purpose.
  1392. ;;
  1393. ;; Note that starting a file with <% breaks indentation, a
  1394. ;; limitation we can/should live with.
  1395. (,(regexp-opt '("<?php" "<?=" "?>"
  1396. "<?" ;; obsolete short open tag
  1397. "<%" "%>" ;; obsolete ASP tag
  1398. ;; Obsoleted tags were deleted in PHP 7.
  1399. ;; @see http://php.net/manual/language.basic-syntax.phptags.php
  1400. ))
  1401. 0 'php-php-tag))
  1402. ;; cc-mode patterns
  1403. (c-lang-const c-matchers-3 php)
  1404. ;; php-mode patterns *after* cc-mode:
  1405. ;; most patterns should go here, faces will only be applied if not
  1406. ;; already fontified by another pattern. Note that using OVERRIDE
  1407. ;; is usually overkill.
  1408. `(
  1409. ("\\<\\(@\\)" 1 'php-errorcontrol-op)
  1410. ;; Highlight function calls
  1411. ("\\(\\_<\\(?:\\sw\\|\\s_\\)+?\\_>\\)\\s-*(" 1 'php-function-call)
  1412. ;; Highlight all upper-cased symbols as constant
  1413. ("\\<\\([A-Z_][A-Z0-9_]+\\)\\>" 1 'php-constant)
  1414. ;; Highlight all statically accessed class names as constant,
  1415. ;; another valid option would be using type-face, but using
  1416. ;; constant-face because this is how it works in c++-mode.
  1417. ("\\(\\sw+\\)\\(::\\)" (1 'php-constant) (2 'php-paamayim-nekudotayim))
  1418. ;; Highlight class name after "use .. as"
  1419. ("\\<as\\s-+\\(\\sw+\\)" 1 font-lock-type-face)
  1420. ;; Class names are highlighted by cc-mode as defined in
  1421. ;; c-class-decl-kwds, below regexp is a workaround for a bug
  1422. ;; where the class names are not highlighted right after opening
  1423. ;; a buffer (editing a file corrects it).
  1424. ;;
  1425. ;; This behaviour is caused by the preceding '<?php', which
  1426. ;; cc-mode cannot handle easily. Registering it as a cpp
  1427. ;; preprocessor works well (i.e. the next line is not a
  1428. ;; statement-cont) but the highlighting glitch remains.
  1429. (,(concat (regexp-opt (c-lang-const c-class-decl-kwds php))
  1430. " \\(\\sw+\\)")
  1431. 1 font-lock-type-face)
  1432. ;; Highlight the ? character for nullable return types.
  1433. ("function.+:\\s-*\\(\\?\\)\\(?:\\sw\\|\\s_\\|\\\\\\)+" 1 font-lock-type-face)
  1434. (")\\s-*:\\s-*\\(\\?\\)\\(?:\\sw\\|\\s_\\|\\\\\\)+\\s-*\\(?:\{\\|;\\)" 1 font-lock-type-face)
  1435. ;; Highlight the ? character for nullable type hints.
  1436. ("\\(\\?\\)\\(:?\\sw\\|\\s_\\|\\\\\\)+\\s-+\\$" 1 font-lock-type-face)
  1437. ;; Class names without a namespace are not highlighted at all when they
  1438. ;; are used as nullable type hints or return types (both nullable and
  1439. ;; non-nullable). We have to use separate regular expressions, because
  1440. ;; we want to capture the class name as well, not just the ? character
  1441. ;; like the regexps above.
  1442. ("\\?\\(\\(:?\\sw\\|\\s_\\)+\\)\\s-+\\$" 1 font-lock-type-face)
  1443. ("function.+:\\s-*\\??\\(\\(?:\\sw\\|\\s_\\)+\\)" 1 font-lock-type-face)
  1444. (")\\s-*:\\s-*\\??\\(\\(?:\\sw\\|\\s_\\)+\\)\\s-*\\(?:\{\\|;\\)" 1 font-lock-type-face)
  1445. ;; Assignment operators (=, +=, ...)
  1446. ("\\([^=<!>]+?\\([\-+./%]?=\\)[^=<!]+?\\)" 2 'php-assignment-op)
  1447. ;; Comparison operators (==, ===, >=, ...)
  1448. ("\\([!=]=\\{1,2\\}[>]?\\|[<>]=?\\)" 1 'php-comparison-op)
  1449. ;; Arithmetic operators (+, -, *, **, /, %)
  1450. ("\\(?:[A-Za-z0-9[:blank:]]\\)\\([\-+*/%]\\*?\\)\\(?:[A-Za-z0-9[:blank:]]\\)" 1 'php-arithmetic-op)
  1451. ;; Increment and Decrement operators (++, --)
  1452. ("\\(\-\-\\|\+\+\\)\$\\w+" 1 'php-inc-dec-op) ;; pre inc/dec
  1453. ("\$\\w+\\(\-\-\\|\+\+\\)" 1 'php-inc-dec-op) ;; post inc/dec
  1454. ;; Logical operators (and, or, &&, ...)
  1455. ;; Not operator (!) is defined in "before cc-mode" section above.
  1456. ("\\(&&\\|||\\)" 1 'php-logical-op)))
  1457. "Detailed highlighting for PHP Mode.")
  1458. (defvar php-font-lock-keywords php-font-lock-keywords-3
  1459. "Default expressions to highlight in PHP Mode.")
  1460. (add-to-list
  1461. (eval-when-compile
  1462. (if (boundp 'flymake-proc-allowed-file-name-masks)
  1463. 'flymake-proc-allowed-file-name-masks
  1464. 'flymake-allowed-file-name-masks))
  1465. '("\\.php[345s]?\\'" php-flymake-php-init))
  1466. (defun php-send-region (start end)
  1467. "Send the region between `START' and `END' to PHP for execution.
  1468. The output will appear in the buffer *PHP*."
  1469. (interactive "r")
  1470. (let ((php-buffer (get-buffer-create "*PHP*"))
  1471. (code (buffer-substring start end)))
  1472. ;; Calling 'php -r' will fail if we send it code that starts with
  1473. ;; '<?php', which is likely. So we run the code through this
  1474. ;; function to check for that prefix and remove it.
  1475. (let ((cleaned-php-code (if (string-prefix-p "<?php" code t)
  1476. (substring code 5)
  1477. code)))
  1478. (call-process php-executable nil php-buffer nil "-r" cleaned-php-code))))
  1479. (defconst php-string-interpolated-variable-regexp
  1480. "{\\$[^}\n\\\\]*\\(?:\\\\.[^}\n\\\\]*\\)*}\\|\\${\\sw+}\\|\\$\\sw+")
  1481. (defun php-string-intepolated-variable-font-lock-find (limit)
  1482. (while (re-search-forward php-string-interpolated-variable-regexp limit t)
  1483. (let ((quoted-stuff (nth 3 (syntax-ppss))))
  1484. (when (and quoted-stuff (member quoted-stuff '(?\" ?`)))
  1485. (put-text-property (match-beginning 0) (match-end 0)
  1486. 'face 'php-variable-name))))
  1487. nil)
  1488. (eval-after-load 'php-mode
  1489. '(progn
  1490. (font-lock-add-keywords
  1491. 'php-mode
  1492. `((php-string-intepolated-variable-font-lock-find))
  1493. 'append)))
  1494. ;;; Correct the behavior of `delete-indentation' by modifying the
  1495. ;;; logic of `fixup-whitespace'.
  1496. (defadvice fixup-whitespace (after php-mode-fixup-whitespace)
  1497. "Remove whitespace before certain characters in PHP Mode."
  1498. (let* ((no-behind-space ";\\|,\\|->\\|::")
  1499. (no-front-space "->\\|::"))
  1500. (when (and (eq major-mode 'php-mode)
  1501. (or (looking-at-p (concat " \\(" no-behind-space "\\)"))
  1502. (save-excursion
  1503. (forward-char -2)
  1504. (looking-at-p no-front-space))))
  1505. (delete-char 1))))
  1506. (ad-activate 'fixup-whitespace)
  1507. ;;;###autoload
  1508. (progn
  1509. (add-to-list 'auto-mode-alist '("/\\.php_cs\\(?:\\.dist\\)?\\'" . php-mode))
  1510. (add-to-list 'auto-mode-alist '("\\.\\(?:php\\.inc\\|stub\\)\\'" . php-mode))
  1511. (add-to-list 'auto-mode-alist '("\\.\\(?:php[s345]?\\|phtml\\)\\'" . php-mode-maybe)))
  1512. (provide 'php-mode)
  1513. ;;; php-mode.el ends here