Rust PHP Language Server (LSP 3.17) with a VS Code extension.
The extension requires Visual Studio Code 1.82 or newer.
php-lsp targets PHP 7.4-8.4 projects and provides indexed PHP language intelligence: diagnostics, hover, completion, navigation, references, rename, formatting integration, semantic tokens, hierarchy views, and built-in phpstorm-stubs support.
- Syntax diagnostics with incremental tree-sitter parsing.
- Semantic diagnostics for unknown classes, functions, imports, members, and duplicate top-level or class-member declarations.
- PHP-compatible name resolution across bracketed and unbracketed namespace sections, including mixed class/function/constant group imports, namespace-relative qualified names, and symbol-kind-specific casing and fallback rules.
- Member diagnostics for visibility, static/instance misuse, missing methods, missing properties, and missing class constants.
- Basic type compatibility checks for assignments, returns, arguments, properties, and member calls.
- Native type contracts remain authoritative when PHPDoc contradicts them; compatible generics/shapes refine inference, and proven annotation conflicts produce diagnostics.
- Best-effort PHPDoc template metadata, PHPStan/Psalm type aliases and imported aliases, and inherited generic member type substitution for common repository and collection patterns, including foreach values from PHPDoc-generic collection returns.
- Call-site inference for PHPStan/Psalm conditional return types and
class-string<T>factory/service-locator patterns in hover, completion chains, local variable type inlay hints, and DoctrinegetRepository<T>()/ repositoryfind*hovers. - Shape-aware inference for PHPDoc
array{...}/object{...}and literal array shapes in completion and key navigation. - Closure and arrow-function parameter inference from
callable(...)signatures, including generic map/filter-style collection callbacks andarray_map-style helpers. - Framework-aware static providers for common Laravel string keys, Symfony Twig template names, and Symfony route names without booting the application.
- Blade-like and Symfony/Twig template documents use virtual PHP plus source maps for conservative hover, completion, signature help, definition, type definition, implementation, inlay hints, diagnostics, and semantic tokens in supported template expressions and control blocks.
- Override signature and PHP-version compatibility diagnostics.
- Optional PHPStan and Psalm diagnostics through configured external commands.
- Per-category diagnostic severity controls for unknown symbols, unused code, duplicate symbols, members, type compatibility, override signatures, and PHP-version checks.
- Test-friendly diagnostics for common PHPUnit patterns, including assertion helpers, test doubles, trait-based test helpers, anonymous classes, and closure/destructuring variable scopes.
- Hover for symbols, source-like PHP signatures, linked FQN/source metadata, class and method-level relation links, template/generic bindings, Symfony/Doctrine framework roles, indexed PHP 8 attributes, complete parameter lists, types, variables, PHPDoc summaries/descriptions, deprecation, and PHPDoc virtual members, plus call-site-specialized generic return sections with clickable class links where the target can be resolved.
- Completion for classes, interfaces, traits, enums, functions, constants,
methods, properties, variables, namespaces, keywords, snippets, PHPDoc virtual
members, shape keys/properties, framework string keys, template paths, and
auto-import edits.
usecompletion selects class/function/constant candidates by import kind, including grouped imports; inherited member overrides do not repeat equivalent labels. Incomplete one-line$object->expressions remain usable for completion while tree-sitter diagnostics still report the incomplete PHP. - Completion resolve enriches PHPDoc virtual member completions.
- Native member completion uses class-scope visibility for other instances, named static access, inheritance, and nested trait consumption. Inherited private trait members stay hidden from subclasses; unrelated consumers do not share private access.
- Object member completion includes callable native static methods and honors
their class-scope access and private binding. Class constants, enum cases,
and static properties use
::completion. - Signature help for functions, methods, nullsafe calls and constructors, with CST-based argument tracking that ignores comment/string punctuation and supports recognizable incomplete calls.
- Inlay hints for argument labels, inferred PHPDoc parameter/return types, useful inferred local variable types, and end-of-scope labels for functions, methods, closures, and large type/control-flow scopes.
- Semantic tokens with full, delta, and range requests.
- Go to definition for indexed symbols, local variables,
$this, constructors, PHPDoc virtual members, PHPDoc/literal shape keys, static framework string keys, template paths, Symfony Twig route keys, and lazy vendor fallback. - Go to declaration for imports, with definition fallback.
- Go to type definition for inferred variables, members, function returns, and indexed symbol types.
- Go to implementation for interface/trait/base types and methods.
- Find references through indexed closed-file references, authoritative open PHP snapshots, and same-scope local variable references.
- Document highlight for local variables and non-local symbols.
- Selection ranges based on the parsed AST.
- Linked editing for namespace/use alias edits.
- Document links for statically resolvable
include/requirepaths.
- Nested document symbols for namespaces, types, and members, including signatures and deprecation tags.
- Ranked workspace symbol search over the indexed workspace.
- Call hierarchy for functions, methods, constructors, incoming calls, and outgoing calls.
- Type hierarchy for classes, interfaces, traits, enums, supertypes, and subtypes.
- Rename for classes, functions, methods, properties, constants, and local variables. Top-level rename updates import targets while preserving explicit aliases; destructive member edits require an exact or type-resolved receiver.
- Prepare rename rejects unsupported or built-in targets before editing.
- Quick fixes to import unresolved classes/functions, remove unused imports, apply diagnostic replacement metadata, and optionally map PHPStan/Psalm findings to local fixes.
- Source action to organize imports.
- Quick fix to implement missing interface, abstract parent, and abstract trait methods while preserving PHPDoc, analyzer tags, attributes, visibility, static, params, defaults, and native-safe return types.
- Refactor actions to generate constructors and property getters/setters from indexed properties.
- Refactor actions to change member visibility and promote simple constructor assignments to constructor property promotion.
- Refactor action to synchronize PHPDoc
@paramand@returntags from function/method signatures while preserving richer analyzer-specific tags. - Refactor action to add return types from PHPDoc when supported by the target PHP version.
- Refactor actions to extract selected expressions to local variables, extract class-scope literals to constants, and inline simple same-block local variables.
- Heavy refactor edits use
codeAction/resolveso initial code-action requests stay lightweight. - Document formatting, range formatting, and on-type formatting through
auto-detected or configured external formatters (
pint,php-cs-fixer,phpcbf, or a custom command).
- Status bar popup with indexing status, file/percentage progress, symbol count, stubs information, active diagnostics/analyzers, formatter, include paths, and server binary details.
- Code lenses with reference counts.
- Folding ranges for PHP structures, comments, arrays, and blocks.
- Document formatting and range formatting through auto-detected or configured external tools.
- On-type indentation edits for newline, semicolon, and closing brace.
- Running
php-lspwithout a subcommand starts the LSP server on stdio. php-lsp init-configcreates a starter.php-lsp.tomlfile.php-lsp analyze [PATH]runs the same parser, workspace index, and built-in diagnostics pipeline from the command line.analyzesupports--project-root <DIR>,--severity <all|hint|info|warning|error>, and--format <table|json|github>.- Analyze output is available as a local table, stable JSON for scripts, or GitHub workflow annotations.
php-lsp fix [PATH] --dry-runpreviews safe native fixes without writing files.fixsupports repeated--rulevalues forunused-imports,organize-imports, andadd-return-type, plus--format <table|json>.
- Initialization options and runtime configuration updates through
workspace/didChangeConfiguration. - Composer autoload support for PSR-4, PSR-0, classmap, and files entries.
- Additional include and exclude paths from extension configuration.
- Built-in phpstorm-stubs bundle with configurable extension stubs.
- Lazy
vendor/indexing. - Multi-root workspace support with resource-scoped PHP version, diagnostics, Composer/vendor, include/exclude, stubs, formatter, analyzer, and project command-trust settings selected independently for each workspace folder.
- Watched PHP file changes and LSP file-operation notifications.
- Create/change/delete PHP file events reindex or remove symbols from the workspace index.
- Rename notifications remove old-URI state and reindex or reclassify PHP/Blade destinations.
- Open editor buffers remain authoritative over background cache, watcher, and workspace-index results. Closing an ordinary PHP buffer rebuilds its index entry from the saved on-disk file unless the document has already been reopened.
- The latest large-workspace production baseline remains the IDE intelligence
refresh (2026-05-28). It measures a primary 10k-file Symfony workspace and
two additional Laravel-like workspaces. Later per-feature behavior is tracked
in
docs/lsp-features.md; remaining risk/watch items are tracked indocs/production-risk-register.mdanddocs/production-baseline.md. - Workspace, stub, and lazy vendor file symbols are cached in separate disk namespaces with mtime, size, and content-hash validation. Lazy vendor files are persisted after the requested class is verified in the index; Composer vendor metadata is cached in memory with an LRU for lazy vendor symbols. The large-workspace baseline (2026-05-28) met the warm cache target, but that timing has not been rerun after source-provenance checks (schema 26), call-site metadata (schema 27), exact PHPDoc owner ranges (schema 28), native/PHPDoc type provenance (schema 29), qualified type/reference binding (schema 30), lexical variable/capture binding (schema 31), or constant-expression roles (current schema 32). Installed-vendor first-hit behavior remains a watch item.
references,rename, and reference-count code lenses merge indexed closed-file references with ordinary open PHP snapshots, but still iterate workspace reference sets and can be expensive on very large repositories.- Workspace indexing parses files through a bounded CPU-aware task queue; the
primary large-workspace indexing baseline is measured in
docs/production-baseline.md. - Heavy references/rename requests, background indexing, and external analyzers have cancellation coverage; incoming call hierarchy and reference-count code lenses can still scan indexed files and remain benchmark watch items.
- Rapid
didChangebursts still refresh parser/index state on each accepted edit, while diagnostics are debounced and version-checked. - Built-in stubs are configurable and version-filtered for supported phpstorm-stubs version-gating metadata. New metadata forms may require parser updates.
- Cross-file local variable analysis is intentionally limited; variable references and rename are local-scope oriented.
- Type inference includes common PHPDoc generic inheritance bindings,
class-string<T>call-site bindings, conditional return fallbacks, and Doctrine repository call-site bindings, class/file-scoped PHPStan/Psalm type aliases, callback parameter inference,Generator<TKey,TValue>foreach key/value inference, and best-effort PHPDoc/literal shapes, but it is still shallow compared with mature PHP static analyzers. - Built-in semantic diagnostics depend on indexed project and vendor symbols. If Composer/vendor metadata is absent, external framework classes can be reported as unknown; dynamic framework APIs such as some Eloquent relation members are best-effort.
- Template support is conservative. Blade-like and Twig documents use virtual
PHP plus source maps, not full template-engine implementations. Diagnostics
are best-effort and published only for exact source-mapped expression errors
plus conservative Twig delimiter/block syntax errors; generated virtual PHP,
incomplete/magic properties, uncertain ranges, runtime template inheritance,
macros/extensions, and framework container execution stay suppressed. Full
Twig semantics for filters, tests,
in, functions, macros, ternaries, null coalescing, and dynamic/bracket attribute access remain unsupported, but safe subexpressions are source-mapped where possible for hover, completion, signature help, definition, type definition, implementation, and inlay hints. Current supported Twig cases include simple member/root variables inside otherwise unsupported expressions, type-preservingslice/filterforeach inference, getter-backed property-style labels, static template-path and route-key navigation, static render-context inference, one-level include context, Doctrine collection items, Symfony form/app globals, PHPDoc/literal shape keys, and open-template context refresh after relevant PHP changes. Seedocs/lsp-features.mdfor the exact supported template surface. - The initial disk workspace scan currently treats an unopened
*.blade.phpfile as raw PHP because of its suffix. Opening or template-aware reindexing removes that raw PHP index entry; virtual template state is never added to the workspace index. Avoiding the initial raw entry is tracked as a follow-up correctness fix. - Diagnostics are optimized for editor feedback: file changes publish fast in-process diagnostics, while full diagnostics and optional external analyzer runs are used on open/save and reconfiguration.
- External PHPStan/Psalm diagnostics require those tools to be installed and configured by the workspace.
- Formatting is delegated to external tools; php-lsp auto-detects common Composer dev tools but does not implement or advertise a native PHP formatter provider.
The VS Code extension contributes these settings under phpLsp.*:
Runtime environment:
| Environment variable | Default | Description |
|---|---|---|
PHP_LSP_WORKER_THREAD_STACK_SIZE |
8388608 |
Tokio worker-thread stack size in bytes. Values below 1 MiB are ignored. Raising this can help unusually deep framework/type/template workloads, but the default is intended for normal editor use. |
RUST_LOG |
info when unset |
Startup server filter, including target-specific tracing directives. An explicit LSP logLevel overrides it until that setting is removed. |
| Setting | Default | Description |
|---|---|---|
phpLsp.enable |
true |
Enable the language server. |
phpLsp.phpVersion |
8.2 |
Target PHP version for diagnostics and version-aware refactors (7.4-8.4). |
phpLsp.serverPath |
"" |
Custom server binary path. Empty uses the bundled binary, then falls back to php-lsp from PATH if the bundled binary is missing. |
phpLsp.includePaths |
[] |
Additional relative or absolute directories/files to include in workspace indexing. |
phpLsp.excludePaths |
[] |
Relative or absolute directories/files to exclude from workspace indexing. |
phpLsp.indexing.maxFiles |
100000 |
Maximum unique matching files per traversal. Set 0 in trusted global/VS Code config to disable the cap. |
phpLsp.indexing.maxEntries |
1000000 |
Maximum filesystem entries admitted per traversal. Set 0 in trusted global/VS Code config to disable the cap. |
phpLsp.stubs.extensions |
All available stubs | PHP stub extension set to index from the bundled stubs. Leave unset to discover all extension directories; set [] to disable stubs. |
phpLsp.composer.enabled |
true |
Enable composer.json autoload indexing. |
phpLsp.indexVendor |
true |
Index vendor/ lazily. |
phpLsp.diagnostics.mode |
basic-semantic |
off, syntax-only, or basic-semantic. |
phpLsp.diagnostics.severity |
Category warnings | Per-category severity for unknownSymbols, unused, duplicateSymbols, members, typeCompatibility, overrideSignatures, and phpVersion; values are off, error, warning, information, or hint. |
phpLsp.diagnostics.memberTypeNodeBudget |
512 |
Relevant AST-node budget for expensive member/type diagnostics per file. Set 0 to disable the cap. |
phpLsp.diagnostics.partialAnalysisDiagnostic |
true |
Publish an informational diagnostic when member/type diagnostics are skipped by the budget. |
phpLsp.allowProjectCommands |
false |
Trust executable analyzer and formatter settings from .php-lsp.toml. Keep disabled for untrusted workspaces. |
phpLsp.formatting.provider |
auto |
auto, none, pint, php-cs-fixer, phpcbf, or custom. |
phpLsp.formatting.command |
"" |
Custom formatter command; use {file} for the temporary PHP file. |
phpLsp.formatting.timeoutMs |
30000 |
External formatter timeout per request. |
phpLsp.phpstan.enabled |
false |
Enable PHPStan diagnostics. |
phpLsp.phpstan.command |
vendor/bin/phpstan ... {file} |
PHPStan command that prints JSON output. |
phpLsp.phpstan.timeoutMs |
30000 |
PHPStan timeout per file. |
phpLsp.psalm.enabled |
false |
Enable Psalm diagnostics. |
phpLsp.psalm.command |
vendor/bin/psalm ... {file} |
Psalm command that prints JSON output. |
phpLsp.psalm.timeoutMs |
30000 |
Psalm timeout per file. |
phpLsp.analyzerCodeActions.enabled |
false |
Enable opt-in quick fixes for PHPStan and Psalm diagnostics when diagnostic metadata is available. |
phpLsp.trace.server |
off |
LSP transport trace: off, messages, or verbose. |
phpLsp.logLevel |
info |
Server log level: error, warn, info, debug, or trace. Changes apply immediately without restarting, using one level for the whole window. Removing the explicit setting restores the startup RUST_LOG filter. |
Shared project defaults can also be stored in .php-lsp.toml. Use
php-lsp init-config to create a default file without overwriting an existing
one. Config precedence is built-in defaults, global config, project config, then
explicit VS Code settings. Executable analyzer and formatter settings from
project config are ignored unless phpLsp.allowProjectCommands is enabled in
VS Code or allowProjectCommands = true is set in global php-lsp config. See
Configuration.
File and directory symlinks, including external targets, are followed deliberately. Physical file identities stop cycles and duplicate aliases while preserving the first deterministic logical URI. VS Code dynamically watches discovered external targets. If a traversal limit is reached, php-lsp retains a deterministic partial index and shows a warning in the status item; project config may lower the limits, while raising or disabling them requires trusted global or explicit VS Code configuration.
In a multi-root VS Code workspace, the extension sends one explicit-settings snapshot per workspace folder. A document uses the most specific containing folder; settings and trusted project commands from one root do not become defaults for another root. Files outside every workspace root use only global and client-wide fallback settings.
Example external diagnostics setup:
{
"phpLsp.phpstan.enabled": true,
"phpLsp.phpstan.command": "vendor/bin/phpstan analyse --error-format=json --no-progress --no-interaction {file}",
"phpLsp.psalm.enabled": true,
"phpLsp.psalm.command": "vendor/bin/psalm --output-format=json --no-progress {file}",
"phpLsp.analyzerCodeActions.enabled": true
}When the same analyzer settings are stored in project .php-lsp.toml, php-lsp
requires explicit workspace-command trust before it executes them. Prefer VS
Code user/workspace settings or global php-lsp config for commands in
repositories you do not fully trust.
Example external formatting setup:
{
"phpLsp.formatting.provider": "php-cs-fixer"
}Formatter resolution order:
- Explicit
phpLsp.formatting.*settings, global php-lsp config, or trusted.php-lsp.toml[formatting]values. - Composer metadata auto-detection from
require-dev/require:laravel/pint,friendsofphp/php-cs-fixer, thensquizlabs/php_codesniffer. - No formatting provider when no explicit provider or supported Composer tool is available.
External formatter commands are timeout-bound and cancelled when the document changes, closes, or a newer formatting request supersedes the old one. Range formatting stays conservative: php-lsp formats only the selected fragment via a temporary file and never silently formats the whole document for a range request.
php-lsp analyze [PATH] --project-root <DIR> --severity warning --format table
php-lsp fix [PATH] --dry-run --project-root <DIR> --rule unused-imports --format jsonPATH can be a PHP file or directory. When it is omitted, php-lsp analyzes the
effective project root. CLI commands load the same global/project
.php-lsp.toml configuration used by the language server for PHP version,
diagnostic mode/severity, Composer discovery, and include/exclude paths.
Recursive CLI discovery also applies indexing.maxFiles and
indexing.maxEntries: a configured cap produces an explicit partial-result
warning on stderr, while an internal traversal deadline aborts the command with
exit code 1 instead of silently returning an incomplete result.
Analyze exit codes:
| Code | Meaning |
|---|---|
0 |
No diagnostics at the requested severity. |
1 |
Execution or configuration error. |
2 |
Diagnostics were found. |
Output formats:
| Format | Use |
|---|---|
table |
Human-readable local output. |
json |
Stable machine-readable report with schemaVersion, summary, and diagnostics. |
github |
GitHub Actions workflow annotations. |
Fix dry-run mode:
php-lsp fixcurrently requires--dry-runand refuses to write files.- Without
--rule, it runs the preferred safe native fixers: unused imports and PHPDoc-derived return types that can be represented as native PHP return types for the configured PHP version. --rulecan be repeated. Supported values areunused-imports,organize-imports, andadd-return-type.- Exit code
0means no edits would be produced,1means execution or configuration error, and2means edits would be produced. - The fix command does not run project formatters.
- CI and local example scripts are documented in CLI And CI Usage.
The extension contributes these VS Code commands:
| Command palette title | Command ID | Behavior |
|---|---|---|
PHP: Show Language Server Status |
phpLsp.showStatus |
Opens the status quick pick with indexing, cache, stubs, diagnostics, formatter, analyzer, and server-binary details. |
PHP: Show Language Server Version |
phpLsp.showServerVersion |
Shows the initialized server name/version plus resolved binary, platform, stubs, cache roots, and last startup errors. |
PHP: Restart Language Server |
phpLsp.restartServer |
Restarts the client/server process and reuses the existing disk cache. |
PHP: Clear PHP LSP Cache and Restart |
phpLsp.clearCacheAndRestart |
Deletes cache directories for current workspace roots and discovered Composer roots, then restarts the server. |
- Architecture: server/client data flow, indexing, cache model, diagnostics pipeline, and runtime configuration behavior.
- Configuration:
.php-lsp.tomldiscovery, precedence, schema, and examples. - CLI and CI usage: GitHub Actions reporting and local CLI examples.
- LSP feature matrix: supported, partial, and unsupported LSP behavior.
- Test coverage baseline: feature × state × platform evidence, measured Rust coverage, client check scope, and reproduction.
- Performance guide: baseline methodology, profiling commands, cache interpretation, and production acceptance metrics.
- Production baseline: current measured validation and performance numbers.
- Production risk register: tracked production gaps and exit signals.
- Check
PHP: Show Language Server StatusorPHP: Show Language Server Versionfor the resolved server binary path, source, platform target, and last startup error. - If
phpLsp.serverPathis set, verify that it points to an executablephp-lspbinary. - If using the bundled binary, verify that your platform is one of
linux-x64,linux-arm64,darwin-x64,darwin-arm64,win32-x64, orwin32-arm64. - The published
linux-x64binary requires glibc 2.28 or newer and is smoke tested on Ubuntu 20.04. Alpine/musl is not a release target; on an unsupported libc, build a compatible binary and select it withphpLsp.serverPath. - If the bundled binary is absent and
phpLsp.serverPathis empty, the client triesphp-lspfromPATHand logs the selected source in the LSP output channel. - Set
"phpLsp.logLevel": "debug"and"phpLsp.trace.server": "messages"for more output.
- Use
PHP: Show Language Server Statusto inspect indexed file count, cache path, stubs path, include/exclude paths, and analyzer settings. - Add generated directories to
phpLsp.excludePaths. - Keep
phpLsp.indexVendorenabled for lazy vendor lookup, but exclude very large generated vendor subtrees if they are not useful. - Use
PHP: Clear PHP LSP Cache and Restartwhen changing branches, Composer metadata, stubs, or project layout and the disk cache looks stale.
- Ensure Composer metadata and
vendor/composer/installed.jsonare available when you want built-in diagnostics to resolve external framework symbols. - Set
"phpLsp.diagnostics.mode": "syntax-only"to keep only parser syntax diagnostics, plus conservative Twig syntax diagnostics for Twig documents. - Set
"phpLsp.diagnostics.mode": "off"to disable built-in diagnostics. - If a large file reports partial analysis, raise
"phpLsp.diagnostics.memberTypeNodeBudget"or set it to0to run member/type diagnostics without that cap. - Prefer per-category severity controls when only one category is noisy:
{
"phpLsp.diagnostics.severity": {
"members": "off",
"unused": "hint"
}
}- Enable the analyzer explicitly with
phpLsp.phpstan.enabledorphpLsp.psalm.enabled. - If the analyzer is enabled only in
.php-lsp.toml, also enablephpLsp.allowProjectCommandsafter you trust the workspace. - Make sure the configured command works from the workspace root and prints JSON.
- Keep
{file}in the command template unless the tool should receive the file path appended at the end. - Increase
phpLsp.phpstan.timeoutMsorphpLsp.psalm.timeoutMsfor slow projects.
- With the default
autoprovider, make sure Composerrequire-devincludeslaravel/pint,friendsofphp/php-cs-fixer, orsquizlabs/php_codesniffer. - Set
phpLsp.formatting.providerexplicitly topint,php-cs-fixer,phpcbf, orcustomto bypass auto-detection; set it tononeto disable formatting. - If a formatter command or executable provider is configured only in
.php-lsp.toml, enablephpLsp.allowProjectCommandsafter you trust the workspace. - For
custom, configurephpLsp.formatting.commandand include{file}where the temporary PHP file path should be inserted. - Ensure the formatter executable is available from the workspace root.
- Increase
phpLsp.formatting.timeoutMsif the external formatter times out on large files.
- Server: Rust (tokio + tower-lsp-server + tree-sitter-php)
- Client: VS Code extension (TypeScript + vscode-languageclient)
- Transport: stdio (JSON-RPC 2.0)
- Rust 1.85+ (
rustup update stable) - Node.js 20+ and npm
- Git (for submodules)
make # build server + client + stubs → .vsix
make install # build + install extension into VS Code
make check # run stubs, Rust, and TypeScript checks
make check-stubs # verify source and bundled phpstorm-stubs integritymake uses the host Rust target detected from rustc -vV, builds a release
server binary into client/bin/<platform>/, bundles phpstorm-stubs into
client/stubs/, builds the TypeScript extension, and packages a .vsix.
Available targets:
| Command | Description |
|---|---|
make / make all / make package |
Full build: server + client + stubs → .vsix |
make install |
Build and install .vsix into VS Code |
make server |
Build a release Rust binary for the detected host platform and copy it to client/bin/<platform>/ |
make server-all |
Build all configured targets when their Rust targets, linkers, and platform SDKs are already provisioned |
make package-all |
Build all configured targets, then package a local universal .vsix preflight; cross toolchains must be provisioned |
make client |
npm ci + build extension JS |
make stubs |
Init submodule + bundle phpstorm-stubs |
make check-stubs |
Verify source and bundled phpstorm-stubs have enough PHP files and required core stubs |
make check |
Stubs integrity, lint, and tests |
make test |
Run Rust tests |
make lint |
Rust format/Clippy plus VS Code engine, client lifecycle, and TypeScript checks |
make fmt |
Auto-format Rust code |
make release |
Read VERSION, patch package/Cargo versions, commit, force-update the release tag, and push |
make clean |
Remove all build artefacts |
Stubs submodule (server/data/stubs) is pulled automatically on first build if not initialized.
scripts/bundle-stubs.sh, make check-stubs, CI, and release packaging fail
if source or bundled stubs are missing, too small, or lack required core files.
make server-all and make package-all use plain Cargo through
scripts/build-server.sh --all for these VS Code platform directories:
linux-x64linux-arm64darwin-x64darwin-arm64win32-x64win32-arm64
These convenience targets do not install Rust targets, cross-linkers, or Apple and Windows SDKs, so they are only turnkey on a suitably provisioned build host. The GitHub release workflow's per-platform matrix is the canonical published build path.
Published Linux binaries are built from the GNU targets
(*-unknown-linux-gnu). The linux-x64 release binary targets glibc 2.28 and
is checked both for newer glibc symbol requirements and by execution on Ubuntu
20.04 before packaging. Alpine/musl is not part of the universal VSIX release
target set.
The glibc guarantee applies to the linux-x64 artifact produced by the
release workflow, which uses cargo-zigbuild with target
x86_64-unknown-linux-gnu.2.28. A normal host build from make server or
scripts/build-server.sh uses the installed native toolchain and does not by
itself establish that compatibility floor. Given a release-compatible binary,
validate it locally with:
scripts/check-linux-abi.sh client/bin/linux-x64/php-lsp 2.28
scripts/smoke-linux-x64-compat.sh client/bin/linux-x64/php-lsp 2.28The second command requires Docker and executes the binary in Ubuntu 20.04.
make release requires a clean working tree, reads the semver value from
VERSION, updates client/package.json, client/package-lock.json,
server/Cargo.toml, and server/Cargo.lock, commits those version changes
when needed, creates or updates tag v<VERSION>, then pushes main and the
tag to GitHub. That tag starts the release workflow, which builds and validates
the six binaries and the universal package. make package-all is an optional
local preflight, not the publication path. The workflow also publishes the
packaged extension to VS Marketplace when the VSCE_PAT repository secret is
configured.
cd server
cargo build --releasecd client
npm ci
npm run build# 1. Build server binary for current platform → client/bin/<platform>/
./scripts/build-server.sh
# 2. Bundle phpstorm-stubs → client/stubs/
./scripts/bundle-stubs.sh
# Optional: verify source and bundled stubs before packaging
make check-stubs
# 3. Package VSIX
mkdir -p target
cd client
npx @vscode/vsce package --no-dependencies --out ../target/php-lsp-host.vsix
# 4. From the repository root, inspect a Linux-x64 host-only package
cd ..
PHP_LSP_VSIX_PLATFORMS=linux-x64 \
scripts/smoke-vsix.sh target/php-lsp-host.vsixFor another host, replace linux-x64 with the platform directory created under
client/bin/. Packaged CLI execution is part of this smoke only for
linux-x64.
Without PHP_LSP_VSIX_PLATFORMS, scripts/smoke-vsix.sh validates the
universal release contract and requires binaries for all six published
platforms. The smoke also checks manifest/package-lock engine compatibility,
Blade/Twig language configuration files, bundled stubs, Node-shimmed
LanguageClient protocol activation/shutdown while reporting the declared VS
Code floor, watcher disposal, and the packaged CLI.
./scripts/build-server.sh x86_64-unknown-linux-gnu # specific target
./scripts/build-server.sh --all # configured targetsThese commands require the selected Rust targets and cross toolchains to be installed. They do not reproduce the release workflow's Linux ABI guarantee by themselves.
php-lsp/
├── Makefile # Build automation
├── server/ # Rust LSP server (Cargo workspace)
│ ├── data/stubs/ # phpstorm-stubs (git submodule)
│ └── crates/
│ ├── php-lsp-server/ # Main binary
│ ├── php-lsp-parser/ # tree-sitter PHP wrapper
│ ├── php-lsp-index/ # Symbol index
│ ├── php-lsp-completion/ # Completion engine
│ └── php-lsp-types/ # Shared types
├── client/ # VS Code extension (TypeScript)
├── images/ # README and marketplace media
├── scripts/ # Build helpers (build-server.sh, bundle-stubs.sh)
└── test-fixtures/ # Test PHP projects
MIT