29 private $response =
null;
30 private $responseFormat =
null;
31 private $method =
null;
33 private static $requestDataFixed =
false;
34 private static $logger =
null;
35 private static $errorsDefined =
false;
42 parent::__construct($formatter);
43 if (self::$logger ==
null) {
49 foreach (self::getAllHeaders() as $name => $value) {
52 $this->method = isset($_SERVER[
'REQUEST_METHOD']) ?
53 strtoupper($_SERVER[
'REQUEST_METHOD']) :
'';
56 if (!self::$requestDataFixed) {
57 if (isset($_SERVER[
'QUERY_STRING'])) {
58 self::fix($_GET, $_SERVER[
'QUERY_STRING']);
60 if (isset($_SERVER[
'COOKIES'])) {
61 self::fix($_COOKIE, $_SERVER[
'COOKIES']);
63 $requestBody = file_get_contents(
"php://input");
65 self::fix($_POST, $requestBody);
68 $_POST = json_decode($requestBody,
true);
70 self::$requestDataFixed =
true;
78 $this->response = $response;
88 return $this->response;
106 public function initialize($controller=
null, $context=
null, $action=
null) {
108 $basePath = preg_replace(
'/\/?[^\/]*$/',
'', $_SERVER[
'SCRIPT_NAME']);
109 $requestUri = preg_replace(
'/\?.*$/',
'', $_SERVER[
'REQUEST_URI']);
112 if (self::$logger->isInfoEnabled()) {
113 self::$logger->info(
"Request: ".$requestMethod.
" ".$requestPath);
118 if (self::$logger->isDebugEnabled()) {
119 self::$logger->debug(
"Matching routes:");
120 self::$logger->debug($matchingRoutes);
125 'ip' => $_SERVER[
'REMOTE_ADDR'],
126 'agent' => $_SERVER[
'HTTP_USER_AGENT'],
127 'referrer' => $_SERVER[
'HTTP_REFERER']
131 if (
sizeof($matchingRoutes) == 0) {
134 $clientInfo, [
'route' => $requestPath])));
139 if (self::$logger->isDebugEnabled()) {
140 self::$logger->debug(
"Best route:");
141 self::$logger->debug($route);
145 $allowedMethods = $route[
'methods'];
146 if ($allowedMethods !=
null && !in_array($requestMethod, $allowedMethods)) {
149 $clientInfo, [
'method' => $requestMethod,
'route' => $requestPath])));
153 $pathRequestData = $route[
'parameters'];
157 switch ($requestMethod) {
159 $requestData = $_GET;
163 $requestData = array_merge($_POST, $_FILES);
168 $controller = isset($requestData[
'controller']) ?
169 filter_var($requestData[
'controller'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW) :
170 (isset($pathRequestData[
'controller']) ? $pathRequestData[
'controller'] : $controller);
172 $context = isset($requestData[
'context']) ?
173 filter_var($requestData[
'context'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW) :
174 (isset($pathRequestData[
'context']) ? $pathRequestData[
'context'] : $context);
176 $action = isset($requestData[
'action']) ?
177 filter_var($requestData[
'action'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW) :
178 (isset($pathRequestData[
'action']) ? $pathRequestData[
'action'] : $action);
184 $this->
setValues(array_merge($pathRequestData, $requestData));
191 return $this->method;
198 $this->responseFormat = $format;
205 if ($this->responseFormat ==
null) {
208 return $this->responseFormat;
216 $str =
'method='.$this->method.
', ';
217 $str .=
'responseformat='.$this->responseFormat.
', ';
218 $str .= parent::__toString();
229 $matchingRoutes = [];
230 $defaultValuePattern =
'([^/]+)';
231 if (self::$logger->isDebugEnabled()) {
232 self::$logger->debug(
"Get mathching routes for request path: ".$requestPath);
236 if ($config->hasSection(
'routes')) {
237 $routes = $config->getSection(
'routes');
238 foreach ($routes as $route => $requestDef) {
240 $allowedMethods =
null;
241 if (strpos($route,
'/') !==
false) {
242 list($methodStr, $route) = explode(
'/', $route, 2);
243 $allowedMethods = preg_split(
'/\s*,\s*/', trim($methodStr));
244 $route =
'/'.trim($route);
250 $routePattern = preg_replace_callback(
'/\/\{(.+?)\}(?=(\/|$))/',
function ($match)
251 use($defaultValuePattern, &$params, &$numPatterns) {
255 $paramParts = explode(
'|', $match[1], 2);
257 $params[] = $paramParts[0];
259 $hasPattern =
sizeof($paramParts) > 1;
264 return '/'.($hasPattern ?
'('.$paramParts[1].
')' : $defaultValuePattern);
268 $routePattern = str_replace([
'*',
'/'], [
'.*',
'\/'], $routePattern);
271 if (self::$logger->isDebugEnabled()) {
272 self::$logger->debug(
"Check path: ".$route.
" -> ".$routePattern);
275 if (preg_match(
'/^'.$routePattern.
'\/?$/', $requestPath, $matches)) {
276 if (self::$logger->isDebugEnabled()) {
277 self::$logger->debug(
"Match");
280 array_shift($matches);
283 $requestParameters = [];
286 for ($i=0, $count=
sizeof($params); $i<$count; $i++) {
287 $requestParameters[$params[$i]] = isset($matches[$i]) ? $matches[$i] :
null;
291 $requestDefData = [];
292 parse_str($requestDef, $requestDefData);
293 $requestParameters = array_merge($requestParameters, $requestDefData);
297 'numPathParameters' => (preg_match(
'/\*/', $route) ? PHP_INT_MAX :
sizeof($params)),
298 'numPathPatterns' => $numPatterns,
299 'parameters' => $requestParameters,
300 'methods' => $allowedMethods
304 if ($this->
isMatch($routeData)) {
305 $matchingRoutes[] = $routeData;
308 if (self::$logger->isDebugEnabled()) {
309 self::$logger->debug(
"Match removed by custom matching");
315 return $matchingRoutes;
336 usort($routes,
function($a, $b) use ($method) {
337 $hasMethodA = in_array($method, $a[
'methods']);
338 $hasMethodB = in_array($method, $b[
'methods']);
339 $result = ($hasMethodA && !$hasMethodB) ? -1 : 1;
340 if ($hasMethodA && $hasMethodB) {
341 $numParamsA = $a[
'numPathParameters'];
342 $numParamsB = $b[
'numPathParameters'];
343 if ($numParamsA == $numParamsB) {
344 $numPatternsA = $a[
'numPathPatterns'];
345 $numPatternsB = $b[
'numPathPatterns'];
346 if ($numPatternsA == $numPatternsB) {
351 $result = ($numPatternsA < $numPatternsB) ? 1 : -1;
356 $result = ($numParamsA > $numParamsB) ? 1 : -1;
362 if (self::$logger->isDebugEnabled()) {
363 self::$logger->debug(
"Ordered routes:");
364 self::$logger->debug($routes);
367 return array_shift($routes);
374 private static function getAllHeaders() {
376 if (function_exists(
'apache_request_headers')) {
377 foreach (apache_request_headers() as $name => $value) {
378 $headers[$name] = $value;
381 foreach ($_SERVER as $name => $value) {
382 if (substr($name, 0, 5) ==
'HTTP_') {
383 $name = str_replace(
' ',
'-', ucwords(strtolower(str_replace(
'_',
' ', substr($name, 5)))));
384 $headers[$name] = $value;
386 else if ($name ==
"CONTENT_TYPE") {
387 $headers[
"Content-Type"] = $value;
389 else if ($name ==
"CONTENT_LENGTH") {
390 $headers[
"Content-Length"] = $value;
403 private static function fix(&$target, $source, $keep=
false) {
409 $source = preg_replace_callback(
411 # Match at start of string or &
413 # Exclude cases where the period is in brackets, e.g. foo[bar.blarg]
415 # Affected cases: periods and spaces
417 # Keep matching until assignment, next variable, end of string or
421 function ($key) use (&$keys) {
422 $keys[] = $key = base64_encode(urldecode($key[0]));
423 return urlencode($key);
432 parse_str($source, $data);
433 foreach ($data as $key => $val) {
435 if (!in_array($key, $keys)) {
436 $target[$key] = $val;
440 $key = base64_decode($key);
441 $target[$key] = $val;
445 $key = preg_replace(
'/(\.| )/',
'_', $key);
446 $target[$key] = $val;
454 private static function defineErrors() {
455 if (!self::$errorsDefined) {
458 $message->getText(
'No route matching the request path can be found.')
461 $message->getText(
'The HTTP method is not allowed on the requested path.')
463 self::$errorsDefined =
true;