fostam/simplerouter
Simple PHP router, mainly for building API backends., (*1)
Features
- Easy configuration
- Response and error handling
- Parameter input from URL, GET/POST parameters or JSON payload
- CORS support
Install
The easiest way to install SimpleRouter is by using composer:, (*2)
$> composer require fostam/simplerouter
Usage
This is how a typical "handler" would look like:, (*3)
<?php
include "vendor/autoload.php";
use Fostam\SimpleRouter\Response;
use Fostam\SimpleRouter\Router;
use Fostam\SimpleRouter\Http;
use Fostam\SimpleRouter\Exception\InternalApiException;
use Fostam\SimpleRouter\Exception\UserApiException;
$router = new Router();
try {
// all URLs must be prefixed with this string
$router->setOption(Router::OPT_REQUEST_PATH_PREFIX, '/myproject/api');
// send permissive cross origin resource sharing headers
$router->setOption(Router::OPT_CORS_PERMISSIVE, true);
// by default, send the response with application/json content type
$router->setOption(Router::OPT_RESPONSE_TYPE_DEFAULT, Response::TYPE_JSON);
// routes
$router->createRoute('/users', Http::METHOD_GET, \MyProject\GetUsers::class);
$router->createRoute('/users/{id}', Http::METHOD_GET, \MyProject\GetUsers::class);
$router->createRoute('/users', Http::METHOD_POST, \MyProject\CreateUser::class);
// resolve
$router->resolve();
}
catch (UserApiException $e) {
// error messages are automatically passed back as JSON
}
catch (InternalApiException $e) {
// a generic error message is passed back as JSON; trigger the actual errors
// (e.g. a failed database query) that should not be publicly visible, but
// logged internally
error_log($e);
}
$router->sendResult();
This could be a (very simplified) processor class for getting users:, (*4)
class GetUsers extends Processor {
public function execute() {
$userID = $this->getPathParam('id');
if (!is_numeric($userID)) {
throw new UserApiException('userID must be a number', Http::CODE_BAD_REQUEST, $e);
}
try {
$data = $this->myUserModel->getUser(intval($userID));
} catch (\Exception $e) {
throw new InternalApiException("error getting data for user {$userID}", Http::CODE_INTERNAL_SERVER_ERROR, $e);
}
$this->setResponseData(
[
'data' => $data
]
);
}
}
An example class for creating a user:, (*5)
class CreateUser extends Processor {
public function execute() {
$data = $this->getJSONPostData();
if (!isset($data['name'])) {
throw new UserApiException("parameter 'name' is missing");
}
try {
$userID = $this->myUserModel->createUser($data);
} catch (\Exception $e) {
throw new InternalApiException("error creating user", Http::CODE_INTERNAL_SERVER_ERROR, $e);
}
// send "201 Created"
$this->setResponseCode(Http::CODE_CREATED);
// send the location of the new object as header
// here: 'Location: /myproject/api/users/123' (for $userID=123)
$this->getResponseObject()->setLocationPath($this->getPath() . '/' . $userID);
}
}
Reference
Router
setOption()
Router setOption($option, $value)
Set a router option. The router object is returned to allow chaining.
Following options are available:, (*6)
| Option |
Value |
Default Value |
| Router::OPT_REQUEST_PATH_PREFIX |
prefix that is truncated from the path |
"" |
| Router::OPT_RESPONSE_TYPE_DEFAULT |
default response type |
Response::TYPE_HTML |
| Router::OPT_KEY_ERROR_MESSAGE |
key in result JSON for error message |
"error.message" |
| Router::OPT_KEY_ERROR_CODE |
key in result JSON for error code |
"error.code" |
| Router::OPT_INTERNAL_ERROR_MSG |
error message text for internal errors |
"internal server error" |
| Router::OPT_CORS_PERMISSIVE |
permissive CORS mode |
false |
addRoute()
Route addRoute(Route $route), (*7)
Add a Route object to the list of routes. For convenience, the route object is returned to allow direct calling of Route methods., (*8)
createRoute()
Route createRoute(string $path, string $method, mixed $processor), (*9)
Creates a Route object from the given parameters, adds it to the list of routes and returns it. See the
Route documentation for a description of the parameters., (*10)
resolve()
void resolve(string $path = '', string $method = ''), (*11)
Resolve the given path/method and execute the matching processor.
If $path is empty, $_SERVER['SCRIPT_NAME'] is used as path.
If $method is empty, $_SERVER['REQUEST_METHOD'] is used as method., (*12)
sendResult()
void sendResult(), (*13)
Sends the result. This include the HTTP status code, HTTP headers (including content type) and the body.
Content type header and body format depend on the configured response type., (*14)
getResponseObject()
Response getResponseObject(), (*15)
Get the Response object. The response object represents what is sent when calling sendResult()., (*16)
Route
Constructor
Route __construct(array $config), (*17)
The constructor is the preferred way of creating a route when the route configuration comes from
a dynamic source, e.g. from a database., (*18)
It builds a route object with the given associative array $config. The following
configuration keys are available:, (*19)
| Key |
Value |
Mandatory |
| Route::PATH |
path used for matching |
yes |
| Route::METHOD |
method used for matching |
yes |
| Route::PROCESSOR |
processor that is executed on a match |
yes |
The path is the endpoint that defines when this route matches. When comparing to the actual path,
the Router::OPT_REQUEST_PATH_PREFIX is prefixed, if configured., (*20)
The method is the HTTP method that needs to match along with the path, e.g. Http::METHOD_GET or
Http::METHOD_POST., (*21)
If both path and method match, the processor is called. There are three different possibilities to
define the processor:
- A class name - this is suitable if the class has a constructor without arguments, because the instantiation
is done when the class is used
- An array consisting of a class name and another array with the constructor parameters - this method can be
used when the constructors needs arguments
- An object - this is suitable when the processor object is created in advance, (*22)
In either case, the class/object passed is required to extend the abstract Processor class., (*23)
create()
Route ::create(string $path, string $method, mixed $processor), (*24)
Static method to create a Route object from parameters. The create() method is the preferred way of creating
a route when the route definitions are hardwired in PHP. Refer to the constructor for a description of the
parameters., (*25)
Processor
getPath()
string getPath(), (*26)
Get the path this processor is configured for., (*27)
getMethod()
string getMethod(), (*28)
Get the HTTP method this processor is configured for., (*29)
getResponseObject()
Response getResponseObject(), (*30)
Get the response object that will be used to create the response after the processor has finished., (*31)
setResponseCode()
void setResponseCode(int $code), (*32)
Set the HTTP response code, e.g. Http::CODE_OK (_200_)., (*33)
Shorthand for getResponseObject()->setCode($code)., (*34)
setResponseType()
void setResponseType(string $type), (*35)
Set the HTTP response type, e.g. Response::TYPE_JSON. This can be either one of the TYPE_ constants from the
Response class, or any valid MIME type string, e.g. application/xml.
If no response type is set, the router will send the default response type (Router::OPT_RESPONSE_TYPE_DEFAULT),
if set. If no default is set, text/html is sent., (*36)
Shorthand for getResponseObject()->setType($type)., (*37)
setResponseData()
void setResponseData(mixed $data), (*38)
Set the body data. This can be either an array for the Response::TYPE_JSON response type, or a string
for Response::TYPE_HTML or any other response type., (*39)
Shorthand for getResponseObject()->setData($data)., (*40)
getPathParams()
string[] getPathParams(), (*41)
Get all path parameter values as key/values pairs., (*42)
Example:, (*43)
$router->createRoute('/test/{id}/user/{name}', Http::METHOD_GET, Test::class);
$router->resolve('/test/123/user/john', Http::METHOD_GET);
A call to getPathParams() in the Test processor call will return:, (*44)
array(2) {
'id' =>
string(3) "123"
'name' =>
string(4) "john"
}
getPathParam()
string getPathParam(string $param), (*45)
Get the path parameter value for a single parameter. For the example above, getPathParam('name') would return john., (*46)
getQueryParams()
string[] getQueryParams(), (*47)
Return the query parameters as key/value pairs. This is equivalent to PHP's $_GET superglobal., (*48)
getQueryParam()
string getQueryParam(string $param), (*49)
Get the query parameter value for a single parameter., (*50)
getPostParams()
string[] getPostParams(), (*51)
Return the POST parameters as key/value pairs. This is equivalent to PHP's $_POST superglobal., (*52)
getPostParam()
string getPostParam(string $param), (*53)
Get the POST parameter value for a single parameter., (*54)
getJSONPostData()
mixed getJSONPostData(), (*55)
When the body of a POST, PUT or PATCH call contains a JSON string, it's structure can be retrieved
as a PHP array., (*56)
Example:, (*57)
curl 'https://example.com/test' -X POST -d '{"id": 123, "name": "john"}'
A call to getJSONPostData() will return this array:, (*58)
array(2) {
'id' =>
int(123)
'name' =>
string(4) "john"
}
Response
setCode()
void setCode(int $code), (*59)
Set the HTTP response code, e.g. Http::CODE_OK (_200_)., (*60)
getCode()
int getCode(), (*61)
Get the current response code., (*62)
setType()
void setType(string $type), (*63)
Set the HTTP response type, e.g. Response::TYPE_JSON. This can be either one of the TYPE_ constants from the
Response class, or any valid MIME type string, e.g. application/xml.
If no response type is set, the router will send the default response type (Router::OPT_RESPONSE_TYPE_DEFAULT),
if set. If no default is set, text/html is sent., (*64)
getType()
int getType(), (*65)
Get the current response code., (*66)
setData()
void setData(mixed $data), (*67)
Set the body data. This can be either an array for the Response::TYPE_JSON response type, or a string
for Response::TYPE_HTML or any other response type., (*68)
getData()
mixed getData(), (*69)
Get the current data., (*70)
setLocationPath()
void setLocationPath(string $path), (*71)
Set a location path. This path prefixed with Router::OPT_REQUEST_PATH_PREFIX, if set, and then sent as Location
header., (*72)
Example:, (*73)
$router->setOption(Router::OPT_REQUEST_PATH_PREFIX, '/test/api');
...
$this->getResponseObject->setLocationPath('/users');
...
Location: /test/api/users
getLocationPath()
string getLocationPath(), (*74)
Get the location path., (*75)
void setHeader(string $header, string $value, bool $append = false), (*76)
Set the HTTP header $header to the value $value. If $append is true and the header
had been set before, an additional header line for the same header is added., (*77)
bool isHeaderSet(string $header), (*78)
Returns whether the header $header has been set or not., (*79)
array getHeader(string $header), (*80)
Get the header $header. An array with the following elements is returned:
- Response::HEADER_NAME: the name of the header, e.h. Content-Type
- Response::HEADER_VALUES: an array of strings, representing the values, (*81)
array getHeaders(), (*82)
Get all headers. It returns an array of entries as returned by getHeader()., (*83)
void clearHeader(string $header), (*84)
Clear the header $header., (*85)
void clearHeaders(), (*86)
Clear all headers., (*87)
addResponseData()
void addResponseData(string $key, string $value, string $separator = '.'), (*88)
Adds data to the response array (for the JSON response type). Sub-array keys can be separated by the $separator
string., (*89)
Example:, (*90)
addResponseData('data.user.name', 'John')
would set this value:, (*91)
{
"data": {
"user": {
"name": "John"
}
}
}
void corsSetOrigin(string $origin), (*92)
Set the Access-Control-Allow-Origin to $origin, e.g. corsSetOrigin('*') to allow all origins., (*93)
corsAddOrigin()
void corsAddOrigin(string $origin), (*94)
If multiple origins are required, an origin can be added, e.g. corsAddOrigin('example.org'). The list of origins
is compared to the Origin request header. On a match, the Access-Control-Allow-Origin is set to the matching origin., (*95)
corsGetOrigins()
array corsGetOrigins(), (*96)
Get the list of allowed origins., (*97)
corsAllowCredentials()
void corsAllowCredentials(bool $allowed), (*98)
Used to set the Access-Control-Allow-Credentials header., (*99)
void corsAddAllowedHeader(string $header), (*100)
Add a header to the list of allowed headers used for Access-Control-Allow-Headers., (*101)
void corsAddAllowedHeaders(string[] $headers), (*102)
Set an array of allowed headers., (*103)
void corsSetMaxAge(int $maxAgeSeconds), (*104)
Set the maximum age in seconds used for the Access-Control-Max-Age header., (*105)