Skip to content

Commit dc67eae

Browse files
committed
Add php-flymake.el
1 parent 683211c commit dc67eae

File tree

3 files changed

+143
-1
lines changed

3 files changed

+143
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ 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)

Makefile

Lines changed: 1 addition & 1 deletion
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

Lines changed: 140 additions & 0 deletions
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

0 commit comments

Comments
 (0)