Skip to content

feat: split Swoole adapters, add compression, adopt utopia-php/servers#230

Open
ChiragAgg5k wants to merge 3 commits into0.34.xfrom
feat/swoole-adapters-and-compression
Open

feat: split Swoole adapters, add compression, adopt utopia-php/servers#230
ChiragAgg5k wants to merge 3 commits into0.34.xfrom
feat/swoole-adapters-and-compression

Conversation

@ChiragAgg5k
Copy link
Member

Summary

  • Split Swoole adapter into two: Swoole\Server now uses Swoole\Http\Server (SWOOLE_PROCESS mode) for traditional multi-process serving, while the new SwooleCoroutine\Server uses Swoole\Coroutine\Http\Server for coroutine-based serving
  • Add response compression support with configurable minimum size and algorithm selection via setCompression(), setCompressionMinSize(), and setCompressionSupported() on the Http class
  • Migrate Hook to utopia-php/servers: removed local Hook class; Route now extends Utopia\Servers\Hook
  • Add View class for template rendering with nested view support and content-type helpers
  • Enhance Request with trusted IP header support, IP validation, and cookie parsing improvements
  • Enhance Response with cookie management, content-type helpers, chunked transfer encoding, and compression integration
  • Add dependencies: utopia-php/servers (0.3.) and utopia-php/compression (0.1.)
  • Fix test infrastructure: Swoole test server no longer wraps start() in Coroutine\run() (incompatible with SWOOLE_PROCESS mode), and http_parse_cookie is disabled on both Swoole adapters to preserve raw Cookie headers

Test plan

  • All 95 PHPUnit tests pass (unit + e2e for both FPM and Swoole)
  • Verify SwooleCoroutine adapter works with coroutine-based server setup
  • Verify compression works end-to-end with supported algorithms

…a-php/servers

- Split Swoole adapter into Swoole (SWOOLE_PROCESS) and SwooleCoroutine (coroutine-based) servers
- Add response compression support with configurable min size and algorithm selection
- Migrate Hook to utopia-php/servers and Route now extends Servers\Hook
- Add View class for template rendering
- Add trusted IP header support and IP validation in Request
- Enhance Response with cookie management, content-type helpers, and chunked transfer
- Add utopia-php/servers and utopia-php/compression dependencies
- Fix server-swoole.php test server to work with non-coroutine Swoole adapter
- Disable Swoole cookie parsing to preserve raw Cookie headers
@greptile-apps
Copy link

greptile-apps bot commented Mar 19, 2026

Greptile Summary

This PR is a broad feature branch that splits the monolithic Swoole adapter into a process-based (Swoole\Server) and a coroutine-based (SwooleCoroutine\Server) variant, adds response compression via utopia-php/compression, migrates Hook to utopia-php/servers, introduces a View template engine, and enriches Request/Response with trusted-IP support, cookie management, and chunked transfer encoding.

Key issues found during review:

  • Bug – removeCookie() silently fails (src/Http/Response.php): addCookie() lowercases cookie names before storing them, but removeCookie() compares against the raw caller-supplied name. The comparison will never match, so cookies are never actually removed.
  • Bug – duplicate Content-Length on compressed responses (src/Http/Response.php): addHeader() is accumulative. If a Content-Length header was set before send() is called, the compression block appends a second value, producing an invalid response.
  • Bug – getReferer() ignores $default (src/Http/Adapter/Swoole/Request.php): The $default parameter is declared but hardcoded to '' in the getHeader() call, breaking the interface contract.
  • Behavioural inconsistency – SwooleCoroutine\Server::onStart() (src/Http/Adapter/SwooleCoroutine/Server.php): The callback is invoked synchronously before the coroutine event loop starts, while the Swoole\Server counterpart runs it inside a coroutine. Any start hook relying on coroutine-context I/O will fail on the coroutine adapter.
  • Style – View::render() unhandled preg_replace null return (src/Http/View.php): On a PCRE error, preg_replace() returns null, which causes a TypeError in the subsequent str_replace calls on PHP 8.x.

Confidence Score: 3/5

  • Not safe to merge as-is; two confirmed bugs in the new Response cookie and compression logic, and a behavioural inconsistency in the new coroutine adapter that is also unverified per the PR's own test plan.
  • The PR introduces two concrete bugs in Response (cookie removal case mismatch and potential duplicate Content-Length), a parameter-contract violation in the Swoole Request adapter, and a coroutine adapter whose onStart behaviour diverges from the process-based adapter in a way that can silently break start hooks. Two items in the test plan are also left unchecked, indicating the coroutine adapter and compression pipeline have not been fully verified end-to-end.
  • Pay close attention to src/Http/Response.php (cookie management and compression headers) and src/Http/Adapter/SwooleCoroutine/Server.php (onStart coroutine context).

Important Files Changed

