/** * 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; }
/** * Generate a URL from ourself given a set of keyword arguments * * @param array $kargs Keyword arguments * @param null|string Null if generation failed, URL otherwise */ public function generate($kargs) { $defaultKargs = array('_ignoreReqList' => false, '_appendSlash' => false); $kargs = array_merge($defaultKargs, $kargs); $_appendSlash = $kargs['_appendSlash']; unset($kargs['_appendSlash']); $_ignoreReqList = $kargs['_ignoreReqList']; unset($kargs['_ignoreReqList']); // Verify that our args pass any regexp requirements if (!$_ignoreReqList) { foreach ($this->reqs as $key => $v) { $value = isset($kargs[$key]) ? $kargs[$key] : null; if (!empty($value) && !preg_match($this->_reqRegs[$key], $value)) { return null; } } } // Verify that if we have a method arg, it's in the method accept list. // Also, method will be changed to _method for route generation. $meth = isset($kargs['method']) ? $kargs['method'] : null; if ($meth) { if ($this->conditions && isset($this->conditions['method']) && !in_array(Horde_String::upper($meth), $this->conditions['method'])) { return null; } unset($kargs['method']); } $routeList = $this->_routeBackwards; $urlList = array(); $gaps = false; foreach ($routeList as $part) { if (is_array($part) && $part['type'] == ':') { $arg = $part['name']; // For efficiency, check these just once $hasArg = array_key_exists($arg, $kargs); $hasDefault = array_key_exists($arg, $this->defaults); // Determine if we can leave this part off // First check if the default exists and wasn't provided in the // call (also no gaps) if ($hasDefault && !$hasArg && !$gaps) { continue; } // Now check to see if there's a default and it matches the // incoming call arg if ($hasDefault && $hasArg && $kargs[$arg] == $this->defaults[$arg] && !$gaps) { continue; } // We need to pull the value to append, if the arg is NULL and // we have a default, use that if ($hasArg && $kargs[$arg] === null && $hasDefault && !$gaps) { continue; // Otherwise if we do have an arg, use that } elseif ($hasArg) { $val = $kargs[$arg] === null ? 'null' : $kargs[$arg]; } elseif ($hasDefault && $this->defaults[$arg] != null) { $val = $this->defaults[$arg]; // No arg at all? This won't work } else { return null; } $urlList[] = Horde_Routes_Utils::urlQuote($val, $this->encoding); if ($hasArg) { unset($kargs[$arg]); } $gaps = true; } elseif (is_array($part) && $part['type'] == '*') { $arg = $part['name']; $kar = isset($kargs[$arg]) ? $kargs[$arg] : null; if ($kar != null) { $urlList[] = Horde_Routes_Utils::urlQuote($kar, $this->encoding); $gaps = true; } } elseif (!empty($part) && in_array(substr($part, -1), $this->_splitChars)) { if (!$gaps && in_array($part, $this->_splitChars)) { continue; } elseif (!$gaps) { $gaps = true; $urlList[] = substr($part, 0, -1); } else { $gaps = true; $urlList[] = $part; } } else { $gaps = true; $urlList[] = $part; } } $urlList = array_reverse($urlList); $url = implode('', $urlList); if (substr($url, 0, 1) != '/') { $url = '/' . $url; } $extras = $kargs; foreach ($this->maxKeys as $key) { unset($extras[$key]); } $extras = array_keys($extras); if (!empty($extras)) { if ($_appendSlash && substr($url, -1) != '/') { $url .= '/'; } $url .= '?'; $newExtras = array(); foreach ($kargs as $key => $value) { if (in_array($key, $extras) && ($key != 'action' || $key != 'controller')) { $newExtras[$key] = $value; } } $url .= http_build_query($newExtras); } elseif ($_appendSlash && substr($url, -1) != '/') { $url .= '/'; } return $url; }
public function testControllerScan() { $hereDir = __DIR__; $controllerDir = "{$hereDir}/fixtures/controllers"; $controllers = Horde_Routes_Utils::controllerScan($controllerDir); $this->assertEquals(3, count($controllers)); $this->assertEquals('admin/users', $controllers[0]); $this->assertEquals('content', $controllers[1]); $this->assertEquals('users', $controllers[2]); }