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.

1719 lines
66 KiB

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