Drukowana wersja tematu

Kliknij tu, aby zobaczyć temat w orginalnym formacie

Forum PHP.pl _ Pro _ Routery i sposoby routingu

Napisany przez: kwiateusz 21.07.2008, 09:31:30

Zgłoszono zapotrzebowanie na taki temat więc tworzę smile.gif

Myśl przewodnia: "W jaki sposób tworzyć routery na potrzeby naszych aplikacji, jakie są wydajne a jakie nie. Jakich używać a jakich nie używać."

Napisany przez: eai 21.07.2008, 10:04:26

Przeglądając framework Zenda, system Rutowania oparty jest o jeden Router_Rewrite zawierający tablicę obiektów tras, na których poźniej wykonywana jest pętla foreach i każdemu z osobna jest wywoływana metoda $route->match($pathInfo).

Pomysł bardzo wygodny, bo możemy definiować sobie tyle tras różnego rodzaju ile chcemy, implementując oczywiście odpowiedni interfejs. Minusem jest jednak wydajność takiego rozwiązania.

Myśle że lepszym pomysłem byłoby rozdzielić Router_Rewrite na routery:
a) Rewrite (domyślny ostatni w kolejności do sprawdzania router)
- brak magazynowania tras, bo nie byłoby takiej potrzeby ponieważ router wyciąga parametry z tego co dostaje (/onas/kontakt)
B) Static (statyczny router):
- magazynuje tylko statyczne trasy i jej parametry
- brak dynamicznych parametrów
- przechowuje trasy w tablicy a indeksem tablicy jest szablon trasy (np. o-nas,kontakt.html)
- błyskawiczne znajdowanie trasy
c) Static Rewrite (statyczno-dynamiczny router):
- magazynuje tylko statyczne trasy i jej parametry
- możliwość dynamicznych parametrów (id/3)
- przechowuje trasy w tablicy a indeksem tablicy jest szablon(np. /pl/wydarzenia-o-biznesie) trasy (np. /pl/wydarzenia-o-biznesie/id/5 gdzie /pl/wydarzenia-o-biznesie to trasa statyczna a id/5 to dodatkowy parametr dynamiczny)
- dość szybkie znajdowanie tras

d) Regex (rutowanie za pomocą wyrażeń regularnych)
- magazynuje wszelkiego rodzaju trasy i ich parametry
- przechowuje trasy w tablicy a indeksem jest pattern wyrażenia regularnego
- niestety mało wydajne wyszukiwanie - konieczność użycia wyrażeń regularnych

W pierwszej kolejności nasz framework zacząłby od sprawdzania tras statycznych, następnie od statyczno - dynamicznych, potem wyrażenia regularne i na koniec rewrite który wyciąga z urla parametry jeśli nie znaleziono nic w powyższych routerach.

Po testach szybkości:
3 krotnie szybciej routing się wykonuje jeśli mamy 3 routery a trasy i parametry przechowywujemy na tablicach, niż używając jednego routera i przechowywując instancje obiektów tras w jednej tablicy.

Routing oparty o instancje obiektów tras:
Dodanie tras statycznych:0.00089502334594727
Dodanie tras regex:0.0009760856628418
Szukanie tras:0.00070309638977051
Czas całkowity:0.0026209354400635

Routing oparty o 3 routery i tablicę do przechowywania tras i parametrów:
Dodanie tras statycznych:0.00034093856811523
Dodanie tras regex:0.00036096572875977
Szukanie tras:0.00030183792114258
Czas całkowity:0.0010831356048584

Napisany przez: Cysiaczek 21.07.2008, 15:46:24

Mam prośbę: czy mógłbyś wykonać miarodajne testy np. w pętli? Bo obecne mogą być wynikiem działania winampa, albo chwilowego wzrostu obciążenie procka, czy zatkania się danych na FSB ;]

Pozdrawiam.

Napisany przez: eai 21.07.2008, 15:49:12

To były testy w pętli for i <= 100, robiłem testy dla 1000 i dla 1 000 000, dla 1 000 000 tablice ukończyły test w 16 sekund, a instancje obiektow w tablicy nie ukonczyly testu z powodu braku pamięci.

Napisany przez: Sedziwoj 23.07.2008, 09:58:10

Cytat(eai @ 21.07.2008, 16:49:12 ) *
To były testy w pętli for i <= 100, robiłem testy dla 1000 i dla 1 000 000, dla 1 000 000 tablice ukończyły test w 16 sekund, a instancje obiektów w tablicy nie ukończyły testu z powodu braku pamięci.


