Probleem met zelfgemaakte php router

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Jorn Reed

Jorn Reed

07/02/2018 18:05:00
Quote Anchor link
Hallo,

Ik heb een php router gemaakt aan de hand van een tutorial.

Hier is de code van de htaccess:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
RewriteEngine On
RewriteBase /example/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.+)$ index.php?uri=$1 [QSA,L]


en hier de code van de router class zelf:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php

class Route
{
    private $_uri = array();
    private $_method = array();

    /*
     * Builds a collection of internal URL's to look for
     * @param type $uri
     */

    public function add($uri, $method = null)
    {

        $this->_uri[] = '/' . trim($uri, '/');

        if($method != null){
            $this->_method[] = $method;
        }
    }


    public function submit()
    {


        $uriGetParam = isset($_GET['uri']) ? '/' . $_GET['uri'] : '/';
        $routeFound = false;
        foreach($this->_uri as $key => $value){
            if(preg_match("#^$value$#",$uriGetParam)){
                if(is_string($this->_method[$key])){
                    $routeFound = true;
                    $useMethod = $this->_method[$key];
                    new
$useMethod();
                }

                else{
                    $routeFound = true;
                    call_user_func($this->_method[$key]);
                }
            }
        }

        if(!$routeFound){
            http_response_code(404);
            require_once 'pages/404.php';
        }
    }

}

?>


Helaas kan ik met deze code alleen een pagina url aanmaken zoals '/contact', '/about' etc. Ik ben nu zo ver met het project dat ik een pagina heb gemaakt voor een product, dit is dus een pagina met de product details van 1 specifiek product. Ik weet alleen niet hoe ik het voor elkaar krijg om een url als '/product/{id}' te maken aangezien ik via de code niet zomaar bijvoorbeeld een variabel kan meegeven. Ik maak namelijk routes aan met $route->add('/contact', function(){}); Ik hoop graag dat iemand me kan helpen :)

Edit:
Code-tags toegevoegd.
Gewijzigd op 07/02/2018 18:08:34 door - Ariën -
 
PHP hulp

PHP hulp

28/03/2024 19:57:06
 
Rob Doemaarwat

Rob Doemaarwat

07/02/2018 19:11:53
Quote Anchor link
Door die preg_match("#^$value$#",$uriGetParam) kun je "wildcards" in de URI opnemen dmv letterlijke stukken reg-ex. Als je dus een $router->add('/product/\d+',...) meegeeft zal die matchen met /product/123. Alleen zul je dat ID d'r dan nog wel zelf uit moeten prutsen (tenzij je de 3e parameter van de preg_match() opslaat en dan $router->add('/product/(\d+)',...) doet; ID staat dan met index 1 in die 3e parameter).

Nog wat anders: Na die preg_match() zal linksom of rechts $routeFound = true worden. Je kunt 'm dus een niveautje omhoog halen. Scheelt je weer een regel.

Voorzetje met params in constructor of aan te roepen functie, en nog wat andere tweaks:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php

class Route
{
    private $_uri = array();
    private $_method = array();

    /*
     * Builds a collection of internal URL's to look for
     * @param type $uri
     */

    public function add($uri, $method = null)
    {

        $this->_uri[] = '/' . trim($uri, '/');

        if($method != null){
            $this->_method[] = $method;
        }
    }


    public function submit()
    {


        $uriGetParam = isset($_GET['uri']) ? '/' . $_GET['uri'] : '/';
        $routeFound = false;
        foreach($this->_uri as $key => $value){
            if($routeFound = preg_match("#^$value$#",$uriGetParam,$params)){
                if(is_string($this->_method[$key])){
                    $useMethod = $this->_method[$key];
                    new
$useMethod($params);
                }

                else{
                    call_user_func_array($this->_method[$key],$params);
                }

                break;
            }
        }

        if(!$routeFound){
            http_response_code(404);
            require_once 'pages/404.php';
        }
    }

}

