diff --git a/exporter/packages.json b/exporter/packages.json index e97658d..2998804 100755 --- a/exporter/packages.json +++ b/exporter/packages.json @@ -55,7 +55,8 @@ "1.0": [] }, "router": { - "1.0": [] + "1.0": [], + "2.0": [] } }, "installed": { diff --git a/src/config/router/2.0/routes.json b/src/config/router/2.0/routes.json new file mode 100644 index 0000000..dc2a93b --- /dev/null +++ b/src/config/router/2.0/routes.json @@ -0,0 +1,35 @@ +{ + + "methods": [ "GET", "POST", "PUT", "DELETE" ], + + + "routes": { + + "/{page}/": { + "methods": ["GET"], + "controller": "page:load", + "arguments": { + "page": "[a-z]+" + } + }, + + "/api/{module}/{method}{url_arguments}": { + "controller": "api:call", + "arguments": { + "module": "[a-zA-Z_]+", + "method": "[a-zA-Z_]+", + "url_arguments": "(\\/[\\w:-]+)*\\/?" + } + }, + + "/{any}": { + "methods": ["GET"], + "controller": "redirect:homepage", + "arguments": { + "any": ".+" + } + } + + } + +} diff --git a/src/files/public_html/.htaccess b/src/files/public_html/.htaccess index ab2545e..760dba5 100644 --- a/src/files/public_html/.htaccess +++ b/src/files/public_html/.htaccess @@ -1,4 +1,4 @@ RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f -RewriteRule ^(.*)$ index.php?url=$1 [QSA,L] +RewriteRule ^(.*)$ index.php?url=/$1 [QSA,L] diff --git a/src/packages/api/1.0/core/ModuleRequest.php b/src/packages/api/1.0/core/ModuleRequest.php index 052d7c5..1d4afae 100755 --- a/src/packages/api/1.0/core/ModuleRequest.php +++ b/src/packages/api/1.0/core/ModuleRequest.php @@ -370,7 +370,8 @@ $method = $this->modules[$this->path['module']][$this->path['method']]; // Si aucune permission n'est definie - if( !isset($method['permissions']) ) return true; + if( !isset($method['permissions']) || !is_array($method['permissions']) || count($method['permissions']) < 1 ) + return true; diff --git a/src/packages/api/2.0/core/Authentification.php b/src/packages/api/2.0/core/Authentification.php index 03903d7..b52e350 100644 --- a/src/packages/api/2.0/core/Authentification.php +++ b/src/packages/api/2.0/core/Authentification.php @@ -2,59 +2,21 @@ namespace api\core; - use \database\core\Repo; - use \error\core\Error; use \error\core\Err; + use \error\core\Error; - class Authentification - { + class Authentification{ - private static $instance; - - private $userType; - private $userId; - - public function __construct() - { - $token = !empty($_SERVER['PHP_AUTH_DIGEST']) ? $_SERVER['PHP_AUTH_DIGEST'] : false; - - $user = new Repo("AuthentificationRepo/identifyUserByToken", [$token]); - $user = $user->answer(); - - if( $user != false ){ - $this->userType = $user["Type"]; - $this->userId = $user["Id"]; - } - - new Repo("AuthentificationRepo/updateToken",[$token]); - - new Repo("AuthentificationRepo/purgeOldTokens",[]); - - self::$instance = $this; + /* VERIFICATION DES ACCES EN FONCTION DE PERMISSIONS ATTENDUES + * + * @expected Liste des permissions attendues + * + * @return status Si FALSE, pas la permission, sinon TRUE + * + */ + public static function permission($expected){ + return new Error(Err::Success); } - - public function getUserType(){ - return $this->userType; - } - - public function getUserId(){ - return $this->userId; - } - - public static function permission(array $perm){ - if(in_array(self::$instance->userType,$perm)){ - return new Error(Err::Success); - } - - return new Error(Err::TokenError); - } - - /** - * @return Authentification - */ - public static function getInstance(){ - return self::$instance; - } - - } + +?> diff --git a/src/packages/api/2.0/core/Request.php b/src/packages/api/2.0/core/Request.php index 9c8f4e3..8b9f9aa 100644 --- a/src/packages/api/2.0/core/Request.php +++ b/src/packages/api/2.0/core/Request.php @@ -427,20 +427,19 @@ $method = $this->modules[$this->path['module']][$this->path['method']]; // Si aucune permission n'est definie - if( !isset($method['permissions']) ) return true; + if( !isset($method['permissions']) || !is_array($method['permissions']) || count($method['permissions']) < 1 ) + return true; /* [2] Vérification des permissions et de l'authentification =========================================================*/ - if(!empty($method['permissions'])){ - $granted = Authentification::permission($method['permissions']); + $granted = Authentification::permission($method['permissions']); - /* (1) On retourne FAUX si aucun droit n'a ete trouve */ - if( $granted->get() !== Err::Success ){ - $this->error = $granted; - return false; - } + /* (1) On retourne FAUX si aucun droit n'a ete trouve */ + if( $granted->get() !== Err::Success ){ + $this->error = $granted; + return false; } diff --git a/src/packages/api/2.0/module/RESTexample.php b/src/packages/api/2.0/module/RESTexample.php index decfdec..d2d9114 100644 --- a/src/packages/api/2.0/module/RESTexample.php +++ b/src/packages/api/2.0/module/RESTexample.php @@ -29,12 +29,13 @@ class RESTexample{ // ... // process to create article and get $output_created_id + $success = true; // ... if( !$success ) return ['error' => new Error(Err::ModuleError)]; // or other `Err` constant - return ['created_id' => $output_created_id]; + return ['created_id' => "Article with title = `$title` and content=`$content`"]; break; case 'GET': @@ -43,12 +44,13 @@ class RESTexample{ // ... // process to get articles and get $output_get_articles + $success = true; // ... if( !$success ) return ['error' => new Error(Err::ModuleError)]; // or other `Err` constant - return ['articles' => $output_get_articles]; + return ['articles' => "Article number `$URL_0`: sometitle / somecontent"]; break; case 'VIEW': @@ -57,6 +59,7 @@ class RESTexample{ // ... // process to get articles and get $output_get_articles + $success = true; // ... if( !$success ) @@ -70,7 +73,7 @@ class RESTexample{ 'Pragma' => 'no-cache', 'Expires' => '0' ], - 'body' => json_encode($output_get_articles) + 'body' => "Article number `$URL_0`: sometitle / somecontent" ]; break; @@ -82,12 +85,13 @@ class RESTexample{ // ... // process to get $output_updated_article + $success = true; // ... if( !$success ) return ['error' => new Error(Err::ModuleError)]; // or other `Err` constant - return ['article' => $output_updated_article]; + return ['article' => "Article number `$URL_0`: $title / $content"]; break; case 'DELETE': @@ -96,12 +100,13 @@ class RESTexample{ // ... // process to delete article + $success = true; // ... if( !$success ) return ['error' => new Error(Err::ModuleError)]; // or other `Err` constant - return []; // returns success + return ['log' => "Article `$URL_0` successfully deleted"]; // returns success break; // if no match -> error diff --git a/src/packages/api/2.2/core/Authentification.php b/src/packages/api/2.2/core/Authentification.php index 03903d7..b52e350 100644 --- a/src/packages/api/2.2/core/Authentification.php +++ b/src/packages/api/2.2/core/Authentification.php @@ -2,59 +2,21 @@ namespace api\core; - use \database\core\Repo; - use \error\core\Error; use \error\core\Err; + use \error\core\Error; - class Authentification - { + class Authentification{ - private static $instance; - - private $userType; - private $userId; - - public function __construct() - { - $token = !empty($_SERVER['PHP_AUTH_DIGEST']) ? $_SERVER['PHP_AUTH_DIGEST'] : false; - - $user = new Repo("AuthentificationRepo/identifyUserByToken", [$token]); - $user = $user->answer(); - - if( $user != false ){ - $this->userType = $user["Type"]; - $this->userId = $user["Id"]; - } - - new Repo("AuthentificationRepo/updateToken",[$token]); - - new Repo("AuthentificationRepo/purgeOldTokens",[]); - - self::$instance = $this; + /* VERIFICATION DES ACCES EN FONCTION DE PERMISSIONS ATTENDUES + * + * @expected Liste des permissions attendues + * + * @return status Si FALSE, pas la permission, sinon TRUE + * + */ + public static function permission($expected){ + return new Error(Err::Success); } - - public function getUserType(){ - return $this->userType; - } - - public function getUserId(){ - return $this->userId; - } - - public static function permission(array $perm){ - if(in_array(self::$instance->userType,$perm)){ - return new Error(Err::Success); - } - - return new Error(Err::TokenError); - } - - /** - * @return Authentification - */ - public static function getInstance(){ - return self::$instance; - } - - } + +?> diff --git a/src/packages/api/2.2/core/Request.php b/src/packages/api/2.2/core/Request.php index b352037..ad14914 100644 --- a/src/packages/api/2.2/core/Request.php +++ b/src/packages/api/2.2/core/Request.php @@ -12,8 +12,8 @@ class Request{ // Constantes - public static function config_path(){ return __ROOT__.'/config/modules.json'; } - public static $default_options = [ + private static function config_path(){ return __ROOT__.'/config/modules.json'; } + private static $default_options = [ 'download' => false ]; @@ -47,7 +47,7 @@ /* (1) Parse HttpRequest data because php doesn't parse it for non-POST HTTP method */ $httprequest = new HttpRequest(); $_POST = $httprequest->POST(); - + /* [2] Initialisation =========================================================*/ @@ -296,7 +296,7 @@ /* DESERIALISATION A PARTIR DE L'URL ET DES DONNEES POST (OPT) * - * @url Contenu de l'url après api/ (si existe) + * @url Contenu de l'url formatté (commence à "/module/methode") * @post [opt] Tableau des donnes * * @return instance Retourne un objet de type @@ -315,7 +315,7 @@ /* [1] On verifie que le @path est renseigne =========================================================*/ /* (1) Si le path est dans @url */ - $pathInUrl = count($url) > 0 && is_string($url[0]) && strlen($url[0]) > 0 && preg_match('#^([\w_-]+/[\w_-]+)(?:/?|/((?:\w+/)*(?:\w+/?)))$#', $url[0], $urlMatches); + $pathInUrl = is_string($url) && preg_match('#^/?([\w_-]+/[\w_-]+)(?:/?|/((?:\w+/)*(?:\w+/?)))$#', $url, $urlMatches); /* (2) On récupère le @path + les arguments dans l'URL */ if( $pathInUrl ){ @@ -435,24 +435,24 @@ $method = $this->modules[$this->path['module']][$this->path['method']]; // Si aucune permission n'est definie - if( !isset($method['permissions']) ) return true; + if( !isset($method['permissions']) || !is_array($method['permissions']) || count($method['permissions']) < 1 ) + return true; /* [2] Vérification des permissions et de l'authentification =========================================================*/ - if(!empty($method['permissions'])){ - $granted = Authentification::permission($method['permissions']); + $granted = Authentification::permission($method['permissions']); - /* (1) On retourne FAUX si aucun droit n'a ete trouve */ - if( $granted->get() !== Err::Success ){ - $this->error = $granted; - return false; - } + /* (1) On retourne FAUX si aucun droit n'a ete trouve */ + if( $granted->get() !== Err::Success ){ + $this->error = $granted; + return false; } + /* On retourne VRAI si la permission est ok */ return true; } diff --git a/src/packages/api/2.2/module/RESTexample.php b/src/packages/api/2.2/module/RESTexample.php index decfdec..d2d9114 100644 --- a/src/packages/api/2.2/module/RESTexample.php +++ b/src/packages/api/2.2/module/RESTexample.php @@ -29,12 +29,13 @@ class RESTexample{ // ... // process to create article and get $output_created_id + $success = true; // ... if( !$success ) return ['error' => new Error(Err::ModuleError)]; // or other `Err` constant - return ['created_id' => $output_created_id]; + return ['created_id' => "Article with title = `$title` and content=`$content`"]; break; case 'GET': @@ -43,12 +44,13 @@ class RESTexample{ // ... // process to get articles and get $output_get_articles + $success = true; // ... if( !$success ) return ['error' => new Error(Err::ModuleError)]; // or other `Err` constant - return ['articles' => $output_get_articles]; + return ['articles' => "Article number `$URL_0`: sometitle / somecontent"]; break; case 'VIEW': @@ -57,6 +59,7 @@ class RESTexample{ // ... // process to get articles and get $output_get_articles + $success = true; // ... if( !$success ) @@ -70,7 +73,7 @@ class RESTexample{ 'Pragma' => 'no-cache', 'Expires' => '0' ], - 'body' => json_encode($output_get_articles) + 'body' => "Article number `$URL_0`: sometitle / somecontent" ]; break; @@ -82,12 +85,13 @@ class RESTexample{ // ... // process to get $output_updated_article + $success = true; // ... if( !$success ) return ['error' => new Error(Err::ModuleError)]; // or other `Err` constant - return ['article' => $output_updated_article]; + return ['article' => "Article number `$URL_0`: $title / $content"]; break; case 'DELETE': @@ -96,12 +100,13 @@ class RESTexample{ // ... // process to delete article + $success = true; // ... if( !$success ) return ['error' => new Error(Err::ModuleError)]; // or other `Err` constant - return []; // returns success + return ['log' => "Article `$URL_0` successfully deleted"]; // returns success break; // if no match -> error diff --git a/src/packages/router/2.0/controller/api.php b/src/packages/router/2.0/controller/api.php new file mode 100644 index 0000000..5d8e628 --- /dev/null +++ b/src/packages/router/2.0/controller/api.php @@ -0,0 +1,51 @@ + Calling URI + * + */ + public function __construct($matches){ + /* (1) Rebuild request url */ + $url = $matches['module'].'/'.$matches['method'].$matches['inurl_arguments']; + + /* (2) Creates request */ + $this->request = Request::remote($url, $_POST); + } + + + /* CALL + * + */ + public function call(){ + /* (1) Process response */ + $this->response = $this->request->dispatch(); + + /* (2) Manages result */ + if( $this->response instanceof Response ) + echo $this->response->serialize(); + + return true; + } + + /* POST-CALL + * + */ + public function __destruct(){ + + } + + + + } diff --git a/src/packages/router/2.0/controller/page.php b/src/packages/router/2.0/controller/page.php new file mode 100644 index 0000000..2b45776 --- /dev/null +++ b/src/packages/router/2.0/controller/page.php @@ -0,0 +1,41 @@ + Calling URI + * + */ + public function __construct($pagename){ + $this->pagename = $pagename; + + } + + + /* CALL + * + */ + public function load(){ + if( file_exists(__PUBLIC__."/view/".$this->pagename.".php") ) + include __PUBLIC__."/view/".$this->pagename.".php"; + else + echo "page not found"; + } + + /* POST-CALL + * + */ + public function __destruct(){ + + } + + + + } diff --git a/src/packages/router/2.0/controller/redirect.php b/src/packages/router/2.0/controller/redirect.php new file mode 100644 index 0000000..63f6b29 --- /dev/null +++ b/src/packages/router/2.0/controller/redirect.php @@ -0,0 +1,34 @@ + Calling URI + * + */ + public function __construct($url){ + } + + + /* CALL + * + */ + public function homepage(){ + header('Location: /testxfw/homepage/'); + } + + /* POST-CALL + * + */ + public function __destruct(){ + + } + + + + } diff --git a/src/packages/router/2.0/core/ControllerFactory.php b/src/packages/router/2.0/core/ControllerFactory.php new file mode 100644 index 0000000..ad2d642 --- /dev/null +++ b/src/packages/router/2.0/core/ControllerFactory.php @@ -0,0 +1,51 @@ + Nom du controller + * + * @return exists Si oui ou non le controller existe + * + */ + public static function checkController($controller){ + /* (1) Check type + pattern */ + if( !is_string($controller) || !preg_match('/^[A-Za-z_]\w+$/', $controller) ) + return false; + + /* (2) On vérifie que la classe existe */ + if( !file_exists(__BUILD__."/router/controller/$controller.php") ) + return false; + + /* (3) Sinon il existe */ + return true; + } + + + + /* INSTANCIE UN CONTROLLER + * + * @controller Nom du controller + * @arguments [OPTIONNEL] Arguments à passer au constructeur + * + * @return instance Instance du controller en question + * + */ + public static function getController($controller, $arguments=[]){ + /* (1) On vérifie l'existance du controller */ + if( !self::checkController($controller) ) + return false; + + /* (2) On récupère la classe */ + $class_name = "\\router\\controller\\$controller"; + + /* (3) On retourne une instance */ + return new $class_name($arguments); + } + + } diff --git a/src/packages/router/2.0/core/Route.php b/src/packages/router/2.0/core/Route.php new file mode 100644 index 0000000..83bfd95 --- /dev/null +++ b/src/packages/router/2.0/core/Route.php @@ -0,0 +1,100 @@ + Pattern correspondant a la route + * @controller Controller de la route + * @method Methode du controller + * + * @return instance Retour de l'instance courante + * + =========================================================*/ + public function __construct($pattern=null, $controller=null, $method=null){ + // Note: all arguments must be verified by 'Router->add' method + + /* (1) Pattern -> regex format */ + $this->pattern = "/^$pattern$/"; + + /* (2) Controller */ + $this->controller = $controller; + + /* (3) Controller's method */ + $this->method = $method; + + /* (4) Initialize matches */ + $this->matches = []; + + } + + + + /* [3] Checks if route matches URL + * + * @url URL + * + * @return matches If matches URL + * + =========================================================*/ + public function match($url){ + + /* (1) If doesn't matches @url -> false */ + if( !preg_match($this->pattern, $url, $matches) ) + return false; + + /* (2) Return only named matches */ + foreach($matches as $name=>$match) + if( !is_numeric($name) ) + $this->matches[$name] = $match; + + /* (4) Add complete URL */ + $this->matches['__URL__'] = $url; + + /* (5) Return status */ + return true; + } + + + /* [4] Method call + * + * @return response Response + * + =========================================================*/ + public function call(){ + /* (1) Instanciate controller */ + $instance = ControllerFactory::getController($this->controller, $this->matches); + + /* (2) Launch method & catch response */ + $response = call_user_func([$instance, $this->method]); + + /* (3) Call controller's destructor */ + $instance = null; + + /* (4) Return response */ + return $response; + } + + } diff --git a/src/packages/router/2.0/core/Router.php b/src/packages/router/2.0/core/Router.php new file mode 100644 index 0000000..5fc72ff --- /dev/null +++ b/src/packages/router/2.0/core/Router.php @@ -0,0 +1,321 @@ + Current URL + * + * @return instance Instance du routeur + * + =========================================================*/ + public function __construct($url=null){ + /* (1) Checks arguments + ---------------------------------------------------------*/ + /* (1) Default value if incorrect */ + $this->url = is_string($url) ? $url : ''; + + /* (2) Add first '/' if missing */ + if( !preg_match('/^\//', $url) ) + $this->url = '/'.$this->url; + + + /* (2) Loads configuration + ---------------------------------------------------------*/ + /* (1) Tries to load configuration */ + $this->cnf = self::loadConfig(); + + /* (2) If error occurs, throw Exception */ + if( is_null($this->cnf) ) + throw new \Exception("[Router] Configuration file error found"); + + + /* (3) Set allowed HTTP methods + ---------------------------------------------------------*/ + /* (1) If not defined */ + if( !isset($this->cnf['methods']) ) + throw new \Exception('[Router] Configuration file error, \'methods\' clause missing'); + + /* (2) Try to clean methods */ + $this->http_methods = self::cleanMethods($this->cnf['methods']); + + /* (3) Manage error */ + if( is_null($this->http_methods) ) + throw new \Exception('[Router] Configuration file error. \'methods\' must be an array of HTTP methods ["GET", "POST", ...]'); + + + /* (4) Initialize routes + ---------------------------------------------------------*/ + /* (1) Init routes */ + $this->routes = []; + + foreach($this->http_methods as $method) + $this->routes[$method] = []; + + /* (2) Default configuration if missing */ + if( !isset($this->cnf['routes']) || !is_array($this->cnf['routes']) ) + $this->cnf['routes'] = []; + + + /* (5) Loads each route + ---------------------------------------------------------*/ + foreach($this->cnf['routes'] as $pattern=>$route){ + + /* (1) If missing (required) parameters */ + if( !isset($route['controller']) ) + continue; + + /* (2) Default value for 'methods' */ + ( !isset($route['methods']) || !is_array($route['methods']) ) && ($route['methods'] = $this->http_methods); + + /* (3) Default value for 'arguments' */ + ( !isset($route['arguments']) || !is_array($route['arguments']) ) && ($route['arguments'] = []); + + /* (4) Add route */ + $added = $this->add($pattern, $route['controller'], $route['arguments']); + + // if error -> next + if( $added === false ) + continue; + + + /* (5) Add route for each method */ + foreach($route['methods'] as $method) + if( in_array($method, $this->http_methods) ) + $this->routes[$method][] = $added; + + } + + + } + + + + + /* [4] Adds a route + * + * @pattern URL pattern with {somevar} variables within + * @controller Controller name + method "controllername:methodname" + * @arguments List of pattern's arguments and their RegExp composition (default is alphanumeric) + * + * @return route New instance of Route || false on error + * + =========================================================*/ + public function add($pattern=null, $controller=null, $arguments=[]){ + + /* (1) Format and check pattern + ---------------------------------------------------------*/ + /* (1) If not a string */ + if( !is_string($pattern) ) + return false; + + /* (2) Format pattern and check result */ + $pattern = self::formatPattern($pattern, $arguments); + + if( $pattern === false ) + return false; + + + /* (2) Check controller + ---------------------------------------------------------*/ + /* (1) Check default type */ + if( !is_string($controller) || !preg_match('/^([A-Za-z_]\w+):([A-Za-z_]\w+)$/', $controller, $c_matches) ) + return false; + + /* (2) Check existence */ + if( !ControllerFactory::checkController($c_matches[1]) ) + return false; + + + /* (3) Check method + ---------------------------------------------------------*/ + if( !method_exists('\\router\\controller\\'.$c_matches[1], $c_matches[2]) ) + return false; + + + /* (4) Return new route + ---------------------------------------------------------*/ + return new Route($pattern, $c_matches[1], $c_matches[2]); + } + + + + + /* [5] Router launch + * + =========================================================*/ + public function run(){ + /* (1) Manage HTTP method + ---------------------------------------------------------*/ + /* (1) Fetch HTTP method */ + $httpMethod = $_SERVER['REQUEST_METHOD']; + + /* (2) If no route for this -> exit */ + if( !isset($this->routes[$httpMethod]) || count($this->routes[$httpMethod]) <= 0 ) + return false; + + + /* (2) Manage routes (matching) + ---------------------------------------------------------*/ + /* (1) Check for each HTTP method's route */ + foreach($this->routes[$httpMethod] as $route) + + /* (2) First route that matches -> call & return response */ + if( $route->match($this->url) ) + return $route->call(); + + + /* (3) If no route found -> return false + ---------------------------------------------------------*/ + return false; + } + + + /* FORMATS A PATTERN + * + * @pattern Pattern to process on + * @arguments List of used arguments, with regex if given + * @vars [OPT] If variable replacement have to be done + * + * @return formatted Formatted pattern || false on error + * + */ + public static function formatPattern($pattern, $arguments=[]){ + + /* (1) Check arguments + ---------------------------------------------------------*/ + /* (1) Check minimal length */ + if( strlen($pattern) < 1 ) + return false; + + /* (2) Arguments formatting */ + $arguments = !is_array($arguments) ? [] : $arguments; + + + /* (2) Replace special characters + replace vars + ---------------------------------------------------------*/ + /* (1) Check default URL format */ + if( !preg_match('/^(\/[\w\{\}-]*)*\/?$/', $pattern) ) + return false; + + /* (2) Escape special characters */ + $pattern = str_replace('/', '\\/', $pattern); + + /* (3) Add optional ending '/' */ + if( !preg_match('/\/$/', $pattern) ) + $pattern .= '\\/?'; + + /* (4) Replace variable by tagged capturing groups */ + $boundary = self::randBoundary(); + $pattern = preg_replace('/\{([a-z_][a-z0-9_]*)\}/i', '(?P<$1>'.$boundary.'-$1-'.$boundary.')', $pattern); + + + /* (3) Variable replacement + ---------------------------------------------------------*/ + /* (1) List variables */ + $vars = []; + $var_pattern = '/'.$boundary.'\-([A-Za-z_][A-Za-z0-9_]*)\-'.$boundary.'/'; + preg_match_all($var_pattern, $pattern, $matches); + + /* (2) For each matching variable -> replace with associated regex */ + if( is_array($matches) && isset($matches[1]) ){ + + foreach($matches[1] as $m=>$varname){ + + // {3.1.1} Not in @arguments -> default regex // + if( !isset($arguments[$varname]) || !is_string($arguments[$varname]) ){ + $pattern = str_replace($matches[0][$m], '[A-Za-z0-9_]+', $pattern); + continue; + } + + // {3.1.2} If variable in @arguments -> set regex without capturing-groups // + // $without_capg = str_replace('(', '(?:', $arguments[$varname]); + $pattern = str_replace($matches[0][$m], $arguments[$varname], $pattern); + + } + + } + + + /* (4) Return formatted pattern + ---------------------------------------------------------*/ + return $pattern; + } + + + + /* LOADS CONFIGURATION + * + * @return cnf Configuration content || NULL if error + * + */ + private static function loadConfig(){ + /* (1) Set configuration file's path */ + $cnfpath = self::config_path(); + + /* (2) Checks file */ + if( !file_exists($cnfpath) ) + return null; // throw new \Exception("[Router] Configuration file not found"); + + /* (3) Checks format -> null if error */ + return json_decode( file_get_contents($cnfpath), true ); + + } + + + /* CHECKS METHODS AND CLEAN THE LIST IF CORRECT + * + * @wanted List of wanted methods + * + * @return cleaned Cleaned methods || null if error + * + */ + private static function cleanMethods($wanted=[]){ + /* (1) Checks @wanted */ + if( !is_array($wanted) || count($wanted) < 1 ) + return null; // throw new \Exception('[Router] Configuration file error, \'methods\' must be an array containing managed HTTP methods'); + + /* (2) Set methods (uppercase and unique) */ + $cleaned = []; + + foreach($wanted as $method) + if( !in_array(strtoupper($method), $cleaned) ) + $cleaned[] = strtoupper($method); + + /* (3) Return cleaned method list */ + return $cleaned; + } + + }