slug = $slug; if(isset($description)) $this->description = $description; $this->route = array(); return $this; } function route($slug = null,$description = null,$method = null,$callback){ $route = new ApiRoute($slug,$description,$method,$callback); $this->route[$route->slug][$method] = $route; } function register(){ Plugin::addHook('api',function(&$apis){ $apis[$this->slug] = $this; }); } //retourne la liste des headers de la requete courante (clé normalisées) public static function headers(){ $headers = array(); foreach(getallheaders() as $key=>$value) $headers[mb_strtolower($key)] = $value; return $headers; } //définis si la requete courante est via api ou en http classique (ou autre) public static function requested(){ return preg_match('/\/api\/v[0-9]*\//i', $_SERVER['REQUEST_URI']); } //SI un JWToken est fournis en header Authorization: Bearer , on récupere la session associée //si la session a expirée on la relance public static function token_session(){ if(php_sapi_name() == 'cli') return; if(!self::requested()) return; $headers = self::headers(); if(!isset($headers['authorization'])) return; try{ $authorization = explode(' ',$headers['authorization']); if(count($authorization)!=2 || strtolower($authorization[0])!='bearer') throw new Exception("Bad Authorization header (required Authorization: Bearer )",403); global $conf,$myUser; $token = JWToken::parse(trim($authorization[1]),$conf->get('jwtauth_secret')); //si une session est en cours if(session_status() == PHP_SESSION_ACTIVE){ //si la session courant a le mauvais id (ex une session anonyme ou expirée) on la détruit et on la recréé avec le bon id if( session_id() !=$token['attributes']['session_id']){ session_destroy(); session_id($token['attributes']['session_id']); session_start(); } }else{ //si aucun session en cours, on la créé avec le bon id session_id($token['attributes']['session_id']); session_start(); } if(!isset($_SESSION['currentUser'])) throw new Exception('Token session has expired',498); }catch(Exception $e){ $response['error'] = $e->getMessage(); $response['errorCode'] = $e->getCode(); echo json_encode($response); exit; } } public static function run(){ global $_,$myUser; $response = array(); try{ set_error_handler(function( $errno , $errstr , $errfile , $errline){ throw new Exception("Error ".$errno." Processing Request : ".$errfile." => ".$errstr.' L'.$errline, 500); }); // register_shutdown_function(function(){ // $error = error_get_last(); // throw new Exception("Error 0 Processing Request ".$error['file'], 500); // }); $command = explode('/',$_['command']); if(count($command)<1) throw new Exception("Unspecified API"); $headers = self::headers(); //basic auth (deprecated, utiliser plutot JWToken et Authorization Bearer) if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])){ $user = User::check($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']); if(!$user) throw new Exception('Le compte spécifié est inexistant'); $myUser = $user; } $request = array( 'version' =>array_shift($command), 'module' =>array_shift($command), 'route' =>array_shift($command), 'body' =>file_get_contents("php://input"), 'headers' =>$headers, 'method' => strtoupper($_SERVER['REQUEST_METHOD']), 'parameters' => $_, 'pathes' => array_filter($command) ); $code = 200; $headers = array( 'Content-Type: application/json' ); $apis = array(); Plugin::callHook('api',array(&$apis)); //format de l'url : /api/v{version}/ if(!isset($request['version']) || !is_numeric(substr($request['version'],1)) || substr($request['version'], 0,1)!='v') throw new Exception("Api version is missing or invalid, specify ".ROOT_URL."/api/{version} for valid requests", 404); if($request['module'] == 'schema'){ if(!$myUser->connected()) throw new Exception("Credentials are missing",401); foreach ($apis as $api) { $routes= array(); foreach($api->route as $route){ foreach($route as $method=>$infos){ if(!isset($routes[$infos->slug])) $routes[$infos->slug] = array() ; $routes[$infos->slug][] = $infos->method.' '.$api->slug .'/'.$infos->pattern .': '. $infos->description ; } } $response[] = array( $api->slug => $api->description,'routes'=> $routes); } }else{ if(!isset($apis[$request['module']])) throw new Exception("Api '".$request['module']."' is missing, see ".ROOT_URL."/api/{version}/schema for available calls", 404); $api = $apis[$request['module']]; if(!isset($api->route[$request['route']])) throw new Exception("Route '".$api->slug.'/'.$request['route']."' is missing, see ".ROOT_URL."/api/schema?pretty for available calls", 404); $route = $api->route[$request['route']]; if(!isset($route[$request['method']])) throw new Exception("Method ".$request['method']." '".$api->slug.'/'.$route->slug."' is not allowed", 405); $method = $route[$request['method']]; $callback = $method->callback; $callback($request,$response); if(isset($response['code'])) $code = $response['code']; if(isset($response['headers'])) $headers = array_merge($headers,$response['headers']); unset($response['code']); unset($response['headers']); } }catch(Exception $e){ $response['error'] = $e->getMessage(); $code = $e->getCode(); if(empty($code)) $code = 0; $response['errorCode'] = $code; } $codes = array( 200 => 'HTTP/1.1 200 Ok', 201 => 'HTTP/1.1 201 Created', 204 => 'HTTP/1.1 204 No Content', 207 => 'HTTP/1.1 207 Multi-Status', 400 => 'HTTP/1.0 400 Bad Request', 401 => 'HTTP/1.0 401 Unauthorized', 403 => 'HTTP/1.1 403 Forbidden', 404 => 'HTTP/1.1 404 Not Found', 404 => 'HTTP/1.1 405 Method Not Allowed', 406 => 'HTTP/1.1 406 Not acceptable', 409 => 'HTTP/1.1 409 Conflict', 415 => 'HTTP/1.1 415 Unsupported Media Type', 423 => 'HTTP/1.1 423 Locked', 500 => 'HTTP/1.1 500 Internal Server Error', 501 => 'HTTP/1.1 501 Not implemented', 0 => 'HTTP/1.1 0 Unknown Error', ); if (ob_get_length()) ob_end_clean(); header(isset($codes[$code]) ? $codes[$code] : $codes[0]); foreach ($headers as $header) { header($header); } if(isset($_['pretty'])){ echo json_encode($response,JSON_PRETTY_PRINT); }else{ echo json_encode($response); } exit(); } } class ApiRoute{ public $slug,$description,$callback,$method,$pathes,$parameters,$pattern; function __construct($slug='default',$description='No description',$method='GET',$callback){ $this->pattern = $slug; $infos = explode('?',$slug); if(isset($infos[1])) $this->parameters = $infos[1]; $infos = explode('/',$infos[0]); $this->slug = array_shift($infos); $this->pathes = $infos; $this->description = $description; $this->method = $method; $this->callback = $callback; } } ?>