pop-http

The popphp/pop-http component provides a robust API to handle HTTP requests and responses. Also, it provides HTTP client adapters via cURL and streams, as well as file uploads via HTTP.

Installation

Install it directly into your project:

composer require popphp/pop-http

Or, include it in your composer.json file:

{
    "require": {
        "popphp/pop-http": "^3.2.0",
    }
}

Basic Use

The popphp/pop-http component contains a request object and a response object that can assist in capturing and managing the incoming requests to your application and handle assembling the appropriate response back to the user.

Requests

The main request class is Pop\Http\Request. It has a robust API to allow you to interact with the incoming request and extract data from it. If you pass nothing to the constructor a new request object, it will attempt to parse the value contained in $_SERVER['REQUEST_URI']. You can, however, pass it a $uri to force a specific request, and also a $basePath to let the request object know that the base of the application is contained in a sub-folder under the document root.

Creating a new request object with a base path

In the following example, let’s assume our application is in a sub-folder under the main document root:

  • /httpdocs
  • /httpdocs/system
  • /httpdocs/system/index.php

We create a request object and pass it the base path /system so that application knows to parse incoming request after the /system base path.

$request = new Pop\Http\Request(null, '/system');

For example, if a request of /system/users came in, the application would know to use /users as the request and route it accordingly. If you need to reference the request URI, there are a couple of different methods to do so:

  • $request->getBasePath(); - returns only the base path (‘/system’)
  • $request->getRequestUri(); - returns only the request URI (‘/users’)
  • $request->getFullRequestUri(); - returns the full request URI string (‘/system/users’)

Getting path segments

If you need to break apart a URI into its segments access them for your application, you can do it with the getSegment() method. Consider the URI /users/edit/1001:

  • $request->getSegment(0); - returns ‘users’
  • $request->getSegment(1); - returns ‘edit’
  • $request->getSegment(2); - returns ‘1001’
  • $request->getSegments(); - returns an array containing all of the path segments

Check the HTTP Method

  • $request->isGet();
  • $request->isHead();
  • $request->isPost();
  • $request->isPut();
  • $request->isPatch();
  • $request->isDelete();
  • $request->isTrace();
  • $request->isHead();
  • $request->isOptions();
  • $request->isConnect();

Retrieve Data from the Request

  • $request->getQuery($key = null);
  • $request->getPost($key = null);
  • $request->getFiles($key = null);
  • $request->getPut($key = null);
  • $request->getPatch($key = null);
  • $request->getDelete($key = null);
  • $request->getServer($key = null);
  • $request->getEnv($key = null);

If you do not pass the $key parameter in the above methods, the full array of values will be returned. The results from the getQuery(), getPost() and getFiles() methods mirror what is contained in the $_GET, $_POST and $_FILES global arrays, respectively. The getServer() and getEnv() methods mirror the $_SERVER and $_ENV global arrays, respectively.

If the request method passed is PUT, PATCH or DELETE, the request object will attempt to parse the raw request data to provide the data from that. The request object will also attempt to be content-aware and parse JSON or XML from the data if it successfully detects a content type from the request.

If you need to access the raw request data or the parsed request data, you can do so with these methods:

  • $request->getRawData();
  • $request->getParsedData();

Retrieve Request Headers

  • $request->getHeader($key); - return a single request header value
  • $request->getHeaders(); - return all header values in an array

Responses

The Pop\Http\Response class has a full-featured API that allows you to create a outbound response to send back to the user or parse an inbound response from a request. The main constructor of the response object accepts a configuration array with the basic data to get the response object started:

$response = new Pop\Http\Response([
    'code'    => 200,
    'message' => 'OK',
    'version' => '1.1',
    'body'    => 'Some body content',
    'headers' => [
        'Content-Type' => 'text/plain'
    ]
]);

