RoutingExtension.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.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. namespace Symfony\Bridge\Twig\Extension;
  11. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  12. use Twig\Extension\AbstractExtension;
  13. use Twig\Node\Expression\ArrayExpression;
  14. use Twig\Node\Expression\ConstantExpression;
  15. use Twig\Node\Node;
  16. use Twig\TwigFunction;
  17. /**
  18. * Provides integration of the Routing component with Twig.
  19. *
  20. * @author Fabien Potencier <fabien@symfony.com>
  21. */
  22. final class RoutingExtension extends AbstractExtension
  23. {
  24. private $generator;
  25. public function __construct(UrlGeneratorInterface $generator)
  26. {
  27. $this->generator = $generator;
  28. }
  29. /**
  30. * {@inheritdoc}
  31. */
  32. public function getFunctions(): array
  33. {
  34. return [
  35. new TwigFunction('url', [$this, 'getUrl'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']]),
  36. new TwigFunction('path', [$this, 'getPath'], ['is_safe_callback' => [$this, 'isUrlGenerationSafe']]),
  37. ];
  38. }
  39. public function getPath(string $name, array $parameters = [], bool $relative = false): string
  40. {
  41. return $this->generator->generate($name, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH);
  42. }
  43. public function getUrl(string $name, array $parameters = [], bool $schemeRelative = false): string
  44. {
  45. return $this->generator->generate($name, $parameters, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL);
  46. }
  47. /**
  48. * Determines at compile time whether the generated URL will be safe and thus
  49. * saving the unneeded automatic escaping for performance reasons.
  50. *
  51. * The URL generation process percent encodes non-alphanumeric characters. So there is no risk
  52. * that malicious/invalid characters are part of the URL. The only character within an URL that
  53. * must be escaped in html is the ampersand ("&") which separates query params. So we cannot mark
  54. * the URL generation as always safe, but only when we are sure there won't be multiple query
  55. * params. This is the case when there are none or only one constant parameter given.
  56. * E.g. we know beforehand this will be safe:
  57. * - path('route')
  58. * - path('route', {'param': 'value'})
  59. * But the following may not:
  60. * - path('route', var)
  61. * - path('route', {'param': ['val1', 'val2'] }) // a sub-array
  62. * - path('route', {'param1': 'value1', 'param2': 'value2'})
  63. * If param1 and param2 reference placeholder in the route, it would still be safe. But we don't know.
  64. *
  65. * @param Node $argsNode The arguments of the path/url function
  66. *
  67. * @return array An array with the contexts the URL is safe
  68. */
  69. public function isUrlGenerationSafe(Node $argsNode): array
  70. {
  71. // support named arguments
  72. $paramsNode = $argsNode->hasNode('parameters') ? $argsNode->getNode('parameters') : (
  73. $argsNode->hasNode(1) ? $argsNode->getNode(1) : null
  74. );
  75. if (null === $paramsNode || $paramsNode instanceof ArrayExpression && \count($paramsNode) <= 2 &&
  76. (!$paramsNode->hasNode(1) || $paramsNode->getNode(1) instanceof ConstantExpression)
  77. ) {
  78. return ['html'];
  79. }
  80. return [];
  81. }
  82. }