Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Http Laravel Package

windwalker/http

Windwalker HTTP is a PHP library for working with HTTP requests and responses, offering request/response objects, URI handling, input filtering, file uploads, and middleware for building and inspecting web application traffic with a clean, fluent API.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require windwalker/http

Two caveats before you start:

  • The repo you're looking at (ventoviro/windwalker-http) is a fork/vendor mirror. The package name you actually require is windwalker/http; if you pin the fork you may lag the mainline by several releases.
  • The last tagged release is August 2016. Treat this as a legacy dependency you inherit or maintain inside an existing Windwalker application, not something you reach for in a greenfield project. Check composer.json for the PHP constraint on the tag you pull — 2016-era Windwalker targets PHP 5.5+/7.0 and predates the PSR-15/PSR-17 ecosystem.

Where to look first

Path What lives there
src/Request/Request.php createFromGlobals(), superglobal-backed request object
src/Response/ Response, JsonResponse, HtmlResponse, TextResponse, RedirectResponse
src/Uri/ URI parsing/building, query-string handling
src/Stream/ Stream, StringStream, PhpInputStream
src/Transport/ Client-side senders (CurlTransport, SocketTransport, StreamTransport)
src/Output/ The piece that actually pushes headers + body to the SAPI

Read Request and Response end-to-end before anything else. The rest of the package is plumbing around those two objects.

First use case — a front controller

use Windwalker\Http\Request\Request;
use Windwalker\Http\Response\HtmlResponse;
use Windwalker\Http\Output\Output;

$request  = Request::createFromGlobals();
$response = new HtmlResponse('<h1>Hello</h1>');

(new Output())->respond($response);

That's the whole mental model: build a request from globals, produce a response object, hand it to Output. Everything else in the package exists to make those three steps testable, composable, and reusable on the client side.


Implementation Patterns

1. Routing on request data, not on superglobals

The main win over raw $_GET/$_POST is that the request object can be constructed in tests without touching globals.

$path   = $request->getUri()->getPath();
$method = $request->getMethod();

switch (true) {
    case $path === '/api/users' && $method === 'GET':
        return $this->listUsers($request);
    case $path === '/api/users' && $method === 'POST':
        return $this->createUser($request);
    default:
        return new TextResponse('Not Found', 404);
}

2. Typed input reading with defaults

Windwalker's input layer (in the 2.x line, Windwalker\Http\Input; in 3.x, getQuery()/getPost() on the request) gives you casted reads with defaults. Use the casted variants rather than casting by hand at every call site:

$page    = $input->getInt('page', 1);
$search  = $input->getString('q', '');
$filters = $input->getArray('filter', []);

Cast-at-the-boundary means your controller code never has to ask "is this a string or an int today?"

3. JSON APIs

use Windwalker\Http\Response\JsonResponse;

return new JsonResponse([
    'data'  => $users,
    'total' => $total,
]);

JsonResponse sets Content-Type: application/json for you and handles encoding. If you need control over encoding flags (unicode escaping, pretty-print in debug), pass them through rather than post-processing the body — the response owns its own serialization.

4. Redirects and status

use Windwalker\Http\Response\RedirectResponse;

return new RedirectResponse('/login', 302);

For error handling, prefer returning a response over header()/exit — the moment you call exit inside a controller you've broken the testability the package was bought for.

5. The client side (same abstractions, outbound)

This is the underrated half of the package. Transport lets you reuse the same Request/Response types for outbound calls:

use Windwalker\Http\Transport\CurlTransport;
use Windwalker\Http\Request\Request as HttpRequest;

$transport = new CurlTransport();
$response  = $transport->request(
    (new HttpRequest('https://api.example.com/users'))
        ->withMethod('GET')
        ->withHeader('Accept', 'application/json')
);

$status = $response->getStatusCode();
$body   = (string) $response->getBody();

The symmetry matters: you can unit-test the outbound layer by swapping in a stub transport that returns a canned Response.

6. Testing pattern

Never let createFromGlobals() into a test. Construct the request directly:

$request = (new Request())
    ->withMethod('POST')
    ->withUri(new Uri('/api/users'))
    ->withHeader('Content-Type', 'application/json');