All of that basic response data can also be set as needed through the API:

  • $response->setCode($code); - set the response code
  • $response->setMessage($message); - set the response message
  • $response->setVersion($version); - set the response version
  • $response->setBody($body); - set the response body
  • $response->setHeader($name, $value); - set a response header
  • $response->setHeaders($headers); - set response headers from an array

And retrieved as well:

  • $response->getCode(); - get the response code
  • $response->getMessage(); - get the response message
  • $response->getVersion(); - get the response version
  • $response->getBody(); - get the response body
  • $response->getHeader($name); - get a response header
  • $response->getHeaders($headers); - get response headers as an array
  • $response->getHeadersAsString(); - get response headers as a string

Check the Response

  • $response->isSuccess(); - 100, 200 or 300 level response code
  • $response->isRedirect(); - 300 level response code
  • $response->isError(); - 400 or 500 level response code
  • $response->isClientError(); - 400 level response code
  • $response->isServerError(); - 500 level response code

And you can get the appropriate response message from the code like this:

use Pop\Http\Response;

$response = new Response();
$response->setCode(403);
$response->setMessage(Response::getMessageFromCode(403)); // Sets 'Forbidden'

Sending the Response

$response = new Pop\Http\Response([
    'code'    => 200,
    'message' => 'OK',
    'version' => '1.1',
    'body'    => 'Some body content',
    'headers' => [
        'Content-Type'   => 'text/plain'
    ]
]);

$response->setHeader('Content-Length', strlen($response->getBody()));
$response->send();

The above example would produce something like:

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 19

Some body content

Redirecting a Response

Pop\Http\Response::redirect('http://www.domain.com/some-new-page');
exit();

Parsing a Response

In parsing a response from a request, you pass either the URL or a response string that already exists. A new response object with all of its data parsed from that response will be created:

$response = Pop\Http\Response\Parser::parseFromUri('http://www.domain.com/some-page');

if ($response->getCode() == 200) {
    // Do something with the response
} else if ($response->isError()) {
    // Uh oh. Something went wrong
}

File Uploads

With the file upload class, you can not only control basic file uploads, but also enforce a set of rules and conditions to control what type of files are uploaded. Please note, the upload() method expects to have an element from the $_FILES array passed into it.

use Pop\Http\Upload;

$upload = new Upload('/path/to/uploads');
$upload->useDefaults();

$upload->upload($_FILES['file_upload']);

// Do something with the newly uploaded file
if ($upload->isSuccess()) {
    $file = $upload->getUploadedFile();
} else {
    echo $upload->getErrorMessage();
}

The setDefaults() method sets a standard group of rules and conditions for basic web file uploads. The max filesize is set to 10 MBs and a set of media and document file types (jpg, pdf, doc, etc.) are set as allowed and a set of web and script file types (js, php, html, etc.) are set as disallowed.

If you’d like to set your own custom rules, you can do so like this:

use Pop\Http\Upload;

$upload = new Upload('/path/to/uploads');
$upload->setMaxSize(25000000)
       ->setAllowedTypes('pdf')
       ->setDisallowedTypes('php');

The example above sets the max filesize to 25 MBs and allows only PDF files and disallows PHP files.

Checking file names

The upload object is set to NOT overwrite existing files on upload. It will perform a check and rename the uploaded file accordingly with a underscore and a number (‘filename_1.doc’, ‘filename_2.doc’, etc.) If you may want to test the filename on your own you can like this:

use Pop\Http\Upload;

$upload   = new Upload('/path/to/uploads');
$fileName = $upload->checkFilename($_FILES['file_upload']['name']);

If the name of the file being uploaded is found on disk in the upload directory, the returned value of the newly renamed file will be something like ‘filename_1.doc’. You can then pass that value (or any other custom filename value) into the upload() method:

$upload->upload($_FILES['file_upload'], $fileName);

If you want to override this behavior and overwrite any existing files, you can set the overwrite property before you upload the file:

$upload->overwrite(true);