To zadbaj o ten wyciek pamięci, bo każdy obrót pętli powinien być na "świeżo", ponieważ w normalnym użytkowaniu Router jest tylko raz wywoływany.
Ja bym szybciej rozdzielił rodzaje tras, może nawet na takie co wymieniłaś, i odpowiednio je kolejkował.

Mnie jeszcze zastanawia wielopoziomowe przekierowanie, czyli wykorzystanie tylko części informacji, a dalsze dobieranie akcji już odbywa się na poziomi niżej. (nie jestem pewien ale coś jak urls w Django)

Napisany przez: SHiP 31.07.2008, 10:41:15

Ja swój router oparłem o dynamiczne dobieranie typów. Pierwsze 2 parametry adresu tj. /klasa/metoda to nic innego jak odwolania do konkretnego controllera i jego metody. Pozostałe dane z adresu dobiera juz sobie indywidualnie controller podajac tablice typow np.

Kod
id => integer
title => string
mydate => date


Dzieki temu aplikacja moze dzialac w kilku różnych trybach np.

/klasa/metoda/1/tytul/2008-07-31
/klasa/metoda/2008-07-31/tytul/1
/klasa/metoda/2008-07-31/1/tytul
itd.

Oczywiście mam tez system linków, dzięki którym router sam dobiera wszystkie lub tylko część parametrów adresu.
Tak więc u mnie jest to uklad statyczno-dynamiczny(linki) a nastepnie dynamiczny.

Przemielanie ogromnej tablicy w poszukiwaniu odpowiedniego wzorca adresu to imho pomyłka. Tak routera tworzyc się nie powinno. Zmniejsza to elastyczność(tworząc controller trzeba edytować konfiguracje routera) i działa duużo wolniej.

Tworzenie routera obslugującego tylko skladnię opartą o rewrite również jest dziwne(żechyba robi się aplikacje na własny użytek) Dobrze jest zrobić 2 tryby adres.pl/zmienne oraz adres.pl/index.php?/zmienne

Napisany przez: Sedziwoj 31.07.2008, 14:06:06

@SHiP
Wiesz, można mieć tak że jest obiekt który tłumaczy dane wejściowe (czy to z URL, konsola czy jakkolwiek) i dopiero potem przekazuję te informacje obiektowi który uruchamia odpowiednią akcję. Takie rozdzielenie pozwala zmieniać, skąd dane pochodzą, niezależnie od tego jak są wykorzystywane przy uruchamianiu odpowiednich akcji.

Napisany przez: splatch 9.09.2008, 08:02:23

Bardzo dobry routing posiada Agavi, http://agavi.org/docs/tutorial/topics/basics-routing.html i http://www.agavi.org/docs/latest/manuals/manual/ch03s06.html#id930531.

Napisany przez: jarek_bolo 9.09.2008, 15:26:44

W Kohanie integrują Routing oparty na tym: http://dev.horde.org/routes/
Całkiem rozbudowany system routingu.

Napisany przez: bigZbig 21.10.2008, 18:33:01

Cytat(SHiP @ 31.07.2008, 11:41:15 ) *
...Dzieki temu aplikacja moze dzialac w kilku różnych trybach np.

/klasa/metoda/1/tytul/2008-07-31
/klasa/metoda/2008-07-31/tytul/1
/klasa/metoda/2008-07-31/1/tytul
itd.


Jeśli cię dobrze zrozumiałem do jednego zasobu możesz dzięki temu stworzyć 3 różne linki, które pokarzą to samo. Osobiście proponowałbym zaimplementowanie mechanizmu, który do danego zasobu umożliwi stworzenie tylko jednego linka. Każda zmiana w linku powinna prowadzić do innego zasobu albo też zwracać kod 404. Jest to niezwykle istotne z punktu widzenia pozycjonowania stron.

Napisany przez: Sedziwoj 21.10.2008, 22:51:19

@bigZbig
Nie chodzi o to aby wykorzystywać wiele różnych linków w aplikacji, ale aby router umożliwiał użycie takich jakie mamy ochotę i zmianę, jeśli jest taka potrzeba.
Po zmianie stare linki też powinny funkcjonować, więc nie powinno się ich po prostu pozbywać.

Napisany przez: Sh4dow 22.10.2008, 10:04:23