$response = $controller->handle($request);

$this->assertSame(201, $response->getStatusCode());
$this->assertSame('application/json', $response->getHeaderLine('Content-Type'));

If createFromGlobals() is baked into your bootstrap, refactor so the front controller accepts an injected Request. That single change is what makes the package worth keeping.

7. Bridging into a non-Windwalker stack

If you're migrating off Windwalker, put an adapter at the edge rather than rewriting handlers:

// PSR-7 in, Windwalker in the middle, PSR-7 out
$psrResponse = $psr17Factory->createResponse($response->getStatusCode());
foreach ($response->getHeaders() as $name => $values) {
    $psrResponse = $psrResponse->withHeader($name, $values);
}
$psrResponse->getBody()->write((string) $response->getBody());

Keep the adapter thin and stateless so you can delete it later without touching business logic.


Gotchas and Tips

Mutability is version-dependent. Windwalker 2.x used setters (setStatus(), setHeader()) that mutate in place; the 3.x line moved toward PSR-7-style immutable with*() methods that return clones. Mixing the two idioms is the #1 source of "my header didn't get set" bugs — assigning $response->withStatus(404); without capturing the return value silently does nothing. Pick one idiom per file and check the return type in the source if you're unsure.

createFromGlobals() is a trap in disguise. It reads $_GET/$_POST/$_FILES/$_SERVER at construction time. Under CLI, PHP-FPM behind a reverse proxy, or a test harness, those are empty or wrong. Also watch X-Forwarded-* — the package will not trust or parse them for you, so getUri()->getScheme() and getClientIp()-style helpers will report the proxy, not the client.

Header lookup is case-insensitive; header storage may not be. Always read through getHeaderLine($name) rather than indexing the array from getHeaders(). If you index directly, content-type and Content-Type will look like different keys depending on how they were set.

Streams and the read cursor. (string) $response->getBody() on some stream implementations reads from the current position — if a transport or middleware already consumed it, you get an empty string. If you see truncated bodies, rewind() first or prefer StringStream for anything you build yourself.

Output assumes it owns the SAPI. It will call header() and echo. If you've already emitted output (a stray var_dump, a BOM in a config file, a closing ?> with trailing whitespace), headers-already-sent warnings follow. Keep Output as the very last statement and never echo in a handler that returns a response.

JSON encoding failures are quiet on old PHP. json_encode on invalid UTF-8 returns false, and a naive JsonResponse will happily send an empty body with a 200. Validate/sanitize input encoding, and in debug mode assert that the encoded body is non-empty.

File uploads need explicit error inspection. $_FILES entries have an error code that is 0 for success; the UploadedFile wrapper exposes it, but nothing throws for you. Check getError() before calling moveTo()/move(), and remember that a UPLOAD_ERR_OK entry can still be a partial write on some configurations.

Query strings with arrays are lossy. ?filter[a]=1 round-trips through parse_str/http_build_query, which mangles keys containing dots or spaces (they become underscores). If you accept arbitrary filter names, encode as JSON in a single query param instead.

Debugging recipe. When a request looks wrong, dump in this order: getMethod(), getUri() (__toString()), getHeaders(), then the raw body string. Nine times out of ten the problem is that the URI was constructed without the base path, or the body was consumed by a middleware earlier in the chain.

Extension points worth knowing:

  • Subclass Response (or the abstract base) to add project-wide defaults like security headers — do it in one place, not in every handler.
  • Implement a custom Transport for retry logic, logging, or a mock in tests; it's a small interface and the cleanest seam in the whole package.
  • Wrap Output if you need to buffer or post-process the final payload (e.g. gzip, or capturing output in an integration test).

Long-term advice. With 6 stars, 0 dependents, and no release since 2016, this package will not receive compatibility fixes for modern PHP (8.x typed properties, deprecations around dynamic properties and strlen(null)). Budget for a migration to nyholm/psr7 + laminas/laminas-diactoros (or Symfony HttpFoundation if you're not PSR-minded) rather than planning new features on top of it. The Transport abstraction is the piece most worth porting conceptually; the URI and header helpers will be replaced by PSR-7 equivalents almost one-for-one.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource