Development

/branches/1.4/lib/routing/sfPatternRouting.class.php

You must first sign up to be able to contribute.

root/branches/1.4/lib/routing/sfPatternRouting.class.php

Revision 32845, 13.8 kB (checked in by fabien, 3 years ago)

[1.4] removed dead code (closes #9881)

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) 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  * sfPatternRouting class controls the generation and parsing of URLs.
13  *
14  * It parses and generates URLs by delegating the work to an array of sfRoute objects.
15  *
16  * @package    symfony
17  * @subpackage routing
18  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
19  * @version    SVN: $Id$
20  */
21 class sfPatternRouting extends sfRouting
22 {
23   protected
24     $currentRouteName   = null,
25     $currentInternalUri = array(),
26     $routes             = array(),
27     $defaultParamsDirty = false,
28     $cacheData          = array(),
29     $cacheChanged       = false;
30
31   /**
32    * Initializes this Routing.
33    *
34    * Available options:
35    *
36    *  * suffix:                           The default suffix
37    *  * variable_prefixes:                An array of characters that starts a variable name (: by default)
38    *  * segment_separators:               An array of allowed characters for segment separators (/ and . by default)
39    *  * variable_regex:                   A regex that match a valid variable name ([\w\d_]+ by default)
40    *  * generate_shortest_url:            Whether to generate the shortest URL possible (true by default)
41    *  * extra_parameters_as_query_string: Whether to generate extra parameters as a query string
42    *  * lookup_cache_dedicated_keys:      Whether to use dedicated keys for parse/generate cache (false by default)
43    *                                      WARNING: When this option is activated, do not use sfFileCache; use a fast access
44    *                                      cache backend (like sfAPCCache).
45    *
46    * @see sfRouting
47    */
48   public function initialize(sfEventDispatcher $dispatcher, sfCache $cache = null, $options = array())
49   {
50     $options = array_merge(array(
51       'variable_prefixes'                => array(':'),
52       'segment_separators'               => array('/', '.'),
53       'variable_regex'                   => '[\w\d_]+',
54       'load_configuration'               => false,
55       'suffix'                           => '',
56       'generate_shortest_url'            => true,
57       'extra_parameters_as_query_string' => true,
58       'lookup_cache_dedicated_keys'      => false,
59     ), $options);
60
61     // for BC
62     if ('.' == $options['suffix'])
63     {
64       $options['suffix'] = '';
65     }
66
67     parent::initialize($dispatcher, $cache, $options);
68
69     if (null !== $this->cache && !$options['lookup_cache_dedicated_keys'] && $cacheData = $this->cache->get('symfony.routing.data'))
70     {
71       $this->cacheData = unserialize($cacheData);
72     }
73   }
74
75   /**
76    * @see sfRouting
77    */
78   public function loadConfiguration()
79   {
80     if ($this->options['load_configuration'] && $config = $this->getConfigFilename())
81     {
82       include($config);
83     }
84
85     parent::loadConfiguration();
86   }
87
88   /**
89    * Added for better performance. We need to ensure that changed default parameters
90    * are set, but resetting them everytime wastes many cpu cycles
91    */
92   protected function ensureDefaultParametersAreSet()
93   {
94     if ($this->defaultParamsDirty)
95     {
96       foreach ($this->routes as $route)
97       {
98         $route->setDefaultParameters($this->defaultParameters);
99       }
100       $this->defaultParamsDirty = false;
101     }
102   }
103
104   /**
105    * @see sfRouting
106    */
107   public function setDefaultParameter($key, $value)
108   {
109     parent::setDefaultParameter($key, $value);
110     $this->defaultParamsDirty = true;
111   }
112
113   /**
114    * @see sfRouting
115    */
116   public function setDefaultParameters($parameters)
117   {
118     parent::setDefaultParameters($parameters);
119     $this->defaultParamsDirty = true;
120   }
121
122   protected function getConfigFileName()
123   {
124     return sfContext::getInstance()->getConfigCache()->checkConfig('config/routing.yml', true);
125   }
126
127   /**
128    * @see sfRouting
129    */
130   public function getCurrentInternalUri($withRouteName = false)
131   {
132     return null === $this->currentRouteName ? null : $this->currentInternalUri[$withRouteName ? 0 : 1];
133   }
134
135   /**
136    * Gets the current route name.
137    *
138    * @return string The route name
139    */
140   public function getCurrentRouteName()
141   {
142     return $this->currentRouteName;
143   }
144
145   /**
146    * @see sfRouting
147    */
148   public function getRoutes()
149   {
150     return $this->routes;
151   }
152
153   /**
154    * @see sfRouting
155    */
156   public function setRoutes($routes)
157   {
158     foreach ($routes as $name => $route)
159     {
160       $this->connect($name, $route);
161     }
162   }
163
164   /**
165    * @see sfRouting
166    */
167   public function hasRoutes()
168   {
169     return count($this->routes) ? true : false;
170   }
171
172   /**
173    * @see sfRouting
174    */
175   public function clearRoutes()
176   {
177     if ($this->options['logging'])
178     {
179       $this->dispatcher->notify(new sfEvent($this, 'application.log', array('Clear all current routes')));
180     }
181
182     $this->routes = array();
183   }
184
185   /**
186    * Returns true if the route name given is defined.
187    *
188    * @param  string $name  The route name
189    *
190    * @return boolean
191    */
192   public function hasRouteName($name)
193   {
194     return isset($this->routes[$name]) ? true : false;
195   }
196
197   /**
198    * Adds a new route at the beginning of the current list of routes.
199    *
200    * @see connect
201    */
202   public function prependRoute($name, $route)
203   {
204     $routes = $this->routes;
205     $this->routes = array();
206     $this->connect($name, $route);
207     $this->routes = array_merge($this->routes, $routes);
208   }
209
210   /**
211    * Adds a new route.
212    *
213    * Alias for the connect method.
214    *
215    * @see connect
216    */
217   public function appendRoute($name, $route)
218   {
219     return $this->connect($name, $route);
220   }
221
222   /**
223    * Adds a new route before a given one in the current list of routes.
224    *
225    * @see connect
226    */
227   public function insertRouteBefore($pivot, $name, $route)
228   {
229     if (!isset($this->routes[$pivot]))
230     {
231       throw new sfConfigurationException(sprintf('Unable to insert route "%s" before inexistent route "%s".', $name, $pivot));
232     }
233
234     $routes = $this->routes;
235     $this->routes = array();
236     $newroutes = array();
237     foreach ($routes as $key => $value)
238     {
239       if ($key == $pivot)
240       {
241         $this->connect($name, $route);
242         $newroutes = array_merge($newroutes, $this->routes);
243       }
244       $newroutes[$key] = $value;
245     }
246
247     $this->routes = $newroutes;
248   }
249
250   /**
251    * Adds a new route at the end of the current list of routes.
252    *
253    * A route string is a string with 2 special constructions:
254    * - :string: :string denotes a named parameter (available later as $request->getParameter('string'))
255    * - *: * match an indefinite number of parameters in a route
256    *
257    * Here is a very common rule in a symfony project:
258    *
259    * <code>
260    * $r->connect('default', new sfRoute('/:module/:action/*'));
261    * </code>
262    *
263    * @param  string  $name  The route name
264    * @param  sfRoute $route A sfRoute instance
265    *
266    * @return array  current routes
267    */
268   public function connect($name, $route)
269   {
270     $routes = $route instanceof sfRouteCollection ? $route : array($name => $route);
271     foreach (self::flattenRoutes($routes) as $name => $route)
272     {
273       $this->routes[$name] = $route;
274       $this->configureRoute($route);
275
276       if ($this->options['logging'])
277       {
278         $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Connect %s "%s" (%s)', get_class($route), $name, $route->getPattern()))));
279       }
280     }
281   }
282
283   public function configureRoute(sfRoute $route)
284   {
285     $route->setDefaultParameters($this->defaultParameters);
286     $route->setDefaultOptions($this->options);
287   }
288
289   /**
290    * @see sfRouting
291    */
292   public function generate($name, $params = array(), $absolute = false)
293   {
294     // fetch from cache
295     if (null !== $this->cache)
296     {
297       $cacheKey = 'generate_'.$name.'_'.md5(serialize(array_merge($this->defaultParameters, $params))).'_'.md5(serialize($this->options['context']));
298       if ($this->options['lookup_cache_dedicated_keys'] && $url = $this->cache->get('symfony.routing.data.'.$cacheKey))
299       {
300         return $this->fixGeneratedUrl($url, $absolute);
301       }
302       elseif (isset($this->cacheData[$cacheKey]))
303       {
304         return $this->fixGeneratedUrl($this->cacheData[$cacheKey], $absolute);
305       }
306     }
307
308     if ($name)
309     {
310       // named route
311       if (!isset($this->routes[$name]))
312       {
313         throw new sfConfigurationException(sprintf('The route "%s" does not exist.', $name));
314       }
315       $route = $this->routes[$name];
316       $this->ensureDefaultParametersAreSet();
317     }
318     else
319     {
320       // find a matching route
321       if (false === $route = $this->getRouteThatMatchesParameters($params))
322       {
323         throw new sfConfigurationException(sprintf('Unable to find a matching route to generate url for params "%s".', is_object($params) ? 'Object('.get_class($params).')' : str_replace("\n", '', var_export($params, true))));
324       }
325     }
326
327     $url = $route->generate($params, $this->options['context'], $absolute);
328
329     // store in cache
330     if (null !== $this->cache)
331     {
332       if ($this->options['lookup_cache_dedicated_keys'])
333       {
334         $this->cache->set('symfony.routing.data.'.$cacheKey, $url);
335       }
336       else
337       {
338         $this->cacheChanged = true;
339         $this->cacheData[$cacheKey] = $url;
340       }
341     }
342
343     return $this->fixGeneratedUrl($url, $absolute);
344   }
345
346   /**
347    * @see sfRouting
348    */
349   public function parse($url)
350   {
351     if (false === $info = $this->findRoute($url))
352     {
353       $this->currentRouteName = null;
354       $this->currentInternalUri = array();
355
356       return false;
357     }
358
359     if ($this->options['logging'])
360     {
361       $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Match route "%s" (%s) for %s with parameters %s', $info['name'], $info['pattern'], $url, str_replace("\n", '', var_export($info['parameters'], true))))));
362     }
363
364     // store the current internal URI
365     $this->updateCurrentInternalUri($info['name'], $info['parameters']);
366
367     $route = $this->routes[$info['name']];
368
369     $this->ensureDefaultParametersAreSet();
370
371     $route->bind($this->options['context'], $info['parameters']);
372     $info['parameters']['_sf_route'] = $route;
373
374     return $info['parameters'];
375   }
376
377   protected function updateCurrentInternalUri($name, array $parameters)
378   {
379     // store the route name
380     $this->currentRouteName = $name;
381
382     $internalUri = array('@'.$this->currentRouteName, $parameters['module'].'/'.$parameters['action']);
383     unset($parameters['module'], $parameters['action']);
384
385     $params = array();
386     foreach ($parameters as $key => $value)
387     {
388       $params[] = $key.'='.$value;
389     }
390
391     // sort to guaranty unicity
392     sort($params);
393
394     $params = $params ? '?'.implode('&', $params) : '';
395
396     $this->currentInternalUri = array($internalUri[0].$params, $internalUri[1].$params);
397   }
398
399   /**
400    * Finds a matching route for given URL.
401    *
402    * Returns false if no route matches.
403    *
404    * Returned array contains:
405    *
406    *  - name:       name or alias of the route that matched
407    *  - pattern:    the compiled pattern of the route that matched
408    *  - parameters: array containing key value pairs of the request parameters including defaults
409    *
410    * @param  string $url     URL to be parsed
411    *
412    * @return array|false  An array with routing information or false if no route matched
413    */
414   public function findRoute($url)
415   {
416     $url = $this->normalizeUrl($url);
417
418     // fetch from cache
419     if (null !== $this->cache)
420     {
421       $cacheKey = 'parse_'.$url.'_'.md5(serialize($this->options['context']));
422       if ($this->options['lookup_cache_dedicated_keys'] && $info = $this->cache->get('symfony.routing.data.'.$cacheKey))
423       {
424         return unserialize($info);
425       }
426       elseif (isset($this->cacheData[$cacheKey]))
427       {
428         return $this->cacheData[$cacheKey];
429       }
430     }
431
432     $info = $this->getRouteThatMatchesUrl($url);
433
434     // store in cache
435     if (null !== $this->cache)
436     {
437       if ($this->options['lookup_cache_dedicated_keys'])
438       {
439         $this->cache->set('symfony.routing.data.'.$cacheKey, serialize($info));
440       }
441       else
442       {
443         $this->cacheChanged = true;
444         $this->cacheData[$cacheKey] = $info;
445       }
446     }
447
448     return $info;
449   }
450
451   static public function flattenRoutes($routes)
452   {
453     $flattenRoutes = array();
454     foreach ($routes as $name => $route)
455     {
456       if ($route instanceof sfRouteCollection)
457       {
458         $flattenRoutes = array_merge($flattenRoutes, self::flattenRoutes($route));
459       }
460       else
461       {
462         $flattenRoutes[$name] = $route;
463       }
464     }
465
466     return $flattenRoutes;
467   }
468
469   protected function getRouteThatMatchesUrl($url)
470   {
471     $this->ensureDefaultParametersAreSet();
472     foreach ($this->routes as $name => $route)
473     {
474       if (false === $parameters = $route->matchesUrl($url, $this->options['context']))
475       {
476         continue;
477       }
478
479       return array('name' => $name, 'pattern' => $route->getPattern(), 'parameters' => $parameters);
480     }
481
482     return false;
483   }
484
485   protected function getRouteThatMatchesParameters($parameters)
486   {
487     $this->ensureDefaultParametersAreSet();
488     foreach ($this->routes as $route)
489     {
490       if ($route->matchesParameters($parameters, $this->options['context']))
491       {
492         return $route;
493       }
494     }
495
496     return false;
497   }
498
499   protected function normalizeUrl($url)
500   {
501     // an URL should start with a '/', mod_rewrite doesn't respect that, but no-mod_rewrite version does.
502     if ('/' != substr($url, 0, 1))
503     {
504       $url = '/'.$url;
505     }
506
507     // we remove the query string
508     if (false !== $pos = strpos($url, '?'))
509     {
510       $url = substr($url, 0, $pos);
511     }
512
513     // remove multiple /
514     $url = preg_replace('#/+#', '/', $url);
515
516     return $url;
517   }
518
519   /**
520    * @see sfRouting
521    */
522   public function shutdown()
523   {
524     if (null !== $this->cache && $this->cacheChanged)
525     {
526       $this->cacheChanged = false;
527       $this->cache->set('symfony.routing.data', serialize($this->cacheData));
528     }
529   }
530 }
531
Note: See TracBrowser for help on using the browser.