namespace Ice\Mvc\Route\DataGenerator; use Ice\Mvc\Route\DataGenerator\DataGeneratorInterface; use Ice\Exception; use Ice\Mvc\FastRoute; abstract class Regex implements DataGeneratorInterface { protected staticRoutes = []; protected methodToRegexToRoutesMap = []; protected abstract function getApproxChunkSize(); protected abstract function processChunk(regexToRoutesMap); public function addRoute(httpMethod, routeData, handler) { if this->isStaticRoute(routeData) { this->addStaticRoute(httpMethod, routeData, handler); } else { this->addVariableRoute(httpMethod, routeData, handler); } } public function getData() { if empty this->methodToRegexToRoutesMap { return [this->staticRoutes, []]; } return [this->staticRoutes, this->generateVariableRouteData()]; } protected function generateVariableRouteData() { var chunkSize, chunks, data, method, regexToRoutesMap; let data = []; for method, regexToRoutesMap in this->methodToRegexToRoutesMap { let chunkSize = this->computeChunkSize(count(regexToRoutesMap)), chunks = array_chunk(regexToRoutesMap, chunkSize, true), data[method] = array_map([this, "processChunk"], chunks); } return data; } protected function computeChunkSize(count) { var numParts; float round, ceil; let round = count / this->getApproxChunkSize(), numParts = max(1, round(round)), ceil = count / numParts; return ceil(ceil); } protected function isStaticRoute(routeData) { return count(routeData) == 1 && is_string(routeData[0]); } protected function addStaticRoute(httpMethod, routeData, handler) { var routeStr, route; let routeStr = routeData[0]; if isset this->staticRoutes[httpMethod] && isset this->staticRoutes[httpMethod][routeStr] { throw new Exception(sprintf("Cannot register two routes matching '%s' for method '%s'", routeStr, httpMethod )); } if isset this->methodToRegexToRoutesMap[httpMethod] { for route in this->methodToRegexToRoutesMap[httpMethod] { if route->matches(routeStr) { throw new Exception(sprintf( "Static route '%s' is shadowed by previously defined variable route '%s' for method '%s'", routeStr, route->regex, httpMethod )); } } } let this->staticRoutes[httpMethod][routeStr] = handler; } protected function addVariableRoute(httpMethod, routeData, handler) { var regex, variables; let regex = this->buildRegexForRoute(routeData)[0], variables = this->buildRegexForRoute(routeData)[1]; if isset this->methodToRegexToRoutesMap[httpMethod] && isset this->methodToRegexToRoutesMap[httpMethod][regex] { throw new Exception(sprintf( "Cannot register two routes matching '%s' for method '%s'", regex, httpMethod )); } let this->methodToRegexToRoutesMap[httpMethod][regex] = new FastRoute( httpMethod, handler, regex, variables ); } protected function buildRegexForRoute(routeData) { var regex, variables, part, varName, regexPart; let regex = "", variables = []; for part in routeData { if typeof part == "string" { let regex .= preg_quote(part, "~"); continue; } let varName = part[0], regexPart = part[1]; if isset variables[varName] { throw new Exception(sprintf( "Cannot use the same placeholder '%s' twice", varName )); } let variables[varName] = varName, regex .= "(" . regexPart . ")"; } return [regex, variables]; } }