Nie wiem ale czasami takiego sposobu rozwiązywania routerów nie pojmuje. Może jest to pro ale jak dla mnie czasami mało elastyczne. Nie chce się spierać czy robię lepiej czy nie, po prostu przedstawię to co ja kiedyś zrobiłem i używam do dziś.
Każdy URL jaki przychodzi do aplikacji leci przez 'index.php', nie wliczając URL'i które są fizycznie istniejącymi plikami lub katalogami. układ URL'a to '/nazwaKlasyAkcji/dowolna/ilosc/parametrow'.
Każda klasa akcji ma swój własny (ja sobie to tak nazywam) miniRouter który na podstawie dowolnych parametrów wywołuje określoną metodę, która wykonuje resztę działań.
Wady to to ze miniRouter należało by definiować dla każdej klasy. Zalety to możliwość tworzenia miniRouterów dla kazdej z klasy i nie działanie na sztywnych schematach co czasami nie jest wygodne.
Pewnie to kiedyś sie rozwinie i może dojdą jakieś gotowe schematy ale na chwile obecna działa i to mnie cieszy smile.gif

Napisany przez: bigZbig 24.10.2008, 14:46:52

Cytat(Sedziwoj @ 21.10.2008, 23:51:19 ) *
Po zmianie stare linki też powinny funkcjonować, więc nie powinno się ich po prostu pozbywać.

Po zmianie stary link powinien zwracać nagłówek 301 Moved Permanently (Zasób trwale przeniesiony) i wskazać nowy link do zasobu

Napisany przez: Sedziwoj 27.10.2008, 10:38:42

Cytat(bigZbig @ 24.10.2008, 15:46:52 ) *
Po zmianie stary link powinien zwracać nagłówek 301 Moved Permanently (Zasób trwale przeniesiony) i wskazać nowy link do zasobu


Czyli mówisz to co ja, powinien nadal działać.

Napisany przez: Moli 29.12.2008, 21:39:15

W temacie ostatnio nikt nie pisze, więc opiszę w jaki sposób ja robie router smile.gif U mnie wszystko opiera się na wyrażeniach regularnych, jeśli jest to domena główna to pobiera tablicę z pliku:

Cytat
config/router.php

jeśli subdomena (np. xxx.nazwa.pl) to:
Cytat
config/router/xxx.php

jeśli adres to nazwa.pl/admin/.... to:
Cytat
config/router/admin.php


Tablica z routerem wygląda tak:

  1. <?php
  2.  
  3. $router = http://www.php.net/array(
  4. 'wyrazenie' => http://www.php.net/array(kontroler,metoda,[opcjonalna tablica ze stalymi elementami]),
  5. );
  6.  
  7. ?>


jeśli w adresie mamy jakieś parametry zmienne pobrane z wyrażenia regularnego to przekazujemy je do danej metody w danym kontrolerze, elementy stale sa tak samo przekazywane. Elementy stałe wydają się mało potrzebne, ale chodzi o to że mam np. metodę wyświetlająca jakieś wiadomości i chce zrobić na tym samym kontrolerze wyświetlanie elementow z archiwum, wtedy daje jako parametr stały
  1. <?php
  2. http://www.php.net/array('isdelete'=>true),
  3. ?>

i w metodzie jednym ifem zmieniam pobieranie danych z modelu czy przekazanie do dbgrida smile.gif

Jest to jedno z fajniejszych (jak dla mnie) rozwiązań, zawszę mogę zmienić format danego adresu bez większych zmian w kontrolerach czy metodach smile.gif

Napisany przez: wlamywacz 30.12.2008, 15:01:24

No i może ja swój router opiszę. Zasada jego działania opiera się na zwykłym http://pl.php.net/explode. Czyli:

Cytat
www.domena.pl/admin-users/create/
lub
Cytat
www.domena.pl/admin/index/

Pierwsza zmienna (admin-users lub admin) to nazwa obiektu kontrolera który ma zostać utworzony. Jeśli jest to tablica, następuje znowu użycie funkcji http://pl.php.net/explode i wychodzi na to że nasz kontroler znajduje się aplication/controllers/adminControllers/usersController.php. Chodziło dokładnie mi o to że mogę tworzyć coś w rodzaju modułów do poszczególnych kontrolerów. Następna zmienna (create lub index) to nazwa funkcji którą ma zostać wywoła, w przypadku jej braku odpala defaultFunction. Następne zmienne są przekazywane jako parametry odpowiednią funkcją i to na tyle smile.gif

Napisany przez: bim2 13.01.2009, 23:25:59

To może ja zapodam już gotowy Router. Pewnie jest do zrobienia parę zmian/dodatków ale swoją podstawową funkcję spełnia. smile.gif

http://hernas.pl/download/entry-b9e3dca913dcf02b1965f0719b6ae3a2.htm

