View file vendor/slim/slim/Slim/Routing/RouteParser.php

File size: 3.81Kb
  1. <?php
  2.  
  3. /**
  4. * Slim Framework (https://slimframework.com)
  5. *
  6. * @license https://github.com/slimphp/Slim/blob/4.x/LICENSE.md (MIT License)
  7. */
  8.  
  9. declare(strict_types=1);
  10.  
  11. namespace Slim\Routing;
  12.  
  13. use FastRoute\RouteParser\Std;
  14. use InvalidArgumentException;
  15. use Psr\Http\Message\UriInterface;
  16. use Slim\Interfaces\RouteCollectorInterface;
  17. use Slim\Interfaces\RouteParserInterface;
  18.  
  19. use function array_key_exists;
  20. use function array_reverse;
  21. use function http_build_query;
  22. use function implode;
  23. use function is_string;
  24.  
  25. class RouteParser implements RouteParserInterface
  26. {
  27. private RouteCollectorInterface $routeCollector;
  28.  
  29. private Std $routeParser;
  30.  
  31. public function __construct(RouteCollectorInterface $routeCollector)
  32. {
  33. $this->routeCollector = $routeCollector;
  34. $this->routeParser = new Std();
  35. }
  36.  
  37. /**
  38. * {@inheritdoc}
  39. */
  40. public function relativeUrlFor(string $routeName, array $data = [], array $queryParams = []): string
  41. {
  42. $route = $this->routeCollector->getNamedRoute($routeName);
  43. $pattern = $route->getPattern();
  44.  
  45. $segments = [];
  46. $segmentName = '';
  47.  
  48. /*
  49. * $routes is an associative array of expressions representing a route as multiple segments
  50. * There is an expression for each optional parameter plus one without the optional parameters
  51. * The most specific is last, hence why we reverse the array before iterating over it
  52. */
  53. $expressions = array_reverse($this->routeParser->parse($pattern));
  54. foreach ($expressions as $expression) {
  55. foreach ($expression as $segment) {
  56. /*
  57. * Each $segment is either a string or an array of strings
  58. * containing optional parameters of an expression
  59. */
  60. if (is_string($segment)) {
  61. $segments[] = $segment;
  62. continue;
  63. }
  64.  
  65. /** @var string[] $segment */
  66. /*
  67. * If we don't have a data element for this segment in the provided $data
  68. * we cancel testing to move onto the next expression with a less specific item
  69. */
  70. if (!array_key_exists($segment[0], $data)) {
  71. $segments = [];
  72. $segmentName = $segment[0];
  73. break;
  74. }
  75.  
  76. $segments[] = $data[$segment[0]];
  77. }
  78.  
  79. /*
  80. * If we get to this logic block we have found all the parameters
  81. * for the provided $data which means we don't need to continue testing
  82. * less specific expressions
  83. */
  84. if (!empty($segments)) {
  85. break;
  86. }
  87. }
  88.  
  89. if (empty($segments)) {
  90. throw new InvalidArgumentException('Missing data for URL segment: ' . $segmentName);
  91. }
  92.  
  93. $url = implode('', $segments);
  94. if ($queryParams) {
  95. $url .= '?' . http_build_query($queryParams);
  96. }
  97.  
  98. return $url;
  99. }
  100.  
  101. /**
  102. * {@inheritdoc}
  103. */
  104. public function urlFor(string $routeName, array $data = [], array $queryParams = []): string
  105. {
  106. $basePath = $this->routeCollector->getBasePath();
  107. $url = $this->relativeUrlFor($routeName, $data, $queryParams);
  108.  
  109. if ($basePath) {
  110. $url = $basePath . $url;
  111. }
  112.  
  113. return $url;
  114. }
  115.  
  116. /**
  117. * {@inheritdoc}
  118. */
  119. public function fullUrlFor(UriInterface $uri, string $routeName, array $data = [], array $queryParams = []): string
  120. {
  121. $path = $this->urlFor($routeName, $data, $queryParams);
  122. $scheme = $uri->getScheme();
  123. $authority = $uri->getAuthority();
  124. $protocol = ($scheme ? $scheme . ':' : '') . ($authority ? '//' . $authority : '');
  125. return $protocol . $path;
  126. }
  127. }