Remove old files
This commit is contained in:
parent
4096295421
commit
b0a0f5262e
|
|
@ -1,131 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Client
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Exceptions\CurlException;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for making HTTP requests using cURL.
|
|
||||||
*/
|
|
||||||
class Client
|
|
||||||
{
|
|
||||||
/** @var array Map of cURL options with cURL constants as keys */
|
|
||||||
private $curlOpts;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new client.
|
|
||||||
*
|
|
||||||
* You may optionally provide an array of cURL options to use by default.
|
|
||||||
* Options passed in the requset method will override these.
|
|
||||||
*
|
|
||||||
* @param array $curlOpts Optional array of cURL options
|
|
||||||
*/
|
|
||||||
public function __construct(array $curlOpts = null)
|
|
||||||
{
|
|
||||||
if (is_array($curlOpts)) {
|
|
||||||
$this->curlOpts = $curlOpts;
|
|
||||||
} else {
|
|
||||||
$this->curlOpts = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make an HTTP request and return a Response.
|
|
||||||
*
|
|
||||||
* @param RequestInterface $rqst
|
|
||||||
* @param array $curlOpts Optional array of cURL options
|
|
||||||
* @throws \pjdietz\WellRESTed\Exceptions\CurlException
|
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
|
||||||
public function request(RequestInterface $rqst, $curlOpts = null)
|
|
||||||
{
|
|
||||||
$ch = curl_init();
|
|
||||||
|
|
||||||
$headers = array();
|
|
||||||
foreach ($rqst->getHeaders() as $field => $value) {
|
|
||||||
$headers[] = sprintf('%s: %s', $field, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
$options = $this->curlOpts;
|
|
||||||
$options[CURLOPT_URL] = $rqst->getUri();
|
|
||||||
$options[CURLOPT_PORT] = $rqst->getPort();
|
|
||||||
$options[CURLOPT_HEADER] = 1;
|
|
||||||
$options[CURLOPT_RETURNTRANSFER] = 1;
|
|
||||||
$options[CURLOPT_HTTPHEADER] = $headers;
|
|
||||||
|
|
||||||
// Set the method. Include the body, if needed.
|
|
||||||
switch ($rqst->getMethod()) {
|
|
||||||
case 'GET':
|
|
||||||
$options[CURLOPT_HTTPGET] = 1;
|
|
||||||
break;
|
|
||||||
case 'POST':
|
|
||||||
$options[CURLOPT_POST] = 1;
|
|
||||||
$options[CURLOPT_POSTFIELDS] = $rqst->getBody();
|
|
||||||
break;
|
|
||||||
case 'PUT':
|
|
||||||
$options[CURLOPT_CUSTOMREQUEST] = 'PUT';
|
|
||||||
$options[CURLOPT_POSTFIELDS] = $rqst->getBody();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$options[CURLOPT_CUSTOMREQUEST] = $rqst->getMethod();
|
|
||||||
$options[CURLOPT_POSTFIELDS] = $rqst->getBody();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override cURL options with the user options passed in.
|
|
||||||
if ($curlOpts) {
|
|
||||||
foreach ($curlOpts as $optKey => $optValue) {
|
|
||||||
$options[$optKey] = $optValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the cURL options.
|
|
||||||
curl_setopt_array($ch, $options);
|
|
||||||
|
|
||||||
// Make the cURL request.
|
|
||||||
$result = curl_exec($ch);
|
|
||||||
|
|
||||||
// Throw an exception in the event of a cURL error.
|
|
||||||
if ($result === false) {
|
|
||||||
$error = curl_error($ch);
|
|
||||||
$errno = curl_errno($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
throw new CurlException($error, $errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a reponse to populate and return with data obtained via cURL.
|
|
||||||
$resp = new Response();
|
|
||||||
|
|
||||||
$resp->setStatusCode(curl_getinfo($ch, CURLINFO_HTTP_CODE));
|
|
||||||
|
|
||||||
// Split the result into headers and body.
|
|
||||||
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
||||||
$headers = substr($result, 0, $headerSize);
|
|
||||||
$body = substr($result, $headerSize);
|
|
||||||
|
|
||||||
// Set the body. Do not auto-add the Content-length header.
|
|
||||||
$resp->setBody($body, false);
|
|
||||||
|
|
||||||
// Iterate over the headers line by line and add each one.
|
|
||||||
foreach (explode("\r\n", $headers) as $header) {
|
|
||||||
if (strpos($header, ':')) {
|
|
||||||
list ($headerName, $headerValue) = explode(':', $header, 2);
|
|
||||||
$resp->setHeader($headerName, ltrim($headerValue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
return $resp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Exceptions\CurlException
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Exceptions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception related to a cURL operations. The message and code should correspond
|
|
||||||
* to the cURL error and error number that caused the excpetion.
|
|
||||||
*/
|
|
||||||
class CurlException extends WellRESTedException
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Exceptions\WellRESTedException
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2013 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Exceptions;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Top-level class for custom exceptions thrown by WellRESTed.
|
|
||||||
*/
|
|
||||||
class WellRESTedException extends Exception
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,207 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Handler
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responds to a request based on the HTTP method.
|
|
||||||
*
|
|
||||||
* To use Handler, create a subclass and implement the methods for any HTTP
|
|
||||||
* verbs you would like to support. (get() for GET, post() for POST, etc).
|
|
||||||
*
|
|
||||||
* - Access the request via the protected member $this->request
|
|
||||||
* - Access a map of arguments via $this->args (e.g., URI path variables)
|
|
||||||
* - Modify $this->response to provide the response the instance will return
|
|
||||||
*/
|
|
||||||
abstract class Handler implements HandlerInterface
|
|
||||||
{
|
|
||||||
/** @var array Map of variables to supplement the request, usually path variables. */
|
|
||||||
protected $args;
|
|
||||||
/** @var RequestInterface The HTTP request to respond to. */
|
|
||||||
protected $request;
|
|
||||||
/** @var ResponseInterface The HTTP response to send based on the request. */
|
|
||||||
protected $response;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the handled response.
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request
|
|
||||||
* @param array|null $args
|
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
|
||||||
public function getResponse(RequestInterface $request, array $args = null)
|
|
||||||
{
|
|
||||||
$this->request = $request;
|
|
||||||
$this->args = $args;
|
|
||||||
$this->response = new Response();
|
|
||||||
try {
|
|
||||||
$this->buildResponse();
|
|
||||||
} catch (HttpException $e) {
|
|
||||||
$this->response->setStatusCode($e->getCode());
|
|
||||||
$this->response->setBody($e->getMessage());
|
|
||||||
}
|
|
||||||
return $this->response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepare the Response. Override this method if your subclass needs to
|
|
||||||
* repond to any non-standard HTTP methods. Otherwise, override the
|
|
||||||
* get, post, put, etc. methods.
|
|
||||||
*
|
|
||||||
* An uncaught HttpException (or subclass) will be converted to a response
|
|
||||||
* using the exception's code as the status code and the exceptios message
|
|
||||||
* as the body.
|
|
||||||
*/
|
|
||||||
protected function buildResponse()
|
|
||||||
{
|
|
||||||
switch ($this->request->getMethod()) {
|
|
||||||
case 'GET':
|
|
||||||
$this->get();
|
|
||||||
break;
|
|
||||||
case 'HEAD':
|
|
||||||
$this->head();
|
|
||||||
break;
|
|
||||||
case 'POST':
|
|
||||||
$this->post();
|
|
||||||
break;
|
|
||||||
case 'PUT':
|
|
||||||
$this->put();
|
|
||||||
break;
|
|
||||||
case 'DELETE':
|
|
||||||
$this->delete();
|
|
||||||
break;
|
|
||||||
case 'PATCH':
|
|
||||||
$this->patch();
|
|
||||||
break;
|
|
||||||
case 'OPTIONS':
|
|
||||||
$this->options();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$this->respondWithMethodNotAllowed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for handling HTTP GET requests.
|
|
||||||
*
|
|
||||||
* This method should modify the instance's response member.
|
|
||||||
*/
|
|
||||||
protected function get()
|
|
||||||
{
|
|
||||||
$this->respondWithMethodNotAllowed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for handling HTTP HEAD requests.
|
|
||||||
*
|
|
||||||
* This method should modify the instance's response member.
|
|
||||||
*/
|
|
||||||
protected function head()
|
|
||||||
{
|
|
||||||
// The default function calls the instance's get() method, then sets
|
|
||||||
// the resonse's body member to an empty string.
|
|
||||||
$this->get();
|
|
||||||
$this->response->setBody('', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for handling HTTP POST requests.
|
|
||||||
*
|
|
||||||
* This method should modify the instance's response member.
|
|
||||||
*/
|
|
||||||
protected function post()
|
|
||||||
{
|
|
||||||
$this->respondWithMethodNotAllowed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for handling HTTP PUT requests.
|
|
||||||
*
|
|
||||||
* This method should modify the instance's response member.
|
|
||||||
*/
|
|
||||||
protected function put()
|
|
||||||
{
|
|
||||||
$this->respondWithMethodNotAllowed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for handling HTTP DELETE requests.
|
|
||||||
*
|
|
||||||
* This method should modify the instance's response member.
|
|
||||||
*/
|
|
||||||
protected function delete()
|
|
||||||
{
|
|
||||||
$this->respondWithMethodNotAllowed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for handling HTTP PATCH requests.
|
|
||||||
*
|
|
||||||
* This method should modify the instance's response member.
|
|
||||||
*/
|
|
||||||
protected function patch()
|
|
||||||
{
|
|
||||||
$this->respondWithMethodNotAllowed();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method for handling HTTP OPTION requests.
|
|
||||||
*
|
|
||||||
* This method should modify the instance's response member.
|
|
||||||
*/
|
|
||||||
protected function options()
|
|
||||||
{
|
|
||||||
if ($this->addAllowHeader()) {
|
|
||||||
$this->response->setStatusCode(200);
|
|
||||||
} else {
|
|
||||||
$this->response->setStatusCode(405);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide a default response for unsupported methods.
|
|
||||||
*/
|
|
||||||
protected function respondWithMethodNotAllowed()
|
|
||||||
{
|
|
||||||
$this->response->setStatusCode(405);
|
|
||||||
$this->addAllowHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an array of HTTP verbs this handler supports.
|
|
||||||
*
|
|
||||||
* For example, to support GET and POST, return array("GET","POST");
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function getAllowedMethods()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an Allow: header using the methods returned by getAllowedMethods()
|
|
||||||
*
|
|
||||||
* @return bool The header was added.
|
|
||||||
*/
|
|
||||||
protected function addAllowHeader()
|
|
||||||
{
|
|
||||||
$methods = $this->getAllowedMethods();
|
|
||||||
if ($methods) {
|
|
||||||
$this->response->setHeader('Allow', join($methods, ', '));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\HandlerUnpacker
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for retreiving a handler or response from a callable, string, or instance.
|
|
||||||
*/
|
|
||||||
class HandlerUnpacker
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return the handler or response from a callable, string, or instance.
|
|
||||||
*
|
|
||||||
* @param $handler
|
|
||||||
* @param RequestInterface $request
|
|
||||||
* @param array $args
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function unpack($handler, RequestInterface $request = null, array $args = null)
|
|
||||||
{
|
|
||||||
if (is_callable($handler)) {
|
|
||||||
$handler = $handler($request, $args);
|
|
||||||
} elseif (is_string($handler)) {
|
|
||||||
$handler = new $handler();
|
|
||||||
}
|
|
||||||
return $handler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Interfaces\HandlerInterface
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Interfaces;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a mechanism for obtaining a response given a request.
|
|
||||||
*/
|
|
||||||
interface HandlerInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return the handled response.
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request The request to respond to.
|
|
||||||
* @param array|null $args Optional additional arguments.
|
|
||||||
* @return ResponseInterface The handled response.
|
|
||||||
*/
|
|
||||||
public function getResponse(RequestInterface $request, array $args = null);
|
|
||||||
}
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Interfaces\RequestInterface
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Interfaces;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for representing an HTTP request.
|
|
||||||
*/
|
|
||||||
interface RequestInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return the HTTP verb (e.g., GET, POST, PUT).
|
|
||||||
*
|
|
||||||
* @return string Request verb
|
|
||||||
*/
|
|
||||||
public function getMethod();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the full URI for the request.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getUri();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the hostname portion of the URI
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getHostname();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return path component of the request URI.
|
|
||||||
*
|
|
||||||
* @return string Path component
|
|
||||||
*/
|
|
||||||
public function getPath();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the HTTP port
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getPort();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an associative array of query paramters.
|
|
||||||
*
|
|
||||||
* @return array Query paramters
|
|
||||||
*/
|
|
||||||
public function getQuery();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the value for a given header.
|
|
||||||
*
|
|
||||||
* Per RFC 2616, HTTP headers are case-insensitive. Take care to conform to
|
|
||||||
* this when implementing.
|
|
||||||
*
|
|
||||||
* @param string $headerName Field name of the header
|
|
||||||
* @return string Header field value
|
|
||||||
*/
|
|
||||||
public function getHeader($headerName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an associative array of headers.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getHeaders();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the body of the request.
|
|
||||||
*
|
|
||||||
* @return string Request body
|
|
||||||
*/
|
|
||||||
public function getBody();
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Interfaces\ResponseInterface
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Interfaces;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for representing an HTTP response.
|
|
||||||
*/
|
|
||||||
interface ResponseInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return the HTTP status code
|
|
||||||
*
|
|
||||||
* @return int HTTP status code
|
|
||||||
*/
|
|
||||||
public function getStatusCode();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the status code for the response.
|
|
||||||
*
|
|
||||||
* @param int $statusCode HTTP status code
|
|
||||||
*/
|
|
||||||
public function setStatusCode($statusCode);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the value for a given header.
|
|
||||||
*
|
|
||||||
* Per RFC 2616, HTTP headers are case-insensitive. Take care to conform to
|
|
||||||
* this when implementing.
|
|
||||||
*
|
|
||||||
* @param string $headerName Field name of the header
|
|
||||||
* @return string Header field value
|
|
||||||
*/
|
|
||||||
public function getHeader($headerName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the value for a given header.
|
|
||||||
*
|
|
||||||
* Per RFC 2616, HTTP headers are case-insensitive. Take care to conform to
|
|
||||||
* this when implementing.
|
|
||||||
*
|
|
||||||
* @param string $headerName Field name
|
|
||||||
* @param string $headerValue Field value
|
|
||||||
*/
|
|
||||||
public function setHeader($headerName, $headerValue);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the body of the response.
|
|
||||||
*
|
|
||||||
* @return string Response body
|
|
||||||
*/
|
|
||||||
public function getBody();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the body of the response.
|
|
||||||
*
|
|
||||||
* @param string $body Response body
|
|
||||||
*/
|
|
||||||
public function setBody($body);
|
|
||||||
|
|
||||||
/** Issue the reponse to the client. */
|
|
||||||
public function respond();
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Interfaces\Route\PrefixRouteInterface
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Interfaces\Routes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for routes that map to paths begining with a given prefix or prefixes
|
|
||||||
*/
|
|
||||||
interface PrefixRouteInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns the path prefixes the instance maps to a target handler.
|
|
||||||
*
|
|
||||||
* @return string[] List array of path prefixes.
|
|
||||||
*/
|
|
||||||
public function getPrefixes();
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Interfaces\Route\StaticRouteInterface
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Interfaces\Routes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for routes that map to an exact path or paths
|
|
||||||
*/
|
|
||||||
interface StaticRouteInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Returns the paths the instance maps to a target handler.
|
|
||||||
*
|
|
||||||
* @return string[] List array of paths.
|
|
||||||
*/
|
|
||||||
public function getPaths();
|
|
||||||
}
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Message
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Common base class for the Request and Response classes.
|
|
||||||
*/
|
|
||||||
abstract class Message
|
|
||||||
{
|
|
||||||
/** @var string Entity body of the message */
|
|
||||||
protected $body;
|
|
||||||
/** @var array Associative array of HTTP headers */
|
|
||||||
protected $headers;
|
|
||||||
/**
|
|
||||||
* Associative array of lowercase header field names as keys with
|
|
||||||
* corresponding case sensitive field names from the $headers array as
|
|
||||||
* values.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $headerLookup;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new HTTP message.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->headers = array();
|
|
||||||
$this->headerLookup = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Accessors
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the body payload of the instance.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getBody()
|
|
||||||
{
|
|
||||||
return $this->body;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the body for the request.
|
|
||||||
*
|
|
||||||
* @param string $body
|
|
||||||
*/
|
|
||||||
public function setBody($body)
|
|
||||||
{
|
|
||||||
$this->body = $body;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an associative array of all set headers.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getHeaders()
|
|
||||||
{
|
|
||||||
return $this->headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the value of a given header, or null if it does not exist.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
public function getHeader($name)
|
|
||||||
{
|
|
||||||
$lowerName = strtolower($name);
|
|
||||||
if (isset($this->headerLookup[$lowerName])) {
|
|
||||||
$realName = $this->headerLookup[$lowerName];
|
|
||||||
return $this->headers[$realName];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add or update a header to a given value
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
* @param string $value
|
|
||||||
*/
|
|
||||||
public function setHeader($name, $value)
|
|
||||||
{
|
|
||||||
$lowerName = strtolower($name);
|
|
||||||
|
|
||||||
// Check if a mapping already exists for this header.
|
|
||||||
// Remove it, if needed.
|
|
||||||
if (isset($this->headerLookup[$lowerName])
|
|
||||||
&& $this->headerLookup[$lowerName] !== $name
|
|
||||||
) {
|
|
||||||
unset($this->headers[$this->headerLookup[$lowerName]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the actual header.
|
|
||||||
$this->headers[$name] = $value;
|
|
||||||
|
|
||||||
// Store a mapping to the user's prefered case.
|
|
||||||
$this->headerLookup[$lowerName] = $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return if the response contains a header with the given key.
|
|
||||||
*
|
|
||||||
* @param $name
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function issetHeader($name)
|
|
||||||
{
|
|
||||||
$lowerName = strtolower($name);
|
|
||||||
return isset($this->headerLookup[$lowerName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a header. This method does nothing if the header does not exist.
|
|
||||||
*
|
|
||||||
* @param string $name
|
|
||||||
*/
|
|
||||||
public function unsetHeader($name)
|
|
||||||
{
|
|
||||||
$lowerName = strtolower($name);
|
|
||||||
if (isset($this->headerLookup[$lowerName])) {
|
|
||||||
$realName = $this->headerLookup[$lowerName];
|
|
||||||
if (isset($this->headers[$realName])) {
|
|
||||||
unset($this->headers[$realName]);
|
|
||||||
}
|
|
||||||
unset($this->headerLookup[$lowerName]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,365 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Request
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
use UnexpectedValueException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Request instance represents an HTTP request.
|
|
||||||
*/
|
|
||||||
class Request extends Message implements RequestInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Singleton instance derived from reading the request sent to the server.
|
|
||||||
*
|
|
||||||
* @var Request
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
static protected $theRequest;
|
|
||||||
/** @var string HTTP method or verb for the request */
|
|
||||||
private $method = "GET";
|
|
||||||
/** @var string Scheme for the request (Must be "http" or "https" */
|
|
||||||
private $scheme;
|
|
||||||
/** @var string The Hostname for the request */
|
|
||||||
private $hostname = "localhost";
|
|
||||||
/** @var string Path component of the URI for the request */
|
|
||||||
private $path = "/";
|
|
||||||
/** @var array Array of fragments of the path, delimited by slashes */
|
|
||||||
private $pathParts;
|
|
||||||
/** @var int HTTP Port */
|
|
||||||
private $port = 80;
|
|
||||||
/** @var array Associative array of query parameters */
|
|
||||||
private $query;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new Request instance.
|
|
||||||
*
|
|
||||||
* @param string|null $uri
|
|
||||||
* @param string $method
|
|
||||||
*/
|
|
||||||
public function __construct($uri = null, $method = "GET")
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
if (!is_null($uri)) {
|
|
||||||
$this->setUri($uri);
|
|
||||||
}
|
|
||||||
$this->method = $method;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a reference to the singleton instance of the Request derived
|
|
||||||
* from the server's information about the request sent to the server.
|
|
||||||
*
|
|
||||||
* @return Request
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
public static function getRequest()
|
|
||||||
{
|
|
||||||
if (!isset(self::$theRequest)) {
|
|
||||||
$request = new Request();
|
|
||||||
$request->readHttpRequest();
|
|
||||||
self::$theRequest = $request;
|
|
||||||
}
|
|
||||||
return self::$theRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read and return all request headers from the request issued to the server.
|
|
||||||
*
|
|
||||||
* @return array Associative array of headers
|
|
||||||
*/
|
|
||||||
public static function getRequestHeaders()
|
|
||||||
{
|
|
||||||
// Prefer apache_request_headers is available.
|
|
||||||
if (function_exists('apache_request_headers')) {
|
|
||||||
return apache_request_headers();
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://www.php.net/manual/en/function.getallheaders.php#84262
|
|
||||||
$headers = array();
|
|
||||||
foreach ($_SERVER as $name => $value) {
|
|
||||||
if (substr($name, 0, 5) === "HTTP_") {
|
|
||||||
$headers[str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($name, 5)))))] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set instance members based on the HTTP request sent to the server.
|
|
||||||
*/
|
|
||||||
public function readHttpRequest()
|
|
||||||
{
|
|
||||||
$this->setBody(file_get_contents("php://input"), false);
|
|
||||||
$this->headers = self::getRequestHeaders();
|
|
||||||
|
|
||||||
// Add case insensitive headers to the lookup table.
|
|
||||||
foreach ($this->headers as $key => $value) {
|
|
||||||
$this->headerLookup[strtolower($key)] = $key;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->setMethod($_SERVER["REQUEST_METHOD"]);
|
|
||||||
$this->setUri($_SERVER["REQUEST_URI"]);
|
|
||||||
$this->setHostname($_SERVER["HTTP_HOST"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the method (e.g., GET, POST, PUT, DELETE)
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getMethod()
|
|
||||||
{
|
|
||||||
return $this->method;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign the method (e.g., GET, POST, PUT, DELETE)
|
|
||||||
*
|
|
||||||
* @param string $method
|
|
||||||
*/
|
|
||||||
public function setMethod($method)
|
|
||||||
{
|
|
||||||
$this->method = $method;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the full URI includeing protocol, hostname, path, and query.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getUri()
|
|
||||||
{
|
|
||||||
$uri = $this->scheme . "://" . $this->hostname;
|
|
||||||
if ($this->port !== $this->getDefaultPort()) {
|
|
||||||
$uri .= ":" . $this->port;
|
|
||||||
}
|
|
||||||
if ($this->path !== "/") {
|
|
||||||
$uri .= $this->path;
|
|
||||||
}
|
|
||||||
if ($this->query) {
|
|
||||||
$uri .= "?" . http_build_query($this->query);
|
|
||||||
}
|
|
||||||
return $uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the URI for the Request. This sets the other members: hostname,
|
|
||||||
* path, port, and query.
|
|
||||||
*
|
|
||||||
* @param string $uri
|
|
||||||
*/
|
|
||||||
public function setUri($uri)
|
|
||||||
{
|
|
||||||
// Provide http and localhost if missing.
|
|
||||||
if ($uri[0] === "/") {
|
|
||||||
$uri = "http://localhost" . $uri;
|
|
||||||
} elseif (strpos($uri, "://") === false) {
|
|
||||||
$uri = "http://" . $uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
$parsed = parse_url($uri);
|
|
||||||
|
|
||||||
$scheme = isset($parsed["scheme"]) ? $parsed["scheme"] : "http";
|
|
||||||
$this->setScheme($scheme);
|
|
||||||
|
|
||||||
$host = isset($parsed["host"]) ? $parsed["host"] : "localhost";
|
|
||||||
$this->setHostname($host);
|
|
||||||
|
|
||||||
$port = isset($parsed["port"]) ? (int) $parsed["port"] : $this->getDefaultPort();
|
|
||||||
$this->setPort($port);
|
|
||||||
|
|
||||||
$path = isset($parsed["path"]) ? $parsed["path"] : "/";
|
|
||||||
$this->setPath($path);
|
|
||||||
|
|
||||||
$query = isset($parsed["query"]) ? $parsed["query"] : "";
|
|
||||||
$this->setQuery($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the hostname portion of the URI
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getHostname()
|
|
||||||
{
|
|
||||||
return $this->hostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign the hostname portion of the URI
|
|
||||||
*
|
|
||||||
* @param string $hostname
|
|
||||||
*/
|
|
||||||
public function setHostname($hostname)
|
|
||||||
{
|
|
||||||
$this->hostname = $hostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the scheme for the request (either "http" or "https")
|
|
||||||
*
|
|
||||||
* @param string $scheme
|
|
||||||
* @throws \UnexpectedValueException
|
|
||||||
*/
|
|
||||||
public function setScheme($scheme)
|
|
||||||
{
|
|
||||||
$scheme = strtolower($scheme);
|
|
||||||
if (!in_array($scheme, array("http", "https"))) {
|
|
||||||
throw new UnexpectedValueException('Scheme must be "http" or "https".');
|
|
||||||
}
|
|
||||||
$this->scheme = $scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the scheme for the request (either "http" or "https")
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getScheme()
|
|
||||||
{
|
|
||||||
return $this->scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path part of the URI as a string.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getPath()
|
|
||||||
{
|
|
||||||
return $this->path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the path and pathParts members.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
*/
|
|
||||||
public function setPath($path)
|
|
||||||
{
|
|
||||||
$this->path = $path;
|
|
||||||
if ($path !== "/") {
|
|
||||||
$this->pathParts = explode("/", substr($path, 1));
|
|
||||||
} else {
|
|
||||||
$this->pathParts = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an array of the sections of the path delimited by slashes.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getPathParts()
|
|
||||||
{
|
|
||||||
return $this->pathParts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the HTTP port
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getPort()
|
|
||||||
{
|
|
||||||
return $this->port;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the HTTP port
|
|
||||||
*
|
|
||||||
* @param int $port
|
|
||||||
*/
|
|
||||||
public function setPort($port = null)
|
|
||||||
{
|
|
||||||
if (is_null($port)) {
|
|
||||||
$port = $this->getDefaultPort();
|
|
||||||
}
|
|
||||||
$this->port = $port;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an associative array representing the query.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getQuery()
|
|
||||||
{
|
|
||||||
return $this->query;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the query. The value passed can be a query string of key-value pairs
|
|
||||||
* joined by ampersands or it can be an associative array.
|
|
||||||
*
|
|
||||||
* @param string|array $query
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function setQuery($query)
|
|
||||||
{
|
|
||||||
if (is_string($query)) {
|
|
||||||
$qs = $query;
|
|
||||||
parse_str($qs, $query);
|
|
||||||
} elseif (is_object($query)) {
|
|
||||||
$query = (array) $query;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_array($query)) {
|
|
||||||
ksort($query);
|
|
||||||
$this->query = $query;
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException("Unable to parse query string.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the form fields for this request.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getFormFields()
|
|
||||||
{
|
|
||||||
parse_str($this->getBody(), $fields);
|
|
||||||
return $fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the body by supplying an associative array of form fields.
|
|
||||||
*
|
|
||||||
* In addition, add a "Content-type: application/x-www-form-urlencoded" header
|
|
||||||
*
|
|
||||||
* @param array $fields
|
|
||||||
*/
|
|
||||||
public function setFormFields(array $fields)
|
|
||||||
{
|
|
||||||
$this->setBody(http_build_query($fields));
|
|
||||||
$this->setHeader("Content-type", "application/x-www-form-urlencoded");
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the default port for the currently set scheme.
|
|
||||||
*
|
|
||||||
* @return int;
|
|
||||||
*/
|
|
||||||
protected function getDefaultPort()
|
|
||||||
{
|
|
||||||
return $this->scheme === "http" ? 80 : 443;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,321 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Response
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Response instance allows you to build an HTTP response and send it when
|
|
||||||
* finished.
|
|
||||||
*/
|
|
||||||
class Response extends Message implements ResponseInterface
|
|
||||||
{
|
|
||||||
const CHUNK_SIZE = 1048576;
|
|
||||||
|
|
||||||
/** @var string Path to a file to read and output as the body. */
|
|
||||||
private $bodyFilePath;
|
|
||||||
/**
|
|
||||||
* Text explanation of the HTTP Status Code. You only need to set this if
|
|
||||||
* you are using nonstandard status codes. Otherwise, the instance will
|
|
||||||
* set the when you update the status code.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $reasonPhrase;
|
|
||||||
/** @var int HTTP status code */
|
|
||||||
private $statusCode;
|
|
||||||
/** @var string HTTP protocol and version */
|
|
||||||
private $protocol = "HTTP/1.1";
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new Response instance, optionally passing a status code, body,
|
|
||||||
* and headers.
|
|
||||||
*
|
|
||||||
* @param int $statusCode
|
|
||||||
* @param string $body
|
|
||||||
* @param array $headers
|
|
||||||
*/
|
|
||||||
public function __construct($statusCode = 500, $body = null, $headers = null)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
|
|
||||||
$this->setStatusCode($statusCode);
|
|
||||||
|
|
||||||
if (is_array($headers)) {
|
|
||||||
foreach ($headers as $key => $value) {
|
|
||||||
$this->setHeader($key, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_null($body)) {
|
|
||||||
$this->body = $body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Accessors
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide a new entity body for the respone.
|
|
||||||
* This method also updates the content-length header based on the length
|
|
||||||
* of the new body string.
|
|
||||||
*
|
|
||||||
* @param string $value
|
|
||||||
* @param bool $setContentLength Automatically add a Content-length header
|
|
||||||
*/
|
|
||||||
public function setBody($value, $setContentLength = true)
|
|
||||||
{
|
|
||||||
$this->body = $value;
|
|
||||||
if ($setContentLength === true) {
|
|
||||||
$this->setHeader('Content-Length', strlen($value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide the path to a file to output as the response body.
|
|
||||||
*
|
|
||||||
* @param string $bodyFilePath Filepath
|
|
||||||
*/
|
|
||||||
public function setBodyFilePath($bodyFilePath)
|
|
||||||
{
|
|
||||||
$this->bodyFilePath = $bodyFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the path to the file to output as the response body.
|
|
||||||
*
|
|
||||||
* @return string Filepath
|
|
||||||
*/
|
|
||||||
public function getBodyFilePath()
|
|
||||||
{
|
|
||||||
return $this->bodyFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the portion of the status line explaining the status.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getReasonPhrase()
|
|
||||||
{
|
|
||||||
return $this->reasonPhrase;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true for status codes in the 1xx-3xx range.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function getSuccess()
|
|
||||||
{
|
|
||||||
return $this->statusCode < 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the HTTP status code for the response.
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getStatusCode()
|
|
||||||
{
|
|
||||||
return $this->statusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the HTTP status line, e.g. HTTP/1.1 200 OK.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getStatusLine()
|
|
||||||
{
|
|
||||||
return $this->protocol . " " . $this->statusCode . " " . $this->reasonPhrase;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the status code and optionally the reason phrase explaining it.
|
|
||||||
*
|
|
||||||
* @param int $statusCode
|
|
||||||
* @param string|null $reasonPhrase
|
|
||||||
* @throws InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function setStatusCode($statusCode, $reasonPhrase = null)
|
|
||||||
{
|
|
||||||
$this->statusCode = (int) $statusCode;
|
|
||||||
if (is_null($reasonPhrase)) {
|
|
||||||
switch ($this->statusCode) {
|
|
||||||
case 100:
|
|
||||||
$text = 'Continue';
|
|
||||||
break;
|
|
||||||
case 101:
|
|
||||||
$text = 'Switching Protocols';
|
|
||||||
break;
|
|
||||||
case 200:
|
|
||||||
$text = 'OK';
|
|
||||||
break;
|
|
||||||
case 201:
|
|
||||||
$text = 'Created';
|
|
||||||
break;
|
|
||||||
case 202:
|
|
||||||
$text = 'Accepted';
|
|
||||||
break;
|
|
||||||
case 203:
|
|
||||||
$text = 'Non-Authoritative Information';
|
|
||||||
break;
|
|
||||||
case 204:
|
|
||||||
$text = 'No Content';
|
|
||||||
break;
|
|
||||||
case 205:
|
|
||||||
$text = 'Reset Content';
|
|
||||||
break;
|
|
||||||
case 206:
|
|
||||||
$text = 'Partial Content';
|
|
||||||
break;
|
|
||||||
case 300:
|
|
||||||
$text = 'Multiple Choices';
|
|
||||||
break;
|
|
||||||
case 301:
|
|
||||||
$text = 'Moved Permanently';
|
|
||||||
break;
|
|
||||||
case 302:
|
|
||||||
$text = 'Found';
|
|
||||||
break;
|
|
||||||
case 303:
|
|
||||||
$text = 'See Other';
|
|
||||||
break;
|
|
||||||
case 304:
|
|
||||||
$text = 'Not Modified';
|
|
||||||
break;
|
|
||||||
case 305:
|
|
||||||
$text = 'Use Proxy';
|
|
||||||
break;
|
|
||||||
case 400:
|
|
||||||
$text = 'Bad Request';
|
|
||||||
break;
|
|
||||||
case 401:
|
|
||||||
$text = 'Unauthorized';
|
|
||||||
break;
|
|
||||||
case 402:
|
|
||||||
$text = 'Payment Required';
|
|
||||||
break;
|
|
||||||
case 403:
|
|
||||||
$text = 'Forbidden';
|
|
||||||
break;
|
|
||||||
case 404:
|
|
||||||
$text = 'Not Found';
|
|
||||||
break;
|
|
||||||
case 405:
|
|
||||||
$text = 'Method Not Allowed';
|
|
||||||
break;
|
|
||||||
case 406:
|
|
||||||
$text = 'Not Acceptable';
|
|
||||||
break;
|
|
||||||
case 407:
|
|
||||||
$text = 'Proxy Authentication Required';
|
|
||||||
break;
|
|
||||||
case 408:
|
|
||||||
$text = 'Request Timeout';
|
|
||||||
break;
|
|
||||||
case 409:
|
|
||||||
$text = 'Conflict';
|
|
||||||
break;
|
|
||||||
case 410:
|
|
||||||
$text = 'Gone';
|
|
||||||
break;
|
|
||||||
case 411:
|
|
||||||
$text = 'Length Required';
|
|
||||||
break;
|
|
||||||
case 412:
|
|
||||||
$text = 'Precondition Failed';
|
|
||||||
break;
|
|
||||||
case 413:
|
|
||||||
$text = 'Request Entity Too Large';
|
|
||||||
break;
|
|
||||||
case 414:
|
|
||||||
$text = 'Request-URI Too Long';
|
|
||||||
break;
|
|
||||||
case 415:
|
|
||||||
$text = 'Unsupported Media Type';
|
|
||||||
break;
|
|
||||||
case 500:
|
|
||||||
$text = 'Internal Server Error';
|
|
||||||
break;
|
|
||||||
case 501:
|
|
||||||
$text = 'Not Implemented';
|
|
||||||
break;
|
|
||||||
case 502:
|
|
||||||
$text = 'Bad Gateway';
|
|
||||||
break;
|
|
||||||
case 503:
|
|
||||||
$text = 'Service Unavailable';
|
|
||||||
break;
|
|
||||||
case 504:
|
|
||||||
$text = 'Gateway Timeout';
|
|
||||||
break;
|
|
||||||
case 505:
|
|
||||||
$text = 'HTTP Version Not Supported';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$text = 'Nonstandard';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$this->reasonPhrase = $text;
|
|
||||||
} else {
|
|
||||||
if (is_string($reasonPhrase)) {
|
|
||||||
$this->reasonPhrase = $reasonPhrase;
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException('$reasonPhrase must be a string (or null to use standard HTTP Reason-Phrase');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output the response to the client.
|
|
||||||
*
|
|
||||||
* @param bool $headersOnly Do not include the body, only the headers.
|
|
||||||
*/
|
|
||||||
public function respond($headersOnly = false)
|
|
||||||
{
|
|
||||||
// Output the HTTP status code.
|
|
||||||
header($this->getStatusLine());
|
|
||||||
|
|
||||||
// Output each header.
|
|
||||||
foreach ($this->headers as $header => $value) {
|
|
||||||
header($header . ': ' . $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output the entity body.
|
|
||||||
if (!$headersOnly) {
|
|
||||||
if (isset($this->bodyFilePath) && $this->bodyFilePath && file_exists($this->bodyFilePath)) {
|
|
||||||
$this->outputBodyFile();
|
|
||||||
} else {
|
|
||||||
print $this->body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/** Output the contents of a file */
|
|
||||||
private function outputBodyFile()
|
|
||||||
{
|
|
||||||
$handle = fopen($this->getBodyFilePath(), 'rb');
|
|
||||||
if ($handle !== false) {
|
|
||||||
while (!feof($handle)) {
|
|
||||||
$buffer = fread($handle, self::CHUNK_SIZE);
|
|
||||||
print $buffer;
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\RouteTable
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\Routes\PrefixRouteInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\Routes\StaticRouteInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RouteTable
|
|
||||||
*
|
|
||||||
* A RouteTable uses the request path to dispatche the best-matching handler.
|
|
||||||
*/
|
|
||||||
class RouteTable implements HandlerInterface
|
|
||||||
{
|
|
||||||
/** @var HandlerInterface[] Array of Route objects */
|
|
||||||
private $routes;
|
|
||||||
/** @var array Hash array mapping exact paths to routes */
|
|
||||||
private $staticRoutes;
|
|
||||||
/** @var array Hash array mapping path prefixes to routes */
|
|
||||||
private $prefixRoutes;
|
|
||||||
|
|
||||||
/** Create a new RouteTable */
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->routes = array();
|
|
||||||
$this->prefixRoutes = array();
|
|
||||||
$this->staticRoutes = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the response from the best matching route.
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request
|
|
||||||
* @param array|null $args
|
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
|
||||||
public function getResponse(RequestInterface $request, array $args = null)
|
|
||||||
{
|
|
||||||
$response = null;
|
|
||||||
|
|
||||||
// First check if there is a static route.
|
|
||||||
$response = $this->getStaticResponse($request, $args);
|
|
||||||
if ($response) {
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check prefix routes for any routes that match. Use the longest matching prefix.
|
|
||||||
$response = $this->getPrefixResponse($request, $args);
|
|
||||||
if ($response) {
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try each of the routes.
|
|
||||||
foreach ($this->routes as $route) {
|
|
||||||
/** @var HandlerInterface $route */
|
|
||||||
$response = $route->getResponse($request, $args);
|
|
||||||
if ($response) {
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the response from the matching static route, or null if none match.
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request
|
|
||||||
* @param array|null $args
|
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
|
||||||
private function getStaticResponse(RequestInterface $request, array $args = null)
|
|
||||||
{
|
|
||||||
$path = $request->getPath();
|
|
||||||
if (isset($this->staticRoutes[$path])) {
|
|
||||||
$route = $this->staticRoutes[$path];
|
|
||||||
return $route->getResponse($request, $args);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returning the response from the best-matching prefix handler, or null if none match.
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request
|
|
||||||
* @param array|null $args
|
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
|
||||||
private function getPrefixResponse(RequestInterface $request, array $args = null)
|
|
||||||
{
|
|
||||||
$path = $request->getPath();
|
|
||||||
|
|
||||||
// Find all prefixes that match the start of this path.
|
|
||||||
$prefixes = array_keys($this->prefixRoutes);
|
|
||||||
$matches = array_filter(
|
|
||||||
$prefixes,
|
|
||||||
function ($prefix) use ($path) {
|
|
||||||
return (strrpos($path, $prefix, -strlen($path)) !== false);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($matches) {
|
|
||||||
if (count($matches) > 0) {
|
|
||||||
// If there are multiple matches, sort them to find the one with the longest string length.
|
|
||||||
$compareByLength = function ($a, $b) {
|
|
||||||
return strlen($b) - strlen($a);
|
|
||||||
};
|
|
||||||
usort($matches, $compareByLength);
|
|
||||||
}
|
|
||||||
$route = $this->prefixRoutes[$matches[0]];
|
|
||||||
return $route->getResponse($request, $args);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a new route.
|
|
||||||
*
|
|
||||||
* @param HandlerInterface $route
|
|
||||||
*/
|
|
||||||
public function addRoute(HandlerInterface $route)
|
|
||||||
{
|
|
||||||
if ($route instanceof StaticRouteInterface) {
|
|
||||||
$this->addStaticRoute($route);
|
|
||||||
} elseif ($route instanceof PrefixRouteInterface) {
|
|
||||||
$this->addPrefixRoute($route);
|
|
||||||
} else {
|
|
||||||
$this->routes[] = $route;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a new static route.
|
|
||||||
*
|
|
||||||
* @param StaticRouteInterface $staticRoute
|
|
||||||
*/
|
|
||||||
private function addStaticRoute(StaticRouteInterface $staticRoute)
|
|
||||||
{
|
|
||||||
foreach ($staticRoute->getPaths() as $path) {
|
|
||||||
$this->staticRoutes[$path] = $staticRoute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a new prefix route.
|
|
||||||
*
|
|
||||||
* @param PrefixRouteInterface $prefixRoute
|
|
||||||
*/
|
|
||||||
private function addPrefixRoute(PrefixRouteInterface $prefixRoute)
|
|
||||||
{
|
|
||||||
foreach ($prefixRoute->getPrefixes() as $prefix) {
|
|
||||||
$this->prefixRoutes[$prefix] = $prefixRoute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,256 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Router
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
|
||||||
use pjdietz\WellRESTed\Routes\PrefixRoute;
|
|
||||||
use pjdietz\WellRESTed\Routes\RouteFactory;
|
|
||||||
use pjdietz\WellRESTed\Routes\StaticRoute;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Router
|
|
||||||
*
|
|
||||||
* A Router uses a table of Routes to find the appropriate Handler for a request.
|
|
||||||
*/
|
|
||||||
class Router implements HandlerInterface
|
|
||||||
{
|
|
||||||
/** @var array Hash array of status code => error handler */
|
|
||||||
private $errorHandlers;
|
|
||||||
/** @var RouteTable Collection of routes */
|
|
||||||
private $routeTable;
|
|
||||||
|
|
||||||
/** Create a new Router. */
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->errorHandlers = array();
|
|
||||||
$this->routeTable = new RouteTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a route or series of routes to the Router.
|
|
||||||
*
|
|
||||||
* When adding a single route, the first argument should be the path, path
|
|
||||||
* prefix, URI template, or regex pattern. The method will attempt to find
|
|
||||||
* the best type of route based on this argument and send the remaining
|
|
||||||
* arguments to the route's constructor. @see {RouteFactory::createRoute}
|
|
||||||
*
|
|
||||||
* To add multiple routes, pass arrays where each array contains an argument list.
|
|
||||||
*/
|
|
||||||
public function add()
|
|
||||||
{
|
|
||||||
$factory = new RouteFactory();
|
|
||||||
|
|
||||||
$args = func_get_args();
|
|
||||||
if (count($args) > 1 && is_array($args[0])) {
|
|
||||||
foreach ($args as $argumentList) {
|
|
||||||
$route = call_user_func_array(array($factory, "createRoute"), $argumentList);
|
|
||||||
$this->addRoute($route);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$route = call_user_func_array(array($factory, "createRoute"), $args);
|
|
||||||
$this->addRoute($route);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a series of routes.
|
|
||||||
*
|
|
||||||
* @param array $routes List array of routes
|
|
||||||
*/
|
|
||||||
public function addRoutes(array $routes)
|
|
||||||
{
|
|
||||||
foreach ($routes as $route) {
|
|
||||||
if ($route instanceof HandlerInterface) {
|
|
||||||
$this->addRoute($route);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a new route to the route table.
|
|
||||||
*
|
|
||||||
* @param HandlerInterface $route
|
|
||||||
*/
|
|
||||||
public function addRoute(HandlerInterface $route)
|
|
||||||
{
|
|
||||||
$this->routeTable->addRoute($route);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add custom error handlers.
|
|
||||||
*
|
|
||||||
* @param array $errorHandlers Array mapping integer error codes to handlers
|
|
||||||
*/
|
|
||||||
public function setErrorHandlers(array $errorHandlers)
|
|
||||||
{
|
|
||||||
foreach ($errorHandlers as $statusCode => $errorHandler) {
|
|
||||||
$this->setErrorHandler($statusCode, $errorHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a custom error handler.
|
|
||||||
*
|
|
||||||
* @param integer $statusCode The error status code
|
|
||||||
* @param mixed $errorHandler
|
|
||||||
*/
|
|
||||||
public function setErrorHandler($statusCode, $errorHandler)
|
|
||||||
{
|
|
||||||
$this->errorHandlers[$statusCode] = $errorHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatch the server request through the router and output the response.
|
|
||||||
*
|
|
||||||
* Respond with a 404 Not Found if no route provides a response.
|
|
||||||
* @param array|null $args
|
|
||||||
*/
|
|
||||||
public function respond($args = null)
|
|
||||||
{
|
|
||||||
$request = Request::getRequest();
|
|
||||||
$response = $this->getResponse($request, $args);
|
|
||||||
if (!$response) {
|
|
||||||
$response = $this->getNoRouteResponse($request);
|
|
||||||
}
|
|
||||||
if ($response instanceof ResponseInterface) {
|
|
||||||
$response->respond();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the response built by the handler based on the request
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request
|
|
||||||
* @param array|null $args
|
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
|
||||||
public function getResponse(RequestInterface $request, array $args = null)
|
|
||||||
{
|
|
||||||
$response = $this->tryResponse($this->routeTable, $request, $args);
|
|
||||||
if ($response && $response instanceof ResponseInterface) {
|
|
||||||
// Check if the router has an error handler for this status code.
|
|
||||||
$status = $response->getStatusCode();
|
|
||||||
$errorResponse = $this->getErrorResponse($status, $request, $args, $response);
|
|
||||||
if ($errorResponse) {
|
|
||||||
return $errorResponse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepare a response indicating a 404 Not Found error.
|
|
||||||
*
|
|
||||||
* Rather than subclassing and overriding this method, you may provide an
|
|
||||||
* error handler for status code 404. (@see setErrorHandler)
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request
|
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
|
||||||
protected function getNoRouteResponse(RequestInterface $request)
|
|
||||||
{
|
|
||||||
$response = $this->getErrorResponse(404, $request);
|
|
||||||
if ($response) {
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = new Response(404);
|
|
||||||
$response->setBody('No resource at ' . $request->getPath());
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain a response from the registered error handlers.
|
|
||||||
*
|
|
||||||
* @param int $status HTTP Status Code
|
|
||||||
* @param RequestInterface $request The original request
|
|
||||||
* @param null $args Optional additional data
|
|
||||||
* @param null $response The response providing the error
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
private function getErrorResponse($status, $request, $args = null, $response = null)
|
|
||||||
{
|
|
||||||
if (isset($this->errorHandlers[$status])) {
|
|
||||||
// Pass the response triggering this along to the error handler.
|
|
||||||
$errorArgs = array("response" => $response);
|
|
||||||
if ($args) {
|
|
||||||
$errorArgs = array_merge($args, $errorArgs);
|
|
||||||
}
|
|
||||||
$unpacker = new HandlerUnpacker();
|
|
||||||
$handler = $unpacker->unpack($this->errorHandlers[$status], $request, $errorArgs);
|
|
||||||
if (!is_null($handler) && $handler instanceof HandlerInterface) {
|
|
||||||
return $handler->getResponse($request, $errorArgs);
|
|
||||||
}
|
|
||||||
return $handler;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps the getResponse method in a try-catch.
|
|
||||||
*
|
|
||||||
* In an HttpException is caught while trying to get a response, the method
|
|
||||||
* returns a response based on the HttpException's error code and message.
|
|
||||||
*
|
|
||||||
* @param HandlerInterface $handler The Route or Handler to try.
|
|
||||||
* @param RequestInterface $request The incoming request.
|
|
||||||
* @param array $args The array of arguments.
|
|
||||||
* @return Response
|
|
||||||
*/
|
|
||||||
private function tryResponse($handler, $request, $args)
|
|
||||||
{
|
|
||||||
$response = null;
|
|
||||||
try {
|
|
||||||
$response = $handler->getResponse($request, $args);
|
|
||||||
} catch (HttpException $e) {
|
|
||||||
$response = new Response();
|
|
||||||
$response->setStatusCode($e->getCode());
|
|
||||||
$response->setBody($e->getMessage());
|
|
||||||
}
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////
|
|
||||||
// Deprecated //
|
|
||||||
////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a route for specific prefix
|
|
||||||
*
|
|
||||||
* @deprecated Use {@see addRoute} instead.
|
|
||||||
* @see addRoute
|
|
||||||
* @param array|string $prefixes
|
|
||||||
* @param mixed $handler
|
|
||||||
*/
|
|
||||||
public function setPrefixRoute($prefixes, $handler)
|
|
||||||
{
|
|
||||||
$this->addRoute(new PrefixRoute($prefixes, $handler));
|
|
||||||
trigger_error("Router::setPrefixRoute is deprecated. Use addRoute", E_USER_DEPRECATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a route for a given path
|
|
||||||
*
|
|
||||||
* @deprecated Use {@see addRoute} instead.
|
|
||||||
* @see addRoute
|
|
||||||
* @param array|string $paths
|
|
||||||
* @param mixed $handler
|
|
||||||
*/
|
|
||||||
public function setStaticRoute($paths, $handler)
|
|
||||||
{
|
|
||||||
$this->addRoute(new StaticRoute($paths, $handler));
|
|
||||||
trigger_error("Router::setStaticRoute is deprecated. Use addRoute", E_USER_DEPRECATED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\BaseRoute
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Routes;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\HandlerUnpacker;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for Routes.
|
|
||||||
*/
|
|
||||||
abstract class BaseRoute implements HandlerInterface
|
|
||||||
{
|
|
||||||
/** @var callable|string|HandlerInterface Handler to dispatch */
|
|
||||||
private $target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new route that will dispatch an instance of the given handler.
|
|
||||||
*
|
|
||||||
* $target may be:
|
|
||||||
* - A callable
|
|
||||||
* - A string containing the fully qualified class of a HandlerInterface
|
|
||||||
* - A HandlerInterface instance
|
|
||||||
*
|
|
||||||
* Callable targets should expect to receive the same arguments as would
|
|
||||||
* be passed to a HandlerInterface's getResponse() method. The callable
|
|
||||||
* should return a HandlerInterface instance, a ResponseInterface instance,
|
|
||||||
* or null.
|
|
||||||
*
|
|
||||||
* @param mixed $target Handler to dispatch
|
|
||||||
*/
|
|
||||||
public function __construct($target)
|
|
||||||
{
|
|
||||||
$this->target = $target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the handled response.
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request The request to respond to.
|
|
||||||
* @param array|null $args Optional additional arguments.
|
|
||||||
* @return ResponseInterface The response.
|
|
||||||
*/
|
|
||||||
protected function getResponseFromTarget(RequestInterface $request, array $args = null)
|
|
||||||
{
|
|
||||||
$unpacker = new HandlerUnpacker();
|
|
||||||
$target = $unpacker->unpack($this->target, $request, $args);
|
|
||||||
if (!is_null($target) && $target instanceof HandlerInterface) {
|
|
||||||
return $target->getResponse($request, $args);
|
|
||||||
}
|
|
||||||
return $target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\Routes\PrefixRoute
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Routes;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\Routes\PrefixRouteInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps a list of static URI paths to a Handler
|
|
||||||
*/
|
|
||||||
class PrefixRoute extends BaseRoute implements PrefixRouteInterface
|
|
||||||
{
|
|
||||||
/** @var string[] List of static URI path prefixes*/
|
|
||||||
private $prefixes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new PrefixRoute for a given prefix or prefixes and a handler class.
|
|
||||||
*
|
|
||||||
* @param string|string[] $prefix Path or list of paths the request must match
|
|
||||||
* @param mixed $target Handler to dispatch
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
*
|
|
||||||
* @see BaseRoute for details about $target
|
|
||||||
*/
|
|
||||||
public function __construct($prefix, $target)
|
|
||||||
{
|
|
||||||
parent::__construct($target);
|
|
||||||
if (is_string($prefix)) {
|
|
||||||
$this->prefixes = array($prefix);
|
|
||||||
} elseif (is_array($prefix)) {
|
|
||||||
$this->prefixes = $prefix;
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException("$prefix must be a string or array of string");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/* HandlerInterface */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the handled response.
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request The request to respond to.
|
|
||||||
* @param array|null $args Optional additional arguments.
|
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
|
||||||
public function getResponse(RequestInterface $request, array $args = null)
|
|
||||||
{
|
|
||||||
$requestPath = $request->getPath();
|
|
||||||
foreach ($this->prefixes as $prefix) {
|
|
||||||
if (strrpos($requestPath, $prefix, -strlen($requestPath)) !== false) {
|
|
||||||
return $this->getResponseFromTarget($request, $args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/* PrefixRouteInterface */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the path prefixes the instance maps to a target handler.
|
|
||||||
*
|
|
||||||
* @return string[] List array of path prefixes.
|
|
||||||
*/
|
|
||||||
public function getPrefixes()
|
|
||||||
{
|
|
||||||
return $this->prefixes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\RegexRout
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Routes;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Exceptions\ParseException;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps a regular expression pattern for a URI path to a Handler
|
|
||||||
*/
|
|
||||||
class RegexRoute extends BaseRoute
|
|
||||||
{
|
|
||||||
/** @var string Regular expression pattern for the route. */
|
|
||||||
private $pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new route mapping a regex pattern to a handler.
|
|
||||||
*
|
|
||||||
* @param string $pattern Regular expression the path must match.
|
|
||||||
* @param mixed $target Handler to dispatch
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
*
|
|
||||||
* @see BaseRoute for details about $target
|
|
||||||
*/
|
|
||||||
public function __construct($pattern, $target)
|
|
||||||
{
|
|
||||||
parent::__construct($target);
|
|
||||||
$this->pattern = $pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/* HandlerInterface */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the handled response or null.
|
|
||||||
*
|
|
||||||
* A null return value indicates that this route failed to match the request.
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request The request to respond to.
|
|
||||||
* @param array|null $args Optional additional arguments.
|
|
||||||
* @return ResponseInterface The handled response.
|
|
||||||
* @throws ParseException
|
|
||||||
*/
|
|
||||||
public function getResponse(RequestInterface $request, array $args = null)
|
|
||||||
{
|
|
||||||
$matched = @preg_match($this->getPattern(), $request->getPath(), $matches);
|
|
||||||
if ($matched) {
|
|
||||||
if (is_null($args)) {
|
|
||||||
$args = array();
|
|
||||||
}
|
|
||||||
$args = array_merge($args, $matches);
|
|
||||||
return $this->getResponseFromTarget($request, $args);
|
|
||||||
} elseif ($matched === false) {
|
|
||||||
throw new ParseException("Invalid regular expression: " . $this->getPattern());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the regex pattern for the route.
|
|
||||||
*
|
|
||||||
* @return string Regex pattern
|
|
||||||
*/
|
|
||||||
protected function getPattern()
|
|
||||||
{
|
|
||||||
return $this->pattern;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\RouteCreator
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Routes;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
|
|
||||||
use ReflectionClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for creating routes
|
|
||||||
*/
|
|
||||||
class RouteFactory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create and return a route given a string path, a handler, and optional
|
|
||||||
* extra arguments.
|
|
||||||
*
|
|
||||||
* The method will determine the most appropriate route subclass to use
|
|
||||||
* and will forward the arguments on to the subclass's constructor.
|
|
||||||
*
|
|
||||||
* - Paths with no special characters will generate StaticRoutes
|
|
||||||
* - Paths ending with * will generate PrefixRoutes
|
|
||||||
* - Paths containing URI variables (e.g., {id}) will generate TemplateRoutes
|
|
||||||
* - Regular exressions will generate RegexRoutes
|
|
||||||
*
|
|
||||||
* @param mixed
|
|
||||||
* @return HandlerInterface
|
|
||||||
*/
|
|
||||||
public function createRoute()
|
|
||||||
{
|
|
||||||
$args = func_get_args();
|
|
||||||
$path = $args[0];
|
|
||||||
|
|
||||||
if ($path[0] === "/") {
|
|
||||||
|
|
||||||
// Possible static, prefix, or template
|
|
||||||
|
|
||||||
// PrefixRoutes end with *
|
|
||||||
if (substr($path, -1) === "*") {
|
|
||||||
// Remove the trailing *, since the PrefixRoute constructor doesn't expect it.
|
|
||||||
$path = substr($path, 0, -1);
|
|
||||||
$constructorArgs = $args;
|
|
||||||
$constructorArgs[0] = $path;
|
|
||||||
$reflector = new ReflectionClass("\\pjdietz\\WellRESTed\\Routes\\PrefixRoute");
|
|
||||||
return $reflector->newInstanceArgs($constructorArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TempalateRoutes contain {variable}
|
|
||||||
if (preg_match(TemplateRoute::URI_TEMPLATE_EXPRESSION_RE, $path)) {
|
|
||||||
$reflector = new ReflectionClass("\\pjdietz\\WellRESTed\\Routes\\TemplateRoute");
|
|
||||||
return $reflector->newInstanceArgs($args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// StaticRoute
|
|
||||||
$reflector = new ReflectionClass("\\pjdietz\\WellRESTed\\Routes\\StaticRoute");
|
|
||||||
return $reflector->newInstanceArgs($args);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regex
|
|
||||||
$reflector = new ReflectionClass("\\pjdietz\\WellRESTed\\Routes\\RegexRoute");
|
|
||||||
return $reflector->newInstanceArgs($args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\StaticRoute
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Routes;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\Routes\StaticRouteInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps a list of static URI paths to a Handler
|
|
||||||
*/
|
|
||||||
class StaticRoute extends BaseRoute implements StaticRouteInterface
|
|
||||||
{
|
|
||||||
/** @var string[] List of static URI paths */
|
|
||||||
private $paths;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new StaticRoute for a given path or paths and a handler.
|
|
||||||
*
|
|
||||||
* @param string|array $path Path or list of paths the request must match
|
|
||||||
* @param mixed $target Handler to dispatch
|
|
||||||
* @throws \InvalidArgumentException
|
|
||||||
*
|
|
||||||
* @see BaseRoute for details about $target
|
|
||||||
*/
|
|
||||||
public function __construct($path, $target)
|
|
||||||
{
|
|
||||||
parent::__construct($target);
|
|
||||||
if (is_string($path)) {
|
|
||||||
$this->paths = array($path);
|
|
||||||
} elseif (is_array($path)) {
|
|
||||||
$this->paths = $path;
|
|
||||||
} else {
|
|
||||||
throw new InvalidArgumentException("$path must be a string or array of strings");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/* HandlerInterface */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the handled response.
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request The request to respond to.
|
|
||||||
* @param array|null $args Optional additional arguments.
|
|
||||||
* @return ResponseInterface
|
|
||||||
*/
|
|
||||||
public function getResponse(RequestInterface $request, array $args = null)
|
|
||||||
{
|
|
||||||
if (in_array($request->getPath(), $this->paths)) {
|
|
||||||
return $this->getResponseFromTarget($request, $args);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
/* StaticRouteInterface */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the paths the instance maps to a target handler.
|
|
||||||
*
|
|
||||||
* @return string[] List array of paths.
|
|
||||||
*/
|
|
||||||
public function getPaths()
|
|
||||||
{
|
|
||||||
return $this->paths;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pjdietz\WellRESTed\TemplateRoute
|
|
||||||
*
|
|
||||||
* @author PJ Dietz <pj@pjdietz.com>
|
|
||||||
* @copyright Copyright 2015 by PJ Dietz
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Routes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps a URI template to a Handler
|
|
||||||
*/
|
|
||||||
class TemplateRoute extends RegexRoute
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Regular expression matching URL friendly characters (i.e., letters,
|
|
||||||
* digits, hyphen and underscore)
|
|
||||||
*/
|
|
||||||
const RE_SLUG = '[0-9a-zA-Z\-_]+';
|
|
||||||
/** Regular expression matching digitis */
|
|
||||||
const RE_NUM = '[0-9]+';
|
|
||||||
/** Regular expression matching letters */
|
|
||||||
const RE_ALPHA = '[a-zA-Z]+';
|
|
||||||
/** Regular expression matching letters and digits */
|
|
||||||
const RE_ALPHANUM = '[0-9a-zA-Z]+';
|
|
||||||
/** Regular expression matching a URI template variable (e.g., {id}) */
|
|
||||||
const URI_TEMPLATE_EXPRESSION_RE = '/{([[a-zA-Z][a-zA-Z0-_]*)}/';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new route that matches a URI template to a handler.
|
|
||||||
*
|
|
||||||
* Optionally provide patterns for the variables in the template.
|
|
||||||
*
|
|
||||||
* @param string $template URI template the path must match
|
|
||||||
* @param mixed $target Handler to dispatch
|
|
||||||
* @param string $defaultPattern Regular expression for variables
|
|
||||||
* @param array $variablePatterns Map of variable names and partial regular expression
|
|
||||||
*
|
|
||||||
* @see BaseRoute for details about $target
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
$template,
|
|
||||||
$target,
|
|
||||||
$defaultPattern = self::RE_SLUG,
|
|
||||||
$variablePatterns = null
|
|
||||||
) {
|
|
||||||
$pattern = $this->buildPattern($template, $defaultPattern, $variablePatterns);
|
|
||||||
parent::__construct($pattern, $target);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translate the URI template into a regular expression.
|
|
||||||
*
|
|
||||||
* @param string $template URI template the path must match
|
|
||||||
* @param string $defaultPattern Regular expression for variables
|
|
||||||
* @param array $variablePatterns Map of variable names and regular expression
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function buildPattern($template, $defaultPattern, $variablePatterns)
|
|
||||||
{
|
|
||||||
// Ensure $variablePatterns is an array.
|
|
||||||
if (is_null($variablePatterns)) {
|
|
||||||
$variablePatterns = array();
|
|
||||||
} elseif (is_object($variablePatterns)) {
|
|
||||||
$variablePatterns = (array) $variablePatterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure a default is set.
|
|
||||||
if (!$defaultPattern) {
|
|
||||||
$defaultPattern = self::RE_SLUG;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the template into the pattern
|
|
||||||
$pattern = $template;
|
|
||||||
|
|
||||||
// Escape allowable characters with regex meaning.
|
|
||||||
$pattern = str_replace(
|
|
||||||
array("-", "."),
|
|
||||||
array("\\-", "\\."),
|
|
||||||
$pattern);
|
|
||||||
|
|
||||||
// Replace * with .* AFTER escaping to avoid escaping .*
|
|
||||||
$pattern = str_replace("*", ".*", $pattern);
|
|
||||||
|
|
||||||
// Surround the pattern with delimiters.
|
|
||||||
$pattern = "~^{$pattern}$~";
|
|
||||||
|
|
||||||
// Replace all template variables with matching subpatterns.
|
|
||||||
$callback = function ($matches) use ($variablePatterns, $defaultPattern) {
|
|
||||||
$key = $matches[1];
|
|
||||||
if (isset($variablePatterns[$key])) {
|
|
||||||
$pattern = $variablePatterns[$key];
|
|
||||||
} else {
|
|
||||||
$pattern = $defaultPattern;
|
|
||||||
}
|
|
||||||
return "(?<{$key}>{$pattern})";
|
|
||||||
};
|
|
||||||
$pattern = preg_replace_callback(self::URI_TEMPLATE_EXPRESSION_RE, $callback, $pattern);
|
|
||||||
|
|
||||||
return $pattern;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,242 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use Faker\Factory;
|
|
||||||
use pjdietz\ShamServer\ShamServer;
|
|
||||||
use pjdietz\WellRESTed\Client;
|
|
||||||
use pjdietz\WellRESTed\Request;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Client
|
|
||||||
*/
|
|
||||||
class ClientTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @dataProvider httpMethodProvider
|
|
||||||
*/
|
|
||||||
public function testSendsHttpMethod($method)
|
|
||||||
{
|
|
||||||
$host = "localhost";
|
|
||||||
$port = $this->getRandomNumberInRange(getenv("PORT"));
|
|
||||||
$script = realpath(__DIR__ . "/../../sham-routers/method.php");
|
|
||||||
|
|
||||||
$server = new ShamServer($host, $port, $script);
|
|
||||||
|
|
||||||
$rqst = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$rqst->getUri()->willReturn("http://$host:$port");
|
|
||||||
$rqst->getMethod()->willReturn($method);
|
|
||||||
$rqst->getPort()->willReturn($port);
|
|
||||||
$rqst->getHeaders()->willReturn([]);
|
|
||||||
$rqst->getBody()->willReturn(null);
|
|
||||||
|
|
||||||
$client = new Client();
|
|
||||||
$resp = $client->request($rqst->reveal());
|
|
||||||
$body = trim($resp->getBody());
|
|
||||||
$this->assertEquals($method, $body);
|
|
||||||
|
|
||||||
$server->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function httpMethodProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["GET"],
|
|
||||||
["POST"],
|
|
||||||
["PUT"],
|
|
||||||
["DELETE"],
|
|
||||||
["PATCH"],
|
|
||||||
["OPTIONS"]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider httpHeaderProvider
|
|
||||||
*/
|
|
||||||
public function testSendsHttpHeaders($headerKey, $headerValue)
|
|
||||||
{
|
|
||||||
$host = "localhost";
|
|
||||||
$port = $this->getRandomNumberInRange(getenv("PORT"));
|
|
||||||
$script = realpath(__DIR__ . "/../../sham-routers/headers.php");
|
|
||||||
|
|
||||||
$server = new ShamServer($host, $port, $script);
|
|
||||||
|
|
||||||
$rqst = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$rqst->getUri()->willReturn("http://$host:$port");
|
|
||||||
$rqst->getMethod()->willReturn("GET");
|
|
||||||
$rqst->getPort()->willReturn($port);
|
|
||||||
$rqst->getHeaders()->willReturn([$headerKey => $headerValue]);
|
|
||||||
$rqst->getBody()->willReturn(null);
|
|
||||||
|
|
||||||
$client = new Client();
|
|
||||||
$resp = $client->request($rqst->reveal());
|
|
||||||
$headers = json_decode($resp->getBody());
|
|
||||||
$this->assertEquals($headerValue, $headers->{$headerKey});
|
|
||||||
|
|
||||||
$server->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function httpHeaderProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["Cache-Control", "max-age=0"],
|
|
||||||
["X-Custom-Header", "custom value"],
|
|
||||||
["Accept-Charset", "utf-8"]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider bodyProvider
|
|
||||||
*/
|
|
||||||
public function testSendsBody($body)
|
|
||||||
{
|
|
||||||
$host = "localhost";
|
|
||||||
$port = $this->getRandomNumberInRange(getenv("PORT"));
|
|
||||||
$script = realpath(__DIR__ . "/../../sham-routers/body.php");
|
|
||||||
$server = new ShamServer($host, $port, $script);
|
|
||||||
|
|
||||||
$rqst = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$rqst->getUri()->willReturn("http://$host:$port");
|
|
||||||
$rqst->getMethod()->willReturn("POST");
|
|
||||||
$rqst->getPort()->willReturn($port);
|
|
||||||
$rqst->getHeaders()->willReturn([]);
|
|
||||||
$rqst->getBody()->willReturn($body);
|
|
||||||
|
|
||||||
$client = new Client();
|
|
||||||
$resp = $client->request($rqst->reveal());
|
|
||||||
$this->assertEquals($body, $resp->getBody());
|
|
||||||
$server->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function bodyProvider()
|
|
||||||
{
|
|
||||||
$faker = Factory::create();
|
|
||||||
return [
|
|
||||||
[$faker->text()],
|
|
||||||
[$faker->text()],
|
|
||||||
[$faker->text()]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider formProvider
|
|
||||||
*/
|
|
||||||
public function testSendsForm($form)
|
|
||||||
{
|
|
||||||
$host = "localhost";
|
|
||||||
$port = $this->getRandomNumberInRange(getenv("PORT"));
|
|
||||||
$script = realpath(__DIR__ . "/../../sham-routers/formFields.php");
|
|
||||||
$server = new ShamServer($host, $port, $script);
|
|
||||||
|
|
||||||
$rqst = new Request("http://$host:$port");
|
|
||||||
$rqst->setMethod("POST");
|
|
||||||
$rqst->setFormFields($form);
|
|
||||||
$client = new Client();
|
|
||||||
$resp = $client->request($rqst);
|
|
||||||
|
|
||||||
$body = json_decode($resp->getBody(), true);
|
|
||||||
$this->assertEquals($form, $body);
|
|
||||||
|
|
||||||
$server->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function formProvider()
|
|
||||||
{
|
|
||||||
$faker = Factory::create();
|
|
||||||
return [
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"firstName" => $faker->firstName,
|
|
||||||
"lastName" => $faker->lastName,
|
|
||||||
"email" => $faker->email
|
|
||||||
]
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetsCustomCurlOptionsOnInstantiation()
|
|
||||||
{
|
|
||||||
$host = "localhost";
|
|
||||||
$port = $this->getRandomNumberInRange(getenv("PORT"));
|
|
||||||
$script = realpath(__DIR__ . "/../../sham-routers/headers.php");
|
|
||||||
$server = new ShamServer($host, $port, $script);
|
|
||||||
|
|
||||||
$rqst = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$rqst->getUri()->willReturn("http://$host:$port");
|
|
||||||
$rqst->getMethod()->willReturn("GET");
|
|
||||||
$rqst->getPort()->willReturn($port);
|
|
||||||
$rqst->getHeaders()->willReturn([]);
|
|
||||||
$rqst->getBody()->willReturn(null);
|
|
||||||
|
|
||||||
$cookieValue = "key=value";
|
|
||||||
$client = new Client([CURLOPT_COOKIE => $cookieValue]);
|
|
||||||
$resp = $client->request($rqst->reveal());
|
|
||||||
$headers = json_decode($resp->getBody());
|
|
||||||
$this->assertEquals($cookieValue, $headers->Cookie);
|
|
||||||
|
|
||||||
$server->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetsCustomCurlOptionsOnRequest()
|
|
||||||
{
|
|
||||||
$host = "localhost";
|
|
||||||
$port = $this->getRandomNumberInRange(getenv("PORT"));
|
|
||||||
$script = realpath(__DIR__ . "/../../sham-routers/headers.php");
|
|
||||||
$server = new ShamServer($host, $port, $script);
|
|
||||||
|
|
||||||
$rqst = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$rqst->getUri()->willReturn("http://$host:$port");
|
|
||||||
$rqst->getMethod()->willReturn("GET");
|
|
||||||
$rqst->getPort()->willReturn($port);
|
|
||||||
$rqst->getHeaders()->willReturn([]);
|
|
||||||
$rqst->getBody()->willReturn(null);
|
|
||||||
|
|
||||||
$cookieValue = "key=value";
|
|
||||||
$client = new Client();
|
|
||||||
$resp = $client->request($rqst->reveal(), [CURLOPT_COOKIE => $cookieValue]);
|
|
||||||
$headers = json_decode($resp->getBody());
|
|
||||||
$this->assertEquals($cookieValue, $headers->Cookie);
|
|
||||||
|
|
||||||
$server->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider curlErrorProvider
|
|
||||||
* @expectedException \pjdietz\WellRESTed\Exceptions\CurlException
|
|
||||||
*/
|
|
||||||
public function testThrowsCurlException($uri, $opts)
|
|
||||||
{
|
|
||||||
$rqst = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$rqst->getUri()->willReturn($uri);
|
|
||||||
$rqst->getMethod()->willReturn("GET");
|
|
||||||
$rqst->getPort()->willReturn(parse_url($uri, PHP_URL_PORT));
|
|
||||||
$rqst->getHeaders()->willReturn([]);
|
|
||||||
$rqst->getBody()->willReturn(null);
|
|
||||||
|
|
||||||
$client = new Client();
|
|
||||||
$client->request($rqst->reveal(), $opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function curlErrorProvider()
|
|
||||||
{
|
|
||||||
$port = $this->getRandomNumberInRange(getenv("FAIL_PORT"));
|
|
||||||
return [
|
|
||||||
["http://localhost:{$port}", [
|
|
||||||
CURLOPT_FAILONERROR, true,
|
|
||||||
CURLOPT_TIMEOUT_MS, 10
|
|
||||||
]],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getRandomNumberInRange($range)
|
|
||||||
{
|
|
||||||
static $pattern = '/(\d+)\-(\d+)/';
|
|
||||||
if (preg_match($pattern, $range, $matches)) {
|
|
||||||
$lower = $matches[1];
|
|
||||||
$upper = $matches[2];
|
|
||||||
return rand($lower, $upper);
|
|
||||||
} else {
|
|
||||||
return $range;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Exceptions\HttpExceptions\NotFoundException;
|
|
||||||
use pjdietz\WellRESTed\Handler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Handler
|
|
||||||
*/
|
|
||||||
class HandlerTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
public function testReturnsResponse()
|
|
||||||
{
|
|
||||||
$request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$handler = $this->getMockForAbstractClass("\\pjdietz\\WellRESTed\\Handler");
|
|
||||||
$response = $handler->getResponse($request->reveal());
|
|
||||||
$this->assertNotNull($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider verbProvider
|
|
||||||
*/
|
|
||||||
public function testCallsMethodForHttpVerb($method)
|
|
||||||
{
|
|
||||||
$request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$request->getMethod()->willReturn($method);
|
|
||||||
$handler = $this->getMockForAbstractClass("\\pjdietz\\WellRESTed\\Handler");
|
|
||||||
$response = $handler->getResponse($request->reveal());
|
|
||||||
$this->assertNotNull($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function verbProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["GET"],
|
|
||||||
["POST"],
|
|
||||||
["PUT"],
|
|
||||||
["DELETE"],
|
|
||||||
["HEAD"],
|
|
||||||
["PATCH"],
|
|
||||||
["OPTIONS"],
|
|
||||||
["NOTALLOWED"]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testTranslatesHttpExceptionToResponse()
|
|
||||||
{
|
|
||||||
$request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$request->getMethod()->willReturn("GET");
|
|
||||||
|
|
||||||
$handler = new ExceptionHandler();
|
|
||||||
$response = $handler->getResponse($request->reveal());
|
|
||||||
$this->assertEquals(404, $response->getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testProvidesAllowHeader()
|
|
||||||
{
|
|
||||||
$request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$request->getMethod()->willReturn("OPTIONS");
|
|
||||||
|
|
||||||
$handler = new OptionsHandler();
|
|
||||||
$response = $handler->getResponse($request->reveal());
|
|
||||||
$this->assertEquals("GET, POST", $response->getHeader("Allow"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class OptionsHandler extends Handler
|
|
||||||
{
|
|
||||||
protected function getAllowedMethods()
|
|
||||||
{
|
|
||||||
return ["GET","POST"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExceptionHandler extends Handler
|
|
||||||
{
|
|
||||||
protected function get()
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\HandlerUnpacker;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
|
|
||||||
use pjdietz\WellRESTed\Interfaces\RequestInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\HandlerUnpacker
|
|
||||||
*/
|
|
||||||
class HandlerUnpackerTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
public function testUnpacksFromCallable()
|
|
||||||
{
|
|
||||||
$handlerContainer = function () {
|
|
||||||
return new HandlerUnpackerTest_Handler();
|
|
||||||
};
|
|
||||||
$handlerUnpacker = new HandlerUnpacker();
|
|
||||||
$handler = $handlerUnpacker->unpack($handlerContainer);
|
|
||||||
$this->assertInstanceOf("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface", $handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPropagatesArgumentsToCallable()
|
|
||||||
{
|
|
||||||
$request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$args = [
|
|
||||||
"cat" => "Molly"
|
|
||||||
];
|
|
||||||
|
|
||||||
$callableRequest = null;
|
|
||||||
$callableArguments = null;
|
|
||||||
|
|
||||||
$handlerCallable = function ($rqst, $args) use (&$callableRequest, &$callableArguments) {
|
|
||||||
$callableRequest = $rqst;
|
|
||||||
$callableArguments = $args;
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
$handlerUnpacker = new HandlerUnpacker();
|
|
||||||
$handlerUnpacker->unpack($handlerCallable, $request->reveal(), $args);
|
|
||||||
|
|
||||||
$this->assertSame($callableRequest, $request->reveal());
|
|
||||||
$this->assertSame($callableArguments, $args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testUnpacksFromString()
|
|
||||||
{
|
|
||||||
$handlerContainer = __NAMESPACE__ . "\\HandlerUnpackerTest_Handler";
|
|
||||||
$handlerUnpacker = new HandlerUnpacker();
|
|
||||||
$handler = $handlerUnpacker->unpack($handlerContainer);
|
|
||||||
$this->assertInstanceOf("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface", $handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testUnpacksInstance()
|
|
||||||
{
|
|
||||||
$handler = new HandlerUnpackerTest_Handler();
|
|
||||||
$handlerUnpacker = new HandlerUnpacker();
|
|
||||||
$handler = $handlerUnpacker->unpack($handler);
|
|
||||||
$this->assertInstanceOf("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface", $handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HandlerUnpackerTest_Handler implements HandlerInterface
|
|
||||||
{
|
|
||||||
public function getResponse(RequestInterface $request, array $args = null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Message
|
|
||||||
*/
|
|
||||||
class MessageTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
public function testSetsBody()
|
|
||||||
{
|
|
||||||
$message = $this->getMockForAbstractClass("\\pjdietz\\WellRESTed\\Message");
|
|
||||||
$body = "This is the body";
|
|
||||||
$message->setBody($body);
|
|
||||||
$this->assertEquals($body, $message->getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testBodyIsNullByDefault()
|
|
||||||
{
|
|
||||||
$message = $this->getMockForAbstractClass("\\pjdietz\\WellRESTed\\Message");
|
|
||||||
$this->assertNull($message->getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider headerProvider
|
|
||||||
*/
|
|
||||||
public function testSetsHeader($headerKey, $headerValue, $badCapsKey)
|
|
||||||
{
|
|
||||||
$message = $this->getMockForAbstractClass("\\pjdietz\\WellRESTed\\Message");
|
|
||||||
$message->setHeader($headerKey, $headerValue);
|
|
||||||
$this->assertEquals($headerValue, $message->getHeader($badCapsKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider headerProvider
|
|
||||||
*/
|
|
||||||
public function testUpdatesHeader($headerKey, $headerValue, $testName)
|
|
||||||
{
|
|
||||||
$message = $this->getMockForAbstractClass("\\pjdietz\\WellRESTed\\Message");
|
|
||||||
$message->setHeader($headerKey, $headerValue);
|
|
||||||
$newValue = "newvalue";
|
|
||||||
$message->setHeader($testName, "newvalue");
|
|
||||||
$this->assertEquals($newValue, $message->getHeader($testName));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider headerProvider
|
|
||||||
*/
|
|
||||||
public function testNonsetHeaderIsNull()
|
|
||||||
{
|
|
||||||
$message = $this->getMockForAbstractClass("\\pjdietz\\WellRESTed\\Message");
|
|
||||||
$this->assertNull($message->getHeader("no-header"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider headerProvider
|
|
||||||
*/
|
|
||||||
public function testUnsetHeaderIsNull($headerKey, $headerValue, $testName)
|
|
||||||
{
|
|
||||||
$message = $this->getMockForAbstractClass("\\pjdietz\\WellRESTed\\Message");
|
|
||||||
$message->setHeader($headerKey, $headerValue);
|
|
||||||
$message->unsetHeader($testName);
|
|
||||||
$this->assertNull($message->getHeader($headerKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider headerProvider
|
|
||||||
*/
|
|
||||||
public function testChecksIfHeaderIsSet($headerKey, $headerValue, $testName)
|
|
||||||
{
|
|
||||||
$message = $this->getMockForAbstractClass("\\pjdietz\\WellRESTed\\Message");
|
|
||||||
$message->setHeader($headerKey, $headerValue);
|
|
||||||
$this->assertTrue($message->issetHeader($testName));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function headerProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["Accept-Charset", "utf-8", "accept-charset"],
|
|
||||||
["Accept-Encoding", "gzip, deflate", "ACCEPT-ENCODING"],
|
|
||||||
["Cache-Control", "no-cache", "Cache-Control"],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testReturnsListOfHeaders()
|
|
||||||
{
|
|
||||||
$message = $this->getMockForAbstractClass("\\pjdietz\\WellRESTed\\Message");
|
|
||||||
$headers = $this->headerProvider();
|
|
||||||
foreach ($headers as $header) {
|
|
||||||
$message->setHeader($header[0], $header[1]);
|
|
||||||
}
|
|
||||||
$this->assertEquals(count($headers), count($message->getHeaders()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,404 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use Faker\Factory;
|
|
||||||
use pjdietz\WellRESTed\Request;
|
|
||||||
use pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Request
|
|
||||||
*/
|
|
||||||
class RequestTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @dataProvider methodProvider
|
|
||||||
*/
|
|
||||||
public function testSetsMethod($method)
|
|
||||||
{
|
|
||||||
$rqst = new Request();
|
|
||||||
$rqst->setMethod($method);
|
|
||||||
$this->assertEquals($method, $rqst->getMethod());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function methodProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["GET"],
|
|
||||||
["POST"],
|
|
||||||
["PUT"],
|
|
||||||
["DELETE"],
|
|
||||||
["OPTIONS"],
|
|
||||||
["HEAD"]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider uriProvider
|
|
||||||
*/
|
|
||||||
public function testSetsUri($uri, $data)
|
|
||||||
{
|
|
||||||
$rqst = new Request($uri);
|
|
||||||
$this->assertEquals($data->uri, $rqst->getUri());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider uriProvider
|
|
||||||
*/
|
|
||||||
public function testParsesSchemeFromUri($uri, $data)
|
|
||||||
{
|
|
||||||
$rqst = new Request($uri);
|
|
||||||
$this->assertEquals($data->scheme, $rqst->getScheme());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider uriProvider
|
|
||||||
*/
|
|
||||||
public function testParsesHostnameFromUri($uri, $data)
|
|
||||||
{
|
|
||||||
$rqst = new Request($uri);
|
|
||||||
$this->assertEquals($data->hostname, $rqst->getHostname());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider uriProvider
|
|
||||||
*/
|
|
||||||
public function testParsesPortFromUri($uri, $data)
|
|
||||||
{
|
|
||||||
$rqst = new Request($uri);
|
|
||||||
$this->assertEquals($data->port, $rqst->getPort());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider uriProvider
|
|
||||||
*/
|
|
||||||
public function testParsesPathFromUri($uri, $data)
|
|
||||||
{
|
|
||||||
$rqst = new Request($uri);
|
|
||||||
$this->assertEquals($data->path, $rqst->getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider uriProvider
|
|
||||||
*/
|
|
||||||
public function testParsesPathPartsFromUri($uri, $data)
|
|
||||||
{
|
|
||||||
$rqst = new Request($uri);
|
|
||||||
$this->assertEquals($data->parts, $rqst->getPathParts());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider uriProvider
|
|
||||||
*/
|
|
||||||
public function testParsesQueryFromUri($uri, $data)
|
|
||||||
{
|
|
||||||
$rqst = new Request($uri);
|
|
||||||
$this->assertEquals($data->query, $rqst->getQuery());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uriProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
[
|
|
||||||
"http://www.google.com",
|
|
||||||
(object) [
|
|
||||||
"uri" => "http://www.google.com",
|
|
||||||
"scheme" => "http",
|
|
||||||
"hostname" => "www.google.com",
|
|
||||||
"port" => 80,
|
|
||||||
"path" => "/",
|
|
||||||
"query" => [],
|
|
||||||
"parts" => []
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"https://www.google.com",
|
|
||||||
(object) [
|
|
||||||
"uri" => "https://www.google.com",
|
|
||||||
"scheme" => "https",
|
|
||||||
"hostname" => "www.google.com",
|
|
||||||
"port" => 443,
|
|
||||||
"path" => "/",
|
|
||||||
"query" => [],
|
|
||||||
"parts" => []
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"localhost:8080/my/path/with/parts",
|
|
||||||
(object) [
|
|
||||||
"uri" => "http://localhost:8080/my/path/with/parts",
|
|
||||||
"scheme" => "http",
|
|
||||||
"hostname" => "localhost",
|
|
||||||
"port" => 8080,
|
|
||||||
"path" => "/my/path/with/parts",
|
|
||||||
"query" => [],
|
|
||||||
"parts" => ["my", "path", "with", "parts"]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"localhost?dog=bear&cat=molly",
|
|
||||||
(object) [
|
|
||||||
"uri" => "http://localhost?cat=molly&dog=bear",
|
|
||||||
"scheme" => "http",
|
|
||||||
"hostname" => "localhost",
|
|
||||||
"port" => 80,
|
|
||||||
"path" => "/",
|
|
||||||
"query" => [
|
|
||||||
"cat" => "molly",
|
|
||||||
"dog" => "bear"
|
|
||||||
],
|
|
||||||
"parts" => []
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"/my-page?id=2",
|
|
||||||
(object) [
|
|
||||||
"uri" => "http://localhost/my-page?id=2",
|
|
||||||
"scheme" => "http",
|
|
||||||
"hostname" => "localhost",
|
|
||||||
"port" => 80,
|
|
||||||
"path" => "/my-page",
|
|
||||||
"query" => [
|
|
||||||
"id" => "2"
|
|
||||||
],
|
|
||||||
"parts" => ["my-page"]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider formProvider
|
|
||||||
*/
|
|
||||||
public function testEncodesFormFields($form)
|
|
||||||
{
|
|
||||||
$rqst = new Request();
|
|
||||||
$rqst->setFormFields($form);
|
|
||||||
$body = $rqst->getBody();
|
|
||||||
parse_str($body, $fields);
|
|
||||||
$this->assertEquals($form, $fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider formProvider
|
|
||||||
*/
|
|
||||||
public function testDecodesFormFields($form)
|
|
||||||
{
|
|
||||||
$rqst = new Request();
|
|
||||||
$rqst->setFormFields($form);
|
|
||||||
$fields = $rqst->getFormFields();
|
|
||||||
$this->assertEquals($form, $fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function formProvider()
|
|
||||||
{
|
|
||||||
$faker = Factory::create();
|
|
||||||
return [
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"firstName" => $faker->firstName,
|
|
||||||
"lastName" => $faker->lastName,
|
|
||||||
"username" => $faker->userName
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider queryProvider
|
|
||||||
*/
|
|
||||||
public function testSetsQuery($input, $expected)
|
|
||||||
{
|
|
||||||
$rqst = new Request();
|
|
||||||
$rqst->setQuery($input);
|
|
||||||
$this->assertEquals($expected, $rqst->getQuery());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function queryProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
[
|
|
||||||
"cat=molly&dog=bear",
|
|
||||||
[
|
|
||||||
"cat" => "molly",
|
|
||||||
"dog" => "bear"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
["id" => "1"],
|
|
||||||
["id" => "1"]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
(object)["dog" => "bear"],
|
|
||||||
["dog" => "bear"]
|
|
||||||
],
|
|
||||||
["", []],
|
|
||||||
[[], []],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider invalidQueryProvider
|
|
||||||
* @expectedException \InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function testThrowsExceptionOnInvalidQuery($query)
|
|
||||||
{
|
|
||||||
$rqst = new Request();
|
|
||||||
$rqst->setQuery($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function invalidQueryProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
[11],
|
|
||||||
[false],
|
|
||||||
[true],
|
|
||||||
[null]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider invalidSchemeProvider
|
|
||||||
* @expectedException \UnexpectedValueException
|
|
||||||
*/
|
|
||||||
public function testThrowsExceptionOnInvalidScheme($scheme)
|
|
||||||
{
|
|
||||||
$rqst = new Request();
|
|
||||||
$rqst->setScheme($scheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function invalidSchemeProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
[""],
|
|
||||||
["ftp"],
|
|
||||||
["ssh"],
|
|
||||||
[null],
|
|
||||||
[0]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider defaultPortProvider
|
|
||||||
*/
|
|
||||||
public function testSetsDefaultPort($scheme, $port)
|
|
||||||
{
|
|
||||||
$rqst = new Request("http://localhost:9999");
|
|
||||||
$rqst->setScheme($scheme);
|
|
||||||
$rqst->setPort();
|
|
||||||
$this->assertEquals($port, $rqst->getPort());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function defaultPortProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["http", 80],
|
|
||||||
["https", 443]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider serverProvider
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testReadsServerRequestMethod($serverVars, $expected)
|
|
||||||
{
|
|
||||||
$_SERVER = array_merge($_SERVER, $serverVars);
|
|
||||||
$rqst = new Request();
|
|
||||||
$rqst->readHttpRequest();
|
|
||||||
$this->assertEquals($expected->method, $rqst->getMethod());
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @dataProvider serverProvider
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testReadsServerRequestHost($serverVars, $expected)
|
|
||||||
{
|
|
||||||
$_SERVER = array_merge($_SERVER, $serverVars);
|
|
||||||
$rqst = new Request();
|
|
||||||
$rqst->readHttpRequest();
|
|
||||||
$this->assertEquals($expected->host, $rqst->getHostname());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider serverProvider
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testReadsServerRequestPath($serverVars, $expected)
|
|
||||||
{
|
|
||||||
$_SERVER = array_merge($_SERVER, $serverVars);
|
|
||||||
$rqst = new Request();
|
|
||||||
$rqst->readHttpRequest();
|
|
||||||
$this->assertEquals($expected->path, $rqst->getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider serverProvider
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testReadsServerRequestHeaders($serverVars, $expected)
|
|
||||||
{
|
|
||||||
$_SERVER = array_merge($_SERVER, $serverVars);
|
|
||||||
$rqst = new Request();
|
|
||||||
$rqst->readHttpRequest();
|
|
||||||
foreach ($expected->headers as $name => $value) {
|
|
||||||
$this->assertEquals($value, $rqst->getHeader($name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testReadsStaticRequest()
|
|
||||||
{
|
|
||||||
$data = $this->serverProvider();
|
|
||||||
$serverVars = $data[0][0];
|
|
||||||
$expected = $data[0][1];
|
|
||||||
|
|
||||||
$_SERVER = array_merge($_SERVER, $serverVars);
|
|
||||||
$rqst = Request::getRequest();
|
|
||||||
$this->assertEquals($expected->host, $rqst->getHostname());
|
|
||||||
|
|
||||||
$rqst2 = Request::getRequest();
|
|
||||||
$this->assertSame($rqst2, $rqst);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function serverProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"REQUEST_METHOD" => "GET",
|
|
||||||
"REQUEST_URI" => "/",
|
|
||||||
"HTTP_ACCEPT_CHARSET" => "utf-8",
|
|
||||||
"HTTP_HOST" => "localhost"
|
|
||||||
],
|
|
||||||
(object) [
|
|
||||||
"method" => "GET",
|
|
||||||
"host" => "localhost",
|
|
||||||
"path" => "/",
|
|
||||||
"headers" => [
|
|
||||||
"Accept-charset" => "utf-8"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"REQUEST_METHOD" => "POST",
|
|
||||||
"REQUEST_URI" => "/my/page",
|
|
||||||
"HTTP_ACCEPT_CHARSET" => "utf-8",
|
|
||||||
"HTTP_HOST" => "mysite.com"
|
|
||||||
],
|
|
||||||
(object) [
|
|
||||||
"method" => "POST",
|
|
||||||
"host" => "mysite.com",
|
|
||||||
"path" => "/my/page",
|
|
||||||
"headers" => [
|
|
||||||
"Accept-charset" => "utf-8"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,213 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use Faker\Factory;
|
|
||||||
use pjdietz\WellRESTed\Response;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Response
|
|
||||||
*/
|
|
||||||
class ResponseTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @dataProvider statusCodeProvider
|
|
||||||
*/
|
|
||||||
public function testSetsStatusCodeInConstructor($statusCode, $reasonPhrase, $statusLine)
|
|
||||||
{
|
|
||||||
$resp = new Response($statusCode);
|
|
||||||
$this->assertEquals($statusCode, $resp->getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider statusCodeProvider
|
|
||||||
*/
|
|
||||||
public function testProvidesReasonPhrase($statusCode, $reasonPhrase, $statusLine)
|
|
||||||
{
|
|
||||||
$resp = new Response();
|
|
||||||
$resp->setStatusCode($statusCode, $reasonPhrase);
|
|
||||||
$this->assertEquals(substr($statusLine, 13), $resp->getReasonPhrase());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider statusCodeProvider
|
|
||||||
*/
|
|
||||||
public function testProvidesStatusLine($statusCode, $reasonPhrase, $statusLine)
|
|
||||||
{
|
|
||||||
$resp = new Response();
|
|
||||||
$resp->setStatusCode($statusCode, $reasonPhrase);
|
|
||||||
$this->assertEquals($statusLine, $resp->getStatusLine());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider statusCodeProvider
|
|
||||||
*/
|
|
||||||
public function testDeterminesSuccessFromStatusCode($statusCode, $reasonPhrase, $statusLine)
|
|
||||||
{
|
|
||||||
$resp = new Response();
|
|
||||||
$resp->setStatusCode($statusCode, $reasonPhrase);
|
|
||||||
if ($statusCode < 400) {
|
|
||||||
$this->assertTrue($resp->getSuccess());
|
|
||||||
} else {
|
|
||||||
$this->assertFalse($resp->getSuccess());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function statusCodeProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
[100, null, "HTTP/1.1 100 Continue"],
|
|
||||||
[101, null, "HTTP/1.1 101 Switching Protocols"],
|
|
||||||
[200, null, "HTTP/1.1 200 OK"],
|
|
||||||
[201, null, "HTTP/1.1 201 Created"],
|
|
||||||
[202, null, "HTTP/1.1 202 Accepted"],
|
|
||||||
[203, null, "HTTP/1.1 203 Non-Authoritative Information"],
|
|
||||||
[204, null, "HTTP/1.1 204 No Content"],
|
|
||||||
[205, null, "HTTP/1.1 205 Reset Content"],
|
|
||||||
[206, null, "HTTP/1.1 206 Partial Content"],
|
|
||||||
[300, null, "HTTP/1.1 300 Multiple Choices"],
|
|
||||||
[301, null, "HTTP/1.1 301 Moved Permanently"],
|
|
||||||
[302, null, "HTTP/1.1 302 Found"],
|
|
||||||
[303, null, "HTTP/1.1 303 See Other"],
|
|
||||||
[304, null, "HTTP/1.1 304 Not Modified"],
|
|
||||||
[305, null, "HTTP/1.1 305 Use Proxy"],
|
|
||||||
[400, null, "HTTP/1.1 400 Bad Request"],
|
|
||||||
[401, null, "HTTP/1.1 401 Unauthorized"],
|
|
||||||
[402, null, "HTTP/1.1 402 Payment Required"],
|
|
||||||
[403, null, "HTTP/1.1 403 Forbidden"],
|
|
||||||
[404, null, "HTTP/1.1 404 Not Found"],
|
|
||||||
[405, null, "HTTP/1.1 405 Method Not Allowed"],
|
|
||||||
[406, null, "HTTP/1.1 406 Not Acceptable"],
|
|
||||||
[407, null, "HTTP/1.1 407 Proxy Authentication Required"],
|
|
||||||
[408, null, "HTTP/1.1 408 Request Timeout"],
|
|
||||||
[409, null, "HTTP/1.1 409 Conflict"],
|
|
||||||
[410, null, "HTTP/1.1 410 Gone"],
|
|
||||||
[411, null, "HTTP/1.1 411 Length Required"],
|
|
||||||
[412, null, "HTTP/1.1 412 Precondition Failed"],
|
|
||||||
[413, null, "HTTP/1.1 413 Request Entity Too Large"],
|
|
||||||
[414, null, "HTTP/1.1 414 Request-URI Too Long"],
|
|
||||||
[415, null, "HTTP/1.1 415 Unsupported Media Type"],
|
|
||||||
[500, null, "HTTP/1.1 500 Internal Server Error"],
|
|
||||||
[501, null, "HTTP/1.1 501 Not Implemented"],
|
|
||||||
[502, null, "HTTP/1.1 502 Bad Gateway"],
|
|
||||||
[503, null, "HTTP/1.1 503 Service Unavailable"],
|
|
||||||
[504, null, "HTTP/1.1 504 Gateway Timeout"],
|
|
||||||
[505, null, "HTTP/1.1 505 HTTP Version Not Supported"],
|
|
||||||
[598, null, "HTTP/1.1 598 Nonstandard"],
|
|
||||||
[599, "Smelly", "HTTP/1.1 599 Smelly"]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider invalidReasonPhraseProvider
|
|
||||||
* @expectedException \InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function testThrowsExceptionOnInvalidReasonPhrase($statusCode, $reasonPhrase)
|
|
||||||
{
|
|
||||||
$resp = new Response();
|
|
||||||
$resp->setStatusCode($statusCode, $reasonPhrase);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function invalidReasonPhraseProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
[599, false],
|
|
||||||
["100", true],
|
|
||||||
["*", []]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetsBody()
|
|
||||||
{
|
|
||||||
$faker = Factory::create();
|
|
||||||
$body = $faker->text();
|
|
||||||
$resp = new Response();
|
|
||||||
$resp->setBody($body);
|
|
||||||
$this->assertEquals($body, $resp->getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetsBodyInConstructor()
|
|
||||||
{
|
|
||||||
$faker = Factory::create();
|
|
||||||
$body = $faker->text();
|
|
||||||
$resp = new Response(200, $body);
|
|
||||||
$this->assertEquals($body, $resp->getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetsBodyFile()
|
|
||||||
{
|
|
||||||
$path = tempnam(sys_get_temp_dir(), "TST");
|
|
||||||
$resp = new Response();
|
|
||||||
$resp->setBodyFilePath($path);
|
|
||||||
$this->assertEquals($path, $resp->getBodyFilePath());
|
|
||||||
unlink($path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testOutputsResponse()
|
|
||||||
{
|
|
||||||
$faker = Factory::create();
|
|
||||||
$body = $faker->text();
|
|
||||||
|
|
||||||
$resp = new Response(200, $body, ["Content-type" => "text/plain"]);
|
|
||||||
ob_start();
|
|
||||||
$resp->respond();
|
|
||||||
$captured = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
$this->assertEquals($body, $captured);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testOutputsResponseFromFile()
|
|
||||||
{
|
|
||||||
$path = tempnam(sys_get_temp_dir(), "TST");
|
|
||||||
$faker = Factory::create();
|
|
||||||
$body = $faker->text();
|
|
||||||
|
|
||||||
$f = fopen($path, "w");
|
|
||||||
fwrite($f, $body);
|
|
||||||
fclose($f);
|
|
||||||
|
|
||||||
$resp = new Response();
|
|
||||||
$resp->setStatusCode(200);
|
|
||||||
$resp->setBodyFilePath($path);
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
$resp->respond();
|
|
||||||
$captured = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
unlink($path);
|
|
||||||
|
|
||||||
$this->assertEquals($captured, $body);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testRespondsWithNoBodyWhenResponseFileIsMissing()
|
|
||||||
{
|
|
||||||
$path = tempnam(sys_get_temp_dir(), "TST");
|
|
||||||
|
|
||||||
$resp = new Response();
|
|
||||||
$resp->setStatusCode(200);
|
|
||||||
$resp->setBodyFilePath($path);
|
|
||||||
unlink($path);
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
$resp->respond();
|
|
||||||
$captured = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
$this->assertEquals("", $captured);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,201 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\RouteTable;
|
|
||||||
use Prophecy\Argument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\RouteTable
|
|
||||||
*/
|
|
||||||
class RouteTableTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
private $handler;
|
|
||||||
private $request;
|
|
||||||
private $response;
|
|
||||||
private $route;
|
|
||||||
|
|
||||||
public function setUp()
|
|
||||||
{
|
|
||||||
$this->request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$this->response = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
|
|
||||||
$this->route = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$this->handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testReturnsNullWhenNoRoutesMatch()
|
|
||||||
{
|
|
||||||
$table = new RouteTable();
|
|
||||||
$response = $table->getResponse($this->request->reveal());
|
|
||||||
$this->assertNull($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMatchesStaticRoute()
|
|
||||||
{
|
|
||||||
$this->route->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\StaticRouteInterface");
|
|
||||||
$this->route->getPaths()->willReturn(["/cats/"]);
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/cats/");
|
|
||||||
|
|
||||||
$table = new RouteTable();
|
|
||||||
$table->addRoute($this->route->reveal());
|
|
||||||
$table->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$this->route->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMatchesPrefixRoute()
|
|
||||||
{
|
|
||||||
$this->route->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
|
|
||||||
$this->route->getPrefixes()->willReturn(["/cats/"]);
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/cats/molly");
|
|
||||||
|
|
||||||
$table = new RouteTable();
|
|
||||||
$table->addRoute($this->route->reveal());
|
|
||||||
$table->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$this->route->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMatchesBestPrefixRoute()
|
|
||||||
{
|
|
||||||
$route1 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$route1->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
|
|
||||||
$route1->getPrefixes()->willReturn(["/animals/"]);
|
|
||||||
$route1->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$route2 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$route2->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
|
|
||||||
$route2->getPrefixes()->willReturn(["/animals/cats/"]);
|
|
||||||
$route2->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/animals/cats/molly");
|
|
||||||
|
|
||||||
$table = new RouteTable();
|
|
||||||
$table->addRoute($route1->reveal());
|
|
||||||
$table->addRoute($route2->reveal());
|
|
||||||
$table->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$route1->getResponse(Argument::cetera())->shouldNotHaveBeenCalled();
|
|
||||||
$route2->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMatchesStaticRouteBeforePrefixRoute()
|
|
||||||
{
|
|
||||||
$route1 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$route1->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
|
|
||||||
$route1->getPrefixes()->willReturn(["/animals/cats/"]);
|
|
||||||
$route1->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$route2 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$route2->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\StaticRouteInterface");
|
|
||||||
$route2->getPaths()->willReturn(["/animals/cats/molly"]);
|
|
||||||
$route2->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/animals/cats/molly");
|
|
||||||
|
|
||||||
$table = new RouteTable();
|
|
||||||
$table->addRoute($route1->reveal());
|
|
||||||
$table->addRoute($route2->reveal());
|
|
||||||
$table->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$route1->getResponse(Argument::cetera())->shouldNotHaveBeenCalled();
|
|
||||||
$route2->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMatchesPrefixRouteBeforeHandlerRoute()
|
|
||||||
{
|
|
||||||
$route1 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$route1->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
|
|
||||||
$route1->getPrefixes()->willReturn(["/animals/cats/"]);
|
|
||||||
$route1->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$route2 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$route2->getResponse(Argument::cetera())->willReturn(null);
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/animals/cats/molly");
|
|
||||||
|
|
||||||
$table = new RouteTable();
|
|
||||||
$table->addRoute($route1->reveal());
|
|
||||||
$table->addRoute($route2->reveal());
|
|
||||||
$table->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$route1->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
$route2->getResponse(Argument::cetera())->shouldNotHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testReturnsFirstNonNullResponse()
|
|
||||||
{
|
|
||||||
$route1 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$route1->getResponse(Argument::cetera())->willReturn(null);
|
|
||||||
|
|
||||||
$route2 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$route2->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$route3 = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$route3->getResponse(Argument::cetera())->willReturn(null);
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/");
|
|
||||||
|
|
||||||
$table = new RouteTable();
|
|
||||||
$table->addRoute($route1->reveal());
|
|
||||||
$table->addRoute($route2->reveal());
|
|
||||||
$table->addRoute($route3->reveal());
|
|
||||||
$response = $table->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$this->assertNotNull($response);
|
|
||||||
$route1->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
$route2->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
$route3->getResponse(Argument::cetera())->shouldNotHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPropagatesArgumentsToStaticRoute()
|
|
||||||
{
|
|
||||||
$this->route->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\StaticRouteInterface");
|
|
||||||
$this->route->getPaths()->willReturn(["/cats/"]);
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/cats/");
|
|
||||||
|
|
||||||
$args = ["cat" => "molly"];
|
|
||||||
|
|
||||||
$table = new RouteTable();
|
|
||||||
$table->addRoute($this->route->reveal());
|
|
||||||
$table->getResponse($this->request->reveal(), $args);
|
|
||||||
|
|
||||||
$this->route->getResponse($this->request->reveal(), $args)->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPropagatesArgumentsToPrefixRoute()
|
|
||||||
{
|
|
||||||
$this->route->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\PrefixRouteInterface");
|
|
||||||
$this->route->getPrefixes()->willReturn(["/cats/"]);
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/cats/");
|
|
||||||
|
|
||||||
$args = ["cat" => "molly"];
|
|
||||||
|
|
||||||
$table = new RouteTable();
|
|
||||||
$table->addRoute($this->route->reveal());
|
|
||||||
$table->getResponse($this->request->reveal(), $args);
|
|
||||||
|
|
||||||
$this->route->getResponse($this->request->reveal(), $args)->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPropagatesArwgumentsToRoute()
|
|
||||||
{
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
$this->request->getPath()->willReturn("/cats/");
|
|
||||||
$args = ["cat" => "molly"];
|
|
||||||
|
|
||||||
$table = new RouteTable();
|
|
||||||
$table->addRoute($this->route->reveal());
|
|
||||||
$table->getResponse($this->request->reveal(), $args);
|
|
||||||
|
|
||||||
$this->route->getResponse($this->request->reveal(), $args)->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,323 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException;
|
|
||||||
use pjdietz\WellRESTed\Router;
|
|
||||||
use pjdietz\WellRESTed\Routes\TemplateRoute;
|
|
||||||
use Prophecy\Argument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Router
|
|
||||||
*/
|
|
||||||
class RouterTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
private $handler;
|
|
||||||
private $request;
|
|
||||||
private $response;
|
|
||||||
private $route;
|
|
||||||
|
|
||||||
public function setUp()
|
|
||||||
{
|
|
||||||
$this->request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$this->request->getPath()->willReturn("/");
|
|
||||||
$this->request->getMethod()->willReturn("GET");
|
|
||||||
$this->response = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
|
|
||||||
$this->route = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$this->handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAddsSingleRoute()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/cats/");
|
|
||||||
$this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->add("/cats/", $this->handler->reveal());
|
|
||||||
$response = $router->getResponse($this->request->reveal());
|
|
||||||
$this->assertNotNull($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider pathProvider
|
|
||||||
*/
|
|
||||||
public function testAddsMultpleRoutes($path, $exptectedSuccess)
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn($path);
|
|
||||||
$this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->add(
|
|
||||||
["/cats/", $this->handler->reveal()],
|
|
||||||
["/cats/*", $this->handler->reveal()],
|
|
||||||
["/dogs/{name}", $this->handler->reveal(), TemplateRoute::RE_ALPHA],
|
|
||||||
["~/hamsters/[a-z]+~", $this->handler->reveal()]
|
|
||||||
);
|
|
||||||
$response = $router->getResponse($this->request->reveal());
|
|
||||||
$this->assertEquals($exptectedSuccess, !is_null($response));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function pathProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["/cats/", true],
|
|
||||||
["/cats/molly", true],
|
|
||||||
["/dogs/bear", true],
|
|
||||||
["/hamsters/fizzgig", true],
|
|
||||||
["/dogs/", false],
|
|
||||||
["/birds/", false],
|
|
||||||
["/hamsters/23", false]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAddsSingleRouteInstance()
|
|
||||||
{
|
|
||||||
$router = new Router();
|
|
||||||
$router->addRoute($this->route->reveal());
|
|
||||||
$router->getResponse($this->request->reveal());
|
|
||||||
$this->route->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAddsMultipleRouteInstances()
|
|
||||||
{
|
|
||||||
$router = new Router();
|
|
||||||
$router->addRoutes([$this->route->reveal()]);
|
|
||||||
$router->getResponse($this->request->reveal());
|
|
||||||
$this->route->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPropagatesArgumentsToRouteTable()
|
|
||||||
{
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
$this->request->getPath()->willReturn("/cats/");
|
|
||||||
$args = ["cat" => "molly"];
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->addRoute($this->route->reveal());
|
|
||||||
$router->getResponse($this->request->reveal(), $args);
|
|
||||||
|
|
||||||
$this->route->getResponse($this->request->reveal(), $args)->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRespondsWithErrorResponseForHttpException()
|
|
||||||
{
|
|
||||||
$this->route->getResponse(Argument::cetera())->willThrow(new HttpException());
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->addRoute($this->route->reveal());
|
|
||||||
$response = $router->getResponse($this->request->reveal());
|
|
||||||
$this->assertEquals(500, $response->getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDispatchesErrorHandlerForStatusCode()
|
|
||||||
{
|
|
||||||
$this->response->getStatusCode()->willReturn(403);
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$errorHandler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$errorHandler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->addRoute($this->route->reveal());
|
|
||||||
$router->setErrorHandlers([403 => $errorHandler->reveal()]);
|
|
||||||
$router->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$errorHandler->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDispatchesErrorHandlerWithOriginalRequest()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/");
|
|
||||||
$this->response->getStatusCode()->willReturn(403);
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$errorHandler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$errorHandler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$request = $this->request->reveal();
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->addRoute($this->route->reveal());
|
|
||||||
$router->setErrorHandlers([403 => $errorHandler->reveal()]);
|
|
||||||
$router->getResponse($request);
|
|
||||||
|
|
||||||
$errorHandler->getResponse(
|
|
||||||
Argument::that(
|
|
||||||
function ($arg) use ($request) {
|
|
||||||
return $arg === $request;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
Argument::any()
|
|
||||||
)->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDispatchesErrorHandlerWithOriginalArguments()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/");
|
|
||||||
$this->response->getStatusCode()->willReturn(403);
|
|
||||||
$response = $this->response->reveal();
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($response);
|
|
||||||
|
|
||||||
$errorHandler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$errorHandler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$arguments = [
|
|
||||||
"cat" => "Molly",
|
|
||||||
"dog" => "Bear"
|
|
||||||
];
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->addRoute($this->route->reveal());
|
|
||||||
$router->setErrorHandlers([403 => $errorHandler->reveal()]);
|
|
||||||
$router->getResponse($this->request->reveal(), $arguments);
|
|
||||||
|
|
||||||
$errorHandler->getResponse(
|
|
||||||
Argument::any(),
|
|
||||||
Argument::that(
|
|
||||||
function ($args) use ($arguments) {
|
|
||||||
return count(array_diff_assoc($arguments, $args)) === 0;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDispatchesErrorHandlerWithPreviousResponse()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/");
|
|
||||||
$this->response->getStatusCode()->willReturn(403);
|
|
||||||
$response = $this->response->reveal();
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($response);
|
|
||||||
|
|
||||||
$errorHandler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$errorHandler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->addRoute($this->route->reveal());
|
|
||||||
$router->setErrorHandlers([403 => $errorHandler->reveal()]);
|
|
||||||
$router->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$errorHandler->getResponse(
|
|
||||||
Argument::any(),
|
|
||||||
Argument::that(
|
|
||||||
function ($arg) use ($response) {
|
|
||||||
return isset($arg["response"]) && $arg["response"] === $response;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDispatchesErrorCallable()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/");
|
|
||||||
$this->response->getStatusCode()->willReturn(403);
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$errorResponse = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
|
|
||||||
$errorResponse->respond()->willReturn();
|
|
||||||
|
|
||||||
$errorCallable = function () use ($errorResponse) {
|
|
||||||
return $errorResponse->reveal();
|
|
||||||
};
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->addRoute($this->route->reveal());
|
|
||||||
$router->setErrorHandlers([403 => $errorCallable]);
|
|
||||||
$result = $router->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$this->assertSame($errorResponse->reveal(), $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testRoutesServerRequest()
|
|
||||||
{
|
|
||||||
$_SERVER["REQUEST_URI"] = "/cats/";
|
|
||||||
$_SERVER["HTTP_HOST"] = "localhost";
|
|
||||||
$_SERVER["REQUEST_METHOD"] = "GET";
|
|
||||||
|
|
||||||
$this->response->getStatusCode()->willReturn(200);
|
|
||||||
$this->response->respond()->willReturn();
|
|
||||||
|
|
||||||
$this->route->willImplement("\\pjdietz\\WellRESTed\\Interfaces\\Routes\\StaticRouteInterface");
|
|
||||||
$this->route->getPaths()->willReturn(["/cats/"]);
|
|
||||||
$this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->addRoute($this->route->reveal());
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
$router->respond();
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
$this->response->respond()->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testRoutesStaticRequestToNoRouteResponse()
|
|
||||||
{
|
|
||||||
$_SERVER["REQUEST_URI"] = "/cats/";
|
|
||||||
$_SERVER["HTTP_HOST"] = "localhost";
|
|
||||||
$_SERVER["REQUEST_METHOD"] = "GET";
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
$router->respond();
|
|
||||||
$captured = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
$this->assertEquals("No resource at /cats/", $captured);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testRoutesStaticRequestTo404ErrorHandler()
|
|
||||||
{
|
|
||||||
$_SERVER["REQUEST_URI"] = "/cats/";
|
|
||||||
$_SERVER["HTTP_HOST"] = "localhost";
|
|
||||||
$_SERVER["REQUEST_METHOD"] = "GET";
|
|
||||||
|
|
||||||
$errorHandler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$errorHandler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->setErrorHandler(404, $errorHandler->reveal());
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
$router->respond();
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
$errorHandler->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDeprecatedSetsStaticRoute()
|
|
||||||
{
|
|
||||||
$this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
$this->request->getPath()->willReturn("/cats/");
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
@$router->setStaticRoute(["/cats/"], $this->handler->reveal());
|
|
||||||
$router->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$this->handler->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDeprecatedSetsPrefixRoute()
|
|
||||||
{
|
|
||||||
$this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
$this->request->getPath()->willReturn("/cats/molly");
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
@$router->setPrefixRoute(["/cats/"], $this->handler->reveal());
|
|
||||||
$router->getResponse($this->request->reveal());
|
|
||||||
|
|
||||||
$this->handler->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Routes\StaticRoute;
|
|
||||||
use Prophecy\Argument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Routes\BaseRoute
|
|
||||||
*/
|
|
||||||
class BaseRouteTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
public function testDispatchesHandlerTarget()
|
|
||||||
{
|
|
||||||
$request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$request->getPath()->willReturn("/");
|
|
||||||
|
|
||||||
$response = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
|
|
||||||
|
|
||||||
$handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$handler->getResponse(Argument::cetera())->willReturn($response->reveal());
|
|
||||||
|
|
||||||
$route = new StaticRoute("/", $handler->reveal());
|
|
||||||
$result = $route->getResponse($request->reveal());
|
|
||||||
|
|
||||||
$this->assertSame($response->reveal(), $result);
|
|
||||||
$handler->getResponse(Argument::cetera())->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDispatchesResponseTarget()
|
|
||||||
{
|
|
||||||
$request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$request->getPath()->willReturn("/");
|
|
||||||
|
|
||||||
$response = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
|
|
||||||
|
|
||||||
$route = new StaticRoute("/", $response->reveal());
|
|
||||||
$result = $route->getResponse($request->reveal());
|
|
||||||
|
|
||||||
$this->assertSame($response->reveal(), $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDispatchesNullTarget()
|
|
||||||
{
|
|
||||||
$request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$request->getPath()->willReturn("/");
|
|
||||||
|
|
||||||
$route = new StaticRoute("/", function () { return null; });
|
|
||||||
$result = $route->getResponse($request->reveal());
|
|
||||||
|
|
||||||
$this->assertNull($result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPropagatesArgumentsToCallable()
|
|
||||||
{
|
|
||||||
$callableRequest = null;
|
|
||||||
$callableArgs = null;
|
|
||||||
$callable = function ($request, $args) use (&$callableRequest, &$callableArgs) {
|
|
||||||
$callableRequest = $request;
|
|
||||||
$callableArgs = $args;
|
|
||||||
};
|
|
||||||
|
|
||||||
$request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$request->getPath()->willReturn("/");
|
|
||||||
|
|
||||||
$args = ["cat" => "Molly"];
|
|
||||||
|
|
||||||
$route = new StaticRoute("/", $callable);
|
|
||||||
$route->getResponse($request->reveal(), $args);
|
|
||||||
|
|
||||||
$this->assertSame($request->reveal(), $callableRequest);
|
|
||||||
$this->assertSame($args, $callableArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Routes\PrefixRoute;
|
|
||||||
use Prophecy\Argument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Routes\PrefixRoute
|
|
||||||
*/
|
|
||||||
class PrefixRouteTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
private $handler;
|
|
||||||
private $request;
|
|
||||||
private $response;
|
|
||||||
|
|
||||||
public function testMatchesSinglePathExactly()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/cats/");
|
|
||||||
$route = new PrefixRoute("/cats/", $this->handler->reveal());
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNotNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMatchesSinglePathWithPrefix()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/cats/molly");
|
|
||||||
$route = new PrefixRoute("/cats/", $this->handler->reveal());
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNotNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMatchesPathInList()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/cats/molly");
|
|
||||||
$route = new PrefixRoute(array("/cats/", "/dogs/"), $this->handler->reveal());
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNotNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testFailsToMatchPath()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/dogs/");
|
|
||||||
$route = new PrefixRoute("/cats/", $this->handler->reveal());
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider invalidPathsProvider
|
|
||||||
* @expectedException \InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function testThrowsExceptionOnInvalidPath($path)
|
|
||||||
{
|
|
||||||
new PrefixRoute($path, "\\NoClass");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function invalidPathsProvider()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
array(false),
|
|
||||||
array(17),
|
|
||||||
array(null)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testReturnsPrefixes()
|
|
||||||
{
|
|
||||||
$paths = array("/cats/", "/dogs/");
|
|
||||||
$route = new PrefixRoute($paths, $this->handler->reveal());
|
|
||||||
$this->assertEquals($paths, $route->getPrefixes());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPropagatesArgumentsToCallable()
|
|
||||||
{
|
|
||||||
$callableRequest = null;
|
|
||||||
$callableArgs = null;
|
|
||||||
$callable = function ($request, $args) use (&$callableRequest, &$callableArgs) {
|
|
||||||
$callableRequest = $request;
|
|
||||||
$callableArgs = $args;
|
|
||||||
};
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/");
|
|
||||||
$args = ["cat" => "Molly"];
|
|
||||||
|
|
||||||
$route = new PrefixRoute("/", $callable);
|
|
||||||
$route->getResponse($this->request->reveal(), $args);
|
|
||||||
|
|
||||||
$this->assertSame($this->request->reveal(), $callableRequest);
|
|
||||||
$this->assertSame($args, $callableArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setUp()
|
|
||||||
{
|
|
||||||
$this->request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$this->response = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
|
|
||||||
$this->handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Routes\RegexRoute;
|
|
||||||
use Prophecy\Argument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Routes\RegexRoute
|
|
||||||
*/
|
|
||||||
class RegexRouteTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
private $handler;
|
|
||||||
private $request;
|
|
||||||
private $response;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider matchingRouteProvider
|
|
||||||
*/
|
|
||||||
public function testMatchesPattern($pattern, $path)
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn($path);
|
|
||||||
|
|
||||||
$route = new RegexRoute($pattern, $this->handler->reveal());
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNotNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider matchingRouteProvider
|
|
||||||
*/
|
|
||||||
public function testExtractsCaptures($pattern, $path, $captures)
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn($path);
|
|
||||||
|
|
||||||
$route = new RegexRoute($pattern, $this->handler->reveal());
|
|
||||||
$route->getResponse($this->request->reveal());
|
|
||||||
$this->handler->getResponse(Argument::any(), Argument::that(
|
|
||||||
function ($args) use ($captures) {
|
|
||||||
return $args = $captures;
|
|
||||||
}))->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function matchingRouteProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["~/cat/[0-9]+~", "/cat/2", [0 => "/cat/2"]],
|
|
||||||
["#/dog/.*#", "/dog/his-name-is-bear", [0 => "/dog/his-name-is-bear"]],
|
|
||||||
["~/cat/([0-9]+)~", "/cat/2", [
|
|
||||||
0 => "/cat/2",
|
|
||||||
1 => "2"
|
|
||||||
]],
|
|
||||||
["~/dog/(?<id>[0-9+])~", "/dog/2", [
|
|
||||||
0 => "/dog/2",
|
|
||||||
1 => "2",
|
|
||||||
"id" => "2"
|
|
||||||
]]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider mismatchingRouteProvider
|
|
||||||
*/
|
|
||||||
public function testFailsToMatchMismatchingPattern($pattern, $path)
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn($path);
|
|
||||||
|
|
||||||
$route = new RegexRoute($pattern, $this->handler->reveal());
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function mismatchingRouteProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["~/cat/[0-9]+~", "/cat/molly"],
|
|
||||||
["~/cat/[0-9]+~", "/dog/bear"],
|
|
||||||
["#/dog/.*#", "/dog"]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider invalidRouteProvider
|
|
||||||
* @expectedException \pjdietz\WellRESTed\Exceptions\ParseException
|
|
||||||
*/
|
|
||||||
public function testThrowsExceptionOnInvalidPattern($pattern)
|
|
||||||
{
|
|
||||||
$route = new RegexRoute($pattern, $this->handler->reveal());
|
|
||||||
$route->getResponse($this->request->reveal());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function invalidRouteProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["~/unterminated"],
|
|
||||||
["/nope"]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPropagatesArgumentsToCallable()
|
|
||||||
{
|
|
||||||
$callableRequest = null;
|
|
||||||
$callableArgs = null;
|
|
||||||
$callable = function ($request, $args) use (&$callableRequest, &$callableArgs) {
|
|
||||||
$callableRequest = $request;
|
|
||||||
$callableArgs = $args;
|
|
||||||
};
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/dog/bear");
|
|
||||||
$args = ["cat" => "Molly"];
|
|
||||||
|
|
||||||
$route = new RegexRoute("~/dog/(?<dog>[a-z]+)~", $callable);
|
|
||||||
$route->getResponse($this->request->reveal(), $args);
|
|
||||||
|
|
||||||
$this->assertSame($this->request->reveal(), $callableRequest);
|
|
||||||
$this->assertArraySubset($args, $callableArgs);
|
|
||||||
$this->assertArraySubset(["dog" => "bear"], $callableArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setUp()
|
|
||||||
{
|
|
||||||
$this->request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$this->response = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
|
|
||||||
$this->handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Routes\RouteFactory;
|
|
||||||
use Prophecy\Argument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Routes\RouteFactory
|
|
||||||
*/
|
|
||||||
class RouteFactoryTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @dataProvider routeProvider
|
|
||||||
*/
|
|
||||||
public function testCreatesRouteOfCorrectType($path, $expectedType)
|
|
||||||
{
|
|
||||||
$factory = new RouteFactory();
|
|
||||||
$route = $factory->createRoute($path, "\\MyHandler");
|
|
||||||
$this->assertInstanceOf($expectedType, $route);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function routeProvider()
|
|
||||||
{
|
|
||||||
$static = "\\pjdietz\\WellRESTed\\Routes\\StaticRoute";
|
|
||||||
$prefix = "\\pjdietz\\WellRESTed\\Routes\\PrefixRoute";
|
|
||||||
$template = "\\pjdietz\\WellRESTed\\Routes\\TemplateRoute";
|
|
||||||
$regex = "\\pjdietz\\WellRESTed\\Routes\\RegexRoute";
|
|
||||||
|
|
||||||
return [
|
|
||||||
["/cats/", $static],
|
|
||||||
["/cats/*", $prefix],
|
|
||||||
["/cats/{catId}", $template],
|
|
||||||
["~/cat/[0-9]+~", $regex]
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Routes\StaticRoute;
|
|
||||||
use Prophecy\Argument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Routes\StaticRoute
|
|
||||||
*/
|
|
||||||
class StaticRouteTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
private $handler;
|
|
||||||
private $request;
|
|
||||||
private $response;
|
|
||||||
|
|
||||||
public function testMatchesSinglePath()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/cats/");
|
|
||||||
$route = new StaticRoute("/cats/", $this->handler->reveal());
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNotNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMatchesPathInList()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/cats/");
|
|
||||||
$route = new StaticRoute(array("/cats/", "/dogs/"), $this->handler->reveal());
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNotNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testFailsToMatchPath()
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn("/dogs/");
|
|
||||||
$route = new StaticRoute("/cats/", $this->handler->reveal());
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider invalidPathsProvider
|
|
||||||
* @expectedException \InvalidArgumentException
|
|
||||||
*/
|
|
||||||
public function testThrowsExceptionOnInvalidPath($path)
|
|
||||||
{
|
|
||||||
new StaticRoute($path, "\\NoClass");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function invalidPathsProvider()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
array(false),
|
|
||||||
array(17),
|
|
||||||
array(null)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testReturnsPaths()
|
|
||||||
{
|
|
||||||
$paths = array("/cats/", "/dogs/");
|
|
||||||
$route = new StaticRoute($paths, $this->handler->reveal());
|
|
||||||
$this->assertEquals($paths, $route->getPaths());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPropagatesArgumentsToCallable()
|
|
||||||
{
|
|
||||||
$callableRequest = null;
|
|
||||||
$callableArgs = null;
|
|
||||||
$callable = function ($request, $args) use (&$callableRequest, &$callableArgs) {
|
|
||||||
$callableRequest = $request;
|
|
||||||
$callableArgs = $args;
|
|
||||||
};
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/");
|
|
||||||
|
|
||||||
$args = ["cat" => "Molly"];
|
|
||||||
|
|
||||||
$route = new StaticRoute("/", $callable);
|
|
||||||
$route->getResponse($this->request->reveal(), $args);
|
|
||||||
|
|
||||||
$this->assertSame($this->request->reveal(), $callableRequest);
|
|
||||||
$this->assertSame($args, $callableArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setUp()
|
|
||||||
{
|
|
||||||
$this->request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$this->response = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
|
|
||||||
$this->handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,208 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Routes\TemplateRoute;
|
|
||||||
use Prophecy\Argument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers pjdietz\WellRESTed\Routes\TemplateRoute
|
|
||||||
*/
|
|
||||||
class TemplateRouteTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
private $handler;
|
|
||||||
private $request;
|
|
||||||
private $response;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider matchingTemplateProvider
|
|
||||||
*/
|
|
||||||
public function testMatchesTemplate($template, $default, $vars, $path)
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn($path);
|
|
||||||
$route = new TemplateRoute($template, $this->handler->reveal(), $default, $vars);
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNotNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider matchingTemplateProvider
|
|
||||||
*/
|
|
||||||
public function testExtractsCaptures($template, $default, $vars, $path, $expectedCaptures)
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn($path);
|
|
||||||
$route = new TemplateRoute($template, $this->handler->reveal(), $default, $vars);
|
|
||||||
$route->getResponse($this->request->reveal());
|
|
||||||
$this->handler->getResponse(
|
|
||||||
Argument::any(),
|
|
||||||
Argument::that(
|
|
||||||
function ($args) use ($expectedCaptures) {
|
|
||||||
return count(array_diff_assoc($expectedCaptures, $args)) === 0;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function matchingTemplateProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["/cat/{id}", TemplateRoute::RE_NUM, null, "/cat/12", ["id" => "12"]],
|
|
||||||
[
|
|
||||||
"/cat/{catId}/{dogId}",
|
|
||||||
TemplateRoute::RE_SLUG,
|
|
||||||
null,
|
|
||||||
"/cat/molly/bear",
|
|
||||||
[
|
|
||||||
"catId" => "molly",
|
|
||||||
"dogId" => "bear"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"/cat/{catId}/{dogId}",
|
|
||||||
TemplateRoute::RE_NUM,
|
|
||||||
[
|
|
||||||
"catId" => TemplateRoute::RE_SLUG,
|
|
||||||
"dogId" => TemplateRoute::RE_SLUG
|
|
||||||
],
|
|
||||||
"/cat/molly/bear",
|
|
||||||
[
|
|
||||||
"catId" => "molly",
|
|
||||||
"dogId" => "bear"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"/cat/{catId}/{dogId}",
|
|
||||||
TemplateRoute::RE_NUM,
|
|
||||||
(object) [
|
|
||||||
"catId" => TemplateRoute::RE_SLUG,
|
|
||||||
"dogId" => TemplateRoute::RE_SLUG
|
|
||||||
],
|
|
||||||
"/cat/molly/bear",
|
|
||||||
[
|
|
||||||
"catId" => "molly",
|
|
||||||
"dogId" => "bear"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
["/cat/{id}/*", null, null, "/cat/12/molly", ["id" => "12"]],
|
|
||||||
[
|
|
||||||
"/cat/{id}-{width}x{height}.jpg",
|
|
||||||
TemplateRoute::RE_NUM,
|
|
||||||
null,
|
|
||||||
"/cat/17-200x100.jpg",
|
|
||||||
[
|
|
||||||
"id" => "17",
|
|
||||||
"width" => "200",
|
|
||||||
"height" => "100"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
["/cat/{path}", ".*", null, "/cat/this/section/has/slashes", ["path" => "this/section/has/slashes"]]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider allowedVariableNamesProvider
|
|
||||||
*/
|
|
||||||
public function testMatchesAllowedVariablesNames($template, $path, $expectedCaptures)
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn($path);
|
|
||||||
$route = new TemplateRoute($template, $this->handler->reveal(), null, null);
|
|
||||||
$route->getResponse($this->request->reveal());
|
|
||||||
$this->handler->getResponse(
|
|
||||||
Argument::any(),
|
|
||||||
Argument::that(
|
|
||||||
function ($args) use ($expectedCaptures) {
|
|
||||||
return count(array_diff_assoc($expectedCaptures, $args)) === 0;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)->shouldHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function allowedVariableNamesProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["/{n}", "/lower", ["n" => "lower"]],
|
|
||||||
["/{N}", "/UPPER", ["N" => "UPPER"]],
|
|
||||||
["/{var1024}", "/digits", ["var1024" => "digits"]],
|
|
||||||
["/{variable_name}", "/underscore", ["variable_name" => "underscore"]],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider illegalVariableNamesProvider
|
|
||||||
*/
|
|
||||||
public function testFailsToMatchIllegalVariablesNames($template, $path)
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn($path);
|
|
||||||
$route = new TemplateRoute($template, $this->handler->reveal(), null, null);
|
|
||||||
$route->getResponse($this->request->reveal());
|
|
||||||
$this->handler->getResponse(Argument::cetera())->shouldNotHaveBeenCalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function illegalVariableNamesProvider()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
["/{not-legal}", "/hyphen"],
|
|
||||||
["/{1digitfirst}", "/digitfirst"],
|
|
||||||
["/{%2f}", "/percent-encoded"],
|
|
||||||
["/{}", "/empty"],
|
|
||||||
["/{{nested}}", "/nested"]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setUp()
|
|
||||||
{
|
|
||||||
$this->request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$this->response = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
|
|
||||||
$this->handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface");
|
|
||||||
$this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider nonmatchingTemplateProvider
|
|
||||||
*/
|
|
||||||
public function testFailsToMatchNonmatchingTemplate($template, $default, $vars, $path)
|
|
||||||
{
|
|
||||||
$this->request->getPath()->willReturn($path);
|
|
||||||
$route = new TemplateRoute($template, $this->handler->reveal(), $default, $vars);
|
|
||||||
$resp = $route->getResponse($this->request->reveal());
|
|
||||||
$this->assertNull($resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function nonmatchingTemplateProvider()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
array("/cat/{id}", TemplateRoute::RE_NUM, null, "/cat/molly"),
|
|
||||||
array("/cat/{catId}/{dogId}", TemplateRoute::RE_ALPHA, null, "/cat/12/13"),
|
|
||||||
array(
|
|
||||||
"/cat/{catId}/{dogId}",
|
|
||||||
TemplateRoute::RE_NUM,
|
|
||||||
array(
|
|
||||||
"catId" => TemplateRoute::RE_ALPHA,
|
|
||||||
"dogId" => TemplateRoute::RE_ALPHA
|
|
||||||
),
|
|
||||||
"/cat/12/13"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testPropagatesArgumentsToCallable()
|
|
||||||
{
|
|
||||||
$callableRequest = null;
|
|
||||||
$callableArgs = null;
|
|
||||||
$callable = function ($request, $args) use (&$callableRequest, &$callableArgs) {
|
|
||||||
$callableRequest = $request;
|
|
||||||
$callableArgs = $args;
|
|
||||||
};
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/dog/bear");
|
|
||||||
$args = ["cat" => "Molly"];
|
|
||||||
|
|
||||||
$route = new TemplateRoute("/dog/{dog}", $callable);
|
|
||||||
$route->getResponse($this->request->reveal(), $args);
|
|
||||||
|
|
||||||
$this->assertSame($this->request->reveal(), $callableRequest);
|
|
||||||
$this->assertArraySubset($args, $callableArgs);
|
|
||||||
$this->assertArraySubset(["dog" => "bear"], $callableArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace pjdietz\WellRESTed\Test\Integration;
|
|
||||||
|
|
||||||
use pjdietz\WellRESTed\Router;
|
|
||||||
use Prophecy\Argument;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @coversNothing
|
|
||||||
*/
|
|
||||||
class RouterTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
private $request;
|
|
||||||
private $response;
|
|
||||||
|
|
||||||
public function setUp()
|
|
||||||
{
|
|
||||||
$this->request = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\RequestInterface");
|
|
||||||
$this->request->getPath()->willReturn("/");
|
|
||||||
$this->request->getMethod()->willReturn("GET");
|
|
||||||
$this->response = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\ResponseInterface");
|
|
||||||
$this->response->getStatusCode()->willReturn(200);
|
|
||||||
$this->response->getBody()->willReturn("Hello, world!");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDispatchesCallable()
|
|
||||||
{
|
|
||||||
$response = $this->response;
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->add("/", function () use ($response) {
|
|
||||||
return $response->reveal();
|
|
||||||
});
|
|
||||||
|
|
||||||
$result = $router->getResponse($this->request->reveal());
|
|
||||||
$this->assertSame($response->reveal(), $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDispatchesCallableWithArguments()
|
|
||||||
{
|
|
||||||
$response = $this->response;
|
|
||||||
$args = ["cat" => "molly"];
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->add("/", function ($rqst, $args) use ($response) {
|
|
||||||
$response->getBody()->willReturn($args["cat"]);
|
|
||||||
return $response->reveal();
|
|
||||||
});
|
|
||||||
|
|
||||||
$result = $router->getResponse($this->request->reveal(), $args);
|
|
||||||
$this->assertEquals("molly", $result->getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testStopsDispatchingCallablesAfterFirstNonNull()
|
|
||||||
{
|
|
||||||
$router = new Router();
|
|
||||||
$router->add("/cats/{cat}", function () {
|
|
||||||
echo "Hello, cat!";
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
$router->add("/cats/{cat}", function () {
|
|
||||||
echo "Hello, cat!";
|
|
||||||
});
|
|
||||||
|
|
||||||
$this->request->getPath()->willReturn("/cats/molly");
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
$router->getResponse($this->request->reveal());
|
|
||||||
$captured = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
$this->assertEquals("Hello, cat!", $captured);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @runInSeparateProcess
|
|
||||||
* @preserveGlobalState disabled
|
|
||||||
*/
|
|
||||||
public function testRouterRespondsWithNoisyCallable()
|
|
||||||
{
|
|
||||||
$_SERVER["REQUEST_URI"] = "/cats/molly";
|
|
||||||
$_SERVER["HTTP_HOST"] = "localhost";
|
|
||||||
$_SERVER["REQUEST_METHOD"] = "GET";
|
|
||||||
|
|
||||||
$router = new Router();
|
|
||||||
$router->add("/cats/{cat}", function () {
|
|
||||||
echo "Hello, cat!";
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
@$router->respond();
|
|
||||||
$captured = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
$this->assertEquals("Hello, cat!", $captured);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue