Skip to content

Advanced Guide

Nana Axel edited this page Jun 23, 2020 · 10 revisions

Summary


WaterPipe configuration

If you want, you can customize the behavior of WaterPipe using the WaterPipeConfig class. Available options are:

  • queryStringEnabled: This option is a boolean defining if the use of query strings are enabled in your pipes. The default value is true ;
  • defaultCharset: This option is a string defining the default charset to use when processing responses. The default value is "utf-8" ;
  • useStderr: This option is a boolean defining if WaterPipe have to use the STDERR output channel to print errors and uncaught exceptions. The default value is true .

To configure WaterPipe, you just have to retrieve the singleton and define values, before creating pipes.

<?php

use \ElementaryFramework\WaterPipe\WaterPipeConfig;

// Edit configuration
$config = WaterPipeConfig::get();
$config->setUseStderr(false);
$config->setDefaultCharset("ISO-8859-1");

// You can now create your pipes...

Creating Pipes

There are several ways to create routes with WaterPipe. Each of them has been designed to match common Web Design Patterns and architectures.

Using anonymous functions

This is the way you have seen in the Getting Started guide. Here you create your URI callback in an anonymous function, wrapping as parameters the Request and the Response of the current context.

$pipe->request('/hello/to/world', function (Request $req, Response $res) {
    $res->sendText("Hello, world !");
});

By this way, you have the ability to leave URIs and their callbacks at the same place. It can help you for maintenance, but can make the file hard to edit if many routes are created into it...

Using RouteActions

RouteAction help you wrap your URI callback in a specific class. This class must extend the abstract RouteAction class and implement the execute() method. Using RouteAction, you have to access the Request and the Response of the current context through RouteAction::_request and RouteAction::_response properties.

class RouteActionExample extends \ElementaryFramework\Routing\RouteAction
{
    public function execute()
    {
        $this->_response->sendText("Hello, world !");
    }
}

$pipe->request('/hello/to/world', RouteActionExample::class);
// -- OR --
$pipe->request('/hello/to/world', new RouteActionExample);

The main advantage of RouteAction is the code reuse when many URI have the same callback (or the callback change only according to URI parameters).

Using callable

Besides anonymous functions, WaterPipe support any kind of callable forms supported by PHP.

class HelloWorldController
{
    public static function helloToWorldStatic(Request $req, Response $res)
    {
        $res->sendText("Hello, world !");
    }

    public function helloToWorld(Request $req, Response $res)
    {
        $res->sendText("Hello, world !");
    }
}

function helloWorld(Request $req, Response $res)
{
    $res->sendText("Hello, world !");
}

// Using static method with callable array form
$pipe->request('/hello/to/world', [HelloWorldController::class, 'helloToWorldStatic']);

// Using instance method with callable array form
$pipe->request('/hello/to/world', [new HelloWorldController, 'helloToWorld']);

// Using function names
$pipe->request('/hello/to/world', 'helloWorld');

// Using closures
$pipe->request('/hello/to/world', \Closure::fromCallable(/* Any previous callable form as parameter */));

Note it is important that the called function in the callable form must have as parameters the Request and the Response.

Defining routes

When you create pipes, you have to define route on which the pipe will be executed.

$pipe->request('your_route_here', function (Request $req, Response $res) {
    // Execute...
});

There are many ways to define a route, according to your needs.

Static routes

Static routes doesn't change anymore during the pipe resolution process, for example /api/posts/all. They are resolved as is, and used as is.

Routes with named parameters

It's possible to create a route and provide to it a named parameter, for example /api/posts/:id, where :id is the parameter. It's possible to give to the route an infinite number of parameters.

Named parameters are resolved during the pipe resolution process, for example, incoming requests to /api/posts/1, /api/posts/92345, /api/posts/category_php, /api/posts/user5517, /api/posts/no-category, /api/posts/all.json will match the /api/posts/:id route. So, when using named parameters, it's important to parse the resolved value to be sure it contains what you want.

Regex-based routes

With WaterPipe, you can create routes based on Regular Expressions (RegEx). It can give you more flexibility and safety to process your requests. For example, a regex route like /api/posts/(\d+) will only be executed for incoming requests to /api/posts/10, /api/posts/8823, etc...

There is no restriction on the kind of regular expression used, for example, a regex route like /posts/(?<category>\w+)/(?<id>\d+)/(\d+)-(\k<category>)-(\k<id>).html will be executed for incoming requests to /posts/test/1727/123123123-test-1727.html or /posts/programming/33491/20191103-programming-33491.html, but not /posts/games/385/9914346-minecraft-385.html because it's doesn't match the regex.

Handling incoming Requests

The Request class manages in an OOP way all the data sent by the client. When you receive an HTTP request, an unique instance of this class (representing that request) can be accessed via Request::capture().

The Request class is composed of methods and properties allowing you retrieve information from the raw HTTP request:

  • Request::$uri: This property returns an instance of the RequestUri class.
  • Request::getMethod(): This method returns an integer, representing the HTTP request method. This integer represent values from the static class RequestMethod.
  • Request::getParams(): This method returns a RequestData instance, storing all information about HTTP GET parameters.
  • Request::getBody(): This method returns a RequestData instance, storing all information about the request body.
  • Request::getCookies(): This method returns a RequestData instance, storing all information about HTTP cookies associated to the request.
  • Request::getHeader(): This method returns an instance of the RequestHeader class, and allow you to retrieve HTTP headers associated to the request.
  • Request::isAjax(): This method is used to check if the request was sent using AJAX. It's helpful for REST services.

