Skip to content

Commit ee0e42f

Browse files
authored
Merge pull request #64 from emacs-php/fix/container-version-detection
Ask PHPStan for its version, not the program that launches it
2 parents abaaca5 + 86a567a commit ee0e42f

2 files changed

Lines changed: 63 additions & 26 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ All notable changes of the `phpstan.el` are documented in this file using the [K
3434
* Fix the `--tmp-file` copy being created in the system temporary directory when running containerized, where the container cannot see it.
3535
* Fix the JSON report being ignored when the container runtime prefixes it with progress output on STDERR, which made every check with `(phpstan-executable . container)` report no errors.
3636
* Fix `flycheck-phpstan` silently discarding the fallback warning when PHPStan produced no JSON report, hiding failures such as a broken configuration file behind a clean buffer.
37+
* Fix editor mode detection asking the wrong program for its version. Only the first element of the command line was probed, which is the container runtime for `(phpstan-executable . docker)` / `container` and `php` for a PHAR without the executable bit — so `docker --version` and `php --version` were parsed as PHPStan versions (`d1c06ef`, `Technologies`) and editor mode was silently disabled for every setup except a directly executable `phpstan`.
38+
* `phpstan-version` and `phpstan-editor-mode-available-p` now take the whole command line, as returned by `phpstan-get-executable-and-args`. A bare string is still accepted. `phpstan-version` no longer merges STDERR into the version string, which a container runtime pollutes with its progress report.
3739

3840
## [0.9.0]
3941

phpstan.el

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ it returns the value of `SOURCE' as it is."
576576
(cond
577577
((funcall (plist-get editor :analyze-original) original-file)
578578
(list "--" target-file))
579-
((phpstan-editor-mode-available-p (car (phpstan-get-executable-and-args)))
579+
((phpstan-editor-mode-available-p executable-and-args)
580580
;; A container only sees the project, so the temporary copy
581581
;; has to be created inside it. `:temp-file' puts it in the
582582
;; system temporary directory, which is not mounted.
@@ -613,36 +613,71 @@ it returns the value of `SOURCE' as it is."
613613
collect (cons (plist-get message :line)
614614
(substring-no-properties msg (match-end 0))))))))
615615

616-
(defun phpstan-version (executable)
617-
"Return the PHPStan version of EXECUTABLE."
618-
(if-let* ((cached-entry (assoc executable phpstan-executable-versions-alist)))
619-
(cdr cached-entry)
620-
(let* ((version (thread-first
621-
(mapconcat #'shell-quote-argument (list executable "--version") " ")
622-
(shell-command-to-string)
623-
(string-trim-right)
624-
(split-string " ")
625-
(last)
626-
(car-safe))))
627-
(prog1 version
628-
(push (cons executable version) phpstan-executable-versions-alist)))))
629-
630-
(defun phpstan-editor-mode-available-p (executable)
631-
"Check if the specified PHPStan EXECUTABLE supports editor mode.
632-
633-
If a cached result for EXECUTABLE exists, it is returned directly.
616+
(defun phpstan--version-output (command)
617+
"Run COMMAND with --version and return its standard output, or NIL.
618+
619+
STDERR is discarded rather than merged: a container runtime reports its
620+
progress there, and it would otherwise be read as part of the version."
621+
(with-temp-buffer
622+
(let ((status (apply #'process-file (car command) nil (list t nil) nil
623+
(append (cdr command) (list "--version")))))
624+
(when (eq 0 status)
625+
(buffer-string)))))
626+
627+
(defun phpstan--version-from-output (output)
628+
"Return the version number reported in OUTPUT, or NIL.
629+
630+
OUTPUT looks like \"PHPStan - PHP Static Analysis Tool 1.12.33\"."
631+
(when output
632+
(let ((last-line (car (last (split-string (string-trim output) "\n" t)))))
633+
(when last-line
634+
(car (last (split-string last-line " " t)))))))
635+
636+
(defun phpstan-version (command)
637+
"Return the PHPStan version of COMMAND.
638+
639+
COMMAND is the command line that runs PHPStan, as returned by
640+
`phpstan-get-executable-and-args'. A bare string is also accepted, and
641+
taken as the name of an executable.
642+
643+
Passing the whole command line matters: PHPStan is not always the program
644+
being executed. It is `docker' or `container' for a containerized PHPStan,
645+
and `php' for a PHAR without the executable bit, and asking either of those
646+
for its version answers a version that has nothing to do with PHPStan.
647+
648+
The result is cached in `phpstan-executable-versions-alist', keyed by the
649+
command line, because probing may have to start a container."
650+
(let* ((command (if (listp command) command (list command)))
651+
(cache-key (mapconcat #'identity command " ")))
652+
(if-let* ((cached-entry (assoc cache-key phpstan-executable-versions-alist)))
653+
(cdr cached-entry)
654+
(let ((version (phpstan--version-from-output
655+
(phpstan--version-output command))))
656+
(prog1 version
657+
(push (cons cache-key version) phpstan-executable-versions-alist))))))
658+
659+
(defun phpstan-editor-mode-available-p (command)
660+
"Check if the PHPStan invoked by COMMAND supports editor mode.
661+
662+
COMMAND is the command line that runs PHPStan, as returned by
663+
`phpstan-get-executable-and-args'. A bare string is also accepted, and
664+
taken as the name of an executable.
665+
666+
If a cached result for COMMAND exists, it is returned directly.
634667
Otherwise, this function attempts to determine support by retrieving
635-
the PHPStan version using `phpstan --version' command."
668+
the PHPStan version using `phpstan --version' command. Support is
669+
assumed to be absent when the version cannot be determined."
636670
(pcase phpstan-activate-editor-mode
637671
('enabled t)
638672
('disabled nil)
639673
('nil
640-
(let* ((version (phpstan-version executable)))
641-
(if (string-match-p (eval-when-compile (regexp-quote "-dev@")) version)
642-
t
643-
(pcase (elt version 0)
644-
(?1 (version<= "1.12.27" version))
645-
(?2 (version<= "2.1.17" version))))))))
674+
(let ((version (phpstan-version command)))
675+
(when (and version (not (string-empty-p version)))
676+
(if (string-match-p (eval-when-compile (regexp-quote "-dev@")) version)
677+
t
678+
(pcase (elt version 0)
679+
(?1 (version<= "1.12.27" version))
680+
(?2 (version<= "2.1.17" version)))))))))
646681

647682
(defconst phpstan--re-ignore-tag
648683
(eval-when-compile

0 commit comments

Comments
 (0)