Filename Overview
src/Http/Response.php Major additions: compression pipeline, chunked transfer encoding, cookie management, and content-type helpers. Two bugs found: removeCookie() silently fails due to case mismatch with addCookie(), and the compression block can produce a duplicate Content-Length header if the caller pre-sets one.
src/Http/Adapter/Swoole/Server.php Split from the combined adapter to serve only Swoole\Http\Server in SWOOLE_PROCESS mode. Coroutine context handling via Coroutine::getContext() is safe because Swoole HTTP server callbacks run in coroutines by default. http_parse_cookie correctly disabled.
src/Http/Adapter/SwooleCoroutine/Server.php New coroutine-based adapter using Swoole\Coroutine\Http\Server. onStart() calls the callback synchronously before the event loop starts, which is behaviourally inconsistent with the process-based Swoole adapter and could break start hooks that expect a coroutine context. Verification of the coroutine adapter is also listed as unchecked in the PR test plan.
src/Http/Adapter/Swoole/Request.php Added trusted-IP-header support with IP validation, and manual cookie parsing from the raw Cookie header (correctly handling the http_parse_cookie being disabled). Bug: getReferer() ignores its $default parameter and always returns ''.
src/Http/Http.php Added compression configuration (setCompression, setCompressionMinSize, setCompressionSupported), onStart/onRequest hook registration, and wires compression settings into Response in run(). Minor: setCompression() lacks a return type, unlike its sibling setters.
src/Http/View.php New template-rendering class with filter support, HTML minification, and nested view composition. preg_replace() in render() can return null on PCRE error, which would cause a TypeError in the subsequent str_replace calls on PHP 8.x.
src/Http/Route.php Now extends Utopia\Servers\Hook instead of the removed local Hook class. Logic is unchanged; the migration is clean.
src/Http/Adapter/FPM/Request.php Added trusted-IP-header support mirroring the Swoole adapter. Implementation is consistent and correct.
src/Http/Adapter/SwooleCoroutine/Request.php Thin subclass that re-uses the Swoole adapter's Request implementation unchanged. The inherited getReferer() bug applies here too.
src/Http/Adapter/SwooleCoroutine/Response.php Thin subclass that re-uses the Swoole adapter's Response implementation. All Response.php bugs (removeCookie, Content-Length) apply here via inheritance.
composer.json Added utopia-php/servers and utopia-php/compression as required dependencies. Moved Tests\\E2E\\ namespace to autoload-dev. The file has significant trailing whitespace on the autoload block lines, which may cause lint failures.
tests/e2e/server-swoole.php Removed the Coroutine\run() wrapper (incompatible with SWOOLE_PROCESS mode) and updated to use the new Swoole\Adapter\Server. The test server setup is correct.

Comments Outside Diff (2)

  1. src/Http/Response.php, line 573-579 (link)

    P1 Case mismatch: removeCookie() will silently fail

    addCookie() lowercases the cookie name before storing it (line 550: $name = strtolower($name);), but removeCookie() compares against the raw, un-lowercased name passed by the caller. This means calling removeCookie('MySession') after addCookie('MySession', ...) will never remove the cookie because 'mysession' !== 'MySession'.

  2. src/Http/Adapter/Swoole/Request.php, line 207-210 (link)

    P1 $default parameter is silently ignored

    The $default parameter is declared in the signature but hardcoded to '' in the getHeader call, so the caller's default is never honoured. This matches the same bug in the parent abstract contract where the interface guarantees the default is returned when the header is absent.

Last reviewed commit: "feat: split Swoole a..."

ChiragAgg5k added a commit that referenced this pull request Mar 19, 2026
- Remove Content-Length before re-adding after compression to prevent duplicate headers
- Defer onStart callback into coroutine event loop for SwooleCoroutine adapter consistency
- Add null-coalescing fallback for preg_replace in View::render
- Add void return types to compression setter methods for API consistency
@ChiragAgg5k ChiragAgg5k force-pushed the feat/swoole-adapters-and-compression branch from 01c863f to 78ff103 Compare March 19, 2026 08:30
ChiragAgg5k added a commit that referenced this pull request Mar 19, 2026
- Remove Content-Length before re-adding after compression to prevent duplicate headers
- Defer onStart callback into coroutine event loop for SwooleCoroutine adapter consistency
- Add null-coalescing fallback for preg_replace in View::render
- Add void return types to compression setter methods for API consistency
ChiragAgg5k added a commit that referenced this pull request Mar 19, 2026
- Remove Content-Length before re-adding after compression to prevent duplicate headers
- Defer onStart callback into coroutine event loop for SwooleCoroutine adapter consistency
- Add null-coalescing fallback for preg_replace in View::render
- Add void return types to compression setter methods for API consistency
@ChiragAgg5k ChiragAgg5k force-pushed the feat/swoole-adapters-and-compression branch from 78ff103 to 94a3af2 Compare March 19, 2026 08:32
- Remove Content-Length before re-adding after compression to prevent duplicate headers
- Defer onStart callback into coroutine event loop for SwooleCoroutine adapter consistency
- Add null-coalescing fallback for preg_replace in View::render
- Add void return types to compression setter methods for API consistency
@ChiragAgg5k ChiragAgg5k force-pushed the feat/swoole-adapters-and-compression branch from 94a3af2 to 578cdec Compare March 19, 2026 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant