0

Firstly, I tried all the questions & answers related to this topic. Additionally and I tried related questions and try to solve it but no success. So please read my question thoroughly.

How to check url is valid on my Routes using Regex patterns ?

This Code write "Core PHP" not any Framework.

$routes = [
          'PUT' =>
              [
               '/order/{id}' => 'updateOrder'
              ],
          'GET' =>
              [
                '/order' => 'getOrder',
                '/order/{id}' => 'getOrder',
                '/order/status/{id}' =>'getOrderStatus'
              ],
          'POST' =>
              [
               '/order' =>'createOrder'
              ],
          'DELETE' =>
              [
                 '/order/{id}' => 'deleteOrder'
              ]
        ];

My url like :

1) '/order/BPQ153'

2) '/order/status/BPQ123'

3) '/order'

4
  • By "valid" I presume you mean that the URLs you posted as 1), 2) and 3) would return the related function (value of the array index)? Commented Apr 25, 2020 at 12:41
  • yes , right.sir Commented Apr 25, 2020 at 12:42
  • This is a job for the router instance, isn 't it? The router, which processes the requested route should throw an exception, when a route is not valid. So check the routes in your array against the router of the application and look, if an exception was thrown. Commented Apr 25, 2020 at 12:45
  • I am a js developer. can you test it on your PHP '\/([^']+)?' Commented Apr 25, 2020 at 12:51

2 Answers 2

2

You can simply loop through the routes and find the first matching one. Note that the outer loop in below code is only because I am checking all the sample URLs you provided at once:

$routes = [
    'PUT' =>
        [
            '/order/{id}' => 'updateOrder'
        ],
    'GET' =>
        [
            '/order' => 'getOrder',
            '/order/{id}' => 'getOrder',
            '/order/status/{id}' => 'getOrderStatus'
        ],
    'POST' =>
        [
            '/order' => 'createOrder'
        ],
    'DELETE' =>
        [
            '/order/{id}' => 'deleteOrder'
        ]
];

$urlsToCheck = [
    '/order/BPQ153',
    '/order/status/BPQ123',
    '/order',
];

foreach ($urlsToCheck as $url) {
    foreach ($routes as $method => $methodValues) {
        foreach ($methodValues as $route => $function) {

            // match ID to everything that is not a slash
            $regex = str_replace('{id}', '([^\/]+)', $route);

            if (preg_match('@^' . $regex . '$@', $url)) {
                echo "The URL $url matches on $method HTTP method for function $function.";
                echo PHP_EOL;
            }
        }
    }
}

this outputs:

The URL /order/BPQ153 matches on PUT HTTP method for function updateOrder.
The URL /order/BPQ153 matches on GET HTTP method for function getOrder.
The URL /order/BPQ153 matches on DELETE HTTP method for function deleteOrder.
The URL /order/status/BPQ123 matches on GET HTTP method for function getOrderStatus.
The URL /order matches on GET HTTP method for function getOrder.
The URL /order matches on POST HTTP method for function createOrder.

As you can see, I did not check for a specific HTTP method, but you would have to add an extra check in there depending on the current HTTP method that is used. However that was not part of the question, so I am only mentioning it here for completeness.

P.S.: For cleaner code you can of course put this into a function / method / class, I just tried to keep the code as short as possible here.

Sign up to request clarification or add additional context in comments.

5 Comments

id is not only numberic number like order id like "IN12500"
@Sumitpatel I updated my answer accordingly. In this case I think it is simpler to just match {id} against anything that is not a slash. Note that you still have to account for different HTTP methods if relevant.
1 issue are there i call url '/order/status' so call function is "getOrder" it' wrong.. plase can be help us
@Sumitpatel which line of the output do you mean? Are you calling /order/status without the /{id} part? This route is not defined
yes this route no define but i call '/order/status' so return false or other but this return 'getOrder' it's wrong .
1

First of all you have to define your routes a little more detailed. Otherwise, it is not clear how your placeholders in the curly brackets should be used. Of course you as a programmer know that there should be a numerical value for an ID. Your validator may not know this.

So let us have a look, how you can define your routes a little bit more detailed.

$routes = [
    [
        'controller' => SomeController::class,
        'route' => '/order/{id}',
        'parameters' => [ 'id' => '([0-9a-z]+)' ],
        'allowed_methods' => [ 'GET' ],
    ]
];

This is just an example entry for a route. A route contains the controller, which has to be called, when this route is requested. Also the route itself is mentioned here. Beside that we define a parameter called id which acts as a placeholder in your route and we define the allowed request methods. In this case the route should only be accessible via GET requests. In our small example here, we just need the parameters and the route. The below shown router class does not recognize the request method and the controller.

So how a route can be resolved and validated? All we have to know is now defined in the route. When a request happens, we can check against the route.

Here 's a small router class example. Ofcourse this small example should not be used for production.

declare(strict_types=1);
namespace Marcel\Router;

class Router
{
    protected array $routes = [];

    protected array $filters = [];

    public function __construct(array $routes)
    {
        $this->routes = $routes;
    }

    public function match(string $request) : bool
    {
        foreach ($this->routes as $route) {

            // find parameters and filters (regex) in route
            if (preg_match_all('/\{([\w\-%]+)\}/', $route['route'], $keys)) {
                $keys = $keys[1];
            }

            foreach ($keys as $key => $name) {
                if (isset($route['parameters'][$name])) {
                    $this->filters[$name] = $route['parameters'][$name];
                }
            }

            // match requested route against defined route
            $regex = preg_replace_callback('/\{(\w+)\}/', [ $this, 'substituteFilter' ], $route['route']);
            $filter = rtrim($regex, '/');
            $pattern = '@^' . $filter . '/?$@i';

            // if not matching, check the next route
            if (!preg_match($pattern, $request, $matches)) {
                continue;
            }

            return true;
        }

        return false;
    }

    protected function substituteFilter(array $matches): string
    {
        if (isset($matches[1], $this->filters[$matches[1]])) {
            return $this->filters[$matches[1]];
        }

        return '([\w\-%]+)';
    }
}

This small router class example tests the given urls against the collection of routes. The class pays attention to the placeholders that can be filled with a regular expression. So the class checks every request against the defined regex for the given placeholder. So let us test this little class against some requests

$router = new Router($routes);
$result = $router->match('/order/BPQ123');
var_dump($result); // true

$result = $router->match('/bla/yadda/blubb');
var_dump($result); // false

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.