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