Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[FrameworkBundle] added a command to help debugging route matching pr…

…oblems
  • Loading branch information...
commit 8cc3158d8901b8aefadde7f24be92fd9ed2b07e7 1 parent 24b9392
@fabpot fabpot authored
View
88 src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php
@@ -0,0 +1,88 @@
+<?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\Bundle\FrameworkBundle\Command;
+
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Routing\RouterInterface;
+use Symfony\Component\Routing\Matcher\TraceableUrlMatcher;
+
+/**
+ * A console command to test route matching.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class RouterMatchCommand extends ContainerAwareCommand
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function isEnabled()
+ {
+ if (!$this->getContainer()->has('router')) {
+ return false;
+ }
+ $router = $this->getContainer()->get('router');
+ if (!$router instanceof RouterInterface) {
+ return false;
+ }
+ return parent::isEnabled();
+ }
+
+ /**
+ * @see Command
+ */
+ protected function configure()
+ {
+ $this
+ ->setDefinition(array(
+ new InputArgument('path_info', InputArgument::REQUIRED, 'A path info'),
+ ))
+ ->setName('router:match')
+ ->setDescription('Helps debug routes by simulating a path info match')
+ ->setHelp(<<<EOF
+The <info>router:match</info> simulates a path info match:
+
+ <info>router:match /foo</info>
+EOF
+ )
+ ;
+ }
+
+ /**
+ * @see Command
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $router = $this->getContainer()->get('router');
+ $matcher = new TraceableUrlMatcher($router->getRouteCollection(), $router->getContext());
@stof Collaborator
stof added a note

I see an issue with such a setup: getRouteCollection calls the loader (it is intended to do so) so running the command in the prod (non-debug) environment may produce a different result than the routing of a request because the request will use the cache. A common issue according to what I see on IRC is forgetting to clear the prod cache after changing the routing. It could be a good idea to have a command doing the matching with the cached router.

@fabpot Owner
fabpot added a note

Doing the matching with the cached router is not possible. But in the dev env, the cache should never be out of date, and in the production env, there is no way to check if it is up to date or not as we don't have the meta file.

@stof Collaborator
stof added a note

yeah, but using php app/console --env=prod --no-debug router:match or php app/console --env=prod --no-debug router:debug will not produce the same result than doing the prod request. So we should at least give a warning to the user to make sure he clears the cache to update the router when he makes a change because the CLI will show him the new routes even when the cache is outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ $traces = $matcher->getTraces($input->getArgument('path_info'));
+
+ $matches = false;
+ foreach ($traces as $i => $trace) {
+ if (TraceableUrlMatcher::ROUTE_ALMOST_MATCHES == $trace['level']) {
+ $output->writeln(sprintf('<fg=yellow>Route "%s" almost matches but %s</>', $trace['name'], lcfirst($trace['log'])));
+ } elseif (TraceableUrlMatcher::ROUTE_MATCHES == $trace['level']) {
+ $output->writeln(sprintf('<fg=green>Route "%s" matches</>', $trace['name']));
+ $matches = true;
+ } elseif ($input->getOption('verbose')) {
+ $output->writeln(sprintf('Route "%s" does not match: %s', $trace['name'], $trace['log']));
+ }
+ }
+
+ if (!$matches) {
+ $output->writeln('<fg=red>None of the routes matches</>');
+ }
+ }
+}
View
67 src/Symfony/Bundle/FrameworkBundle/DataCollector/RouterDataCollector.php
@@ -0,0 +1,67 @@
+<?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\Bundle\FrameworkBundle\DataCollector;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\DataCollector\DataCollector;
+use Symfony\Component\Routing\Matcher\TraceableUrlMatcher;
+use Symfony\Component\Routing\RouterInterface;
+
+/**
+ * RouterDataCollector.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class RouterDataCollector extends DataCollector
+{
+ private $router;
+
+ public function __construct(RouterInterface $router = null)
+ {
+ $this->router = $router;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function collect(Request $request, Response $response, \Exception $exception = null)
+ {
+ $this->data['path_info'] = $request->getPathInfo();
+
+ if (!$this->router) {
+ $this->data['traces'] = array();
+ } else {
+ $matcher = new TraceableUrlMatcher($this->router->getRouteCollection(), $this->router->getContext());
+
+ $this->data['traces'] = $matcher->getTraces($request->getPathInfo());
+ }
+ }
+
+ public function getPathInfo()
+ {
+ return $this->data['path_info'];
+ }
+
+ public function getTraces()
+ {
+ return $this->data['traces'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return 'router';
+ }
+}
View
6 src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml
@@ -12,6 +12,7 @@
<parameter key="data_collector.logger.class">Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector</parameter>
<parameter key="data_collector.time.class">Symfony\Component\HttpKernel\DataCollector\TimeDataCollector</parameter>
<parameter key="data_collector.memory.class">Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector</parameter>
+ <parameter key="data_collector.router.class">Symfony\Bundle\FrameworkBundle\DataCollector\RouterDataCollector</parameter>
</parameters>
<services>
@@ -50,5 +51,10 @@
<service id="data_collector.memory" class="%data_collector.memory.class%" public="false">
<tag name="data_collector" template="WebProfilerBundle:Collector:memory" id="memory" priority="255" />
</service>
+
+ <service id="data_collector.router" class="%data_collector.router.class%" public="false">
+ <tag name="data_collector" template="WebProfilerBundle:Collector:router" id="router" priority="255" />
+ <argument type="service" id="router" on-invalid="ignore" />
+ </service>
</services>
</container>
Please sign in to comment.
Something went wrong with that request. Please try again.