?>
Gewijzigd op 07/02/2018 19:15:29 door Rob Doemaarwat
 
Jorn Reed

Jorn Reed

07/02/2018 19:31:10
Quote Anchor link
Heel erg bedankt! Het werkt prima.

Bedoelde je met het eruit prummelen van het id bijvoorbeeld door op de product pagina dit te doen?

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
<?php
 $uri
= $_SERVER['REQUEST_URI'];
 $pin = explode('/', $uri);
 $id = $pin[2];
 $query->prepare("SELECT * FROM `table` WHERE `id`=$id");
 $query->execute();
?>



Toevoeging op 07/02/2018 19:38:31:

Oh en dan had ik nog een vraagje, omdat ik nu een stap dieper ga met de url, hoe krijg ik het dan voor elkaar om de styling weer terug te krijgen? Nu gebruik ik
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
<?php require_once '/inc/header.php'; ?>
Maar dat komt te vervallen omdat er iets extra's in de url erbij zit.
 
- Ariën  -
Beheerder

- Ariën -

07/02/2018 21:34:37
Quote Anchor link
Dan lijkt het mij verstandiger om het volledige pad te gebruiken:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
<?php
$base
= dirname(__FILE__);
include_once($base . '\inc\header.php');
?>


En niet $_SERVER['DOCUMENT_ROOT'], want CLI-script (crons bijv.) gaan dan moeilijk doen, omdat ze het pad niet kunnen vinden vanaf waar ze uitgevoerd worden.

Voor voor zover ik zie zal het eigenlijk geen probleem zijn als de URl wordt aangepast. Er wordt met include ook gekeken naar de fysieke plek op de server waar je script in draait, en die verandert nooit.
Gewijzigd op 07/02/2018 21:36:32 door - Ariën -
 
Rob Doemaarwat

Rob Doemaarwat

07/02/2018 22:06:13
Quote Anchor link
Jorn Reed op 07/02/2018 19:31:10:
Bedoelde je met het eruit prummelen van het id bijvoorbeeld door op de product pagina dit te doen?

In mijn voorbeeld geef ik de params uit de URL (de match uit preg_match) mee in de constructor of callable voor de afhandeling van de pagina. Dus bijvoorbeeld:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
$router->add('/product/(\d+)',function($match,$id){
  //$match = complete URL match
  //$id = ID uit de URL
});

//of:
class Pagina{
  public function __construct($params){
    //ID = $params[1]
  }
}

Let trouwens op met je letterlijke $id in de query. Nu dwingt de router min of meer af dat het om een numeriek waarden gaat (\d+ in de reg-ex), maar als je dit een keer vergeet, of ook een andere manier van aanroepen mogelijk maakt, dan kun je hiermee de deur wagenwijd open zetten voor SQL injectie.
Gewijzigd op 07/02/2018 22:06:51 door Rob Doemaarwat
 
Thomas van den Heuvel

Thomas van den Heuvel

07/02/2018 22:50:19
Quote Anchor link
Waarvoor heb je ?uri=$1 uberhaupt nodig in je .htaccess? De oorspronkelijke REQUEST_URI zit in $_SERVER['REQUEST_URI']. Dit is wel de encoded URL, dus gooi hier nog even urldecode() overheen, daarna kun je het applicatiepad en eventuele querystring parameters eenvoudig splitsen met behulp van parse_url().

Met zaken als ?uri=$1 vervuil je in wezen de namespace van $_GET, die eigenlijk volledig transparant zou moeten zijn (what you see is what you have/get).
 
Rob Doemaarwat

Rob Doemaarwat

07/02/2018 23:09:19
Quote Anchor link
Ho, wacht, voordat we dat hele verhaal weer over gaan doen ... zie dit draadje: https://www.phphulp.nl/php/forum/topic/url-rewrite/101732/last/
 
Thomas van den Heuvel

Thomas van den Heuvel

08/02/2018 00:23:21
Quote Anchor link
"Ik blijf het toch herhalen, don't repeat yourself."

:p
 



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.