[ Index ]
 

Code source de Symfony 1.0.0

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/controller/ -> sfRouting.class.php (source)

   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  }


Généré le : Fri Mar 16 22:42:14 2007 par Balluche grâce à PHPXref 0.7