MCPcopy Create free account
hub / github.com/PHPantom-dev/phpantom_lsp / run_phpstan

Function run_phpstan

src/phpstan.rs:145–215  ·  view source on GitHub ↗

Run PHPStan in editor mode on the given buffer content and return LSP diagnostics. `file_path` is the real path of the file on disk (used for the `--instead-of` flag). `content` is the current editor buffer (which may differ from the on-disk version). `workspace_root` is needed to run PHPStan from the project root directory so that it picks up `phpstan.neon` / `phpstan.neon.dist`.

(
    resolved: &ResolvedPhpStan,
    content: &str,
    file_path: &Path,
    workspace_root: &Path,
    config: &PhpStanConfig,
    cancelled: &std::sync::atomic::AtomicBool,
)

Source from the content-addressed store, hash-verified

143/// `workspace_root` is needed to run PHPStan from the project root
144/// directory so that it picks up `phpstan.neon` / `phpstan.neon.dist`.
145pub(crate) fn run_phpstan(
146 resolved: &ResolvedPhpStan,
147 content: &str,
148 file_path: &Path,
149 workspace_root: &Path,
150 config: &PhpStanConfig,
151 cancelled: &std::sync::atomic::AtomicBool,
152) -> Result<Vec<Diagnostic>, String> {
153 let timeout_ms = config.timeout.unwrap_or(DEFAULT_TIMEOUT_MS);
154 let timeout = Duration::from_millis(timeout_ms);
155 let memory_limit = config.memory_limit.as_deref().unwrap_or("1G");
156
157 // Write the buffer to a temp file. We use the system temp dir
158 // (not a sibling file) because PHPStan's --tmp-file is designed
159 // to work with arbitrary temp paths, and we avoid polluting the
160 // project directory.
161 let tmp = write_temp_file(file_path, content)?;
162 let tmp_path = tmp.path().to_path_buf();
163
164 // Build the PHPStan command.
165 //
166 // The file path is passed as a positional argument so that PHPStan
167 // only analyses this single file, not the entire project. Without
168 // it, PHPStan would analyse all paths from phpstan.neon, which can
169 // take minutes on large codebases. The `--tmp-file` / `--instead-of`
170 // flags tell PHPStan to substitute the file's content but do NOT
171 // limit the analysis scope.
172 let mut cmd = Command::new(&resolved.path);
173 cmd.arg("analyse")
174 .arg("--error-format=json")
175 .arg("--no-progress")
176 .arg("--no-ansi")
177 .arg(format!("--memory-limit={}", memory_limit))
178 .arg(format!("--tmp-file={}", tmp_path.display()))
179 .arg(format!("--instead-of={}", file_path.display()))
180 .arg(file_path)
181 .current_dir(workspace_root);
182
183 let result = run_command_with_timeout(&mut cmd, timeout, cancelled);
184
185 // NamedTempFile auto-deletes on drop — but we keep `tmp` alive
186 // until after we've consumed the command output.
187 let _ = &tmp;
188
189 match result {
190 Ok(output) => {
191 // PHPStan exit codes:
192 // 0 = no errors found
193 // 1 = errors found (this is the normal "has diagnostics" case)
194 // 2+ = internal error / misconfiguration
195 match output.code {
196 0 => Ok(Vec::new()),
197 1 => parse_phpstan_json(&output.stdout, file_path),
198 _ => {
199 // For exit code 2+, check if there's still usable JSON
200 // output (PHPStan sometimes returns code 2 with partial
201 // results). If parsing fails, report the error.
202 match parse_phpstan_json(&output.stdout, file_path) {

Callers 1

phpstan_workerMethod · 0.85

Calls 4

write_temp_fileFunction · 0.85
parse_phpstan_jsonFunction · 0.85
run_command_with_timeoutFunction · 0.70
is_emptyMethod · 0.45

Tested by

no test coverage detected