rfc:http-interface

PHP RFC: Standardized PHP Http Interface

Standardizing and building an HTTP interface in PHP for allowing clients to implement their own custom Http Request/Response objects without having to wrap a plethora of HTTP functions/classes/libraries.

Introduction

This RFC proposes creating two standard HTTP interfaces in PHP core that users can implement in their own PHP code to utilize HTTP request and response capabilities and discard the superglobal $_GET, $_POST, $_COOKIE, $_FILES, $_SERVER variables. Along with the interfaces a base HttpRequest and HttpResponse classes should be packaged with PHP so that users can extend or override such default behavior at will and with efficiency. If the user does not chose to implement their own HTTP classes using the HTTP interfaces then default behavior is relied upon by PHP (i.e. no backwards incompatible changes).

Proposal

This RFC proposes creating two standard Http interfaces (HttpMessageReceive and HttpMessageSend) in PHP core along with two base classes: HttpRequest and HttpResponse that handle all incoming and outgoing HTTP messages through a unified set of interfaces.

The ultimate goal of this proposal is to make HTTP message handling in PHP ubiquitous and conforming. Superglobal variables such as $_POST and $_GET are great, but they do not address many of the concerns that current web users are dealing with today such as RESTful APIs. Some RESTful APIs might rely on PUT and DELETE HTTP verbs (methods) to handle certain requests and PHP does not make dealing with such requests intuitive. So some users requested $_PUT and $_DELETE superglobals to be added to the list. However, this RFC does not propose such features. Instead, it is proposed to rid PHP of the super global variables entirely and make HTTP message processing a standard interface that can be implement by the user in any way they deem fit.

This greatly reduces the need to change core PHP for any form of edge cases that may rely on processing HTTP messages differently and improves the flexibility and extensibility of PHP's built-in message processing facilities. Further more, this removes the inefficiencies of reusing/reinitializing GPC variables in custom HttpRequest or HttpResponse classes. Additional benefits can be found in the filtering or validating of data through custom callbacks defined in the implementing class.

Interface Definitions

The following are the interface definitions required by the implementing classes…

abstract class HttpMessage
{
    protected $headers = array();
    protected $body = "";
    protected $version = 1.0;
    protected $message;
}
 
interface HttpMessageReceive
{
 
    public function receiveMessage($message);
    public function parseMessage();
    public function parseParameters();
    public function decodeBody();
    public function onReceive();
 
}
 
interface HttpMessageSend
{
 
    public function sendMessage($message);
    public function composeMessage();
    public function encodeBody();
    public function onSend();
 
}

So an implementing class could look something like this…

class HttpRequest extends HttpMessage implements HttpMessageReceive
{
 
    protected $parameters = array();
    protected $method, $requestURI, $host;
 
    public function receiveMessage($message)
    {
        $this->message = $message;
        $this->onReceive();
        $this->parseMessage();
        $this->parseParameters();
        $this->decodeBody();
    }
 
    public function parseMessage()
    {
        // implement message parsing here
        list($header, $body) = explode("\r\n\r\n", $this->message, 2) + array(null, null);
        $this->body = $body;
        $headers = explode("\r\n", $header);
        $header = array_shift($headers);
        list($this->method, $this->requestURI, $this->version) = explode(" ", $header, 3) + array(null, null, $this->version);
        $this->version = (float) ltrim(substr($this->version, strpos($this->version, "/")), "/");
        foreach($headers as $h) {
            list($key, $value) = explode(":", $h, 2) + array(null, null);
            if ($key === null) {
                throw new Exception("Empty header field in message!");
            }
            $this->headers[$key] = trim($value);
        }
    }
 
    public function parseParameters()        
    {
        // implement parameter parsing method here
    }
 
    public function decodeBody()        
    {
        // implement message body decoding here
    }
 
    public function onReceive()        
    {
        // implement on receive hook here
    }
 
}

Implementation

It appears the SAPI_API to the incoming HTTP request to PHP is already capable of allowing things such as register callbacks for the header, setting different content_type handlers/readers, etc… However, it is not currently exposed directly to userland.

This can be easily achieved by providing a direct interface to the internal API through a new class that will then be called along with the SAPI API on RINIT stage.

Backward Incompatible Changes

The default base class for HttpRequest will implement the existing behavior by populating GPC variables for now, which will leave no backwards incompatible changes in the language. The changes to GPC that affect this proposal are that they will no longer be populated by the Request itself, but left up to the implementing HttpRequest class. Thus users extending the class will be able to override the default behavior (leaving GPC unused if they see fit).

Proposed PHP Version(s)

PHP 7.0.x or PHP 7.NEXT

RFC Impact

To SAPIs

No known impact on individual SAPIs.

To Existing Extensions

None.

To Opcache

Uknown

New Constants

Only individual class constants should be added.

php.ini Defaults

No runtime or loaded configuration changes.

Open Issues

Unaffected PHP Functionality

Future Scope

Proposed Voting Choices

Not a language change. Requires 50%+1 vote majority.

Patches and Tests

References

Rejected Features

rfc/http-interface.txt · Last modified: 2017/09/22 13:28 (external edit)