PHPackages                             xp-forge/web - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. xp-forge/web

ActiveLibrary[Framework](/categories/framework)

xp-forge/web
============

Web applications for the XP Framework

v4.6.0(1y ago)2137.0k—5.9%1[1 issues](https://github.com/xp-forge/web/issues)[2 PRs](https://github.com/xp-forge/web/pulls)6BSD-3-ClausePHPPHP &gt;=7.0.0CI passing

Since May 7Pushed 10mo ago1 watchersCompare

[ Source](https://github.com/xp-forge/web)[ Packagist](https://packagist.org/packages/xp-forge/web)[ Docs](http://xp-framework.net/)[ RSS](/packages/xp-forge-web/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (4)Versions (96)Used By (6)

Web applications for the XP Framework
=====================================

[](#web-applications-for-the-xp-framework)

[![Build status on GitHub](https://github.com/xp-forge/web/workflows/Tests/badge.svg)](https://github.com/xp-forge/web/actions)[![XP Framework Module](https://raw.githubusercontent.com/xp-framework/web/master/static/xp-framework-badge.png)](https://github.com/xp-framework/core)[![BSD Licence](https://raw.githubusercontent.com/xp-framework/web/master/static/licence-bsd.png)](https://github.com/xp-framework/core/blob/master/LICENCE.md)[![Requires PHP 7.4+](https://raw.githubusercontent.com/xp-framework/web/master/static/php-7_4plus.svg)](http://php.net/)[![Supports PHP 8.0+](https://raw.githubusercontent.com/xp-framework/web/master/static/php-8_0plus.svg)](http://php.net/)[![Latest Stable Version](https://camo.githubusercontent.com/1979bfdd18c65390a91c30b8aab2e8fc442fe8f4cfa4f53aa288db5e4b25ca4b/68747470733a2f2f706f7365722e707567782e6f72672f78702d666f7267652f7765622f76657273696f6e2e737667)](https://packagist.org/packages/xp-forge/web)

Low-level functionality for serving HTTP requests, including the `xp web` runner.

Example
-------

[](#example)

```
use web\Application;

class Service extends Application {

  public function routes() {
    return [
      '/hello' => function($req, $res) {
        $res->answer(200, 'OK');
        $res->send('Hello '.$req->param('name', 'Guest'), 'text/plain');
      }
    ];
  }
}
```

Run it using:

```
$ xp -supervise web Service
@xp.web.srv.Standalone(HTTP @ peer.ServerSocket(Resource id #61 -> tcp://127.0.0.1:8080))
# ...
```

Supports a development webserver which is slower but allows an easy edit/save/reload development process. It uses the [PHP development server](http://php.net/features.commandline.webserver) in the background.

```
$ xp -supervise web -m develop Service
@xp.web.srv.Develop(HTTP @ `php -S [...] -t /home/example/devel/shorturl`)
# ...
```

Now open the website at

Server models
-------------

[](#server-models)

The server models (*selectable via `-m [,argument[,argument...]]` on the command line*) are:

- **async** (*the default*): A single-threaded web server. Handlers can yield control back to the server to serve other clients during lengthy operations such as file up- and downloads.
- **prefork**: Much like Apache, forks a given number of children to handle HTTP requests. Requires the `pcntl` extension. Use `prefork,children=` to control the number of child processes.
- **develop**: As mentioned above, built ontop of the PHP development webserver. Application code is recompiled and application setup performed from scratch on every request, errors and debug output are handled by the [development console](https://github.com/xp-forge/web/pull/35). Use `develop,workers=` to control the number of worker processes.

Request and response
--------------------

[](#request-and-response)

The `web.Request` class provides the following basic functionality:

```
use web\Request;

$request= ...

$request->method();       // The HTTP method, e.g. "GET"
$request->uri();          // The request URI, a util.URI instance

$request->headers();      // All request headers as a map
$request->header($name);  // The value of a single header

$request->cookies();      // All cookies
$request->cookie($name);  // The value of a single cookie

$request->params();       // All request parameters as a map
$request->param($name);   // The value of a single parameter
```

The `web.Response` class provides the following basic functionality:

```
use web\{Response, Cookie};

$response= ...

// Set status code, header(s) and cookie(s)
$response->answer($status);
$response->header($name, $value);
$response->cookie(new Cookie($name, $value));

// Sends body using a given content type
$response->send($body, $type);

// Transfers an input stream using a given content type. Uses
// chunked transfer-encoding.
yield from $response->transmit($in, $type);

// Same as above, but specifies content length before-hand
yield from $response->transmit($in, $type, $size);
```

Both *Request* and *Response* have a `stream()` method for accessing the underlying in- and output streams.

Handlers
--------

[](#handlers)

A handler (*also referred to as middleware in some frameworks*) is a function which receives a request and response and uses the above functionality to handle communication.

```
use web\Handler;

$redirect= new class() implements Handler {

  public function handle($req, $res) {
    $req->status(302);
    $req->header('Location', 'https://example.com/');
  }
};
```

This library comes with `web.handler.FilesFrom` - a handler for serving files. It takes care of conditional requests (*with If-Modified-Since*) as well requests for content ranges, and makes use of the asynchronous capabilities if available, see [here](https://github.com/xp-forge/web/pull/72).

Filters
-------

[](#filters)

Filters wrap around handlers and can perform tasks before and after the handlers are invoked. You can use the request's `pass()` method to pass values - handlers can access these using `value($name)` / `values()`.

```
use web\Filter;
use util\profiling\Timer;
use util\log\{Logging, LogCategory};

$timer= new class(Logging::all()->toConsole()) implements Filter {
  private $timer;

  public function __construct(private LogCategory $cat) {
    $this->timer= new Timer();
  }

  public function filter($request, $response, $invocation) {
    $this->timer->start();
    try {
      yield from $invocation->proceed($request, $response);
    } finally {
      $this->cat->debugf('%s: %.3f seconds', $request->uri(), $this->timer->elapsedTime());
    }
  }
}
```

*By using `yield from`, you guarantee asynchronous handlers will have completely executed before the time measurement is run on in the `finally` block.*

File uploads
------------

[](#file-uploads)

File uploads are handled by the request's `multipart()` method. In contrast to how PHP works, file uploads are streamed and your handler starts running with the first byte transmitted!

```
use io\Folder;

$uploads= new Folder('...');
$handler= function($req, $res) use($uploads) {
  if ($multipart= $req->multipart()) {

    // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100
    if ('100-continue' === $req->header('Expect')) {
      $res->hint(100, 'Continue');
    }

    // Transmit files to uploads directory asynchronously
    $files= [];
    $bytes= 0;
    foreach ($multipart->files() as $name => $file) {
      $files[]= $name;
      $bytes+= yield from $file->transmit($uploads);
    }

    // Do something with files and bytes...
  }
};
```

Early hints
-----------

[](#early-hints)

An experimental status code with which headers can be sent to a client early along for it to be able to make optimizations, e.g. preloading scripts and stylesheets.

```
$handler= function($req, $res) {
  $res->header('Link', [
    '; rel=preload; as=style',
    '; rel=preload; as=script'
  ]);
  $res->hint(103);

  // Do some processing here to render $html
  $html= ...

  $res->answer(200, 'OK');
  $res->send($html, 'text/html; charset=utf-8');
}
```

See

Internal redirects
------------------

[](#internal-redirects)

On top of external redirects which are triggered by the 3XX status codes, requests can also be redirected internally using the `dispatch()` method. This has the benefit of not requiring clients to perfom an additional request.

```
use web\Application;

class Site extends Application {

  public function routes() {
    return [
      '/home' => function($req, $res) {
        // Home page
      },
      '/' => function($req, $res) {
        // Routes are re-evaluated as if user had called /home
        return $req->dispatch('/home');
      },
    ];
  }
}
```

WebSockets
----------

[](#websockets)

To use two-way interactive communication sessions between the user's browser and our server, route to the *WebSocket* handler as follows:

```
use web\Application;
use web\handler\WebSocket;

class Ws extends Application {

  public function routes() {
    return [
      '/ws/echo' => new WebSocket(function($conn, $payload) {
        $conn->send('You said: '.$payload);
      }),
      '/'   => function($request, $response) {
        $html= send($html, 'text/html; charset=utf-8');
      }
    ];
  }
}
```

See

Logging
-------

[](#logging)

By default, logging goes to standard output and will be visible in the console the `xp web` command was invoked from. It can be influenced via the command line as follows:

- `-l server.log`: Writes to the file server.log, creating it if necessary
- `-l -`: Writes to standard output
- `-l - -l server.log`: Writes to both of the above

More fine-grained control as well as integrating with [the logging library](https://github.com/xp-framework/logging) can be achieved from inside the application, see [here](https://github.com/xp-forge/web/pull/48).

Performance
-----------

[](#performance)

Because the code for the web application is only compiled once when using production servers, we achieve lightning-fast request/response roundtrip times:

[![Network console screenshot](https://private-user-images.githubusercontent.com/696742/300918134-2707a921-8ae2-4884-ae33-59972a8e7a12.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NzQ3NDE3MzksIm5iZiI6MTc3NDc0MTQzOSwicGF0aCI6Ii82OTY3NDIvMzAwOTE4MTM0LTI3MDdhOTIxLThhZTItNDg4NC1hZTMzLTU5OTcyYThlN2ExMi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwMzI4JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDMyOFQyMzQzNTlaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0yNzQ4NjIxZmMwZGUzMjY4MjkzMjVlNTUyMzM5M2M2ZjVjMzM0ODhjZTdlNTc4ZTM4YzRjYTgwMTY5ODZkYjAzJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.px2pJ7D4JimU8h5fYCzhQMPuAKn1qNw2zEetl86U8bM)](https://private-user-images.githubusercontent.com/696742/300918134-2707a921-8ae2-4884-ae33-59972a8e7a12.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NzQ3NDE3MzksIm5iZiI6MTc3NDc0MTQzOSwicGF0aCI6Ii82OTY3NDIvMzAwOTE4MTM0LTI3MDdhOTIxLThhZTItNDg4NC1hZTMzLTU5OTcyYThlN2ExMi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwMzI4JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDMyOFQyMzQzNTlaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0yNzQ4NjIxZmMwZGUzMjY4MjkzMjVlNTUyMzM5M2M2ZjVjMzM0ODhjZTdlNTc4ZTM4YzRjYTgwMTY5ODZkYjAzJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.px2pJ7D4JimU8h5fYCzhQMPuAKn1qNw2zEetl86U8bM)

See also
--------

[](#see-also)

This library provides for the very basic functionality. To create web frontends or REST APIs, have a look at the following libraries built ontop of this:

- [Web frontends](https://github.com/xp-forge/frontend)
- [Sessions](https://github.com/xp-forge/sessions)
- [Authentication](https://github.com/xp-forge/web-auth)
- [REST APIs](https://github.com/xp-forge/rest-api)
- [Run XP web applications on AWS lambda using API Gateway](https://github.com/xp-forge/lambda-ws)

###  Health Score

45

—

FairBetter than 93% of packages

Maintenance41

Moderate activity, may be stable

Popularity34

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity74

Established project with proven stability

 Bus Factor1

Top contributor holds 99.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~32 days

Recently: every ~57 days

Total

90

Last Release

400d ago

Major Versions

v0.14.1 → v1.0.02018-04-10

v1.10.2 → v2.0.02020-04-10

v2.13.1 → v3.0.02021-09-26

v3.12.0 → v4.0.02024-01-30

PHP version history (2 changes)v0.1.0PHP &gt;=5.6.0

v2.0.0PHP &gt;=7.0.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/07d18d882c8b4aaf3466432f64018214f2771eda333202175431ee7233795376?d=identicon)[thekid](/maintainers/thekid)

---

Top Contributors

[![thekid](https://avatars.githubusercontent.com/u/696742?v=4)](https://github.com/thekid "thekid (888 commits)")[![johannes85](https://avatars.githubusercontent.com/u/470531?v=4)](https://github.com/johannes85 "johannes85 (1 commits)")

---

Tags

asyncdevelopment-webserverhttp-middlewarehttp-serverphpphp7php8routingwebwebsocket-serverxp-frameworkmodulexp

### Embed Badge

![Health badge](/badges/xp-forge-web/health.svg)

```
[![Health](https://phpackages.com/badges/xp-forge-web/health.svg)](https://phpackages.com/packages/xp-forge-web)
```

###  Alternatives

[nwidart/laravel-modules

Laravel Module management

6.1k14.6M274](/packages/nwidart-laravel-modules)[xp-framework/compiler

XP Compiler

2026.0k9](/packages/xp-framework-compiler)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