Pewnie niektórzy mają sprawniejsze pomysły, ale na razie udało mi się napisać coś takiego. smile.gif

Napisany przez: bigZbig 13.02.2009, 15:09:52

@bim2 Widze, że coś podobnego do rootera w Zend Frameworku wykombinowałeś. Ja próbuję zrobić rooter który działa na podobnej zasadzie co Twój tylko, żeby nie trzeba było nazw zmiennych w url-u podawać. Poszczególne zmienne są rozpoznawane na podstawie ich położenia w ścieżce.

Napisany przez: bim2 13.02.2009, 15:53:11

W sumie można dopisać do tych confingu które położenie jak nazywa się zmienna smile.gif Albo w skrypcie podawać po kolejnosci zmiennej, tylko to tyle roboty co dodanie kolejnych sposobów routingu smile.gif

Napisany przez: Helid 10.02.2010, 19:01:33

Najpraktyczniejszym rozwiązaniem jest już powyżej wspominane "sprawdzanie etapowe":
- strony statyczne (/rejestracja.html na moduł register, akcje home)
- wyrażenie regularne (/([a-z]).html na page, akcje view z parametrem $1)
- wszystkie pozostałe na moduł home, 404 itd
Wszystko pięknie działa, a możliwości konfiguracji praktycznie nieograniczone

Napisany przez: tomahawk 1.08.2010, 20:36:41

Pozwolę sobie odkopać temat.

Jak proponujecie przekazywać parametry z url do metody akcji?
Jako tablicę np. $c->akcja($params); Czy może $c->akcja($id,$page); ?

I zależnie od wybranego sposobu jak proponujecie rozwiązać problem error 404?
Bo jeżeli mam url: /c/akcja/id/page to jest ok. Natomiast jeżeli ktoś dopisze /c/akcja/id/page/costam/costam2/ to w jaki sposób rozpoznać że strona nie istnieje? Bo można by w każdej metodzie-akcji zdefiniować ile powinna mieć parametrów i liczyć ile jest parametrów wejściowych... No ale to trzeba by było to samo w każdej akcji klepać.

Acha... mówię o przypadku routingu niestatycznego kiedy nie mamy w tablicy zapisanych tras. Bo jak ma się trasy to wystarczy sprawdzić czy któraś pasuje i jak żadna nie pasuje to 404.

Napisany przez: NuLL 2.08.2010, 20:29:54

1. W mojej opinii dane z routingu powinno sie wykorzystywac jaka dane z GET i tam je wrzucic - ew ubrac w obiekt.

2. Tak czy tak w jakis sposob musisz zapisac liczbe potrzebnych parametrow do akcji, ew parsowac w .htaccess i ustawic customowy 404.

Napisany przez: Ormin 2.09.2010, 17:59:38

A teraz coś innego , hasło Router, ale nie chodzi o URLe.

Mam aplikację. Dużą aplikację , która pobiera z wielu serwerów wiele plików. Zastanawiam się, jak sensownie rozdzielić obciążenie, tak, aby dać maksymalną elastyczność ( środowisko pracy jest bardzo niestabilne, dajmy na to chociazby że tylko niektóre dedykowane moga pobierac z okreslonych serwerów ) a jednocześnie w miare robic to normalnie. No to pomyslalem o ,,Routerze", choc chyba odpowiednia nazwa bardziej bylby Load Balancer, ktory pod kazdy serwis mialby tablice IP serwerów pod ktore sie laczy zasadą Round-Robin.

Dobry pomysl? Zly pomysl? Argumentujcie, propozycje mile widziane ;D


Napisany przez: bmL 15.10.2010, 00:18:58

Cytat(tomahawk @ 1.08.2010, 21:36:41 ) *
Pozwolę sobie odkopać temat.

Jak proponujecie przekazywać parametry z url do metody akcji?
Jako tablicę np. $c->akcja($params); Czy może $c->akcja($id,$page); ?

I zależnie od wybranego sposobu jak proponujecie rozwiązać problem error 404?
Bo jeżeli mam url: /c/akcja/id/page to jest ok. Natomiast jeżeli ktoś dopisze /c/akcja/id/page/costam/costam2/ to w jaki sposób rozpoznać że strona nie istnieje? Bo można by w każdej metodzie-akcji zdefiniować ile powinna mieć parametrów i liczyć ile jest parametrów wejściowych... No ale to trzeba by było to samo w każdej akcji klepać.

Acha... mówię o przypadku routingu niestatycznego kiedy nie mamy w tablicy zapisanych tras. Bo jak ma się trasy to wystarczy sprawdzić czy któraś pasuje i jak żadna nie pasuje to 404.

