Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/xp-forge/web
Web applications for the XP Framework
https://github.com/xp-forge/web
async development-webserver http-middleware http-server php php7 php8 routing web xp-framework
Last synced: 8 days ago
JSON representation
Web applications for the XP Framework
- Host: GitHub
- URL: https://github.com/xp-forge/web
- Owner: xp-forge
- Created: 2017-02-13T19:35:28.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2024-09-29T10:45:12.000Z (about 2 months ago)
- Last Synced: 2024-10-31T23:34:31.318Z (14 days ago)
- Topics: async, development-webserver, http-middleware, http-server, php, php7, php8, routing, web, xp-framework
- Language: PHP
- Homepage:
- Size: 736 KB
- Stars: 2
- Watchers: 3
- Forks: 1
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Changelog: ChangeLog.md
Awesome Lists containing this project
README
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.0+](https://raw.githubusercontent.com/xp-framework/web/master/static/php-7_0plus.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://poser.pugx.org/xp-forge/web/version.svg)](https://packagist.org/packages/xp-forge/web)Low-level functionality for serving HTTP requests, including the `xp web` runner.
Example
-------```php
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:
```bash
$ 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.
```bash
$ xp -supervise web -m develop Service
@xp.web.srv.Develop(HTTP @ `php -S 127.0.0.1:8080 -t /home/example/devel/shorturl`)
# ...
```Now open the website at http://localhost:8080/hello
Server models
-------------
The four server models (*selectable via `-m ` on the command line*) are:* **async** (*the default since 3.0.0*): 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.
* **sequential**: Same as above, but blocks until one client's HTTP request handler has finished executing before serving the next request.
* **prefork**: Much like Apache, forks a given number of children to handle HTTP requests. Requires the `pcntl` extension.
* **develop**: As mentioned above, built ontop of the PHP development wenserver. 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).Request and response
--------------------
The `web.Request` class provides the following basic functionality:```php
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:
```php
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
--------
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.```php
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 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()`.```php
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 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!```php
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
-----------
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.```php
$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 https://evertpot.com/http/103-early-hints
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.```php
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');
},
];
}
}
```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 aboveMore 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
-----------
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://github.com/xp-forge/web/assets/696742/2707a921-8ae2-4884-ae33-59972a8e7a12)
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)