.gitignore 0000644 00000000036 14476160710 0006541 0 ustar 00 /vendor/
/tests/
composer.lock composer.json 0000644 00000000740 14476160710 0007275 0 ustar 00 {
"name": "boru/dhapi",
"type": "library",
"autoload": {
"psr-4": {
"boru\\dhapi\\": "src/"
}
},
"authors": [
{
"name": "Daniel Hayes",
"email": "dhayes@boruapps.com"
}
],
"require": {
"boru/dhutils": ">=2.0.0",
"boru/dhttp": "*"
},
"repositories": [
{
"type": "composer",
"url": "https://satis.boruapps.com"
}
]
}
instructions-composer.txt 0000644 00000000300 14476160710 0011675 0 ustar 00 {
"require": {
"boru/dhapi": "*"
},
"repositories": [
{
"type": "composer",
"url": "https://satis.boruapps.com"
}
]
} src/API.php 0000644 00000020012 14476160710 0006456 0 ustar 00 request = $request;
$this->router = $router;
$this->container = new Container();
}
private $baseClasss = "";
/** @return Request */
public static function requestFromGlobals() {
$method = $_SERVER["REQUEST_METHOD"];
if($method == "POST" && array_key_exists('HTTP_X_HTTP_METHOD', $_SERVER)) {
if(in_array($_SERVER['HTTP_X_HTTP_METHOD'],['DELETE','PUT','PATCH'])) {
$method = $_SERVER['HTTP_X_HTTP_METHOD'];
} else {
return Response::fromError(405,"Unusual Request Method: ".$_SERVER['HTTP_X_HTTP_METHOD'],true,true);
}
}
if(!in_array($method,static::$allowedMethods)) {
Response::fromError(405,"Method Not Allowed",true,true);
}
$rawBody = file_get_contents("php://input");
$body = [];
$content_type = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '';
if(!empty($rawBody) && strpos($content_type,'application/json') !== false) {
$body = json_decode($rawBody,true);
if($body === null) {
return Response::fromError(400,"Invalid JSON",true,true);
}
} elseif(!empty($_POST)) {
$body = $_POST;
}
$path = "/";
if(isset($_GET[static::$requestVar])) {
$path.= trim($_GET[static::$requestVar],"/");
unset($_GET[static::$requestVar]);
}
$actualUrl = (empty($_SERVER['HTTPS']) ? 'http' : 'https') . "://".$_SERVER['HTTP_HOST'];
$requestUri = $_SERVER['REQUEST_URI'];
if(!empty($_SERVER['REQUEST_URI']) && !empty($_SERVER['PHP_SELF'])) {
if(strtolower($_SERVER["REQUEST_URI"]) == strtolower($_SERVER["PHP_SELF"])) {
$pieces = explode("/",$_SERVER["REQUEST_URI"]);
$requestUri = implode("/",array_slice($pieces,0,-1));
}
}
$actualUrl.= $requestUri;
$parts = explode("?", $actualUrl);
if(strlen($path) > 1) {
$rootUrl = str_replace($path,"",$parts[0]);
} else {
$rootUrl = $parts[0];
}
//echo "
";print_r($_REQUEST); echo "
";exit();
$request = new Request($_GET,$body,getallheaders(),$method,$path);
$request->setRootUrl($rootUrl);
$request->setRawBody($rawBody);
return $request;
}
/**
* @param Request $request
* @return API
*/
public function setRequest($request) {
$this->request = $request;
return $this;
}
/**
* @param Router $router
* @return API
*/
public function setRouter($router) {
$this->router = $router;
return $this;
}
/**
* @param Container $container
* @return API
*/
public function setContainer($container) {
$this->container = $container;
return $this;
}
public function setClassBaseNamespace($namespace) {
$this->baseClasss = $namespace;
return $this;
}
public function getClassBaseNamespace() {
return $this->baseClasss;
}
/** @return Request */
public function getRequest() {
if(is_null($this->request)) {
$this->request = static::requestFromGlobals();
}
if(!($this->request instanceof Request)) {
throw new \Exception("Request is not a Request object");
}
return $this->request;
}
/** @return Router */
public function getRouter() {
return $this->router;
}
/** @return Container */
public function getContainer() {
return $this->container;
}
public function getMethod() {
return $this->request->getMethod();
}
public function getPath() {
return $this->request->getPath();
}
public function body($key=null,$defaultValue=null) {
return $this->request->body($key,$defaultValue);
}
public function get($key=null,$defaultValue=null) {
return $this->request->body($key,$defaultValue);
}
public function attr($key=null,$defaultValue=null) {
return $this->container->get($key,$defaultValue);
}
public function header($key=null) {
if(is_null($key)) {
return $this->request->getHeaders();
}
return $this->request->getHeader($key);
}
public function headers() {
return $this->request->getHeaders();
}
/**
* @return Response
*/
public function process() {
$time = -microtime(true);
if(($this->route = $this->router->matchFromPath($this->getPath(), $this->getMethod(),$this)) !== false) {
$dispatcher = new Dispatcher($this,$this->route);
static::applyMiddleware($dispatcher,$this->getMiddleware());
static::applyMiddleware($dispatcher,$this->router->getMiddleware());
static::applyMiddleware($dispatcher,$this->route->getMiddleware());
$response = $dispatcher->dispatch();
static::applyMiddleware($response,$this->route->getMiddlewareResponse());
static::applyMiddleware($response,$this->router->getMiddlewareResponse());
static::applyMiddleware($dispatcher,$this->getMiddlewareResponse());
return $response;
} else {
return Response::fromError("Endpoint Not Found",404,true,true);
}
}
/**
* @param \mrr\api\v3\router\Route $route
*/
public function addRoute($route) {
if(is_null($this->router)) {
$this->router = new Router();
}
$this->router->add($route);
}
public function addMiddleware($middleware) {
$this->middlewares[] = static::assertMiddleware($middleware);
}
public function getMiddleware() {
return $this->middlewares;
}
public function addMiddlewareResponse($middleware) {
$this->middlewareResponse[] = API::assertMiddleware($middleware);
}
public function getMiddlewareResponse() {
return $this->middlewareResponse;
}
public static function assertMiddleware(&$middleware) {
if(is_callable($middleware)) {
$middleware = new CallbackMiddleware($middleware);
}
if(!($middleware instanceof MiddlewareInterface)) {
throw new \Exception("Invalid middleware");
}
return $middleware;
}
public static function applyMiddleware(&$object,$middlewareList=[]) {
if(empty($middlewareList)) {
return;
}
$queue = new \SplQueue();
reset($middlewareList);
foreach($middlewareList as $middleware) {
$queue->enqueue($middleware);
}
$next = function($obj) use ($queue,&$next) {
if($queue->isEmpty()) {
return $obj;
}
$middleware = $queue->dequeue();
return $middleware($obj,$next);
};
$object = $next($object);
}
} src/Controller.php 0000644 00000000474 14476160710 0010202 0 ustar 00 "test/endpoint2!","args"=>$api->getArgs(),"apiArgs"=>$api->getRequest()->body()]);
}
} src/core/Container.php 0000644 00000007216 14476160710 0010732 0 ustar 00 setFromArray($data);
}
}
public function get($key=null,$defaultValue=null) {
if(is_null($key)) {
return $this->toArray(false);
}
$key = $this->camelKey($key);
$value = dhGlobal::getVal($this->data,$key,$defaultValue);
if(is_callable($value)) {
return $value();
}
return $value;
}
public function set($key,$value=null) {
if(is_array($key)) {
$this->data=$key;
return $this;
}
$key = $this->camelKey($key);
if(strpos($key,".")!==false) {
dhGlobal::dotAssign($this->data,$key,$value);
} else {
$this->data[$key] = $value;
}
if(is_callable($value)) {
$this->callableKeys[] = $key;
}
}
public function getRaw($key=null,$defaultValue=null) {
if(is_null($key)) {
return $this->rawData;
}
return isset($this->rawData[$key]) ? $this->rawData[$key] : $defaultValue;
}
public function setRaw($key,$value=null) {
if(is_array($key)) {
$this->rawData=$key;
return $this;
}
$this->rawData[$key] = $value;
return $this;
}
public function toArray($hideNonPublic=true) {
$data = $this->data;
if($hideNonPublic) {
foreach($this->nonPublicFields as $field) {
dhGlobal::dotDelete($data,$field);
}
}
foreach($this->callableKeys as $callableKey) {
if($this->dotExists($callableKey)) {
$value = $this->get($callableKey);
dhGlobal::dotAssign($data,$callableKey,$value);
}
}
return $data;
}
public function __toString() {
return json_encode($this->toArray(),JSON_PRETTY_PRINT);
}
public function jsonSerialize() {
return $this->toArray();
}
public function setFromArray($array) {
if(empty($array)) { return false; }
$this->rawData = $array;
foreach($array as $key=>$value) {
//add it to our data set
$this->set($key,$value);
}
}
public function camelKey($key) {
$time = -microtime(true);
if(strpos($key,".")===false) { return dhGlobal::camelize($key); }
$parts = explode(".",trim($key));
$newKey = [];
for($i=0;$idotExists(implode(".",$tempKey))) {
$newPart = dhGlobal::camelize($part);
$newKey[] = $newPart;
} else {
$newKey[] = $part;
}
}
$time += microtime(true); echo "Time: ".$time."\n"; $time = -microtime(true);
return implode(".",$newKey);
}
public function dotExists($dotString) {
$pieces = explode(".", $dotString);
$pointer = $this->data;
for ($i = 0; $i < count($pieces); $i++) {
if (isset($pointer[$pieces[$i]])) {
$pointer = $pointer[$pieces[$i]];
} else {
return false;
}
}
return true;
}
} src/core/Dispatcher.php 0000644 00000011175 14476160710 0011075 0 ustar 00 API = $API;
}
public function __construct($API,$route) {
$this->API = $API;
$this->route = $route;
$this->router = $API->getRouter();
$this->request = $API->getRequest();
$this->container = $API->getContainer();
$this->args = new Container();
}
public function dispatch() {
$time = -microtime(true);
if($this->route instanceof Response) {
return $this->route;
}
$handler = $this->route->getHandler();
// $arguments = ['id' => 2]
$this->args = new Container($this->route->getVars());
if(is_array($handler)) {
$response = $this->displatchClassController($handler);
} else {
$response = $this->dispatchCallable($handler);
}
if(!($response instanceof Response)) {
//If the response is not a response object, we didn't return valid data from our endpoint.. so not implemented
ob_clean();
return Response::fromError("Endpoint method not implemented",404,true,true);
}
return $response;
}
private function dispatchCallable($handler) {
$controller = $handler;
if (!is_callable($controller)) {
return Response::fromError("Endpoint controller method not found",404,true,true);
}
return $controller($this);
}
private function displatchClassController($handler) {
$controllerName = $handler[0];
if(substr($controllerName,0,1) !== "\\" && !class_exists($controllerName)) {
$controllerName = $this->API->getClassBaseNamespace().$controllerName;
}
$methodName = $handler[1] ? $handler[1] : null;
if(!class_exists($controllerName)) {
return Response::fromError("Endpoint controller Not Found - $controllerName",404,true,true);
}
$controller = new $controllerName();
if (!is_callable($controller)) {
$controller = [$controller, $methodName];
}
if (!is_callable($controller)) {
return Response::fromError("Endpoint controller method not found",404,true,true);
}
return $controller($this);
}
public function getRequest() {
return $this->request;
}
public function getRouter() {
return $this->router;
}
public function getRoute() {
return $this->route;
}
public function getApi() {
return $this->API;
}
public function getArgs() {
return $this->args;
}
public function getContainer() {
return $this->container;
}
public function getPath() {
return $this->request->getPath();
}
public function path() {
return $this->request->getPath();
}
public function baseUrl() {
return $this->request->getRootUrl();
}
public function rawBody() {
return $this->request->getRawBody();
}
public function request($key=null,$defaultValue=null) {
return $this->request->body($key,$defaultValue);
}
public function body($key=null,$defaultValue=null) {
return $this->request->body($key,$defaultValue);
}
public function data($key=null,$defaultValue=null) {
return $this->request->body($key,$defaultValue);
}
public function head($key=null,$defaultValue=null) {
return $this->request->head($key,$defaultValue);
}
public function header($key=null,$defaultValue=null) {
return $this->request->head($key,$defaultValue);
}
public function args($key=null,$defaultValue=null) {
return $this->args->get($key,$defaultValue);
}
public function arg($key=null,$defaultValue=null) {
return $this->args->get($key,$defaultValue);
}
public function attr($key=null,$defaultValue=null) {
if(is_null($key)) {
return $this->container;
}
return $this->container->get($key,$defaultValue);
}
public function setAttr($key,$value) {
$this->container->set($key,$value);
}
} src/core/ErrorResponse.php 0000644 00000000371 14476160710 0011613 0 ustar 00 _cleanInputs($get);
$data = array_merge($data,$this->_cleanInputs($body));
$data = $this->_parseDots($data);
$this->setHeaders($headers);
$this->setMethod($method);
$this->setPath($path);
$this->setData($data);
}
public function body($key=null,$defaultValue=null) {
if($key === null) {
return $this->data;
}
return dhGlobal::getVal($this->data,$key,$defaultValue);
}
public function data($key=null,$defaultValue=null) {
return $this->body($key,$defaultValue);
}
public function head($key=null,$defaultValue=null) {
if($key === null) {
return $this->headers;
}
return dhGlobal::getVal($this->headers,$key,$defaultValue);
}
public function header($key=null,$defaultValue=null) {
return $this->head($key,$defaultValue);
}
public function getData() {
return $this->data;
}
public function getHeader($key) {
$key = strtolower($key);
if(isset($this->headers[$key])) {
return $this->headers[$key];
}
return null;
}
public function getHeaders() {
return $this->headers;
}
public function getMethod() {
return $this->method;
}
public function getPath() {
return $this->path;
}
public function getRootUrl() {
return $this->rootUri;
}
public function getRawBody() {
return $this->rawBody;
}
public function setMethod($method) {
$this->method = $method;
}
public function setPath($path) {
$this->path = $path;
}
public function setData($data) {
$this->data = $data;
}
public function setRootUrl($rootUrl) {
$this->rootUri = $rootUrl;
}
public function setRawBody($rawBody) {
$this->rawBody = $rawBody;
}
public function setHeader($key,$value) {
$key = strtolower($key);
$this->headers[$key] = $value;
}
public function setHeaders($headers) {
foreach($headers as $k=>$v) {
$this->setHeader($k,$v);
}
}
//utility functions
private function _cleanInputs($data) {
$clean_input = Array();
if (is_array($data)) {
foreach ($data as $k => $v) {
$clean_input[$k] = $this->_cleanInputs($v);
}
} else {
$clean_input = trim($data);
}
return $clean_input;
}
private function _parseDots($array) {
foreach($array as $k=>$v) {
if(is_array($v)) {
$array[$k] = $this->_parseDots($v);
}
if(strpos($k,".") !== false) {
$this->_assignArrayByPath($array,$k,$v);
unset($array[$k]);
}
}
return $array;
}
private function _assignArrayByPath(&$arr, $path, $value, $separator='.') {
$keys = explode($separator, $path);
foreach ($keys as $key) {
$arr = &$arr[$key];
}
$arr = $value;
}
} src/core/Response.php 0000644 00000017626 14476160710 0010614 0 ustar 00 "OK",
400=>"Bad Request",
401=>"Unauthorized",
403=>"Forbidden",
404=>"Not Found",
405=>"Method Not Allowed",
406=>"Not Acceptable",
409=>"Conflict",
410=>"Gone",
411=>"Length Required",
412=>"Precondition Failed",
413=>"Payload Too Large",
414=>"URI Too Long",
415=>"Unsupported Media Type",
416=>"Range Not Satisfiable",
417=>"Expectation Failed",
418=>"I'm a teapot",
420=>"Missing Required Inputs",
421=>"Invalid Inputs",
422=>"Unprocessable Entity",
423=>"Locked",
424=>"Failed Dependency",
425=>"Too Early",
426=>"Upgrade Required",
428=>"Precondition Required",
429=>"Too Many Requests",
431=>"Request Header Fields Too Large",
451=>"Unavailable For Legal Reasons",
500=>"Internal Server Error",
501=>"Not Implemented",
502=>"Bad Gateway",
503=>"Service Unavailable",
504=>"Gateway Timeout",
505=>"HTTP Version Not Supported",
506=>"Variant Also Negotiates",
507=>"Insufficient Storage",
508=>"Loop Detected",
510=>"Not Extended",
511=>"Network Authentication Required",
];
protected $data = [];
protected $statusCode = 200;
protected $message = null;
protected static $pretty = true;
protected static $fieldList = [];
public function __construct($data=[], $status=200, $message=null) {
$this->setData($data);
$this->setStatus($status);
$this->setMessage($message);
}
public function emit($terminate=null) {
$this->emitStatusCode();
header("Content-Type: application/json");
echo $this->jsonEncode(static::parseFieldList($this->data));
if(static::shouldTerminate($terminate)) {
exit;
}
}
private function jsonEncode($data) {
return json_encode($data, $this->getJsonFlags());
}
private function emitStatusCode() {
if(!isset(self::$requestCodeStatus[$this->statusCode])) {
$this->statusCode = 500;
}
header("HTTP/1.1 " .$this->statusCode ." " .self::$requestCodeStatus[$this->statusCode]);
}
private function getJsonFlags() {
if(static::$pretty) {
return JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR;
} else {
return JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR;
}
}
public function setStatus($status) {
$this->statusCode = $status;
}
public function setMessage($message) {
$this->message = $message;
}
public static function setPretty($pretty) {
static::$pretty = $pretty;
}
/**
* Set the field list to use when returning data
* @param array $fieldList the fields to return
*/
public static function setFieldList($fieldList) {
if(!is_array($fieldList)) {
$fieldList = Utils::splitToArray($fieldList);
}
static::$fieldList = $fieldList;
}
public function setData($data) {
$this->data = $data;
}
private static function parseFieldList($data) {
if(!empty(static::$fieldList) && is_array($data) && !empty($data)) {
$newData = static::filterArray(json_decode(json_encode($data,JSON_PARTIAL_OUTPUT_ON_ERROR),true) , static::$fieldList);
if(empty($newData)) {
return null;
} else {
return $newData;
}
} else {
return $data;
}
}
private static function filterArray($array,$fieldFilter,$currentKey='') {
$result = [];
foreach ($array as $key => $value) {
$newKey = $currentKey === '' ? $key : $currentKey . '.' . $key;
$matchedFilter = '';
foreach ($fieldFilter as $filter) {
$pattern = "(\.|\.[0-9]+\.)";
$prePattern = "([0-9]+\.)?";
$newFilter = str_replace('.', $pattern, $filter);
$regex = '/^'.$prePattern. $newFilter . '(\.|$)/';
if (preg_match($regex, $newKey)) {
$matchedFilter = $filter;
break;
}
}
if ($matchedFilter !== '') {
$result[$key] = $value;
} elseif (is_array($value)) {
$filteredValue = static::filterArray($value, $fieldFilter, $newKey);
if (!empty($filteredValue)) {
$result[$key] = $filteredValue;
}
}
}
return $result;
}
// Static Methods
/**
* Create a new Response object from an error, emits and terminates (exit) by default.
* @param string|array $error the error message or data array
* @param int $code (default:400) the HTTP status code
* @param bool $emit (default:true) emit the response (false) return the response object (useful for chaining)
* @param bool $terminate (default:true) terminate the script after emitting (false) return the response object (useful for chaining)
* @return Response
*/
public static function fromError($error, $code=400, $emit=null, $terminate=null) {
$response = new ErrorResponse([
"error"=>$error,
"code"=>$code,
], $code);
if(static::shouldEmit($emit)) {
$response->emit($terminate);
}
return $response;
}
/**
* Create a new Response object from an exception, emits and terminates (exit) by default.
* @param \Exception $exception the exception object
* @param int $code (default:500) the HTTP status code
* @param bool $emit (default:true) emit the response (false) return the response object (useful for chaining)
* @param bool $terminate (default:true) terminate the script after emitting (false) return the response object (useful for chaining)
* @return Response
*/
public static function fromException($exception, $code=500, $emit=null, $terminate=null) {
$response = new ErrorResponse([
"error"=>$exception->getMessage(),
"code"=>$code,
], $code);
if(static::shouldEmit($emit)) {
$response->emit($terminate);
}
return $response;
}
/**
* Create a new Response object from a success, emits and terminates (exit) by default.
* @param string|array $data the data to return
* @param int $code (default:200) the HTTP status code
* @param bool $emit (default:true) emit the response (false) return the response object (useful for chaining)
* @param bool $terminate (default:true) terminate the script after emitting (false) return the response object (useful for chaining)
* @return Response
*/
public static function fromSuccess($data, $code=200, $emit=null, $terminate=null) {
$response = new Response($data, $code);
if(static::shouldEmit($emit)) {
$response->emit($terminate);
}
return $response;
}
public static function shouldEmit($emit=null) {
if($emit===null) {
return self::$autoEmit;
} else {
return $emit;
}
}
public static function shouldTerminate($terminate=null) {
if($terminate===null) {
return self::$autoTerminate;
} else {
return $terminate;
}
}
} src/core/Utils.php 0000644 00000000446 14476160710 0010106 0 ustar 00 "authed",
"permitted"=>"permitted",
"authUser"=>"authUser",
"authMessage"=>"authMessage",
"authInfo"=>"authInfo",
];
private $attributes = [
"authed"=>false,
"permitted"=>false,
"authUser"=>null,
"authMessage"=>"",
"authInfo"=>[],
];
public function __invoke($object, $next) {
$this->object = $object;
$this->setup($object);
$this->_authenticate();
$this->_checkPermissed();
$this->attributesToObject($object);
return $next($object);
}
public function attributesToObject(&$object) {
foreach($this->attributeMap as $key=>$value) {
$object->setAttr($value,$this->attributes[$key]);
}
$object->setAttr("auth",$this->attributes);
}
private function _authenticate() {
if($this->attributes["authed"]) {
return true;
}
$this->setAuthed(false);
if($this->authenticate()) {
$this->setAuthed(true);
$this->authSuccess();
} else {
$this->authFailed();
}
return $this->getAuthed();
}
public function _checkPermissed() {
if(!$this->getAuthed()) {
$this->setPermissed(false);
$this->notPermissed();
} else {
$this->setPermissed($this->checkPermissed());
if($this->getPermissed()) {
$this->wasPermissed();
} else {
$this->notPermissed();
}
}
}
abstract public function setup($object);
abstract public function authenticate();
abstract public function checkPermissed();
abstract public function authSuccess();
abstract public function authFailed();
abstract public function wasPermissed();
abstract public function notPermissed();
public function getAuthed() {
return $this->attributes["authed"];
}
public function setAuthed($authed) {
$this->setAttribute("authed",$this->assertBool($authed));
}
public function getPermissed() {
return $this->attributes["permitted"];
}
public function setPermissed($permissed) {
$this->setAttribute("permitted",$this->assertBool($permissed));
}
public function getAuthUser() {
return $this->attributes["authUser"];
}
public function setAuthUser($authUser) {
$this->setAttribute("authUser",$authUser);
}
public function getAuthMessage() {
return $this->attributes["authMessage"];
}
public function setAuthMessage($authMessage) {
$this->setAttribute("authMessage",$authMessage);
}
public function getAuthInfo() {
return $this->attributes["authInfo"];
}
public function setAuthInfo($authInfo) {
$this->setAttribute("authInfo",$authInfo);
}
public function getAttribute($key,$default=null) {
if(isset($this->attributes[$key])) {
return $this->attributes[$key];
}
return $default;
}
public function setAttribute($key,$value) {
$this->attributes[$key] = $value;
}
protected function addAttributeMap($key,$value) {
$this->attributeMap[$key] = $value;
}
public function getAttributes() {
return $this->attributes;
}
private function assertBool($bool) {
if(is_array($bool) || is_object($bool)) {
$bool = false;
}
return $bool ? true : false;
}
}
src/middleware/CallbackMiddleware.php 0000644 00000000540 14476160710 0013660 0 ustar 00 callback = $callback;
}
public function __invoke($object,$next) {
$callback = $this->callback;
return $callback($object,$next);
}
} src/middleware/HmacAuthentication.php 0000644 00000012602 14476160710 0013740 0 ustar 00 assertCallbacks();
$this->apiKey = $object->header($this->headerApiKey,false);
$this->apiNonce = $object->header($this->headerApiNonce,false);
$this->apiSign = $object->header($this->headerApiSign,false);
$this->path = $this->getPath($object);
$this->object = $object;
$this->apiKeyInfo = $this->getApiKeyInfo();
if(isset($this->apiKeyInfo["secret"])) {
$this->apiSecret = $this->apiKeyInfo["secret"];
} else {
$this->apiSecret = null;
}
$this->setAttribute("authUser",$this->apiKeyInfo);
}
public function authenticate() {
if(is_null($this->apiSecret)) {
$this->setAuthMessage("invalid api key");
return false;
}
$signedString = $this->getSignature();
if($signedString == $this->apiSign) {
if($this->validateNonce && isset($this->apiKeyInfo["nonce"])) {
if($this->apiNonce <= $this->apiKeyInfo["nonce"]) {
$this->setAuthMessage("invalid nonce");
return false;
}
}
return true;
}
$this->setAuthMessage("invalid signature");
return false;
}
public function authSuccess() {
$cb = $this->authSuccessCallback;
return $cb($this->apiKey,$this->apiNonce);
}
public function getApiKeyInfo() {
$cb = $this->getKeyInfoCallback;
return $cb($this->apiKey);
}
public function __construct($options=[],$getKeyInfoCallback=null,$authSuccessCallback=null,$permissionCallback=null) {
if(isset($options["headerApiKey"])) {
$this->headerApiKey = $options["headerApiKey"];
}
if(isset($options["headerApiNonce"])) {
$this->headerApiNonce = $options["headerApiNonce"];
}
if(isset($options["headerApiSign"])) {
$this->headerApiSign = $options["headerApiSign"];
}
if(isset($options["signatureTemplate"])) {
$this->signatureTemplate = $options["signatureTemplate"];
}
if(isset($options["hashAlgo"])) {
$this->hashAlgo = $options["hashAlgo"];
}
if(isset($options["withLeadingSlash"])) {
$this->withLeadingSlash = $options["withLeadingSlash"];
}
if(is_callable($getKeyInfoCallback)) {
$this->getKeyInfoCallback = $getKeyInfoCallback;
}
if(is_callable($authSuccessCallback)) {
$this->authSuccessCallback = $authSuccessCallback;
}
if(is_callable($permissionCallback)) {
$this->permissedCallback = $permissionCallback;
}
}
public function getSignature() {
$signature = str_replace("{APIKEY}",$this->apiKey,$this->signatureTemplate);
$signature = str_replace("{NONCE}",$this->apiNonce,$signature);
$signature = str_replace("{PATH}",$this->path,$signature);
$signature = hash_hmac($this->hashAlgo,$signature,$this->apiSecret);
return $signature;
}
private function getPath($object) {
$path = dhGlobal::trimString($object->getPath(),"/");
if($this->withLeadingSlash) {
$path = "/".$path;
}
return $path;
}
public function setHeaderApiKey($headerApiKey) {
$this->headerApiKey = $headerApiKey;
}
public function setHeaderApiNonce($headerApiNonce) {
$this->headerApiNonce = $headerApiNonce;
}
public function setHeaderApiSign($headerApiSign) {
$this->headerApiSign = $headerApiSign;
}
public function setSignatureTemplate($signatureTemplate) {
$this->signatureTemplate = $signatureTemplate;
}
public function setHashAlgo($hashAlgo) {
$this->hashAlgo = $hashAlgo;
}
private function assertCallbacks() {
if(!is_callable($this->getKeyInfoCallback)) {
$this->getKeyInfoCallback = function($apiKey) {
return false;
};
}
if(!is_callable($this->authSuccessCallback)) {
$this->authSuccessCallback = function($apiKey,$apiNonce) {
return false;
};
}
if(!is_callable($this->permissedCallback)) {
$this->permissedCallback = function($apiKey,$object) {
return true;
};
}
}
} src/middleware/MiddlewareInterface.php 0000644 00000000173 14476160710 0014066 0 ustar 00 apiUser = $object->head($this->headerUsername);
$this->apiPass = $object->head($this->headerPassword);
}
public function authenticate() {
$this->assertCallbacks();
$cb = $this->authCheckCallback;
if($cb($this->apiUser,$this->apiPass)) {
return true;
}
return false;
}
public function checkPermissed() {
$this->assertCallbacks();
$cb = $this->permissedCallback;
if($cb($this->apiUser,$this->object)) {
return true;
}
}
public function authSuccess() {
$this->assertCallbacks();
$cbAuth = $this->authSuccessCallback;
$cbAuth($this->apiUser,$this->apiPass);
}
public function authFailed() {}
public function wasPermissed() {}
public function notPermissed() {}
public function assertCallbacks() {
if(!is_callable($this->authCheckCallback)) {
$this->authCheckCallback = function($apiKey) {
return false;
};
}
if(!is_callable($this->authSuccessCallback)) {
$this->authSuccessCallback = function($apiKey,$apiNonce) {
return false;
};
}
if(!is_callable($this->permissedCallback)) {
$this->permissedCallback = function($apiKey,$object) {
return true;
};
}
}
public function __construct($options=[],$authCheckCallback=null,$authSuccessCallback=null,$permissionCallback=null) {
if(isset($options["headerUsername"])) {
$this->headerUsername = $options["headerUsername"];
}
if(isset($options["headerPassword"])) {
$this->headerPassword = $options["headerPassword"];
}
if(is_callable($authCheckCallback)) {
$this->authCheckCallback = $authCheckCallback;
}
if(is_callable($authSuccessCallback)) {
$this->authSuccessCallback = $authSuccessCallback;
}
if(is_callable($permissionCallback)) {
$this->permissedCallback = $permissionCallback;
}
}
} src/routing/Group.php 0000644 00000000152 14476160710 0010633 0 ustar 00 name = $name;
$this->path = $path;
$this->methods = $methods;
$this->handler = $handler;
$this->options = $options;
if(isset($options["middleware"])) {
foreach($options["middleware"] as $middleware) {
$this->addMiddleware($middleware);
}
}
if(isset($options["middlewareResponse"])) {
foreach($options["middlewareResponse"] as $middleware) {
$this->addMiddlewareResponse($middleware);
}
}
}
public function setRouter($router) {
$this->router = $router;
}
public function addMiddleware($middleware) {
$this->middlewares[] = API::assertMiddleware($middleware);
}
public function getMiddleware() {
return $this->middlewares;
}
public function addMiddlewareResponse($middleware) {
$this->middlewareResponse[] = API::assertMiddleware($middleware);
}
public function getMiddlewareResponse() {
return $this->middlewareResponse;
}
public function match($path, $method) {
$regex = $this->getPath();
$this->varCallbacks = [];
foreach ($this->getVarsNames() as $variable) {
$varName = trim($variable, '{\}');
$parts = explode("|",$varName);
$paramName = array_shift($parts);
$nameParts = explode(":",$paramName);
$regexPattern = "[^/]+";
if(count($nameParts) > 1) {
$paramName = array_shift($nameParts);
$shortcut = $this->router->getRegex($nameParts[0]);
$regexPattern = $shortcut["regex"];
if($shortcut["callback"] !== null && (!isset($this->varCallbacks[$paramName]) || !in_array($shortcut["callback"],$this->varCallbacks[$paramName]))) {
$this->varCallbacks[$paramName][] = $shortcut["callback"];
}
}
$regex = str_replace($variable, '(?P<' . $paramName . '>'.$regexPattern.'+)', $regex);
if(!empty($parts)) {
foreach($parts as $part) {
if(!isset($this->varCallbacks[$paramName]) || !in_array($part,$this->varCallbacks[$paramName])) {
$this->varCallbacks[$paramName][] = $part;
}
}
}
}
if (in_array($method, $this->getMethods()) && preg_match('#^' . $regex . '$#sD', self::trimPath($path), $matches)) {
$values = array_filter($matches, static function ($key) {
return is_string($key);
}, ARRAY_FILTER_USE_KEY);
$this->vars = $values;
$this->parseVarCallbacks();
return true;
}
$this->varCallbacks = [];
return false;
}
public function getName() {
return $this->name;
}
public function getPath() {
return $this->path;
}
public function getHandler() {
$handler = $this->handler;
if(!empty($this->vars)) {
if(is_array($handler) && (strpos($handler[0],"{") !== false || strpos($handler[1],"{") !== false)) {
foreach($this->vars as $name=>$val) {
$classVal=$methodVal=null;
if(is_array($val)) {
$classVal = implode("\\",$val);
} else {
$classVal = $val;
$methodVal = $val;
}
if(is_string($handler[0]) && !is_null($classVal)) {
$handler[0] = str_replace("{".$name."}",$classVal,$handler[0]);
}
if(is_string($handler[1]) && !is_null($classVal)) {
$handler[1] = str_replace("{".$name."}",$methodVal,$handler[1]);
}
}
}
}
return $handler;
}
public function getMethods() {
return $this->methods;
}
public function getVarsNames() {
preg_match_all('/{[^}]*}/', $this->path, $matches);
$reset = reset($matches);
return $reset ? $reset : [];
}
public function hasVars() {
return $this->getVarsNames() !== [];
}
public function getVars() {
return $this->vars;
}
public static function trimPath($path) {
return '/' . rtrim(ltrim(trim($path), '/'), '/');
}
public function parseVar($var,$value) {
if(($callable = $this->router->getVariableCallback($var)) !== false) {
if (!is_callable($callable)) {
return $value;
}
return call_user_func($callable, $value);
}
return $value;
}
private function parseVarCallbacks() {
foreach($this->varCallbacks as $varName=>$callbacks) {
if(isset($this->vars[$varName])) {
$value = $this->vars[$varName];
foreach($callbacks as $callback) {
if(($callable = $this->router->getVariableCallback($callback)) !== false) {
if (!is_callable($callable)) {
continue;
}
$value = call_user_func($callable, $value);
}
}
$this->vars[$varName] = $value;
}
}
}
public static function fromArray($path,$methods,$callable,$requireAuth = false) {
return new Route($path,$path,$methods,$callable,$requireAuth);
}
} src/routing/Router.php 0000644 00000007213 14476160710 0011024 0 ustar 00 [
"regex" => "[0-9]+",
"callback" => null,
],
"idlist"=> [
"regex" => "[0-9,;]+",
"callback" => "split",
],
"number" => [
"regex" => "[0-9]+",
"callback" => null,
],
"numberlist"=> [
"regex" => "[0-9,;]+",
"callback" => "split",
],
"string" => [
"regex" => "[a-zA-Z0-9\-_]+",
"callback" => null,
],
"stringlist" => [
"regex" => "[a-zA-Z0-9\-_,;]+",
"callback" => "split",
],
"alpha" => [
"regex" => "[a-zA-Z]+",
"callback" => null,
],
"alphalist" => [
"regex" => "[a-zA-Z,;]+",
"callback" => "split",
],
"all" => [
"regex" => ".+",
"callback" => null,
],
];
private $variableCallbacks = [
"split" => ["\\boru\\dhapi\\routing\\Router","splitToArray"],
];
/** @var MiddlewareInterface[] */
private $middlewares = [];
/** @var MiddlewareInterface[] */
private $middlewareResponse = [];
/**
* @var Route[] $routes
*/
public function __construct($routes=[]) {
foreach($routes as $route) {
$this->add($route);
}
}
/**
* @var Route $route
*/
public function add($route) {
$route->setRouter($this);
$this->routes[$route->getName()] = $route;
return $this;
}
public function addRoute($routeName,$path,$methods,$parameters,$options=[]) {
return $this->add(new Route($routeName,$path,$methods,$parameters,$options));
}
public function addCallback($name,$callback) {
$this->variableCallbacks[$name] = $callback;
return $this;
}
public function addRegex($name,$regex,$callback=null) {
$this->regexShortcuts[$name] = [
"regex" => $regex,
"callback" => $callback,
];
return $this;
}
public function matchFromPath($pathString, $method, $api) {
foreach ($this->routes as $route) {
if ($route->match($pathString, $method) === false) {
continue;
}
return $route;
}
return false;
}
public function addMiddleware($middleware) {
$this->middlewares[] = API::assertMiddleware($middleware);
}
public function getMiddleware() {
return $this->middlewares;
}
public function addMiddlewareResponse($middleware) {
$this->middlewareResponse[] = API::assertMiddleware($middleware);
}
public function getMiddlewareResponse() {
return $this->middlewareResponse;
}
public function getRegex($name) {
if(isset($this->regexShortcuts[$name])) {
return $this->regexShortcuts[$name];
}
return ["regex" => "[^/]+", "callback" => null];
}
public function getVariableCallback($name) {
if(isset($this->variableCallbacks[$name])) {
return $this->variableCallbacks[$name];
}
return false;
}
public static function splitToArray($idInput="") {
if(empty($idInput)) return [];
$delims = [",",";"];
$idInput = str_replace($delims,$delims[0],$idInput);
return explode($delims[0],$idInput);
}
} test/init.php 0000644 00000000073 14476160710 0007205 0 ustar 00
RewriteEngine On
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .+ - [L]
#RewriteRule ^(.+)/?$ index.php?PATH=$1&%{QUERY_STRING} [L]
RewriteRule ^(.+)/?$ v2test.php?APIREQ=$1&%{QUERY_STRING} [L]