From 148a01225dff11e01f26e2e3df1d7b9644a7244c Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Fri, 17 Jul 2026 21:51:11 +0900 Subject: [PATCH 1/3] Translate the analyzed file path for the container mount point `phpstan-get-command-args' translated the config file with `phpstan-normalize-path', but handed the analyzed file to PHPStan as a host path. A containerized PHPStan only sees the project through its mount point, so it answered Path /Users/.../test-docker.php does not exist and reported no errors at all. `flycheck-phpstan' was therefore unusable with `(phpstan-executable . docker)'; `flymake-phpstan' worked only for modified buffers, because it normalized the temporary file itself while passing an unnormalized path for the unmodified case. Normalize the paths in `phpstan-get-command-args', which is the one place that already knows about the mount point, and drop the now-redundant call in `flymake-phpstan--create-temp-file' so the translation lives in a single place. The `--tmp-file' branch needs more than translation: `:temp-file' creates the copy in the system temporary directory, which is not mounted into the container and hence invisible to it. Use the in-place copy instead when running containerized, so the file lands inside the project. That branch is only reachable for containers with `phpstan-activate-editor-mode' set to `enabled', since version detection currently disables editor mode for them, but it is wrong either way. --- flymake-phpstan.el | 5 +++-- phpstan.el | 24 ++++++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/flymake-phpstan.el b/flymake-phpstan.el index 92d0d73..ec93028 100644 --- a/flymake-phpstan.el +++ b/flymake-phpstan.el @@ -95,8 +95,9 @@ (defun flymake-phpstan--create-temp-file () "Create temp file and return the path." - (phpstan-normalize-path - (flymake-proc-init-create-temp-buffer-copy 'flymake-proc-create-temp-inplace))) + ;; `phpstan-get-command-args' translates the path for the container mount + ;; point, so return the path as it is on this side. + (flymake-proc-init-create-temp-buffer-copy 'flymake-proc-create-temp-inplace)) (defun flymake-phpstan (report-fn &rest _ignored-args) "Flymake backend for PHPStan report using REPORT-FN." diff --git a/phpstan.el b/phpstan.el index a375387..aadc5f7 100644 --- a/phpstan.el +++ b/phpstan.el @@ -568,15 +568,27 @@ it returns the value of `SOURCE' as it is." (phpstan-use-xdebug-option (list "--xdebug"))) options (when editor - (let ((original-file (plist-get editor :original-file))) + (let* ((original-file (plist-get editor :original-file)) + ;; PHPStan may see the project through a mount point, so + ;; every path handed to it has to be translated, exactly + ;; like the config file above. + (target-file (phpstan-normalize-path original-file))) (cond ((funcall (plist-get editor :analyze-original) original-file) - (list "--" original-file)) + (list "--" target-file)) ((phpstan-editor-mode-available-p (car (phpstan-get-executable-and-args))) - (list "--tmp-file" (funcall (plist-get editor :temp-file)) - "--instead-of" original-file - "--" original-file)) - ((list "--" (funcall (plist-get editor :inplace))))))) + ;; A container only sees the project, so the temporary copy + ;; has to be created inside it. `:temp-file' puts it in the + ;; system temporary directory, which is not mounted. + (let ((temp-file (funcall (plist-get editor + (if (phpstan--container-executable-p) + :inplace + :temp-file))))) + (list "--tmp-file" (phpstan-normalize-path temp-file) + "--instead-of" target-file + "--" target-file))) + ((list "--" (phpstan-normalize-path + (funcall (plist-get editor :inplace)))))))) (if editor args (cons "--" args))))) (defun phpstan-update-ignorebale-errors-from-json-buffer (errors) From 47f4bd80119fd550813381e4c2a8ecd1d99ec5be Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Fri, 17 Jul 2026 21:51:48 +0900 Subject: [PATCH 2/3] Detect the JSON report anywhere in the output, not just at the start `phpstan--parse-json' deliberately skips everything before the first line starting with `{', so that whatever the process wrote to STDERR is ignored -- the checker process merges STDERR into STDOUT. Its caller disagreed: it only chose the JSON branch when the *whole* output started with `{'. Docker happens to stay quiet once the image is local, so this went unnoticed, but Apple container reports its progress on STDERR for every run: [0/6] [0s] [1/6] Fetching image [0s] ... That prefix meant the JSON was never parsed, and every check came back with no errors. Look for a line starting with `{' instead, which is the same condition `phpstan--parse-json' then acts on. --- flycheck-phpstan.el | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/flycheck-phpstan.el b/flycheck-phpstan.el index f590551..354e9db 100644 --- a/flycheck-phpstan.el +++ b/flycheck-phpstan.el @@ -73,7 +73,12 @@ (erase-buffer) (insert output) (current-buffer))) - (data (if (string-prefix-p "{" output) + ;; Match `phpstan--parse-json', which skips everything before the + ;; first line starting with `{' so that output written to STDERR is + ;; ignored. Anchoring at the start of OUTPUT instead would miss the + ;; JSON whenever the runtime prefixes it, as Apple container does + ;; with its progress report. + (data (if (string-match-p "^{" output) (phpstan--parse-json json-buffer) (list (flycheck-error-new-at 1 1 'warning (string-trim output))))) (errors (phpstan--plist-to-alist (plist-get data :files)))) From abaaca500e80c5f364f561ac7511f93e2c8f09d8 Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Fri, 17 Jul 2026 21:52:58 +0900 Subject: [PATCH 3/3] Report PHPStan failures instead of discarding them When the output held no JSON report, `flycheck-phpstan-parse-output' built a warning carrying the raw output -- and then dropped it on the floor: (data (if json? (phpstan--parse-json ...) (list (flycheck-error-new-at 1 1 'warning ...)))) (errors (phpstan--plist-to-alist (plist-get data :files))) In the fallback branch DATA is a list of `flycheck-error', so `plist-get' found no `:files' and returned nil, and the warning never reached flycheck. Every failure that stopped PHPStan from producing a report -- a broken configuration file, a bad `-c' path, a crash -- was shown to the user as a buffer with no errors. Return the warning directly. With a config file PHPStan cannot use, the checker now says Bootstrap file /.../tests/bootstrap.php does not exist. instead of nothing at all. --- CHANGELOG.md | 4 ++++ flycheck-phpstan.el | 37 ++++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f45a41..a586868 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ All notable changes of the `phpstan.el` are documented in this file using the [K * Fix `phpstan-executable` in the `(STRING . (ARGUMENTS ...))` form dropping the command name, which made the first *argument* run as the program (`("docker" "run" ...)` executed `run`). * Fix `phpstan-get-command-args` destructively modifying its inputs with `nconc`. Each call appended the PHPStan arguments onto the caller's own list, growing `phpstan-executable` in the `(STRING . (ARGUMENTS ...))` form on every check, and appending `"--"` to `phpstan-generate-baseline-options` on every `phpstan-generate-baseline`. * Fix Flycheck getting stuck on a syntax check when PHPStan reported no files to analyse in a modified buffer. The check now finishes with an empty result instead of never reporting a status. +* Fix the analyzed file being passed to a containerized PHPStan as a host path. `flycheck-phpstan` reported no errors at all with `(phpstan-executable . docker)`, because PHPStan answered `Path /Users/... does not exist`. `flymake-phpstan` was affected for unmodified buffers. +* Fix the `--tmp-file` copy being created in the system temporary directory when running containerized, where the container cannot see it. +* 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. +* 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. ## [0.9.0] diff --git a/flycheck-phpstan.el b/flycheck-phpstan.el index 354e9db..c8af850 100644 --- a/flycheck-phpstan.el +++ b/flycheck-phpstan.el @@ -69,23 +69,26 @@ ;; Parsing PHPStan output: (defun flycheck-phpstan-parse-output (output &optional _checker _buffer) "Parse PHPStan errors from OUTPUT." - (let* ((json-buffer (with-current-buffer (flycheck-phpstan--temp-buffer) - (erase-buffer) - (insert output) - (current-buffer))) - ;; Match `phpstan--parse-json', which skips everything before the - ;; first line starting with `{' so that output written to STDERR is - ;; ignored. Anchoring at the start of OUTPUT instead would miss the - ;; JSON whenever the runtime prefixes it, as Apple container does - ;; with its progress report. - (data (if (string-match-p "^{" output) - (phpstan--parse-json json-buffer) - (list (flycheck-error-new-at 1 1 'warning (string-trim output))))) - (errors (phpstan--plist-to-alist (plist-get data :files)))) - (unless phpstan-disable-buffer-errors - (phpstan-update-ignorebale-errors-from-json-buffer errors)) - (phpstan-update-dumped-types errors) - (flycheck-phpstan--build-errors errors))) + ;; Look for a line starting with `{', the same condition + ;; `phpstan--parse-json' acts on: it skips everything before that line so + ;; that output written to STDERR is ignored, since the checker process + ;; merges STDERR into STDOUT. Anchoring at the start of OUTPUT instead + ;; would miss the JSON whenever the runtime prefixes it, as Apple container + ;; does with its progress report. + (if (not (string-match-p "^{" output)) + ;; PHPStan produced no report at all, so OUTPUT is a failure of some + ;; kind. Surface it rather than reporting a clean buffer. + (list (flycheck-error-new-at 1 1 'warning (string-trim output))) + (let* ((json-buffer (with-current-buffer (flycheck-phpstan--temp-buffer) + (erase-buffer) + (insert output) + (current-buffer))) + (data (phpstan--parse-json json-buffer)) + (errors (phpstan--plist-to-alist (plist-get data :files)))) + (unless phpstan-disable-buffer-errors + (phpstan-update-ignorebale-errors-from-json-buffer errors)) + (phpstan-update-dumped-types errors) + (flycheck-phpstan--build-errors errors)))) (defun flycheck-phpstan--temp-buffer () "Return a temporary buffer for decode JSON."