Solicitud HTTP
Les aplicacions web del costat servidor es caracterizen perquè utilitzen el protocol HTTP per interactuar amb les aplicacions client (com pot ser el navegador web).
Aquest protocol és flexible, i s'assembla a cridar funcions remotes. Aquestes "funcions" es crident mitjançant una línea en la capçalera del missatge que envia el client, i que s'anomena línia de solicitud.
GET /hello/david HTTP/1.1
La línia de sol·licitud comença especificant el mètode que es vol utilitzar, seguit del identificador del recurs que es solicita (una paǵina HTML, una imatge, etc.) i la versió del protocol.
Ejemplo
Aquest és un exemple molt senzill que et permetrà entendre com gestiona les sol.licitus una aplicació web en entorn PHP.
Copia aquest fitxer al teu directori arrel index.php
:
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require __DIR__ . '/vendor/autoload.php';
$app = AppFactory::create();
$app->get("/", function (Request $request, Response $response) {
$response->getBody()->write("Hello, World!");
return $response;
});
$app->get('/hello/{name}', function (Request $request, Response $response, $args) {
$name = $args['name'];
$response->getBody()->write("Hello, $name");
return $response;
});
$app->run();
Importa las librerías requeridas con composer
:
$ composer require slim/slim slim/psr7
Using version ^4.13 for slim/slim
...
composer
crea un fichero composer.json
donde guarda la configuración de tu proyecto:
{
"require": {
"slim/slim": "^4.13",
"slim/psr7": "^1.6"
}
}
También descarga las librerías que has solicitatdo, y todas sus dependencias en el directorio vendor
:
$ ls vendor
autoload.php composer fig nikic psr ralouphie slim symfony
Inicia un servidor web:
php -S 0.0.0.0:3000
Si hago una petición HTTP a /
, el servidor ejecuta la primera función y devuelve Hello, World!
:
$ curl localhost:3000
Hello, World!
Si hago una petición HTTP a /hello/david
, el servidor ejecuta la segunda función y , en este caso, devuelve Hello, david!
:
$ curl localhost:3000/hello/david
Hello, david!
Slim
Per desenvolupar un projecte d'aplicació web necessites utilitzar paquets (el que és coneix com a llibreries), ja que encara que PHP et permet crear aplicacions web (és un llenguatge que es va crear específicament per això), el que t'ofereix són primitives molt senzilles.
Per tant necessites per començar utilizar un micro framework com slim i el paquet PSR-7 (contè les interfícies de missatges PHP).
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs. At its core, Slim is a dispatcher that receives an HTTP request, invokes an appropriate callback routine, and returns an HTTP response. That’s it.
Request and response
When you build a Slim app, you are often working directly with Request and Response objects. These objects represent the actual HTTP request received by the web server and the eventual HTTP response returned to the client.
Every Slim app route is given the current Request and Response objects as arguments to its callback routine. These objects implement the popular PSR-7 interfaces. The Slim app route can inspect or manipulate these objects as necessary. Ultimately, each Slim app route MUST return a PSR-7 Response object.
Bring your own components
Slim is designed to play well with other PHP components, too. You can register additional first-party components such as Slim-Csrf, Slim-HttpCache, or Slim-Flash that build upon Slim’s default functionality. It’s also easy to integrate third-party components found on Packagist.
require
Per utilitzar els paquets necessites incluirlos en el fitxer, i per això necessites utilizar l'estructura de control require
require __DIR__ . '/vendor/autoload.php';
Aplicació
El primer que has de fer és crear un objecte Slim\App
mitjançant un mètode de factoria, i guardar l'objecte en una variable $app
per poder ser utilizat amb posterioritat per afegit rutes a l'aplicació. Finalment inicies l'aplicació amb la crida d'el métode run()
Rutes
Una de les decisions de disseny més importants en un servidor web és la definició de les rutes que permetran accedir als diferents recursos de la teva aplicació.
Encara que PHP tè un sistema per defecte ..., aquest no és flexible, a més de ser complexe i no permetre dividir fàcilment una aplicació en diferents components.
Això no només passa amb PHP, sinò tambè amb Java, Go, Rust, etc.
Per tant, totes les aplicacions web modernes utilizant paquets (o llibreries) que gestionen l'encaminament.
Slim utilitza la librería Fast Route.
$app->get("/", function (Request $request, Response $response) {
$response->getBody()->write("Hello, World!");
return $response;
});
How to create routes
You can add a route that handles only one, all, or only a set of http request methods with Slim application specific methods. They accepts two arguments:
- The route pattern (with optional named placeholders)
- The route callback
$app->get('/books/{id}', function ($request, $response) {
// Show book identified by $args['id']
});
$app->put('/books/{id}', function ($request, $response) {
// Update book identified by $args['id']
});
$app->delete('/books/{id}', function ($request, $response) {
// Delete book identified by $args['id']
});
$app->options('/books/{id}', function ($request, $response) {
// Return response headers
});
$app->patch('/books/{id}', function ($request, $response) {
// Apply changes to book identified by $args['id']
});
$app->any('/books/[{id}]', function ($request, $response) {
// Apply changes to books or book identified by $args['id'] if specified.
// To check which method is used:
$method = $request->getMethod();
});
$app->map(['GET', 'POST'], '/books', function ($request, $response) {
// Create new book or list all books
$method = $request->getMethod();
});
Route callbacks
$app->get("/", function (Request $request, Response $response, array $args) {
$response->getBody()->write("Hello, World!");
return $response;
});
Each routing method described above accepts a callback routine as its final argument. This argument can be any PHP callable, and by default it accepts three arguments.
Request
The first argument is aPsr\Http\Message\ServerRequestInterface
object that represents the current HTTP request.Response
The second argument is aPsr\Http\Message\ResponseInterface
object that represents the current HTTP response.Arguments
The third argument is an associative array that contains values for the current route’s named placeholders.
To write content to the response, first get the $response
body and then write to it. This content will be appended to the HTTP $response
object.
You MUST return a PSR-7 Response object.
Route placeholders
Each routing method accepts a URL pattern that is matched against the current HTTP request URI. Route patterns may use named placeholders to dynamically match HTTP request URI segments.
A route pattern placeholder starts with a {
, followed by the placeholder name, ending with a }
.
$app->get('/hello/{name}', function ($request, $response) {
$name = $args['name'];
$response->getBody()->write("Hello, $name");
return $response;
});
To make a section optional, simply wrap in square brackets:
$app->get('/users/[{id}]', function ($request, $response, array $args) {
// responds to both `/users/` and `/users/123`
// but not to `/users`
return $response;
});
Multiple optional parameters are supported by nesting:
$app->get('/news/[{year/}[{month/}]]', function ($request, $response, array $args) {
// reponds to `/news/`, `/news/2016/` and `/news/2016/03/`
// ...
return $response;
});
For "Unlimited" optional parameters, you can do this:
$app->get('/news/[{params:.*}]', function ($request, $response, array $args) {
// $params is an array of all the optional segments
$params = explode('/', $args['params']);
// ...
return $response;
});
In this example, a URI of /news/2016/03/20 would result in the $params array containing three elements: ['2016', '03', '20'].
Regular expression matching
By default the placeholders are written inside {} and can accept any values. However, placeholders can also require the HTTP request URI to match a particular regular expression. If the current HTTP request URI does not match a placeholder regular expression, the route is not invoked. This is an example placeholder named id that requires one or more digits.
$app->get('/users/{id:[0-9]+}', function ($request, $response, array $args) {
// Find user identified by $args['id']
// ...
return $response;
});
Route names
Application routes can be assigned a name. This is useful if you want to programmatically generate a URL to a specific route with the RouteParser’s urlFor()
method. Each routing method described above returns a Slim\Route
object, and this object exposes a setName()
method.
$app->get('/hello/{name}', function ($request, $response, array $args) {
$response->getBody()->write("Hello, " . $args['name']);
return $response;
})->setName('hello');
You can generate a URL for this named route with the application RouteParser’s urlFor()
method.
$routeParser = $app->getRouteCollector()->getRouteParser();
echo $routeParser->urlFor('hello', ['name' => 'Josh'], ['example' => 'name']);
// Outputs "/hello/Josh?example=name"
The RouteParser’s urlFor()
method accepts three arguments:
$routeName
The route name. A route’s name can be set via$route->setName('name')
. Route mapping methods return an instance of Route so you can set the name directly after mapping the route. e.g.:$app->get('/', function () {...})->setName('name')
$data
Associative array of route pattern placeholders and replacement values.$queryParams
Associative array of query parameters to be appended to the generated url.
Route groups
To help organize routes into logical groups, the Slim\App
also provides a group()
method. Each group’s route pattern is prepended to the routes or groups contained within it, and any placeholder arguments in the group pattern are ultimately made available to the nested routes:
use Slim\Routing\RouteCollectorProxy;
// ...
$app->group('/users/{id:[0-9]+}', function (RouteCollectorProxy $group) {
$group->map(['GET', 'DELETE', 'PATCH', 'PUT'], '', function ($request, $response, array $args) {
// Find, delete, patch or replace user identified by $args['id']
// ...
return $response;
})->setName('user');
$group->get('/reset-password', function ($request, $response, array $args) {
// Route for /users/{id:[0-9]+}/reset-password
// Reset the password for user identified by $args['id']
// ...
return $response;
})->setName('user-password-reset');
});
The group pattern can be empty, enabling the logical grouping of routes that do not share a common pattern.
use Slim\Routing\RouteCollectorProxy;
// ...
$app->group('', function (RouteCollectorProxy $group) {
$group->get('/billing', function ($request, $response, array $args) {
// Route for /billing
return $response;
});
$group->get('/invoice/{id:[0-9]+}', function ($request, $response, array $args) {
// Route for /invoice/{id:[0-9]+}
return $response;
});
})->add(new GroupMiddleware());
Note inside the group closure, Slim binds the closure to the container instance.
inside route closure,
$this
is bound to the instance ofPsr\Container\ContainerInterface
Route middleware
You can also attach middleware to any route or route group.
use Slim\Routing\RouteCollectorProxy;
// ...
$app->group('/foo', function (RouteCollectorProxy $group) {
$group->get('/bar', function ($request, $response, array $args) {
// ...
return $response;
})->add(new RouteMiddleware());
})->add(new GroupMiddleware());
PSR7
El document PSR-7 descriu les interfícies habituals per representar missatges HTTP.
Els missatges HTTP són la base del desenvolupament web. Els navegadors web i els clients HTTP, com ara cURL, creen missatges de sol·licitud HTTP que s’envien a un servidor web, que proporciona un missatge de resposta HTTP.
Capçaleres
Case-insensitive header field names
HTTP messages include case-insensitive header field names. Headers are retrieved by name from classes implementing the MessageInterface
in a case-insensitive manner. For example, retrieving the foo
header will return the same result as retrieving the FoO
header. Similarly, setting the Foo
header will overwrite any previously set foo
header value.
$message = $message->withHeader('foo', 'bar');
echo $message->getHeaderLine('foo');
// Outputs: bar
echo $message->getHeaderLine('FOO');
// Outputs: bar
$message = $message->withHeader('fOO', 'baz');
echo $message->getHeaderLine('foo');
// Outputs: baz
Despite that headers may be retrieved case-insensitively, the original case MUST be preserved by the implementation, in particular when retrieved with getHeaders()
.
Non-conforming HTTP applications may depend on a certain case, so it is useful for a user to be able to dictate the case of the HTTP headers when creating a request or response.
Headers
Every HTTP response has headers. These are metadata that describe the HTTP response but are not visible in the response’s body.
Set Header
You can set a header value with the PSR-7 Response object’s withHeader($name, $value)
method.
$response = $response->withHeader('Content-type', 'application/json');
Reminder The Response object is immutable. This method returns a copy of the Response object that has the new header value. This method is destructive, and it replaces existing header values already associated with the same header name.
Append Header
You can append a header value with the PSR-7 Response object’s withAddedHeader($name, $value)
method.
$response = $response->withAddedHeader('Allow', 'PUT');
Reminder Unlike the
withHeader()
method, this method appends the new value to the set of values that already exist for the same header name. The Response object is immutable. This method returns a copy of the Response object that has the appended header value.
Remove Header
You can remove a header with the Response object’s withoutHeader($name) method.
$response = $response->withoutHeader('Allow');
Reminder The Response object is immutable. This method returns a copy of the Response object without the specified header.
Body
An HTTP response typically has a body.
Just like the PSR-7 Request object, the PSR-7 Response object implements the body as an instance of Psr\Http\Message\StreamInterface
. You can get the HTTP response body StreamInterface
instance with the PSR-7 Response object’s getBody()
method. The getBody()
method is preferable if the outgoing HTTP response length is unknown or too large for available memory.
$body = $response->getBody();
The resultant Psr\Http\Message\StreamInterface
instance provides the following methods to read from, iterate, and write to its underlying PHP resource
.
- getSize()
- tell()
- eof()
- isSeekable()
- seek()
- rewind()
- isWritable()
- write($string)
- isReadable()
- read($length)
- getContents()
- getMetadata($key = null)
Most often, you’ll need to write to the PSR-7 Response object. You can write content to the StreamInterface instance with its write()
method like this:
$body = $response->getBody();
$body->write('Hello');