/** * Match a url to our regexp. * * While the regexp might match, this operation isn't * guaranteed as there's other factors that can cause a match to fail * even though the regexp succeeds (Default that was relied on wasn't * given, requirement regexp doesn't pass, etc.). * * Therefore the calling function shouldn't assume this will return a * valid dict, the other possible return is False if a match doesn't work * out. * * @param string $url URL to match * @param array Keyword arguments * @return null|array Array of match data if matched, Null otherwise */ public function match($url, $kargs = array()) { $defaultKargs = array('environ' => array(), 'subDomains' => false, 'subDomainsIgnore' => array(), 'domainMatch' => ''); $kargs = array_merge($defaultKargs, $kargs); // Static routes don't match, they generate only if ($this->static) { return false; } if (substr($url, -1) == '/' && strlen($url) > 1) { $url = substr($url, 0, -1); } // Match the regexps we generated $match = preg_match('@' . str_replace('@', '\\@', $this->regexp) . '@', $url, $matches); if ($match == 0) { return false; } $host = isset($kargs['environ']['HTTP_HOST']) ? $kargs['environ']['HTTP_HOST'] : null; if ($host !== null && !empty($kargs['subDomains'])) { $host = substr($host, 0, strpos(':', $host)); $subMatch = '@^(.+?)\\.' . $kargs['domainMatch'] . '$'; $subdomain = preg_replace($subMatch, '$1', $host); if (!in_array($subdomain, $kargs['subDomainsIgnore']) && $host != $subdomain) { $subDomain = $subdomain; } } if (!empty($this->conditions)) { if (isset($this->conditions['method'])) { if (empty($kargs['environ']['REQUEST_METHOD'])) { return false; } if (!in_array($kargs['environ']['REQUEST_METHOD'], $this->conditions['method'])) { return false; } } // Check sub-domains? $use_sd = isset($this->conditions['subDomain']) ? $this->conditions['subDomain'] : null; if (!empty($use_sd) && empty($subDomain)) { return false; } if (is_array($use_sd) && !in_array($subDomain, $use_sd)) { return false; } } $matchDict = $matches; // Clear out int keys as PHP gives us both the named subgroups and numbered subgroups foreach ($matchDict as $key => $val) { if (is_int($key)) { unset($matchDict[$key]); } } $result = array(); $extras = Horde_Routes_Utils::arraySubtract(array_keys($this->defaults), array_keys($matchDict)); foreach ($matchDict as $key => $val) { // TODO: character set decoding if ($key != 'path_info' && $this->encoding) { $val = urldecode($val); } if (empty($val) && array_key_exists($key, $this->defaults) && !empty($this->defaults[$key])) { $result[$key] = $this->defaults[$key]; } else { $result[$key] = $val; } } foreach ($extras as $key) { $result[$key] = $this->defaults[$key]; } // Add the sub-domain if there is one if (!empty($kargs['subDomains'])) { $result['subDomain'] = $subDomain; } // If there's a function, call it with environ and expire if it // returns False if (!empty($this->conditions) && array_key_exists('function', $this->conditions) && !call_user_func_array($this->conditions['function'], array($kargs['environ'], $result))) { return false; } return $result; }
/** * Generates the URL from a given set of keywords * Returns the URL text, or null if no URL could be generated. * * Usage: * $m->generate(array('controller' => 'content', 'action' => 'view', 'id' => 10)); * * @param array $routeArgs Optional explicit route list * @param array $kargs Keyword arguments (key/value pairs) * @return null|string URL text or null */ public function generate($first = null, $second = null) { if ($second) { $routeArgs = $first; $kargs = is_null($second) ? array() : $second; } else { $routeArgs = array(); $kargs = is_null($first) ? array() : $first; } // Generate ourself if we haven't already if (!$this->_createdGens) { $this->_createGens(); } if ($this->appendSlash) { $kargs['_appendSlash'] = true; } if (!$this->explicit) { if (!in_array('controller', array_keys($kargs))) { $kargs['controller'] = 'content'; } if (!in_array('action', array_keys($kargs))) { $kargs['action'] = 'index'; } } $environ = $this->environ; $controller = isset($kargs['controller']) ? $kargs['controller'] : null; $action = isset($kargs['action']) ? $kargs['action'] : null; // If the URL didn't depend on the SCRIPT_NAME, we'll cache it // keyed by just the $kargs; otherwise we need to cache it with // both SCRIPT_NAME and $kargs: $cacheKey = $kargs; if (!empty($environ['SCRIPT_NAME'])) { $cacheKeyScriptName = sprintf('%s:%s', $environ['SCRIPT_NAME'], $cacheKey); } else { $cacheKeyScriptName = $cacheKey; } // Check the URL cache to see if it exists, use it if it does. foreach (array($cacheKey, $cacheKeyScriptName) as $key) { if (in_array($key, array_keys($this->urlCache))) { return $this->urlCache[$key]; } } if ($routeArgs) { $keyList = $routeArgs; } else { $actionList = isset($this->_gendict[$controller]) ? $this->_gendict[$controller] : $this->_gendict['*']; list($keyList, $sortCache) = isset($actionList[$action]) ? $actionList[$action] : (isset($actionList['*']) ? $actionList['*'] : array(null, null)); if ($keyList === null) { return null; } } $keys = array_keys($kargs); // necessary to pass $keys to _keysort() callback used by PHP's usort() $this->_keysortTmp = $keys; $newList = array(); foreach ($keyList as $route) { $tmp = Horde_Routes_Utils::arraySubtract($route->minKeys, $keys); if (count($tmp) == 0) { $newList[] = $route; } } $keyList = $newList; // inline python function keysort() moved below as _keycmp() $this->_keysort($keyList); foreach ($keyList as $route) { $fail = false; foreach ($route->hardCoded as $key) { $kval = isset($kargs[$key]) ? $kargs[$key] : null; if ($kval == null) { continue; } if ($kval != $route->defaults[$key]) { $fail = true; break; } } if ($fail) { continue; } $path = $route->generate($kargs); if ($path) { if ($this->prefix) { $path = $this->prefix . $path; } if (!empty($environ['SCRIPT_NAME']) && !$route->absolute) { $path = $environ['SCRIPT_NAME'] . $path; $key = $cacheKeyScriptName; } else { $key = $cacheKey; } if ($this->urlCache != null) { $this->urlCache[$key] = $path; } return $path; } else { continue; } } return null; }