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