upscale/swoole-reflection
A niche PHP utility adding reflection support for Swoole classes, helping developers introspect Swoole objects and coroutine-related structures. Lightweight and low-profile, aimed at projects running Swoole or Laravel with Swoole.
This is a single-purpose, low-adoption package (2 stars, 0 dependents), so treat the source as the documentation. Budget 15 minutes to read src/ and tests/ before writing any code against it.
Install and verify the environment
composer require upscale/swoole-reflection
php -r "echo SWOOLE_VERSION, PHP_EOL;"
composer show upscale/swoole-reflection # note the exact version/tag
Confirm two things immediately:
Swoole\Http\Request or Swoole\Coroutine is version-sensitive. Check composer.json for ext-swoole constraints and any SWOOLE_VERSION guards in src/.composer.json and note the PSR-4 root (likely Upscale\SwooleReflection\ or similar). Everything else in this assessment assumes you've mapped the real entry class.First use case: inspect a live request inside a coroutine
The typical first win is dumping the internals of a Swoole request/response object that PHP's native reflection can't reach cleanly, or that var_dump truncates. Run it inside an actual Swoole HTTP handler, not the CLI — Swoole internal objects behave differently outside a coroutine/server context:
$server->on('request', function ($request, $response) {
// Confirm the exact API from src/ first — illustrative only.
$props = \Upscale\SwooleReflection\Reflector::properties($request);
error_log(print_r($props, true));
$response->end('ok');
});
If that works, you have a debugging tool. If you need route/attribute scanning (likely, given "web-server" in the description), jump to the WorkerStart pattern below — do not reflect per request.
1. Cache reflection metadata at worker boot, never per request. Reflection is cheap-ish but not free, and doing it inside a coroutine handler on every request is the fastest way to regress throughput. Build your class/property/method maps once in WorkerStart and store them in a plain array or a container singleton that survives the worker's lifetime.
$server->on('WorkerStart', function ($server, int $workerId) use ($container) {
$container->singleton('swoole.reflection', fn () => new Reflector());
$container->set('routes', RouteScanner::scan(__DIR__ . '/Controllers'));
});
2. Wrap it behind your own interface. With 2 stars and zero dependents, the API surface has no community stability guarantee. Bind it once:
final class SwooleInspector
{
public function __construct(private Reflector $reflector) {}
public function internals(object $swooleObj): array { /* delegate */ }
}
Now a breaking change or an abandoned package means editing one file, not fifty call sites — or forking it. Apache-2.0 makes forking trivially legal, and the package is small enough that a fork is often cheaper than adapting.
3. Use it to fake Swoole objects in tests. The highest-value day-to-day use: constructing Swoole\Http\Request / Response doubles with populated private state so you can unit-test handlers without booting a server. Build a test helper that returns a configured fake, and keep it in tests/Support/ — this is the pattern most likely to pay for the dependency on its own.
4. Debug middleware in non-production environments. A middleware that reflects the current request/coroutine context and logs it, gated on APP_DEBUG and SWOOLE_DEBUG:
if (! config('app.debug')) { return $next($request); }
$log->debug('swoole.request', ['internals' => $inspector->internals($request)]);
Never register this unconditionally — reflecting and serializing internals is slow and can leak client socket data into logs.
5. Attribute/route discovery, if that's the package's focus. Scan controllers once per worker, cache the resulting route table (in memory, or a cache file if startup cost matters), and invalidate on deploy. Do not rely on Swoole's hot-reload for this unless you've verified the worker actually re-runs your scan.
Swoole\Http\Request, Swoole\Coroutine, and friends can be renamed or restructured between Swoole 4.x, 5.x, and 6.x. Pin ext-swoole in CI (pecl install swoole-5.1.x) and run your reflection-dependent tests against that exact version.dev-main. Use "upscale/swoole-reflection": "dev-main#<commit-sha>" so an upstream force-push doesn't silently change behaviour between deploys.null or throws Error: Typed property must not be accessed before initialization. Wrap reads in try/catch and treat failures as "not available yet", not as bugs.setAccessible(true) is a no-op since PHP 8.1. Old examples call it everywhere; it's harmless, but don't assume it's doing anything. Access control on reflection reads was removed.WorkerStart. Cache metadata (class names, property maps), never instances.Co::getCid() > 0). A CLI smoke test that "works" may behave differently under Swoole\Http\Server. Always validate in the real server.(new \ReflectionClass($obj))->getProperties() and log both the Swoole version and the package version together — that pair is what you'll need when filing an issue upstream or deciding to fork.How can I help you explore Laravel packages today?