Path: blob/master/src/aphront/site/AphrontRoutingMap.php
13395 views
<?php12/**3* Collection of routes on a site for an application.4*5* @task info Map Information6* @task routing Routing7*/8final class AphrontRoutingMap extends Phobject {910private $site;11private $application;12private $routes = array();131415/* -( Map Info )----------------------------------------------------------- */161718public function setSite(AphrontSite $site) {19$this->site = $site;20return $this;21}2223public function getSite() {24return $this->site;25}2627public function setApplication(PhabricatorApplication $application) {28$this->application = $application;29return $this;30}3132public function getApplication() {33return $this->application;34}3536public function setRoutes(array $routes) {37$this->routes = $routes;38return $this;39}4041public function getRoutes() {42return $this->routes;43}444546/* -( Routing )------------------------------------------------------------ */474849/**50* Find the route matching a path, if one exists.51*52* @param string Path to route.53* @return AphrontRoutingResult|null Routing result, if path matches map.54* @task routing55*/56public function routePath($path) {57$map = $this->getRoutes();5859foreach ($map as $route => $value) {60$match = $this->tryRoute($route, $value, $path);61if (!$match) {62continue;63}6465$result = $this->newRoutingResult();66$application = $result->getApplication();6768$controller_class = $match['class'];69$controller = newv($controller_class, array());70$controller->setCurrentApplication($application);7172$result73->setController($controller)74->setURIData($match['data']);7576return $result;77}7879return null;80}818283/**84* Test a sub-map to see if any routes match a path.85*86* @param string Path to route.87* @param string Pattern from the map.88* @param string Value from the map.89* @return dict<string, wild>|null Match details, if path matches sub-map.90* @task routing91*/92private function tryRoute($route, $value, $path) {93$has_submap = is_array($value);9495if (!$has_submap) {96// If the value is a controller rather than a sub-map, any matching97// route must completely consume the path.98$pattern = '(^'.$route.'\z)';99} else {100$pattern = '(^'.$route.')';101}102103$data = null;104$ok = preg_match($pattern, $path, $data);105if ($ok === false) {106throw new Exception(107pht(108'Routing fragment "%s" is not a valid regular expression.',109$route));110}111112if (!$ok) {113return null;114}115116$path_match = $data[0];117118// Clean up the data. We only want to retain named capturing groups, not119// the duplicated numeric captures.120foreach ($data as $k => $v) {121if (is_numeric($k)) {122unset($data[$k]);123}124}125126if (!$has_submap) {127return array(128'class' => $value,129'data' => $data,130);131}132133$sub_path = substr($path, strlen($path_match));134foreach ($value as $sub_route => $sub_value) {135$result = $this->tryRoute($sub_route, $sub_value, $sub_path);136if ($result) {137$result['data'] += $data;138return $result;139}140}141142return null;143}144145146/**147* Build a new routing result for this map.148*149* @return AphrontRoutingResult New, empty routing result.150* @task routing151*/152private function newRoutingResult() {153return id(new AphrontRoutingResult())154->setSite($this->getSite())155->setApplication($this->getApplication());156}157158}159160161