123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- <?php
- /*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace Symfony\Component\Routing\Matcher\Dumper;
- use Symfony\Component\Routing\Route;
- use Symfony\Component\Routing\RouteCollection;
- /**
- * PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes.
- *
- * @author Fabien Potencier <fabien@symfony.com>
- */
- class PhpMatcherDumper extends MatcherDumper
- {
- /**
- * Dumps a set of routes to a PHP class.
- *
- * Available options:
- *
- * * class: The class name
- * * base_class: The base class name
- *
- * @param array $options An array of options
- *
- * @return string A PHP class representing the matcher class
- */
- public function dump(array $options = array())
- {
- $options = array_merge(array(
- 'class' => 'ProjectUrlMatcher',
- 'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
- ), $options);
- // trailing slash support is only enabled if we know how to redirect the user
- $interfaces = class_implements($options['base_class']);
- $supportsRedirections = isset($interfaces['Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface']);
- return
- $this->startClass($options['class'], $options['base_class']).
- $this->addConstructor().
- $this->addMatcher($supportsRedirections).
- $this->endClass()
- ;
- }
- private function addMatcher($supportsRedirections)
- {
- $code = implode("\n", $this->compileRoutes($this->getRoutes(), $supportsRedirections));
- return <<<EOF
- public function match(\$pathinfo)
- {
- \$allow = array();
- $code
- throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
- }
- EOF;
- }
- private function compileRoutes(RouteCollection $routes, $supportsRedirections, $parentPrefix = null)
- {
- $code = array();
- $routes = clone $routes;
- $routeIterator = $routes->getIterator();
- $keys = array_keys($routeIterator->getArrayCopy());
- $keysCount = count($keys);
- $i = 0;
- foreach ($routeIterator as $name => $route) {
- $i++;
- $route = clone $route;
- if ($route instanceof RouteCollection) {
- $prefix = $route->getPrefix();
- $optimizable = $prefix && count($route->all()) > 1 && false === strpos($route->getPrefix(), '{');
- $indent = '';
- if ($optimizable) {
- for ($j = $i; $j < $keysCount; $j++) {
- if ($keys[$j] === null) {
- continue;
- }
- $testRoute = $routeIterator->offsetGet($keys[$j]);
- $isCollection = ($testRoute instanceof RouteCollection);
- $testPrefix = $isCollection ? $testRoute->getPrefix() : $testRoute->getPattern();
- if (0 === strpos($testPrefix, $prefix)) {
- $routeIterator->offsetUnset($keys[$j]);
- if ($isCollection) {
- $route->addCollection($testRoute);
- } else {
- $route->add($keys[$j], $testRoute);
- }
- $i++;
- $keys[$j] = null;
- }
- }
- $code[] = sprintf(" if (0 === strpos(\$pathinfo, '%s')) {", $prefix);
- $indent = ' ';
- }
- foreach ($this->compileRoutes($route, $supportsRedirections, $prefix) as $line) {
- foreach (explode("\n", $line) as $l) {
- $code[] = $indent.$l;
- }
- }
- if ($optimizable) {
- $code[] = " throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();";
- $code[] = " }\n";
- }
- } else {
- foreach ($this->compileRoute($route, $name, $supportsRedirections, $parentPrefix) as $line) {
- $code[] = $line;
- }
- }
- }
- return $code;
- }
- private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null)
- {
- $code = array();
- $compiledRoute = $route->compile();
- $conditions = array();
- $hasTrailingSlash = false;
- $matches = false;
- if (!count($compiledRoute->getVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#', str_replace(array("\n", ' '), '', $compiledRoute->getRegex()), $m)) {
- if ($supportsRedirections && substr($m['url'], -1) === '/') {
- $conditions[] = sprintf("rtrim(\$pathinfo, '/') === '%s'", rtrim(str_replace('\\', '', $m['url']), '/'));
- $hasTrailingSlash = true;
- } else {
- $conditions[] = sprintf("\$pathinfo === '%s'", str_replace('\\', '', $m['url']));
- }
- } else {
- if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() != $parentPrefix) {
- $conditions[] = sprintf("0 === strpos(\$pathinfo, '%s')", $compiledRoute->getStaticPrefix());
- }
- $regex = str_replace(array("\n", ' '), '', $compiledRoute->getRegex());
- if ($supportsRedirections && $pos = strpos($regex, '/$')) {
- $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
- $hasTrailingSlash = true;
- }
- $conditions[] = sprintf("preg_match('%s', \$pathinfo, \$matches)", $regex);
- $matches = true;
- }
- $conditions = implode(' && ', $conditions);
- $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name);
- $code[] = <<<EOF
- // $name
- if ($conditions) {
- EOF;
- if ($req = $route->getRequirement('_method')) {
- $methods = explode('|', strtoupper($req));
- // GET and HEAD are equivalent
- if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
- $methods[] = 'HEAD';
- }
- if (1 === count($methods)) {
- $code[] = <<<EOF
- if (\$this->context->getMethod() != '$methods[0]') {
- \$allow[] = '$methods[0]';
- goto $gotoname;
- }
- EOF;
- } else {
- $methods = implode('\', \'', $methods);
- $code[] = <<<EOF
- if (!in_array(\$this->context->getMethod(), array('$methods'))) {
- \$allow = array_merge(\$allow, array('$methods'));
- goto $gotoname;
- }
- EOF;
- }
- }
- if ($hasTrailingSlash) {
- $code[] = sprintf(<<<EOF
- if (substr(\$pathinfo, -1) !== '/') {
- return \$this->redirect(\$pathinfo.'/', '%s');
- }
- EOF
- , $name);
- }
- if ($scheme = $route->getRequirement('_scheme')) {
- if (!$supportsRedirections) {
- throw new \LogicException('The "_scheme" requirement is only supported for route dumper that implements RedirectableUrlMatcherInterface.');
- }
- $code[] = sprintf(<<<EOF
- if (\$this->context->getScheme() !== '$scheme') {
- return \$this->redirect(\$pathinfo, '%s', '$scheme');
- }
- EOF
- , $name);
- }
- // optimize parameters array
- if (true === $matches && $compiledRoute->getDefaults()) {
- $code[] = sprintf(" return array_merge(\$this->mergeDefaults(\$matches, %s), array('_route' => '%s'));"
- , str_replace("\n", '', var_export($compiledRoute->getDefaults(), true)), $name);
- } elseif (true === $matches) {
- $code[] = sprintf(" \$matches['_route'] = '%s';", $name);
- $code[] = sprintf(" return \$matches;", $name);
- } elseif ($compiledRoute->getDefaults()) {
- $code[] = sprintf(' return %s;', str_replace("\n", '', var_export(array_merge($compiledRoute->getDefaults(), array('_route' => $name)), true)));
- } else {
- $code[] = sprintf(" return array('_route' => '%s');", $name);
- }
- $code[] = " }";
- if ($req) {
- $code[] = " $gotoname:";
- }
- $code[] = '';
- return $code;
- }
- private function startClass($class, $baseClass)
- {
- return <<<EOF
- <?php
- use Symfony\Component\Routing\Exception\MethodNotAllowedException;
- use Symfony\Component\Routing\Exception\ResourceNotFoundException;
- use Symfony\Component\Routing\RequestContext;
- /**
- * $class
- *
- * This class has been auto-generated
- * by the Symfony Routing Component.
- */
- class $class extends $baseClass
- {
- EOF;
- }
- private function addConstructor()
- {
- return <<<EOF
- /**
- * Constructor.
- */
- public function __construct(RequestContext \$context)
- {
- \$this->context = \$context;
- }
- EOF;
- }
- private function endClass()
- {
- return <<<EOF
- }
- EOF;
- }
- }
|