Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00%
0 / 1
57.14%
4 / 7
CRAP
85.00%
51 / 60
EndPoint
0.00%
0 / 1
57.14%
4 / 7
27.11
85.00%
51 / 60
 setRoutes()
100.00%
1 / 1
1
100.00%
1 / 1
 mountEndPoint($name, $endPointClassName)
100.00%
1 / 1
1
100.00%
2 / 2
 getEndPointPath()
0.00%
0 / 1
6.07
87.50%
14 / 16
 getEndPointName()
100.00%
1 / 1
3
100.00%
9 / 9
 __construct($virtualhost = null)
100.00%
1 / 1
3
100.00%
9 / 9
 run(Request $request=null)
0.00%
0 / 1
9.65
80.00%
16 / 20
 representationCachingProcessor($method=Caching::NO)
0.00%
0 / 1
6
0.00%
0 / 3
<?php
namespace BOTK\Core;
use \InvalidArgumentException;
use Respect\Rest\Router,
Respect\Rest\Request;
use BOTK\Core\Models\HttpProblem,
BOTK\Core\Representations\Error;
/**
* This class extends Respect\Rest\Router just changing some defaults and adding
* an antipattern to override accept methods from URL request
* Include the apiDoc structure BOTK and the Permission authBasic.
* See http://apidocjs.com/ for more info.
*/
/**
* @apiDefineStructure BOTK
*
* @apiDescription
* Supports HTTP content negotiation for html, json, xml, txt representation.
* Error representation is based on [ietf specs](http://tools.ietf.org/html/draft-nottingham-http-problem-04).
*
* @apiParam {Url encoded Mime type} [_output] allow overriding of content negotiation i.e. `_output=application%2Fjson`
*
*
* @apiError problemType An absolute URI that identifies the problem type.
* @apiError title Human-readable summary of the problem.
* @apiError detail AAn human readable explanation specific to this occurrence of the problem.
* @apiError problemInstance An absolute URI that identifies this occurrence of the problem.
* @apiError httpStatus The HTTP status code ([RFC2616], Section 6)
*
* @apiErrorExample Error 404 example
* {
* problemType: "http://dbpedia.org/resource/Category:HTTP_status_codes"
* title: "Resource not found"
* detail: "Resource report 5441392469671 not found."
* problemInstance: "http://dbpedia.org/resource/HTTP_404"
* httpStatus: 404
* }
*/
/**
* @apiDefinePermission authBasic Access rights needed.
* You need to provide credential to use this api (http Basic autentication).
*/
class EndPoint extends Router
{
protected $endPointCatalog=array();
protected $endPointPath=null; //lazy initialization
protected $endPointName=null;
// Hooks to build resusable end-poins
protected function setRoutes() {}
//this allow to reuse other endpoints routing logic
public function mountEndPoint($name, $endPointClassName)
{
$this->endPointCatalog[$name] = $endPointClassName;
return $this;
}
/*
* EndPointPath path is the portion of an URI Request that prefix the End Point Name
* This can be auto detected even if URL rewite occurs.
*/
public function getEndPointPath()
{
// lazy creation
if (is_null($this->endPointPath)) {
// ----------------------------------------
// Auto Detect End Application Path
// ----------------------------------------
$scriptName = $_SERVER['SCRIPT_NAME'];
$scriptDir = dirname($scriptName);
$requestURI = $_SERVER['REQUEST_URI'];
if (0 === strpos($requestURI, $scriptName)) {
// No url rewriting:
// $_SERVER['REQUEST_URI']: /service/index.php/hello/to/world?query_string#fragment
// $_SERVER['SCRIPT_NAME']: /service/index.php
//
// $endPointPath: /service/index.php
$endPointPath = $scriptName;
} elseif (0 === strpos($requestURI, $scriptDir)) {
// Simple url rewiting occurred:
// $_SERVER['REQUEST_URI']: /service/hello/to/world?query_string#fragment
// $_SERVER['SCRIPT_NAME']: /service/index.php
//
// $endPointPath: /service
$endPointPath = dirname($scriptName);
} elseif (!empty($_SERVER['PATH_INFO']) && $pos = strpos($requestURI,$_SERVER['PATH_INFO'])) {
// Complex rewriting roule. Try extract application path using PATH_INFO variable
// $_SERVER['REQUEST_URI']: /service/v1/hello/to/world?query_string#fragment
// $_SERVER['SCRIPT_NAME']: /serviceV1/index.php
// $_SERVER['PATH_INFO']: /hello/to/world
//
// $endPointPath: /service/v1
$endPointPath = substr($requestURI,0,$pos);
} else {
throw new HttpErrorException( HttpProblem::factory(
404,'End-point not found',"Unable autodetecting application path: URI=$requestURI."));
}
$this->endPointPath = $endPointPath;
}
return $this->endPointPath;
}
/*
* An End Point Name is a portion of a http Resource request that identify a router
*/
public function getEndPointName()
{
// lazy creation
if (is_null($this->endPointName)) {
$endPointPath = $this->getEndPointPath();
$pathinfo = substr($_SERVER['REQUEST_URI'], strlen($endPointPath));
$haystack = $pathinfo . '/';
$this->endPointName = preg_match('@/([a-z0-9]+)[/?#]@', $haystack, $matches)
? $matches[1]
: '';
}
return $this->endPointName;
}
public function __construct($virtualhost = null)
{
if (is_null($virtualhost)) $virtualhost=$this->getEndPointPath();
// Anti-Pattens
// allow ?_output=MIMETYPE on the URI to override HTTP accept header requests.
if (isset($_REQUEST['_output'])) {
$_SERVER['HTTP_ACCEPT'] = $_REQUEST['_output'];
}
parent::__construct($virtualhost);
// Hooks to create routes
$this->setRoutes();
// set botk defaults: you can'nt modyfi this
$this->methodOverriding = true;
$this->isAutoDispatched = false;
return $this;
}
/**
* Redefine run taking into account mountend end points
*/
public function run(Request $request=null)
{
$endPointName = $this->getEndPointName();
$applicationPath = $this->getEndPointPath();
$routerClass = ($endPointName && isset($this->endPointCatalog[$endPointName]))
? $this->endPointCatalog[$endPointName]
: '';
$virtualhost = empty($endPointName)?$applicationPath:($applicationPath.'/'.$endPointName);
if ( !$routerClass ) {
$result = parent::run($request); //fall back to local application routing
} else {
// now we test that $routerclass is a valid end_point
$myClass = get_class();
if ($routerClass ==$myClass || is_subclass_of($routerClass, $myClass)){
// Create new end-point
$endpoint = new $routerClass($virtualhost);
$result = $endpoint->run();
} else {
throw new HttpErrorException(HttpProblem::factory(
500, 'Invalid endpoint', $routerClass.' end point class is not a subClass of '. $myClass));
}
}
//now prepare an error report for humans when something went wrong
//Warning this trigger is called only in php >5.4. Otherwhise just empty content is printed
//(but original status is preserved)
if (empty($result) && ($errorCode=Http::getHttpResponseCode()) >= 400) {
$result = ErrorManager::getInstance()->serializeHttpProblem(new HttpProblem($errorCode));
}
return $result;
}
/*************************************************************************
* Caching control.
*
* use this to allow "resource representation caching", both at rute level:
* $route->through($this->representationCachingProcessor())
* or at endpoint level:entation caching
* $this->always('through',$this->representationCachingProcessor())
*
* See allo cachin at controller level (i.e. caching at resource level)
*************************************************************************/
public function representationCachingProcessor($method=Caching::NO)
{
$cachingClass = '\\BOTK\\Core\\Caching';
if ( !method_exists($cachingClass, $method)) $method =Caching::NO;
return array( $cachingClass, $method);
}
}