Skip to content

Commit 56e5b67

Browse files
authored
Merge pull request #718 from emacs-php/feature/flymake-diagnostic-functions
New feature: php-flymake
2 parents f5c4016 + a8a62f3 commit 56e5b67

File tree

5 files changed

+184
-15
lines changed

5 files changed

+184
-15
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ All notable changes of the PHP Mode 1.19.1 release series are documented in this
88

99
* **New feature: `php-complete`**
1010
* Add `php-complete-complete-function` to autocomplete function names ([#708])
11+
* **New feature: `php-flymake`**
12+
* Add `php-flymake` as a flymake backend compatible with Emacs 26 and above ([#718])
1113
* Supports PHPDoc tags and types for static analysis tools ([#710], [#715], [#716], [#717], thanks to [@takeokunn])
1214
* Please refer to the article below
1315
* PHPStan: [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types)
1416
* PHPStan: [PHPDocs Basics](https://phpstan.org/writing-php-code/phpdocs-basics)
1517
* Psalm: [Atomic Type Reference](https://psalm.dev/docs/annotating_code/type_syntax/atomic_types/)
1618
* Psalm: [Supported Annotations](https://psalm.dev/docs/annotating_code/supported_annotations/)
1719
* Psalm: [Template Annotations](https://psalm.dev/docs/annotating_code/templated_annotations/)
20+
* Add `php-mode-replace-flymake-diag-function` custom variable and default activated it ([#718])
1821

1922
### Changed
2023

@@ -24,11 +27,13 @@ All notable changes of the PHP Mode 1.19.1 release series are documented in this
2427
* Make `php-mode-version` function include a Git tag and revision ([#713])
2528
* Like `"1.23.4-56-xxxxxx"` for example.
2629
* Change `php-phpdoc-type-keywords` to `php-phpdoc-type-names` to avoid confusion ([#717])
30+
* Make `php-flymake-php-init` append to `flymake-allowed-file-name-masks` only in legacy Flymake ([#718])
2731

2832
### Deprecated
2933

3034
* Make obsolete `php-mode-version-number` contstant variable ([#712])
3135
* `(php-mode-version :as-number t)` is provided for use cases comparing as versions, but generally SHOULD NOT be dependent on the PHP Mode version.
36+
* Make obsolete `php-mode-disable-c-mode-hook` customize variable ([#718])
3237

3338
### Fixed
3439

@@ -44,6 +49,7 @@ All notable changes of the PHP Mode 1.19.1 release series are documented in this
4449
[#715]: https://github.com/emacs-php/php-mode/pull/715
4550
[#716]: https://github.com/emacs-php/php-mode/pull/716
4651
[#717]: https://github.com/emacs-php/php-mode/pull/717
52+
[#718]: https://github.com/emacs-php/php-mode/pull/718
4753

4854
## [1.24.1] - 2022-10-08
4955

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
EMACS ?= emacs
22
CASK ?= cask
3-
ELS = lisp/php.el lisp/php-align.el lisp/php-complete.el lisp/php-defs.el lisp/php-face.el lisp/php-project.el lisp/php-local-manual.el lisp/php-mode.el lisp/php-mode-debug.el
3+
ELS = lisp/php.el lisp/php-align.el lisp/php-complete.el lisp/php-defs.el lisp/php-face.el lisp/php-flymake.el lisp/php-project.el lisp/php-local-manual.el lisp/php-mode.el lisp/php-mode-debug.el
44
AUTOLOADS = php-mode-autoloads.el
55
ELCS = $(ELS:.el=.elc)
66

lisp/php-flymake.el

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
;;; php-flymake.el --- Flymake backend for PHP -*- lexical-binding: t; -*-
2+
3+
;; Copyright (C) 2022 Friends of Emacs-PHP development
4+
5+
;; Author: USAMI Kenta <[email protected]>
6+
;; Created: 5 Mar 2022
7+
;; Version: 1.24.1
8+
;; Keywords: tools, languages, php
9+
10+
;; This program is free software; you can redistribute it and/or modify
11+
;; it under the terms of the GNU General Public License as published by
12+
;; the Free Software Foundation, either version 3 of the License, or
13+
;; (at your option) any later version.
14+
15+
;; This program is distributed in the hope that it will be useful,
16+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
;; GNU General Public License for more details.
19+
20+
;; You should have received a copy of the GNU General Public License
21+
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
23+
;;; Commentary:
24+
25+
;; Flymake backend for PHP.
26+
27+
;;; Code:
28+
(require 'flymake)
29+
(require 'cl-lib)
30+
(eval-when-compile
31+
(require 'pcase)
32+
(require 'rx))
33+
34+
(defgroup php-flymake nil
35+
"Flymake backend for PHP."
36+
:tag "PHP Flymake"
37+
:group 'php)
38+
39+
(defcustom php-flymake-executable-command-args nil
40+
"List of command and arguments for `php -l'."
41+
:group 'php-flymake
42+
:type '(repeat string)
43+
:safe (lambda (v) (and (listp v) (cl-every v #'stringp))))
44+
45+
(defconst php-flymake--diaggnostics-pattern
46+
(eval-when-compile
47+
(rx bol (? "PHP ")
48+
(group (or "Parse" "Fatal")) ;; 1: type, not used
49+
" error:" (+ (syntax whitespace))
50+
(group (+? any)) ;; 2: msg
51+
" in " (group (+? any)) ;; 3: file, not used
52+
" on line " (group (+ num)) ;; 4: line
53+
eol)))
54+
55+
(defvar-local php-flymake--proc nil)
56+
57+
;;;###autoload
58+
(defun php-flymake (report-fn &rest args)
59+
"Flymake backend for PHP syntax check.
60+
61+
See `flymake-diagnostic-functions' about REPORT-FN and ARGS parameters."
62+
(setq-local flymake-proc-allowed-file-name-masks nil)
63+
(when (process-live-p php-flymake--proc)
64+
(if (plist-get args :interactive)
65+
(user-error "There's already a Flymake process running in this buffer")
66+
(kill-process php-flymake--proc)))
67+
(save-restriction
68+
(widen)
69+
(cl-multiple-value-bind (use-stdin skip) (php-flymake--buffer-status)
70+
(unless skip
71+
(setq php-flymake--proc (php-flymake--make-process report-fn buffer-file-name (current-buffer) use-stdin))
72+
(when use-stdin
73+
(process-send-region php-flymake--proc (point-min) (point-max)))
74+
(process-send-eof php-flymake--proc)))))
75+
76+
(defun php-flymake--buffer-status ()
77+
"Return buffer status about \"use STDIN\" and \"Skip diagnostic\"."
78+
(let* ((use-stdin (or (null buffer-file-name)
79+
(buffer-modified-p (current-buffer))
80+
(file-remote-p buffer-file-name)))
81+
(skip (and (not use-stdin)
82+
(save-excursion (goto-char (point-min)) (looking-at-p "#!")))))
83+
(cl-values use-stdin skip)))
84+
85+
(defun php-flymake--diagnostics (locus source)
86+
"Parse output of `php -l' command in SOURCE buffer. LOCUS means filename."
87+
(unless (eval-when-compile (and (fboundp 'flymake-make-diagnostic)
88+
(fboundp 'flymake-diag-region)))
89+
(error "`php-flymake' requires Emacs 26.1 or later"))
90+
(cl-loop
91+
while (search-forward-regexp php-flymake--diaggnostics-pattern nil t)
92+
for msg = (match-string 2)
93+
for line = (string-to-number (match-string 4))
94+
for diag = (or (pcase-let ((`(,beg . ,end)
95+
(flymake-diag-region source line)))
96+
(flymake-make-diagnostic source beg end :error msg))
97+
(flymake-make-diagnostic locus (cons line nil) nil :error msg))
98+
return (list diag)))
99+
100+
(defun php-flymake--build-command-line (file)
101+
"Return the command line for `php -l' FILE."
102+
(let* ((command-args (or php-flymake-executable-command-args
103+
(list (or (bound-and-true-p php-executable) "php"))))
104+
(cmd (car-safe command-args))
105+
(default-directory (expand-file-name "~")))
106+
(unless (or (file-executable-p cmd)
107+
(executable-find cmd))
108+
(user-error "`%s' is not valid command" cmd))
109+
(nconc command-args
110+
(list "-d" "display_errors=0")
111+
(when file (list "-f" file))
112+
(list "-l"))))
113+
114+
(defun php-flymake--make-process (report-fn locus source use-stdin)
115+
"Make PHP process for syntax check SOURCE buffer.
116+
117+
See `flymake-diagnostic-functions' about REPORT-FN parameter.
118+
See `flymake-make-diagnostic' about LOCUS parameter."
119+
(make-process
120+
:name "php-flymake"
121+
:buffer (generate-new-buffer "*flymake-php-flymake*")
122+
:command (php-flymake--build-command-line (unless use-stdin locus))
123+
:noquery t :connection-type 'pipe
124+
:sentinel
125+
(lambda (p _ev)
126+
(unwind-protect
127+
(when (and (eq 'exit (process-status p))
128+
(with-current-buffer source (eq p php-flymake--proc)))
129+
(with-current-buffer (process-buffer p)
130+
(goto-char (point-min))
131+
(funcall report-fn
132+
(if (zerop (process-exit-status p))
133+
nil
134+
(php-flymake--diagnostics locus source)))))
135+
(unless (process-live-p p)
136+
;; (display-buffer (process-buffer p)) ; uncomment to debug
137+
(kill-buffer (process-buffer p)))))))
138+
139+
(provide 'php-flymake)
140+
;;; php-flymake.el ends here

lisp/php-mode.el

+34-9
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@
8181
(eval-when-compile
8282
(require 'rx)
8383
(require 'cl-lib)
84+
(require 'flymake)
85+
(require 'php-flymake)
8486
(require 'regexp-opt)
8587
(defvar add-log-current-defun-header-regexp)
8688
(defvar add-log-current-defun-function)
@@ -179,6 +181,15 @@ Turning this on will open it whenever `php-mode' is loaded."
179181
:tag "PHP Mode Page Delimiter"
180182
:type 'regexp)
181183

184+
(defcustom php-mode-replace-flymake-diag-function
185+
(eval-when-compile (when (boundp 'flymake-diagnostic-functions)
186+
#'php-flymake))
187+
"Flymake function to replace, if NIL do not replace."
188+
:group 'php-mode
189+
:tag "PHP Mode Replace Flymake Diag Function"
190+
:type '(choice 'function
191+
(const :tag "Disable to replace" nil)))
192+
182193
(define-obsolete-variable-alias 'php-do-not-use-semantic-imenu 'php-mode-do-not-use-semantic-imenu "1.20.0")
183194
(defcustom php-mode-do-not-use-semantic-imenu t
184195
"Customize `imenu-create-index-function' for `php-mode'.
@@ -301,6 +312,7 @@ In that case set to `NIL'."
301312
:group 'php-mode
302313
:tag "PHP Mode Disable C Mode Hook"
303314
:type 'boolean)
315+
(make-obsolete-variable 'php-mode-disable-c-mode-hook nil "1.24.2")
304316

305317
(defcustom php-mode-enable-project-local-variable t
306318
"When set to `T', apply project local variable to buffer local variable."
@@ -1147,6 +1159,14 @@ After setting the stylevars run hooks according to STYLENAME
11471159
(php-project-apply-local-variables)
11481160
(remove-hook 'hack-local-variables-hook #'php-mode-set-local-variable-delay))
11491161

1162+
(defun php-mode-neutralize-cc-mode-effect ()
1163+
"Reset PHP-irrelevant variables set by Cc Mode initialization."
1164+
(setq-local c-mode-hook nil)
1165+
(setq-local java-mode-hook nil)
1166+
(when (eval-when-compile (boundp 'flymake-diagnostic-functions))
1167+
(remove-hook 'flymake-diagnostic-functions 'flymake-cc t))
1168+
t)
1169+
11501170
(defvar php-mode-syntax-table
11511171
(let ((table (make-syntax-table)))
11521172
(c-populate-syntax-table table)
@@ -1173,9 +1193,12 @@ After setting the stylevars run hooks according to STYLENAME
11731193
"Please run `M-x package-reinstall php-mode' command."
11741194
"Please byte recompile PHP Mode files.")))
11751195

1176-
(when php-mode-disable-c-mode-hook
1177-
(setq-local c-mode-hook nil)
1178-
(setq-local java-mode-hook nil))
1196+
(if php-mode-disable-c-mode-hook
1197+
(php-mode-neutralize-cc-mode-effect)
1198+
(display-warning 'php-mode
1199+
"`php-mode-disable-c-mode-hook' will be removed. Do not depends on this variable."
1200+
:warning))
1201+
11791202
(c-initialize-cc-mode t)
11801203
(c-init-language-vars php-mode)
11811204
(c-common-init 'php-mode)
@@ -1249,6 +1272,10 @@ After setting the stylevars run hooks according to STYLENAME
12491272
(setq-local add-log-current-defun-function nil)
12501273
(setq-local add-log-current-defun-header-regexp php-beginning-of-defun-regexp)
12511274

1275+
(when (and (eval-when-compile (boundp 'flymake-diagnostic-functions))
1276+
php-mode-replace-flymake-diag-function)
1277+
(add-hook 'flymake-diagnostic-functions php-mode-replace-flymake-diag-function nil t))
1278+
12521279
(when (fboundp 'c-looking-at-or-maybe-in-bracelist)
12531280
(advice-add #'c-looking-at-or-maybe-in-bracelist
12541281
:override 'php-c-looking-at-or-maybe-in-bracelist '(local)))
@@ -1515,12 +1542,10 @@ for \\[find-tag] (which see)."
15151542
(defvar php-font-lock-keywords php-font-lock-keywords-3
15161543
"Default expressions to highlight in PHP Mode.")
15171544

1518-
(add-to-list
1519-
(eval-when-compile
1520-
(if (boundp 'flymake-proc-allowed-file-name-masks)
1521-
'flymake-proc-allowed-file-name-masks
1522-
'flymake-allowed-file-name-masks))
1523-
'("\\.php[345s]?\\'" php-flymake-php-init))
1545+
(eval-when-compile
1546+
(unless (boundp 'flymake-proc-allowed-file-name-masks)
1547+
(add-to-list 'flymake-allowed-file-name-masks
1548+
'("\\.php[345s]?\\'" php-flymake-php-init))))
15241549

15251550

15261551
(defun php-send-region (start end)

lisp/php.el

+3-5
Original file line numberDiff line numberDiff line change
@@ -550,11 +550,9 @@ The order is reversed by calling as follows:
550550
551551
This is an alternative function of `flymake-php-init'.
552552
Look at the `php-executable' variable instead of the constant \"php\" command."
553-
(let* ((init (funcall (eval-when-compile
554-
(if (fboundp 'flymake-proc-php-init)
555-
'flymake-proc-php-init
556-
'flymake-php-init)))))
557-
(list php-executable (cdr init))))
553+
(let ((init (with-no-warnings (flymake-php-init))))
554+
(setf (car init) php-executable)
555+
init))
558556

559557
(defconst php-re-detect-html-tag-aggressive
560558
(eval-when-compile

0 commit comments

Comments
 (0)