swoole/ide-helper
Fully documented IDE stub files for the Swoole PHP extension, giving PhpStorm and VS Code accurate autocompletion, parameter hints, and inline docs for every class, method, function, and constant. Install as a dev dependency and match the version to your Swoole release.
This package is IDE-only scaffolding for the swoole PHP extension. It does nothing at runtime, so the first step is confirming you actually have the extension — and that you actually write code against it. For a Laravel developer that usually means Laravel Octane on the Swoole driver, or a Hyperf-adjacent service. If your stack is PHP-FPM or Octane-on-RoadRunner, stop here; the package will earn nothing.
Confirm the runtime version, because the helper's version must match it:
php --ri swoole | grep Version # e.g. Version => 6.0.2
composer require --dev swoole/ide-helper:~6.0.2
Pin the line, not a caret range. ~6.0.2 keeps you on 6.0.x; ^6.0 would silently pull 6.1/6.2 stubs whose signatures no longer describe your binary.
Where to look first:
vendor/swoole/ide-helper/src/swoole/ — one file per class, e.g. Swoole/Coroutine/Http/Client.php. This is the fastest reference for parameter names and associative-option arrays, which the official docs describe in prose but rarely name.vendor/swoole/ide-helper/src/swoole_library/ — the userland PHP that ships inside the extension (loaded when swoole.enable_library=On). Real source, not stubs.vendor/swoole/ide-helper/src/swoole/shortnames.php — the definitive list of Co\* aliases.After composer require, run File → Invalidate Caches / Restart in PhpStorm, then Settings → PHP → Stubs and deselect swoole. PhpStorm bundles its own thinner stubs; leaving them enabled gives you "multiple definitions exist" on every Swoole class.
First useful moment: open an Octane listener or a job that uses Swoole\Coroutine\Channel or Swoole\Timer, and Ctrl+Click a method. You land in the stub with full PHPDoc — that navigation alone is worth the install, because it is the only place the extension's API exists as readable code.
Write against the stubs, not the docs, when building coroutine plumbing. Octane gives you the request lifecycle for free, but concurrency is still hand-rolled: channels, wait groups, timers. Type-hint the stub classes in your own code and let completion drive:
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
use Swoole\Coroutine\WaitGroup;
function fanOut(array $urls): array
{
$wg = new WaitGroup();
$out = [];
foreach ($urls as $i => $url) {
$wg->add();
Coroutine::create(function () use ($wg, $url, $i, &$out) {
try {
$out[$i] = Http::get($url); // autocompleted signature + return type
} finally {
$wg->done();
}
});
}
$wg->wait();
return $out;
}
Use Swoole\Table / Swoole\Atomic for cross-worker state and let the stubs tell you the API, not the semantics. The stubs give you Table::create, column(), set(), incr() with correct types. They will not tell you that column sizes and row counts are fixed at create() time and that changing them requires a restart — that is runtime knowledge. Treat stubs as a signature reference, never as a behaviour spec.
Pick one naming style and enforce it. The stubs advertise both Swoole\Coroutine\Channel and Co\Channel. Mixed usage is a review smell and a latent class_exists() failure (see Gotchas). Choose the long form unless your team already standardised on shortnames, and add it to your PHP CS Fixer / Pint config or a CI grep.
Keep the extension and the helper in the same commit. In your Docker image, the extension is installed via pecl install swoole-6.0.2 (or a compiled layer); in composer.json, the helper is ~6.0.2. Put a comment next to the require, and if you use Renovate/Dependabot, group them so a bump to one is a signal to bump the other.
Production installs are unaffected — verify it once. composer install --no-dev --optimize-autoloader drops the package entirely; nothing references it. Run that build in CI at least once so nobody "fixes" a missing-class error by moving the helper into require.
Static analysis needs a decision, not a default. PHPStan/Psalm may already ship their own Swoole stubs; adding vendor/swoole/ide-helper/src to scanDirectories is generally fine (stub bodies are empty, so no false flow), but if you get duplicate-symbol errors, exclude the package and rely on the analyser's own stubs. Never require the stub files in a bootstrap or test harness.
Use the stubs as a documentation surface for your own wrappers. When you write a SwooleClientFactory or a CoroutineAwareJob, put @param Swoole\Coroutine\Channel $channel in the docblock. The IDE resolves it, but more importantly the next developer gets a Ctrl+Click path straight into the extension's API.
Version drift is the whole risk. A 6.2 helper against a 5.1 extension means completion for methods that do not exist. Because nothing fails at build time, you discover it in a debugger. Check both sides whenever you touch a base image or a lockfile.
swoole.use_shortname can only be set in php.ini — not at runtime, not in .env, not by Octane. The stubs index Co\Channel unconditionally, so code that autocompletes cleanly can fatal in a container where the directive is Off. Diagnose with:
php -r 'var_dump(ini_get("swoole.use_shortname"), class_exists("Co\\Channel"));'
If it prints ""/false, either enable it in the image's php.ini or ban shortnames in review.
swoole.enable_library=Off hides the Swoole Library classes entirely. The src/swoole_library/ stubs exist regardless, so Swoole\Coroutine\FastCGI\Client, Swoole\Curl\Handler and friends will complete but not exist. Same fix: verify with class_exists() in the actual image, not on your laptop.
Do not composer require swoole/library alongside this. The extension already loads that code; adding the Composer package produces duplicate class declarations in the IDE index (harmless at runtime, noisy and confusing in the editor).
Compile flags change the surface. A build without OpenSSL, or without --enable-swoole-curl, is missing methods the stubs describe. Stubs model a fully-featured build. If a method "exists" per the IDE but throws at runtime, check php --ri swoole for the feature list before assuming a bug in your code.
Stubs make code look valid without the extension installed. On a Mac dev box without Swoole, everything type-checks; then the first coroutine call fatals. Keep at least one CI job that boots the real extension and runs Octane's test path — the helper should never be your only correctness signal.
@dev is not for teams. composer require --dev swoole/ide-helper:@dev tracks master, which documents unreleased APIs. Fine for exploring, wrong in a shared composer.lock.
Debugging "still undefined" in PhpStorm: confirm vendor/ isn't excluded, confirm the package is actually installed (composer show swoole/ide-helper), confirm the bundled swoole stub is deselected, then invalidate caches. In VS Code, Intelephense needs vendor indexed (it is by default) — a stale index after adding the package is usually fixed by restarting the language server.
There are no extension points, and that is deliberate. The package is generated stubs with empty bodies; there is nothing to configure or override. If you need to teach the IDE about dynamically returned Swoole objects, do it in your code with @method/@property docblocks or a small project-level stub file — don't fork this one.
How can I help you explore Laravel packages today?