PHP completion, type inference, and code navigation for GNU Emacs, with frontends for auto-complete and company-mode.
ac-php indexes project sources and Composer dependencies, then combines that index with native PHP types and PHPDoc annotations to provide completion and jump to definition. It supports both the Mago-based Rust tag generator and the bundled PHP generator.
| Package | Purpose | MELPA | MELPA Stable |
|---|---|---|---|
ac-php-core |
Shared indexing, type inference, completion, and navigation | ||
ac-php |
Completion source for auto-complete | ||
company-php |
Completion backend for company-mode |
The repository also includes helm-ac-php-apropros.el for searching the project
index with Helm. See the screenshots for examples.
- Installation
- Quick start
- Tag generators
- Project configuration and tags
- Completion and type inference
- Commands
- Project completion providers
- Troubleshooting
- Development
- License
- GNU Emacs 24.4 or later.
- php-mode and one of the completion frontends above.
- PHP CLI for the bundled
phpctagsbackend and common fallback tags outside a project.
MELPA installs the Emacs package dependencies for the frontend you choose,
including ac-php-core. The core uses s, f, popup, dash, and xcscope;
auto-complete also uses yasnippet. The external Cscope program is
optional.
The Mago generator is an optional native executable. Prebuilt binaries do not require Rust; see Tag generators for installation options.
Add MELPA to your Emacs configuration if it is not already configured:
(require 'package)
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/") t)
(package-initialize)For tagged releases, use MELPA Stable instead:
(require 'package)
(add-to-list 'package-archives
'("melpa-stable" . "https://stable.melpa.org/packages/") t)
(package-initialize)Run M-x package-refresh-contents, then install the frontend you want:
M-x package-install RET ac-php RETfor auto-complete.M-x package-install RET company-php RETfor company-mode.
See MELPA Getting Started for package archive setup details.
Choose a frontend, then initialize your PHP project.
Add this to your Emacs configuration:
(require 'php-mode)
(require 'ac-php)
(add-hook 'php-mode-hook
(lambda ()
(setq-local ac-sources '(ac-source-php))
(auto-complete-mode 1)
(yas-minor-mode 1)
;; Optional: show signatures in the echo area.
(ac-php-core-eldoc-setup)))Use M-x auto-complete to trigger completion manually.
Add this to your Emacs configuration:
(require 'php-mode)
(require 'company-php)
(add-hook 'php-mode-hook
(lambda ()
(setq-local company-backends
'((company-ac-php-backend company-dabbrev-code)
company-capf company-files))
(company-mode 1)
;; Optional: show signatures in the echo area.
(ac-php-core-eldoc-setup)))Use M-x company-complete to trigger completion manually.
Create a project marker at the root of your PHP project:
cd /path/to/project
touch .ac-php-conf.jsonOpen a PHP file in that project and run M-x ac-php-remake-tags. ac-php fills an
empty configuration file with defaults and builds the index in the background.
Wait for the successful re-indexing message before requesting project
completion.
A project containing either .projectile or vendor/autoload.php can also be
detected automatically. In that case, ac-php creates .ac-php-conf.json when it
reads the project configuration. See Project configuration and
tags to customize the files being indexed.
These bindings work with either frontend:
(with-eval-after-load 'php-mode
(define-key php-mode-map (kbd "M-]") #'ac-php-find-symbol-at-point)
(define-key php-mode-map (kbd "M-[") #'ac-php-location-stack-back))For Spacemacs integration, see its PHP layer.
Both generators write the ac-php-specific Emacs Lisp index used by completion and navigation. This is a separate format from standard ctags or etags files.
The ac-php-tags-backend variable selects the generator:
| Value | Behavior |
|---|---|
auto (default) |
Use Mago when ac-php-mago-tags-executable is executable; otherwise use the bundled PHP generator. |
mago |
Require the Mago executable configured by ac-php-mago-tags-executable. |
phpctags |
Always use the bundled PHP generator. |
The GitHub Releases include ac-php-mago-tags archives for these
platforms:
| System | Architecture | Archive suffix |
|---|---|---|
| Linux | x86_64 | linux-x86_64.tar.gz |
| Linux | ARM64 / aarch64 | linux-aarch64.tar.gz |
| macOS | Intel | macos-x86_64.tar.gz |
| macOS | Apple Silicon | macos-aarch64.tar.gz |
Download the archive for your platform and its accompanying SHA256SUMS file.
Verify the archive checksum, extract it, and install the executable in a
directory visible to Emacs. For example, for the v2.8.3 Linux x86_64 archive:
mkdir -p ~/.local/bin
tar -xzf ac-php-mago-tags-v2.8.3-linux-x86_64.tar.gz
install -m 755 \
ac-php-mago-tags-v2.8.3-linux-x86_64/ac-php-mago-tags \
~/.local/bin/ac-php-mago-tagsWith ac-php-mago-tags on Emacs's exec-path, the default auto backend selects
it automatically. To specify its location explicitly:
(setq ac-php-tags-backend 'mago
ac-php-mago-tags-executable "/path/to/ac-php-mago-tags")Building requires Rust 1.97 or later, as declared in Cargo.toml. From the repository root:
cargo build --manifest-path ac-php-mago-tags/Cargo.toml --release --lockedThe executable is written to
ac-php-mago-tags/target/release/ac-php-mago-tags. Install it as above or point
ac-php-mago-tags-executable to that file. See the generator
README for CLI usage and incremental cache details.
ac-php ships with its own modified phpctags; no separate download is needed. To select it explicitly:
(setq ac-php-tags-backend 'phpctags)Emacs must be able to locate PHP CLI. If necessary, set
ac-php-php-executable to the absolute path of your php executable.
.ac-php-conf.json identifies the project root and controls indexing. This
example scans PHP files recursively from the project root while excluding
vendor test files:
{
"tag-dir": null,
"filter": {
"php-file-ext-list": ["php"],
"php-path-list": ["."],
"php-path-list-without-subdir": [],
"ignore-ruleset": [
"/vendor/**/[tT]ests/**/*.php"
]
}
}| Field | Purpose |
|---|---|
tag-dir |
Override the tag output directory. null uses the default cache. Relative paths are resolved from the project root. |
filter.php-file-ext-list |
File extensions to index, without the leading dot. |
filter.php-path-list |
Directories to scan recursively, relative to the project root. |
filter.php-path-list-without-subdir |
Directories to scan without descending into subdirectories. |
filter.ignore-ruleset |
Exclusion rules using gitignore-style patterns. |
Parsed configuration is cached and reread only when its file signature changes.
For projects accessed through TRAMP, ac-php checks the configuration at most once
every five seconds, so repeated completion requests reuse it without fetching
the file. External configuration changes become visible on the next check.
Customize ac-php-remote-config-check-interval to adjust this delay, or set it to
zero to check on every lookup. Remote configuration files are never created or
populated automatically.
To include additional PHP library declarations, you can install PhpStorm stubs in the project:
composer require --dev jetbrains/phpstorm-stubsBy default, each project has its own directory:
~/.cache/ac-php/tags-<SHA-256>/
The digest is derived from the normalized project path. Customize
ac-php-tags-path to change the base cache directory, or set tag-dir in the
project configuration to choose a specific output directory. Both generators
use that resolved directory.
tags.el stores project symbols, and tags-vendor.el stores Composer dependency
symbols. With Mago, mago-cache/ also holds incremental per-file results.
When upgrading from the older path-based cache layout, ac-php creates a fresh
index in the new directory and leaves old cache files in place. Explicit
tag-dir settings continue to select the configured directory.
Run M-x ac-php-remake-tags after changing source files. To force a full rebuild,
run M-x ac-php-remake-tags-all.
Mago reuses unchanged file results. On normal incremental runs, it also reuses
the vendor index when composer.lock is unchanged and tags-vendor.el exists.
Use a full rebuild after editing vendor sources directly or changing indexing
filters. Indexing from a file inside vendor/ automatically forces a rebuild.
ac-php also requests an update when an index is older than
ac-php-auto-update-intval, which defaults to 3,600 seconds. Indexing runs in the
background, and different projects can be indexed independently.
Both frontends share the same core type resolution and completion logic. Completion covers classes, members, functions, constants, and variables, with support for namespaces, imports, inheritance, and chained member access.
ac-php uses native parameter, return, and property types, together with PHPDoc
annotations such as @param, @return, @var, @property, @property-read,
@property-write, and @method.
use Acme\Services\HelloService;
/**
* @property-read HelloService $hello
* @method HelloService createHello()
*/
class Example
{
public HelloService $service;
public function greet(HelloService $service): HelloService
{
return $service;
}
}
/** @var HelloService $hello */
$hello = create_service();Add PHPDoc when a native declaration or inferred expression does not provide enough type information.
Array key completion understands PHPStan array shapes, @phpstan-type, and
@phpstan-import-type, including aliases renamed with as and aliases referenced
by an imported shape.
/**
* @phpstan-type Options array{title?: string, capabilities?: array{export?: bool}}
*/
final class ConfigTypes {}
/** @phpstan-import-type Options from ConfigTypes as Overrides */
class BrokerConfig
{
/** @return Overrides */
protected static function get_overrides(): array
{
return ['capabilities' => ['export' => false]];
}
}Keys are completed in quoted array offsets and in array literals used as typed
call arguments, return values, or assignments annotated with @var. Completion
follows nested shapes, lists, and maps.
In an array literal key slot, completion also works before the opening quote.
Selecting a key inserts 'key' => and excludes keys already present in the
current array.
Imported source classes are resolved through project tags and PHP namespace or
use declarations. Classes in the same file or a neighboring ClassName.php
can also be read without tags. Jump to definition on an imported or renamed
alias opens its original @phpstan-type declaration.
Complete parameter names as name: in function, method, and constructor calls.
Parameters already supplied by name or position are excluded, and an existing
colon is preserved. Jump to definition on a named argument label opens the
matching parameter declaration.
PHPDoc can constrain an argument to a set of string values:
class Article
{
/** @param 'draft'|'ready' $status */
public function set_status($status) {}
public function publish()
{
$this->set_status('');
}
}Request completion inside the quoted argument to choose draft or ready.
This also supports string literal constraints on type parameters in magic
@method declarations.
Run these commands with M-x:
| Command | Description |
|---|---|
ac-php-remake-tags |
Update the project index incrementally. |
ac-php-remake-tags-all |
Force a full rebuild, including vendor tags. |
ac-php-find-symbol-at-point |
Jump to a symbol, named parameter, PHPDoc type alias, or included file. |
ac-php-location-stack-back |
Return to the previous location. |
ac-php-location-stack-forward |
Move forward through navigation history. |
ac-php-show-tip |
Show documentation for the symbol at point. |
ac-php-toggle-debug |
Toggle debug messages. |
helm-ac-php-apropos |
Search the project index with Helm. |
helm-ac-php-apropos-at-point |
Search with the symbol at point as the initial query. |
ac-php-show-tip and the documentation views in auto-complete and company-mode
include source PHPDoc below a compact signature, return type, visibility, and
declaring class. The symbol tooltip distinguishes the header elements with
colors on its normal background, keeps them on one logical line, and wraps when
space is limited. Headers omit the [system] and [user] origin labels.
PHPDoc displays remove common HTML markup and excess blank lines. Documentation
is read on demand from project or dependency sources, preferring unsaved Emacs
buffers. Inherited members use their declaring class's documentation; magic
members show the matching @method or @property annotation. Constructor
candidates show the constructor's PHPDoc, or the class PHPDoc when there is no
constructor. Symbols without available source keep their signature information.
@inheritdoc references are displayed as written rather than expanded.
The Helm commands require Helm and helm-ac-php-apropros.el. When loading it
explicitly, use (require 'helm-ac-php-apropros); the filename and feature name
retain that spelling.
Projects can add completion contexts through
ac-php-extra-completion-functions. Providers are tried until one returns a
non-nil result. Each provider receives tags-data:
- When
tags-datais nil, the frontend is only requesting the prefix. - Return nil when the provider is inactive, so normal completion can continue.
- Otherwise, return a plist with
:prefixand, when tags data is supplied,:candidates. Candidate strings may carry normal ac-php text properties.
The following sketch uses my-project-context-p as a placeholder for your own
context check:
(defun my-php-extra-completion (tags-data)
(when (my-project-context-p)
(if tags-data
(list :prefix "item"
:candidates (list (propertize "item_id"
'ac-php-return-type "int")))
(list :prefix "item"))))
(add-hook 'ac-php-extra-completion-functions
#'my-php-extra-completion)The core candidate API, company-mode, and auto-complete all use the same provider.
Create .ac-php-conf.json in the project root, then run
M-x ac-php-remake-tags from a PHP buffer inside the project. A .projectile
file or vendor/autoload.php is also sufficient for root detection.
Outside a recognized project, both rebuild commands report a user error and leave indexing available for other projects.
Older versions could leave ac-php-gen-tags-flag set after a missing-root
error, causing later rebuild commands to do nothing. After upgrading, restart
Emacs or reload the updated ac-php-core.el with M-x load-file. Evaluate
(symbol-file 'ac-php--remake-tags 'defun) to check which copy Emacs is using
if another installation still appears to be active.
Check that the correct frontend is enabled and the files you need are included
by the project configuration. Run M-x ac-php-remake-tags-all and wait for
indexing to finish.
If you selected Mago explicitly, ensure the configured executable is available
to Emacs. For the PHP backend, check ac-php-php-executable. A graphical Emacs
session may have a different executable search path from your shell.
Inspect *Messages* for indexing status and reported errors. To log the
generator command and output, enable M-x ac-php-toggle-debug and rerun
indexing. For parser errors, check the reported source location and rerun
indexing after correcting the problem.
When reporting an issue, include your Emacs and PHP versions, the selected tag backend and its version, your frontend configuration, relevant generator output, and a small PHP example that reproduces the behavior. See this example issue for a reproducible report.
The Cask development environment requires Emacs 25.1 or later. Install Cask, then run these commands from the repository root:
make init
make test
make buildmake init installs dependencies, make test runs Emacs's built-in ERT suite,
and make build byte-compiles the four Emacs Lisp modules. TESTFLAGS supplies
additional Emacs batch arguments; the old ert-runner reporter options are not
supported.
Dependencies are installed per Emacs major/minor version, for example
.cask/31.1/elpa. Run make init again after upgrading Emacs. Once the selected
version's dependencies are installed, tests and builds can reuse that local
directory without Cask; installing or updating dependencies still requires it.
The Makefile finds Emacs and Cask on PATH, with fallbacks to
/Applications/Emacs.app/Contents/MacOS/Emacs and ~/.cask/bin/cask. Override
locations when needed:
make init EMACS=/path/to/emacs CASK=/path/to/cask
make test EMACS=/path/to/emacs CASK=/path/to/caskCASK_EMACS defaults to the selected EMACS, keeping dependency installation
and tests on the same version.
To copy local sources into existing package installations, run ./cp2elpa.sh.
It scans ~/.emacs.d/elpa/*/*/ by default. To select a specific package directory:
PACKAGE_USER_DIR="$HOME/.emacs.d/elpa/31.1/develop" ./cp2elpa.shThe script updates installed packages and removes their stale bytecode. Restart
Emacs or use M-x load-file to reload the updated ac-php-core.el and installed
frontend files; copying files does not replace functions already loaded in memory.
From the repository root, run:
cargo fmt --manifest-path ac-php-mago-tags/Cargo.toml --all -- --check
cargo test --manifest-path ac-php-mago-tags/Cargo.toml --locked
cargo clippy --manifest-path ac-php-mago-tags/Cargo.toml --all-targets --locked -- -D warnings
cargo build --manifest-path ac-php-mago-tags/Cargo.toml --release --lockedFor generator changes, also generate tags from the example project and verify that Emacs can load both outputs:
ac_php_tags_dir="$(mktemp -d)"
cargo run --manifest-path ac-php-mago-tags/Cargo.toml --release --locked -- \
--workspace phptest \
--config-file phptest/.ac-php-conf.json \
--output-dir "$ac_php_tags_dir" \
--rebuild
emacs -Q --batch \
-l "$ac_php_tags_dir/tags-vendor.el" \
-l "$ac_php_tags_dir/tags.el"| Path | Purpose |
|---|---|
ac-php-core.el |
Shared implementation; maintained as a single file for package distribution. |
ac-php.el, company-php.el |
Frontend adapters and completion insertion. |
helm-ac-php-apropros.el |
Helm project index search. |
ac-php-mago-tags/ |
Rust tag generator. |
test/ |
ERT tests and fixtures. |
phptest/ |
Example PHP project and indexing inputs. |
See AGENTS.md for repository conventions, the core design
notes for implementation structure, and
RELEASING.md for the release process. Run git diff --check
before submitting changes.
ac-php is licensed under the GNU General Public License, version 3 or later.