Skip to content

Instantly share code, notes, and snippets.

@gubi
Last active February 9, 2017 00:32
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save gubi/5de08a2fe256d4929ba3eeafc344a84a to your computer and use it in GitHub Desktop.
Basic API routing with klein. For HTTP Status Codes see https://github.com/gubi/HTTP-Error-Codes

Basic API routing

PHP klein config for a basic API routing

{
"name": "<OWNER>/<PROJECT>",
"description": "The API service for the <PROJECT>",
"authors": [
{
"name": "Alessandro Gubitosi",
"email": "gubi.ale@iod.io"
}
],
"minimum-stability": "dev",
"require": {
"klein/klein": "^2.1",
"misd/linkify": "^1.1"
}
}
<?php
header("Content-type: text/plain; charset=utf-8");
require_once("vendor/autoload.php");
/**
* Class Routing
*
* @author Alessandro Gubitosi <gubi.ale@iod.io>
* @version 1.0.0
* @uses yaml_parse_file http://php.net/manual/en/book.yaml.php
*/
class Routing {
static $klein;
// The base url prefix
static $prefix = "/path/api";
// The API version
static $version = "v1";
static $mapped_url = ["menu", "posts"];
// The response
static $j = [];
/**
* The main method
*/
public static function run() {
self::$klein = new \Klein\Klein();
self::$j["id"] = "";
self::$j["date"] = self::getDate();
self::$j["status"] = new stdClass();
self::$j["errors"] = new stdClass();
self::$j["response"] = new stdClass();
self::$j["request"] = new stdClass();
self::showError(200);
self::route_checkVersion();
self::route_getGeneralRoute();
self::route_getMenu();
self::route_getPosts();
self::$klein->dispatch();
}
/**
* Load current error data
* and generate the errors object data in the response object
* @see errors.yml
* @uses yaml_parse_file
* @link http://php.net/manual/en/book.yaml.php
*
* @param int $error The error code to load
* @return object The error data
*/
private static function showError($error, $request = "") {
$errors = json_decode(json_encode(yaml_parse_file("errors.yml")));
if(is_object($errors->$error)) {
self::$j = array_merge(self::$j, (array)$errors->$error);
}
self::$j["id"] = self::generateID($request);
self::$j["request"] = self::generateRequest($request);
return self::$j;
}
/**
* Extract current date in multiple formats
* @return object The generated date object
*/
private static function getDate() {
$d = new stdClass();
$d->UTC = gmdate("Y-m-d\TH:i:s\Z");
$d->readable = date("l, F j, Y");
$date = new DateTime();
$d->timestamp = $date->getTimestamp();
return $d;
}
/**
* Generate an ID from the request
* @param string $request The GET request
* @return string The generated data
*/
private static function generateID($request) {
return sha1($request);
}
/**
* Generate a request summary object from the request
* @param string $request The GET request
* @return object The generated data
*/
private static function generateRequest($request) {
$req = new stdClass();
$req->hash = md5($request);
$req->length = strlen($request);
$req->text = $request;
return $req;
}
/* -------------------------------------------------------------------------
ROUTING
------------------------------------------------------------------------- */
/**
* If not in the "$version" path redirect to the correct url
*/
public static function route_checkVersion() {
$klein = self::$klein;
/**
* If not in the "$version" path return an error
*/
$klein->respond("GET", "!@^/" . self::$version . "/", function($request, $response) use($klein) {
$right_url = preg_replace('/[^a-zA-Z0-9\/]+/', "", str_replace(self::$prefix, "", self::$version . $request->uri()));
$info = explode("/", trim($request->uri(), "/"));
if("/" . $info[0] . "/" . $info[1] == self::$prefix && (!isset($info[2]) || isset($info[2]) && $info[2] !== self::$version)) {
// Redirect to correct url
$klein->response()->redirect(self::$prefix . "/" . $right_url);
}
});
}
/**
* Intercept general routes
*/
public static function route_getGeneralRoute() {
$klein = self::$klein;
$klein->with(self::$prefix . "/" . self::$version, function() use ($klein) {
$klein->respond("GET", "/?", function($request, $response) {
return $response->json(self::showError(411));
});
$klein->respond("GET", "/[*:action]", function($request, $response) use($klein) {
$dirname = pathinfo($request->action)["dirname"];
$basename = pathinfo($request->action)["basename"];
$filename = pathinfo($request->action)["filename"];
$root = ($dirname == ".") ? $basename : $dirname;
if(strlen(trim(preg_replace('/[^a-zA-Z0-9\/]+/', "", $request->action))) > 0) {
// Handle non-mapped requests
if(in_array($root, self::$mapped_url) === false) {
return $response->json(self::showError(400, $request->action));
}
} else {
// $right_url = preg_replace('/[^a-zA-Z0-9\/]+/', "", $request->uri());
// $klein->response()->redirect($right_url);
}
});
});
}
/**
* Get the menu
* @uses yaml_parse_file
* @link http://php.net/manual/en/book.yaml.php
*/
public static function route_getMenu() {
$klein = self::$klein;
$klein->with(self::$prefix . "/" . self::$version, function() use ($klein) {
$klein->with("/menu", function() use($klein) {
$klein->respond("GET", "/?", function($request, $response) {
// Parse YAML file
$menu = json_decode(json_encode(yaml_parse_file(YML_DIR . "menu.yml")));
self::$j["response"]->id = 1;
self::$j["response"]->type = "menu";
self::$j["response"]->data = $menu;
self::$j["request"] = self::generateRequest("/menu");
return $response->json(self::$j);
});
$klein->respond("GET", "/[:action]", function($request, $response, $service) use($klein) {
$klein->response()->redirect("./");
});
});
});
}
/**
* Get posts
*/
public static function route_getPosts() {
$klein = self::$klein;
$klein->with(self::$prefix . "/" . self::$version, function() use ($klein) {
$klein->with("/posts", function() use($klein) {
/**
* All posts
*/
$klein->respond("GET", "/?", function($request, $response) {
// return "select * from `table`";
self::$j["response"]->id = "*";
self::$j["response"]->type = "posts";
self::$j["response"]->data = ""; // Database query response
return $response->json(self::$j);
});
/**
* Single post
*/
$klein->respond("GET", "/[:id]", function($request, $response, $service) use($klein) {
$klein->onError(function($klein, $err_msg) {
$klein->response()->redirect("./" . preg_replace('/[^0-9]+/', "", $klein->request()->id));
});
$service->validateParam("id", "Please enter a valid id")->isChars("0-9");
// return "select * from `table` where `id` = '" . $request->id . "'";
$query = ""; // Database query response
if(!is_null($query)) {
self::$j["response"]->id = $request->id;
self::$j["response"]->type = "posts";
self::$j["response"]->data = $query;
header("Content-type: application/json");
return json_encode(self::$j, JSON_NUMERIC_CHECK);
} else {
return $response->json(self::showError(404));
}
});
});
});
}
}
Routing::run();
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment