[ Index ] |
|
Code source de Symfony 1.0.0 |
1 <?php 2 3 /* 4 * This file is part of the symfony package. 5 * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11 /** 12 * sfRouting class controls the creation of URLs and parses URLs. It maps an array of parameters to URLs definition. 13 * Each map is called a route. 14 * It implements the Singleton pattern. 15 * 16 * Routing can be disabled when [sf_routing] is set to false. 17 * 18 * This class is based on the Routes class of Cake framework. 19 * 20 * @package symfony 21 * @subpackage controller 22 * @author Fabien Potencier <fabien.potencier@symfony-project.com> 23 * @version SVN: $Id: sfRouting.class.php 3234 2007-01-11 21:09:21Z fabien $ 24 */ 25 class sfRouting 26 { 27 protected static 28 $instance = null; 29 30 protected 31 $current_route_name = '', 32 $routes = array(); 33 34 /** 35 * Retrieve the singleton instance of this class. 36 * 37 * @return sfRouting The sfRouting implementation instance 38 */ 39 public static function getInstance() 40 { 41 if (!isset(self::$instance)) 42 { 43 self::$instance = new sfRouting(); 44 } 45 46 return self::$instance; 47 } 48 49 /** 50 * Sets the current route name. 51 * 52 * @param string The route name 53 */ 54 protected function setCurrentRouteName($name) 55 { 56 $this->current_route_name = $name; 57 } 58 59 /** 60 * Gets the current route name. 61 * 62 * @return string The route name 63 */ 64 public function getCurrentRouteName() 65 { 66 return $this->current_route_name; 67 } 68 69 /** 70 * Gets the internal URI for the current request. 71 * 72 * @param boolean Whether to give an internal URI with the route name (@route) 73 * or with the module/action pair 74 * 75 * @return string The current internal URI 76 */ 77 public function getCurrentInternalUri($with_route_name = false) 78 { 79 if ($this->current_route_name) 80 { 81 list($url, $regexp, $names, $names_hash, $defaults, $requirements, $suffix) = $this->routes[$this->current_route_name]; 82 83 $request = sfContext::getInstance()->getRequest(); 84 85 if ($with_route_name) 86 { 87 $internal_uri = '@'.$this->current_route_name; 88 } 89 else 90 { 91 $internal_uri = $request->getParameter('module', isset($defaults['module']) ? $defaults['module'] : '').'/'.$request->getParameter('action', isset($defaults['action']) ? $defaults['action'] : ''); 92 } 93 94 $params = array(); 95 96 // add parameters 97 foreach ($names as $name) 98 { 99 if ($name == 'module' || $name == 'action') continue; 100 101 $params[] = $name.'='.$request->getParameter($name, isset($defaults[$name]) ? $defaults[$name] : ''); 102 } 103 104 // add * parameters if needed 105 if (strpos($url, '*')) 106 { 107 foreach ($request->getParameterHolder()->getAll() as $key => $value) 108 { 109 if ($key == 'module' || $key == 'action' || in_array($key, $names)) 110 { 111 continue; 112 } 113 114 $params[] = $key.'='.$value; 115 } 116 } 117 118 // sort to guaranty unicity 119 sort($params); 120 121 return $internal_uri.($params ? '?'.implode('&', $params) : ''); 122 } 123 } 124 125 /** 126 * Gets the current compiled route array. 127 * 128 * @return array The route array 129 */ 130 public function getRoutes() 131 { 132 return $this->routes; 133 } 134 135 /** 136 * Sets the compiled route array. 137 * 138 * @param array The route array 139 * 140 * @return array The route array 141 */ 142 public function setRoutes($routes) 143 { 144 return $this->routes = $routes; 145 } 146 147 /** 148 * Returns true if this instance has some routes. 149 * 150 * @return boolean 151 */ 152 public function hasRoutes() 153 { 154 return count($this->routes) ? true : false; 155 } 156 157 /** 158 * Returns true if the route name given is defined. 159 * 160 * @param string The route name 161 * 162 * @return boolean 163 */ 164 public function hasRouteName($name) 165 { 166 return isset($this->routes[$name]) ? true : false; 167 } 168 169 /** 170 * Gets a route by its name. 171 * 172 * @param string The route name 173 * 174 * @return array A route array 175 */ 176 public function getRouteByName($name) 177 { 178 if ($name[0] == '@') 179 { 180 $name = substr($name, 1); 181 } 182 183 if (!isset($this->routes[$name])) 184 { 185 $error = 'The route "%s" does not exist'; 186 $error = sprintf($error, $name); 187 188 throw new sfConfigurationException($error); 189 } 190 191 return $this->routes[$name]; 192 } 193 194 /** 195 * Clears all current routes. 196 */ 197 public function clearRoutes() 198 { 199 if (sfConfig::get('sf_logging_enabled')) 200 { 201 sfLogger::getInstance()->info('{sfRouting} clear all current routes'); 202 } 203 204 $this->routes = array(); 205 } 206 207 /** 208 * Adds a new route at the beginning of the current list of routes. 209 * 210 * @see connect 211 */ 212 public function prependRoute($name, $route, $default = array(), $requirements = array()) 213 { 214 $routes = $this->routes; 215 $this->routes = array(); 216 $newroutes = $this->connect($name, $route, $default, $requirements); 217 $this->routes = array_merge($newroutes, $routes); 218 219 return $this->routes; 220 } 221 222 /** 223 * Adds a new route. 224 * 225 * Alias for the connect method. 226 * 227 * @see connect 228 */ 229 public function appendRoute($name, $route, $default = array(), $requirements = array()) 230 { 231 return $this->connect($name, $route, $default, $requirements); 232 } 233 234 /** 235 * Adds a new route at the end of the current list of routes. 236 * 237 * A route string is a string with 2 special constructions: 238 * - :string: :string denotes a named paramater (available later as $request->getParameter('string')) 239 * - *: * match an indefinite number of parameters in a route 240 * 241 * Here is a very common rule in a symfony project: 242 * 243 * <code> 244 * $r->connect('/:module/:action/*'); 245 * </code> 246 * 247 * @param string The route name 248 * @param string The route string 249 * @param array The default parameter values 250 * @param array The regexps parameters must match 251 * 252 * @return array current routes 253 */ 254 public function connect($name, $route, $default = array(), $requirements = array()) 255 { 256 // route already exists? 257 if (isset($this->routes[$name])) 258 { 259 $error = 'This named route already exists ("%s").'; 260 $error = sprintf($error, $name); 261 262 throw new sfConfigurationException($error); 263 } 264 265 $parsed = array(); 266 $names = array(); 267 $suffix = (($sf_suffix = sfConfig::get('sf_suffix')) == '.') ? '' : $sf_suffix; 268 269 // used for performance reasons 270 $names_hash = array(); 271 272 $r = null; 273 if (($route == '') || ($route == '/')) 274 { 275 $regexp = '/^[\/]*$/'; 276 $this->routes[$name] = array($route, $regexp, array(), array(), $default, $requirements, $suffix); 277 } 278 else 279 { 280 $elements = array(); 281 foreach (explode('/', $route) as $element) 282 { 283 if (trim($element)) 284 { 285 $elements[] = $element; 286 } 287 } 288 289 if (!isset($elements[0])) 290 { 291 return false; 292 } 293 294 // specific suffix for this route? 295 // or /$ directory 296 if (preg_match('/^(.+)(\.\w*)$/i', $elements[count($elements) - 1], $matches)) 297 { 298 $suffix = ($matches[2] == '.') ? '' : $matches[2]; 299 $elements[count($elements) - 1] = $matches[1]; 300 $route = '/'.implode('/', $elements); 301 } 302 else if ($route{strlen($route) - 1} == '/') 303 { 304 $suffix = '/'; 305 } 306 307 $regexp_suffix = preg_quote($suffix); 308 309 foreach ($elements as $element) 310 { 311 if (preg_match('/^:(.+)$/', $element, $r)) 312 { 313 $element = $r[1]; 314 315 // regex is [^\/]+ or the requirement regex 316 if (isset($requirements[$element])) 317 { 318 $regex = $requirements[$element]; 319 if (0 === strpos($regex, '^')) 320 { 321 $regex = substr($regex, 1); 322 } 323 if (strlen($regex) - 1 === strpos($regex, '$')) 324 { 325 $regex = substr($regex, 0, -1); 326 } 327 } 328 else 329 { 330 $regex = '[^\/]+'; 331 } 332 333 $parsed[] = '(?:\/('.$regex.'))?'; 334 $names[] = $element; 335 $names_hash[$element] = 1; 336 } 337 elseif (preg_match('/^\*$/', $element, $r)) 338 { 339 $parsed[] = '(?:\/(.*))?'; 340 } 341 else 342 { 343 $parsed[] = '/'.$element; 344 } 345 } 346 $regexp = '#^'.join('', $parsed).$regexp_suffix.'$#'; 347 348 $this->routes[$name] = array($route, $regexp, $names, $names_hash, $default, $requirements, $suffix); 349 } 350 351 if (sfConfig::get('sf_logging_enabled')) 352 { 353 sfLogger::getInstance()->info('{sfRouting} connect "'.$route.'"'.($suffix ? ' ("'.$suffix.'" suffix)' : '')); 354 } 355 356 return $this->routes; 357 } 358 359 /** 360 * Generates a valid URLs for parameters. 361 * 362 * @param array The parameter values 363 * @param string The divider between key/value pairs 364 * @param string The equal sign to use between key and value 365 * 366 * @return string The generated URL 367 */ 368 public function generate($name, $params, $querydiv = '/', $divider = '/', $equals = '/') 369 { 370 $global_defaults = sfConfig::get('sf_routing_defaults', null); 371 372 // named route? 373 if ($name) 374 { 375 if (!isset($this->routes[$name])) 376 { 377 $error = 'The route "%s" does not exist.'; 378 $error = sprintf($error, $name); 379 380 throw new sfConfigurationException($error); 381 } 382 383 list($url, $regexp, $names, $names_hash, $defaults, $requirements, $suffix) = $this->routes[$name]; 384 if ($global_defaults !== null) 385 { 386 $defaults = array_merge($defaults, $global_defaults); 387 } 388 389 // all params must be given 390 foreach ($names as $tmp) 391 { 392 if (!isset($params[$tmp]) && !isset($defaults[$tmp])) 393 { 394 throw new sfException(sprintf('Route named "%s" have a mandatory "%s" parameter', $name, $tmp)); 395 } 396 } 397 } 398 else 399 { 400 // find a matching route 401 $found = false; 402 foreach ($this->routes as $name => $route) 403 { 404 list($url, $regexp, $names, $names_hash, $defaults, $requirements, $suffix) = $route; 405 if ($global_defaults !== null) 406 { 407 $defaults = array_merge($defaults, $global_defaults); 408 } 409 410 $tparams = array_merge($defaults, $params); 411 412 // we must match all names (all $names keys must be in $params array) 413 foreach ($names as $key) 414 { 415 if (!isset($tparams[$key])) continue 2; 416 } 417 418 // we must match all defaults with value except if present in names 419 foreach ($defaults as $key => $value) 420 { 421 if (isset($names_hash[$key])) continue; 422 423 if (!isset($tparams[$key]) || $tparams[$key] != $value) continue 2; 424 } 425 426 // we must match all requirements for rule 427 foreach ($requirements as $req_param => $req_regexp) 428 { 429 if (!preg_match('/'.str_replace('/', '\\/', $req_regexp).'/', $tparams[$req_param])) 430 { 431 continue 2; 432 } 433 } 434 435 // we must have consumed all $params keys if there is no * in route 436 if (!strpos($url, '*')) 437 { 438 if (count(array_diff(array_keys($tparams), $names, array_keys($defaults)))) 439 { 440 continue; 441 } 442 } 443 444 // match found 445 $found = true; 446 break; 447 } 448 449 if (!$found) 450 { 451 $error = 'Unable to find a matching routing rule to generate url for params "%s".'; 452 $error = sprintf($error, var_export($params)); 453 454 throw new sfConfigurationException($error); 455 } 456 } 457 458 $params = array_merge($defaults, $params); 459 460 $real_url = preg_replace('/\:([^\/]+)/e', 'urlencode($params["\\1"])', $url); 461 462 // we add all other params if * 463 if (strpos($real_url, '*')) 464 { 465 $tmp = ''; 466 467 foreach ($params as $key => $value) 468 { 469 if (isset($names_hash[$key]) || isset($defaults[$key])) continue; 470 471 if (is_array($value)) 472 { 473 foreach ($value as $v) 474 { 475 $tmp .= $key.$equals.urlencode($v).$divider; 476 } 477 } 478 else 479 { 480 $tmp .= urlencode($key).$equals.urlencode($value).$divider; 481 } 482 } 483 if (strlen($tmp) > 0) 484 { 485 $tmp = $querydiv.$tmp; 486 } 487 $real_url = preg_replace('/\/\*(\/|$)/', $tmp, $real_url); 488 } 489 490 // strip off last divider character 491 if (strlen($real_url) > 1) 492 { 493 $real_url = rtrim($real_url, $divider); 494 } 495 496 if ($real_url != '/') 497 { 498 $real_url .= $suffix; 499 } 500 501 return $real_url; 502 } 503 504 /** 505 * Parses a URL to find a matching route. 506 * 507 * Returns null if no route match the URL. 508 * 509 * @param string URL to be parsed 510 * 511 * @return array An array of parameters 512 */ 513 public function parse($url) 514 { 515 // an URL should start with a '/', mod_rewrite doesn't respect that, but no-mod_rewrite version does. 516 if ($url && ('/' != $url[0])) 517 { 518 $url = '/'.$url; 519 } 520 521 // we remove the query string 522 if ($pos = strpos($url, '?')) 523 { 524 $url = substr($url, 0, $pos); 525 } 526 527 // we remove multiple / 528 $url = preg_replace('#/+#', '/', $url); 529 foreach ($this->routes as $route_name => $route) 530 { 531 $out = array(); 532 $r = null; 533 534 list($route, $regexp, $names, $names_hash, $defaults, $requirements, $suffix) = $route; 535 536 $break = false; 537 538 if (preg_match($regexp, $url, $r)) 539 { 540 $break = true; 541 542 // remove the first element, which is the url 543 array_shift($r); 544 545 // hack, pre-fill the default route names 546 foreach ($names as $name) 547 { 548 $out[$name] = null; 549 } 550 551 // defaults 552 foreach ($defaults as $name => $value) 553 { 554 if (preg_match('#[a-z_\-]#i', $name)) 555 { 556 $out[$name] = urldecode($value); 557 } 558 else 559 { 560 $out[$value] = true; 561 } 562 } 563 564 $pos = 0; 565 foreach ($r as $found) 566 { 567 // if $found is a named url element (i.e. ':action') 568 if (isset($names[$pos])) 569 { 570 $out[$names[$pos]] = urldecode($found); 571 } 572 // unnamed elements go in as 'pass' 573 else 574 { 575 $pass = explode('/', $found); 576 $found = ''; 577 for ($i = 0, $max = count($pass); $i < $max; $i += 2) 578 { 579 if (!isset($pass[$i + 1])) continue; 580 581 $found .= $pass[$i].'='.$pass[$i + 1].'&'; 582 } 583 parse_str($found, $pass); 584 foreach ($pass as $key => $value) 585 { 586 // we add this parameters if not in conflict with named url element (i.e. ':action') 587 if (!isset($names_hash[$key])) 588 { 589 $out[$key] = $value; 590 } 591 } 592 } 593 $pos++; 594 } 595 596 // we must have found all :var stuffs in url? except if default values exists 597 foreach ($names as $name) 598 { 599 if ($out[$name] == null) 600 { 601 $break = false; 602 } 603 } 604 605 if ($break) 606 { 607 // we store route name 608 $this->setCurrentRouteName($route_name); 609 610 if (sfConfig::get('sf_logging_enabled')) 611 { 612 sfLogger::getInstance()->info('{sfRouting} match route ['.$route_name.'] "'.$route.'"'); 613 } 614 615 break; 616 } 617 } 618 } 619 620 // no route found 621 if (!$break) 622 { 623 if (sfConfig::get('sf_logging_enabled')) 624 { 625 sfLogger::getInstance()->info('{sfRouting} no matching route found'); 626 } 627 628 return null; 629 } 630 631 return $out; 632 } 633 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Fri Mar 16 22:42:14 2007 | par Balluche grâce à PHPXref 0.7 |