Ja osobiście chwalę sobie przekazywanie jako parametry metody czyli:
articles/view/What_about_routing/5/2
wywołało by $articlesController -> view('What_about_routing', 5, 2);

Oczywiście warto też zrobić tak żeby example.com/articles wywołało np. $articlesController -> main(); smile.gif
  1. private function view($articleName, $articleCategory, $articleID)
  2. {
  3. ...
  4. }

dzięki temu metoda kontrolera staje się czytelna a parametry tejże metody dodatkowo można zgrabnie opisać phpdoc jako @param. Jedyny minus to to że articleName (które dopisaliśmy do adresu tylko po to żeby google ładnie indeksowało) jest nie potrzebnie przekazywane. Ale po 1. jest to mały minus a po 2. nie zawsze wrzucamy do adresu takie rzeczy.

Co do rozpoznawanie czy strona nie istnieje, to czy na prawdę musimy to robić? Kto przy zdrowych zmysłach będzie tam próbował coś dopisać? smile.gif Tylko ewentualnie jakieś próby hackingu. Wychodzę z założenia że jeżeli zdarzenie nie jest czymś co normalny użytkownik robi to nie ma co się trudzić z jego obsłużeniem. Na upartego trzeba by było do każdej metody dopisywać coś w stylu:
  1. if(http://www.php.net/func_num_args() > X) // strona nie istnieje

Napisany przez: hind 15.10.2010, 09:15:35

ja u siebie mam rozwiązane to w ten sposób że do klasy kontrolera przekazywany jest obiekt z parametrami
i potem w kontrolerze już tylko

  1. public function indexPage() {
  2. http://www.php.net/echo $this->router->args[0] ;
  3. }

gdzie $this->router jest ustawiany w konstruktorze (jedyny argument do konstruktora), a następnie dane w metodzie exec() są przetwarzane

a w tedy tak jak u @bmL dla celów SEO dalsze argumenty są ignorowane (/art/43/jakis-malo-istotny-tekst")

Napisany przez: Crozin 15.10.2010, 14:28:21

Cytat
Jedyny minus to to że articleName (które dopisaliśmy do adresu tylko po to żeby google ładnie indeksowało) jest nie potrzebnie przekazywane.
Nie patrzyłem jak to dokładnie zaimplementowało, ale Symfony2 przekazuje parametry wywołania do metod właśnie w taki sposób jak pokazałeś i jest w stanie pominąć zbędne parametry oraz zignorować ich kolejność:
  1. /article/:category/:id/:slug
  2.  
  3. public function viewAction($id);
  4. public function viewAction($id, $category);
  5. public function viewAction($category, $id);
  6. public function viewAction($category, $id, $slug);
  7.  
  8. // Wszysystkie powyższe są poprawne

Napisany przez: bmL 15.10.2010, 15:05:59

Będę musiał sprawdzić jak to jest dokładnie zrobione, bo wygląda co najmniej interesująco smile.gif
Tak na szybkiego spojrzałem i widzę że wymaga to także definiowania kolejnego pliku z regułami routera więc jednak trzeba niestety więc kodu nastukaćtongue.gif Ale rozwiązanie jest interesujące smile.gif

Napisany przez: mrWodoo 2.05.2013, 14:47:24

Mój router
http://wklej.to/UbZca
Klucz zabezp. - 123

Dobrze to zrobiłem? Jeszcze nie skończone, bo brakuje wymagań dla zmiennych tj. tylko liczby, tekst, bez myslnikow itd. itd.

Glownie prosze o analize metody route, bo calkiem rozbudowana mi sie wydaje, moze namieszalem? Może okaże się, że dodam kilka ścieżek i samo przeprasowanie tego przez router zajmie 70% czasu wykonywania sie skryptu?

Napisany przez: em1X 19.10.2013, 00:08:43

Cytat(mrWodoo @ 2.05.2013, 15:47:24 ) *
Mój router
http://wklej.to/UbZca
Klucz zabezp. - 123

Dobrze to zrobiłem? Jeszcze nie skończone, bo brakuje wymagań dla zmiennych tj. tylko liczby, tekst, bez myslnikow itd. itd.

Glownie prosze o analize metody route, bo calkiem rozbudowana mi sie wydaje, moze namieszalem? Może okaże się, że dodam kilka ścieżek i samo przeprasowanie tego przez router zajmie 70% czasu wykonywania sie skryptu?


Niemożliwe do przetestowania testami jednostkowymi
Nie przestrzegasz standardów kodowania
Tworzysz "hard dependencies"

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)