RequestUri

The URI of the incoming request is accessed via the Request::$uri property:

$pipe->request('your_route_here', function (Request $req, Response $res) {
    // Access to data about the URI via $req->uri
});

This will return an instance of the RequestUri class.

With this, you can access resolved values of variables in routes with named parameters or regex-based routes, using the RequestUri::getParams() method, or directly by indexing the RequestUri instance (the class implement the ArrayAccess interface).

The RequestUri::getParams() method is totally different than the Request::getParams() method. The first one returns URI parameters, and the second returns request parameters, formerly GET parameters.

$pipe->request('/api/posts/:id', function (Request $req, Response $res) {
    // URI named parameters are accessed with their name
    $id = $req->uri['id'];
});

$pipe->request('/api/users/(new|edit|ban)/(\d+)', function (Request $req, Response $res) {
    // URI regex-based parameters are accessed with their positions in the uri (the position is zero-based)
    $action = $req->uri[0]; // new,  edit, or ban
    $userId = $req->uri[1]; // Any integer
});

$pipe->request('/api/billing/:userId/(new|cancel)/:billId', function (Request $req, Response $res) {
    // The same previous rules apply when named parameters and regex-based parameters are used together
    $userId = $req->uri['userId']; // Any string
    $action = $req->uri[1]; // new or cancel (notice that the parameter is accessed at position 1, since it is the second parameter in the uri)
    $billId = $req->uri['billId']; // Any string
});

RequestMethod

Some times, you will need to check what request method the client used to connect to your endpoint. This is pretty helpful when you use the WaterPipe::request() method to create your pipes, since a pipe created with this method will be executed regardless of the request method.

Getting the request method of the current request is simple as calling Request::getMethod(), which returns an integer with a value equals to one of the static class (this class is likely an enumeration) RequestMethod.

$pipe->request('/api/posts/:id', function (Request $req, Response $res) {
    // Any request method can fall into this pipe if the request uri match, so to check which request method we are currently running
    switch ($req->getMethod()
    {
        case RequestMethod::GET:
            // Do something...
            break;
        case RequestMethod::POST:
            // Do something...
            break;
        case RequestMethod::PUT:
            // Do something...
            break;
        case RequestMethod::DELETE:
            // Do something...
            break;
        case RequestMethod::HEAD:
            // Do something...
            break;
        case RequestMethod::PATCH:
            // Do something...
            break;
        case RequestMethod::OPTIONS:
            // Do something...
            break;

        default:
        case RequestMethod::UNKNOWN:
            // Humm... Something surely went wrong...
            break;
    }
});

If you are using a one of the special methods to create your pipes (eg. WaterPipe::post(), WaterPipe::patch()), you can be sure that Request::getMethod() will always return the right request method.

RequestData

A RequestData class instance is, simply, a collection of data contained in a request. When a client send a request, there are many data we can collect from it. WaterPipe actually collect three (03) of them: GET parameters, request body, HTTP cookies.

GET parameters

Collecting GET parameters from a request is pretty simple:

// In this example, a client send a request to /api/posts/all?category=games&from=2020-01-01&to=2020-12-31
$pipe->get('/api/posts/all', function (Request $req, Response $res) {
    // Get the GET parameters dictionary
    $params = $req->getParams();

    // Access parameters
    $category = $params['category'];
    $from     = date_create($params['from']);
    $to       = date_create($params['to']);

    // Find your posts...
});

Request body

You can get the request body of a POST or PUT request with the Request::getBody() method. This method will return two types fo data:

  • An instance of RequestData, which means that the request body was successfully parsed by WaterPipe (eg. requests with JSON, XML, or url-encoded form content)
  • A string, which represent the raw request body content. In this case, WaterPipe was not able to automatically parse the request body. This can happen when the request body contains non-key-value-pair parsable value (eg. binary file content, raw text, etc...), so WaterPipe let you the right to do whatever you want with the raw content.
$pipe->post('/api/users/login', function (Request $req, Response $res) {
    // Get login details from form
    $data = $req->getBody(); // This will return a RequestData instance

    $username = $data['username'];
    $password = $data['password'];
    $remember = boolval($data['remember']);

    // Process login...
});

$pipe->put('/api/users/:id/avatar/upload', function (Request $req, Response $res) {
    // Get the user id
    $userId = $req->uri['id'];

    // Retrieve raw image content
    $image = $req->getBody(); // This will return a string

    // Save image to disk, in the personal folder of the user with $userId...
});

HTTP cookies

Like request method and request body, HTTP cookies associated to the current request are captured by WaterPipe, and can be retrieved with the Request::getCookies() method, which also return a RequestData instance:

$pipe->get('/shop/cart/process', function (Request $req, Response $res) {
    // Get customer cart from cookies
    $cookies = $req->getCookies();
    $productIds = explode(',', $cookies['cart']); // Construct an array of product id from a string of comma-separated id

    // Get products data from database and output the payment form...
});

RequestHeader

Clone this wiki locally