[ Index ] |
PHP Cross Reference of phpBB-3.1.12-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12 namespace Symfony\Component\Routing\Generator; 13 14 use Symfony\Component\Routing\RouteCollection; 15 use Symfony\Component\Routing\RequestContext; 16 use Symfony\Component\Routing\Exception\InvalidParameterException; 17 use Symfony\Component\Routing\Exception\RouteNotFoundException; 18 use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; 19 use Psr\Log\LoggerInterface; 20 21 /** 22 * UrlGenerator can generate a URL or a path for any route in the RouteCollection 23 * based on the passed parameters. 24 * 25 * @author Fabien Potencier <fabien@symfony.com> 26 * @author Tobias Schultze <http://tobion.de> 27 */ 28 class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInterface 29 { 30 /** 31 * @var RouteCollection 32 */ 33 protected $routes; 34 35 /** 36 * @var RequestContext 37 */ 38 protected $context; 39 40 /** 41 * @var bool|null 42 */ 43 protected $strictRequirements = true; 44 45 /** 46 * @var LoggerInterface|null 47 */ 48 protected $logger; 49 50 /** 51 * This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL. 52 * 53 * PHP's rawurlencode() encodes all chars except "a-zA-Z0-9-._~" according to RFC 3986. But we want to allow some chars 54 * to be used in their literal form (reasons below). Other chars inside the path must of course be encoded, e.g. 55 * "?" and "#" (would be interpreted wrongly as query and fragment identifier), 56 * "'" and """ (are used as delimiters in HTML). 57 */ 58 protected $decodedChars = array( 59 // the slash can be used to designate a hierarchical structure and we want allow using it with this meaning 60 // some webservers don't allow the slash in encoded form in the path for security reasons anyway 61 // see http://stackoverflow.com/questions/4069002/http-400-if-2f-part-of-get-url-in-jboss 62 '%2F' => '/', 63 // the following chars are general delimiters in the URI specification but have only special meaning in the authority component 64 // so they can safely be used in the path in unencoded form 65 '%40' => '@', 66 '%3A' => ':', 67 // these chars are only sub-delimiters that have no predefined meaning and can therefore be used literally 68 // so URI producing applications can use these chars to delimit subcomponents in a path segment without being encoded for better readability 69 '%3B' => ';', 70 '%2C' => ',', 71 '%3D' => '=', 72 '%2B' => '+', 73 '%21' => '!', 74 '%2A' => '*', 75 '%7C' => '|', 76 ); 77 78 /** 79 * Constructor. 80 * 81 * @param RouteCollection $routes A RouteCollection instance 82 * @param RequestContext $context The context 83 * @param LoggerInterface|null $logger A logger instance 84 */ 85 public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null) 86 { 87 $this->routes = $routes; 88 $this->context = $context; 89 $this->logger = $logger; 90 } 91 92 /** 93 * {@inheritdoc} 94 */ 95 public function setContext(RequestContext $context) 96 { 97 $this->context = $context; 98 } 99 100 /** 101 * {@inheritdoc} 102 */ 103 public function getContext() 104 { 105 return $this->context; 106 } 107 108 /** 109 * {@inheritdoc} 110 */ 111 public function setStrictRequirements($enabled) 112 { 113 $this->strictRequirements = null === $enabled ? null : (bool) $enabled; 114 } 115 116 /** 117 * {@inheritdoc} 118 */ 119 public function isStrictRequirements() 120 { 121 return $this->strictRequirements; 122 } 123 124 /** 125 * {@inheritdoc} 126 */ 127 public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH) 128 { 129 if (null === $route = $this->routes->get($name)) { 130 throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name)); 131 } 132 133 // the Route has a cache of its own and is not recompiled as long as it does not get modified 134 $compiledRoute = $route->compile(); 135 136 return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens()); 137 } 138 139 /** 140 * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route 141 * @throws InvalidParameterException When a parameter value for a placeholder is not correct because 142 * it does not match the requirement 143 */ 144 protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens) 145 { 146 $variables = array_flip($variables); 147 $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters); 148 149 // all params must be given 150 if ($diff = array_diff_key($variables, $mergedParams)) { 151 throw new MissingMandatoryParametersException(sprintf('Some mandatory parameters are missing ("%s") to generate a URL for route "%s".', implode('", "', array_keys($diff)), $name)); 152 } 153 154 $url = ''; 155 $optional = true; 156 foreach ($tokens as $token) { 157 if ('variable' === $token[0]) { 158 if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) { 159 // check requirement 160 if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#', $mergedParams[$token[3]])) { 161 $message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given) to generate a corresponding URL.', $token[3], $name, $token[2], $mergedParams[$token[3]]); 162 if ($this->strictRequirements) { 163 throw new InvalidParameterException($message); 164 } 165 166 if ($this->logger) { 167 $this->logger->error($message); 168 } 169 170 return; 171 } 172 173 $url = $token[1].$mergedParams[$token[3]].$url; 174 $optional = false; 175 } 176 } else { 177 // static text 178 $url = $token[1].$url; 179 $optional = false; 180 } 181 } 182 183 if ('' === $url) { 184 $url = '/'; 185 } 186 187 // the contexts base URL is already encoded (see Symfony\Component\HttpFoundation\Request) 188 $url = strtr(rawurlencode($url), $this->decodedChars); 189 190 // the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3 191 // so we need to encode them as they are not used for this purpose here 192 // otherwise we would generate a URI that, when followed by a user agent (e.g. browser), does not match this route 193 $url = strtr($url, array('/../' => '/%2E%2E/', '/./' => '/%2E/')); 194 if ('/..' === substr($url, -3)) { 195 $url = substr($url, 0, -2).'%2E%2E'; 196 } elseif ('/.' === substr($url, -2)) { 197 $url = substr($url, 0, -1).'%2E'; 198 } 199 200 $schemeAuthority = ''; 201 if ($host = $this->context->getHost()) { 202 $scheme = $this->context->getScheme(); 203 if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme !== $req) { 204 $referenceType = self::ABSOLUTE_URL; 205 $scheme = $req; 206 } 207 208 if ($hostTokens) { 209 $routeHost = ''; 210 foreach ($hostTokens as $token) { 211 if ('variable' === $token[0]) { 212 if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#i', $mergedParams[$token[3]])) { 213 $message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given) to generate a corresponding URL.', $token[3], $name, $token[2], $mergedParams[$token[3]]); 214 215 if ($this->strictRequirements) { 216 throw new InvalidParameterException($message); 217 } 218 219 if ($this->logger) { 220 $this->logger->error($message); 221 } 222 223 return; 224 } 225 226 $routeHost = $token[1].$mergedParams[$token[3]].$routeHost; 227 } else { 228 $routeHost = $token[1].$routeHost; 229 } 230 } 231 232 if ($routeHost !== $host) { 233 $host = $routeHost; 234 if (self::ABSOLUTE_URL !== $referenceType) { 235 $referenceType = self::NETWORK_PATH; 236 } 237 } 238 } 239 240 if (self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) { 241 $port = ''; 242 if ('http' === $scheme && 80 != $this->context->getHttpPort()) { 243 $port = ':'.$this->context->getHttpPort(); 244 } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) { 245 $port = ':'.$this->context->getHttpsPort(); 246 } 247 248 $schemeAuthority = self::NETWORK_PATH === $referenceType ? '//' : "$scheme://"; 249 $schemeAuthority .= $host.$port; 250 } 251 } 252 253 if (self::RELATIVE_PATH === $referenceType) { 254 $url = self::getRelativePath($this->context->getPathInfo(), $url); 255 } else { 256 $url = $schemeAuthority.$this->context->getBaseUrl().$url; 257 } 258 259 // add a query string if needed 260 $extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, function ($a, $b) { 261 return $a == $b ? 0 : 1; 262 }); 263 264 if ($extra && $query = http_build_query($extra, '', '&')) { 265 $url .= '?'.$query; 266 } 267 268 return $url; 269 } 270 271 /** 272 * Returns the target path as relative reference from the base path. 273 * 274 * Only the URIs path component (no schema, host etc.) is relevant and must be given, starting with a slash. 275 * Both paths must be absolute and not contain relative parts. 276 * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives. 277 * Furthermore, they can be used to reduce the link size in documents. 278 * 279 * Example target paths, given a base path of "/a/b/c/d": 280 * - "/a/b/c/d" -> "" 281 * - "/a/b/c/" -> "./" 282 * - "/a/b/" -> "../" 283 * - "/a/b/c/other" -> "other" 284 * - "/a/x/y" -> "../../x/y" 285 * 286 * @param string $basePath The base path 287 * @param string $targetPath The target path 288 * 289 * @return string The relative target path 290 */ 291 public static function getRelativePath($basePath, $targetPath) 292 { 293 if ($basePath === $targetPath) { 294 return ''; 295 } 296 297 $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath); 298 $targetDirs = explode('/', isset($targetPath[0]) && '/' === $targetPath[0] ? substr($targetPath, 1) : $targetPath); 299 array_pop($sourceDirs); 300 $targetFile = array_pop($targetDirs); 301 302 foreach ($sourceDirs as $i => $dir) { 303 if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) { 304 unset($sourceDirs[$i], $targetDirs[$i]); 305 } else { 306 break; 307 } 308 } 309 310 $targetDirs[] = $targetFile; 311 $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs); 312 313 // A reference to the same base directory or an empty subdirectory must be prefixed with "./". 314 // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used 315 // as the first segment of a relative-path reference, as it would be mistaken for a scheme name 316 // (see http://tools.ietf.org/html/rfc3986#section-4.2). 317 return '' === $path || '/' === $path[0] 318 || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos) 319 ? "./$path" : $path; 320 } 321 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |