PHP paraméterátadás url-ben és szép url-ek htaccess-el

Ismét egy gyakori kérdést dolgoztam fel, amit amatőr php programozók tesznek fel rendszeresen. Szokásomhoz híven igyekszem ismét a már működő de rossz megoldástól a minél szebb megoldás felé haladni. A Többféle megoldás bemutatásának célja, hogy az olvasó ne csak egy kész megoldást kapjon, hanem lássa is az alternatívákat. Hogy ő maga választhasson.


Feltetted már valaha, vagy fel akartad tenni a következő kérdést valakinek?

Hogy lehet olyant csinálni, ami itt ezen az oldalon is van, hogy ilyen kérdőjelek, meg & jelek vannak az url-ben, és úgy változik az oldal tartalma?

Talán nem pont így fogalmazódott meg benned a kérdés, de ha valaha is ott motoszkált a fejedben hasonló, és még nem kaptál rá választ, vagy már kaptál választ, de érdekel többféle módszer, akkor olvasd tovább ezt a bejegyzést és megkapod a választ.

Ezen kívül kitérek még a szép url-ek kérdésére is. Amikor már nem látszik az url-ben sok & jel,
hanem olyan, mintha mindig más mappát nyitna meg. Valójában pedig csak az apache szervernek mondjuk meg, hogy irányítson át egy másik url-re, ami már a fent említett megoldást használja, vagy php-ból szimuláljuk.

Valamit már az elején tisztáznunk kell. Egy URL több részből áll, de beszéljünk csak kettőről. Az egyik az a fájl útvonala a szerveren. A másik pedig változók és értékek sorozata. Ezt a kettőt pedig kérdőjel választja el egymástól.

utvonal/fajlneve.php?valtozo1=ertek1&valtozo2=ertek2

Egy-egy változó-érték párt & jelek választanak el. Az egyenlőség jeltől balra a változó neve, jobbra pedig az értéke található, pont ahogy azt egyébként is megszokhattuk már PHP-ben is. Már csak azt kellene tudni, hogy ezt egy PHP programban hogyan kell elérni. Erre a $_GET nevű szuperglobális tömb való. Ami attól szuperglobális, hogy bárhol elérhető. Akár egy függvényen belül is. A fenti példából így az "ertek1" -et a következő módon lehetne kiírni.

print $_GET['valtozo1'];

Aki ismeri az elágazások fogalmát, az ezzel már meg is tudja oldani a változó tartalmat. Azért mutatok néhány példát is, ahol az "oldal" és "aloldal" nevű változókat fogom használni. Az első az a bizonyos működő, de nem szép megoldás.

  1. <?php
  2. if ($_GET['oldal'] == 'elso')
  3. {
  4.         print "Ez az első oldal";
  5. }
  6. else if($_GET['oldal'] == 'masodik')
  7. {
  8.         print "Ez a második oldal";
  9. }
  10. else if ($_GET['oldal'] == 'harmadik')
  11. {
  12.         print "Ez a harmadik oldal";
  13. }
  14. else
  15. {
  16.         print "Főoldal";
  17. }
  18. ?>

Most aki megpróbálja lefuttatni és nem adja meg az oldal nevű változót az url-ben, annak vagy hiba nélkül lefut, vagy nem. Ez szerver beállítás kérdése. Tehát most, és a jövőben is javaslom az error_reporting függvény használatát az E_ALL paraméterrel (figyelem! Ez egy konstans! Csak azért nem kell idézőjelbe tenni), amivel az ilyen hibák megjeleníthetők. Ezt a programkódok elé kell írni természetesen.

  1. <?php
  2. if ($_GET['oldal'] == 'elso')
  3. {
  4.         print "Ez az első oldal";
  5. }
  6. else if($_GET['oldal'] == 'masodik')
  7. {
  8.         print "Ez a második oldal";
  9. }
  10. else if ($_GET['oldal'] == 'harmadik')
  11. {
  12.         print "Ez a harmadik oldal";
  13. }
  14. else
  15. {
  16.         print "Főoldal";
  17. }
  18. ?>

Erre egy hasonló hibaüzenet fog megjelenni

Notice: Undefined index: oldal in /var/www/phpparams/index.php on line 3

Az ilyeneket pedig nagyon nem szeretjük, tehát meg kell vizsgálni, hogy létezik-e az oldal nevű változó. Erre pedig az isset() -et használhatjuk. Erre is a csúnya módszer:

  1. <?php
  2. if (isset($_GET['oldal']) and $_GET['oldal'] == 'elso')
  3. {
  4.         print "Ez az első oldal";
  5. }
  6. else if(isset($_GET['oldal']) and $_GET['oldal'] == 'masodik')
  7. {
  8.         print "Ez a második oldal";
  9. }
  10. else if (isset($_GET['oldal']) and $_GET['oldal'] == 'harmadik')
  11. {
  12.         print "Ez a harmadik oldal";
  13. }
  14. else
  15. {
  16.         print "Főoldal";
  17. }
  18. ?>

A hibaüzenet eltűnik, mert minden feltételben először ellenőrizve lett a változó létezése is. Itt fontos, hogy a létezés vizsgálata legyen előbb. Másképp a hibaüzenet mindenképp megjelenne. Mégis egy rövidebb megoldást javaslok, ahol a ternáris operátor (ternary operator) siet segítségünkre.

  1. <?php
  2. $oldal = isset($_GET['oldal']) ? $_GET['oldal'] : '';
  3. if ($oldal == 'elso')
  4. {
  5.         print "Ez az első oldal";
  6. }
  7. else if($oldal == 'masodik')
  8. {
  9.         print "Ez a második oldal";
  10. }
  11. else if ($oldal == 'harmadik')
  12. {
  13.         print "Ez a harmadik oldal";
  14. }
  15. else
  16. {
  17.         print "Főoldal";
  18. }
  19. ?>

Az $oldal változó a $_GET['oldal'] értékét kapja, ha létezik. Egyébként üreset. De innentől az $oldal biztos, hogy létezik. If helyett lehetne switch elágazással is megoldani.

  1. <?php
  2. $oldal = isset($_GET['oldal']) ? $_GET['oldal'] : '';
  3. switch ($oldal)
  4. {
  5.         case 'elso':
  6.                 print "Ez az első oldal";
  7.                 break;
  8.         case 'masodik':
  9.                 print "Ez a második oldal";
  10.                 break;
  11.         case 'harmadik':
  12.                 print "Ez a harmadik oldal";
  13.                 break;
  14.         default:
  15.                 print "Főoldal";
  16.                 break;
  17. }
  18. ?>

A break-re szükség van, hogy csak az a kód fusson le, amelyik case utáni érték megfelel az $oldal változónak. A default pedig az if-ekbeni else ág alternatívája.

Ezzel már lehetne is kezdeni valamit. De ha már sokkal több kódot kéne írni egy feltételnek megfelelve, akkor ez így nem praktikus. Helyette az include használata javasolt. Ezzel lehet fájlokat importálni. És így külön fájlokba csoportosíthatók a forráskódok. Minden oldal forráskódja egy-egy fájlban lehet.
Létezik a párja, a require. Ez, ha nem található a fájl, akkor megszakítja a program futását. Mindkettőnek van egy olyan verziója, ami _once -ra végződik. (require_once, include_once) Ezekkel pedig, ha már egyszer be lett hívva az adott fájl, többször nem próbálja meg.

  1. <?php
  2. $oldal = isset($_GET['oldal']) ? $_GET['oldal'] : '';
  3. switch ($oldal)
  4. {
  5.         case 'elso':
  6.                 include 'oldalak/elso.php';
  7.                 break;
  8.         case 'masodik':
  9.                 include 'oldalak/masodik.php';
  10.                 break;
  11.         case 'harmadik':
  12.                 include 'oldalak/harmadik.php';
  13.                 break;
  14.         default:
  15.                 include "oldalak/fooldal.php";
  16.                 break;
  17. }
  18. ?>

A példában az oldalak forráskódjai az "oldalak" nevű mappában levő fájlokban vannak. Észrevehető, hogy a fájloknak azonos nevet adtam, mint az átadott oldal változó értékei. Így még ennél is rövidebb megoldás lehet, ha egyszerűen a változó értékét behelyettesítjük az include-ba. Itt viszont arra kell figyelni, hogy beírható akár a ../index vagy a mappa/fajl is. Akkor pedig bármilyen fájlt meg lehet nyitni és így lefuttatni, ami nem szerencsés. Ebben segít a basename() függvény, ami az utolsó / utáni részt vágja le és adja vissza. Illetve a file_exists, amivel egy fájl létezése vizsgálható. Ha nem létezik, akkor a főoldalt jelenítjük meg.

  1. <?php
  2. $oldal = isset($_GET['oldal']) ? $_GET['oldal'] : '';
  3. $oldal = 'oldalak/'.basename($oldal).'.php';
  4. if (file_exists($oldal))
  5. {
  6.         include $oldal;
  7. }
  8. else
  9. {
  10.         include 'oldalak/fooldal.php';
  11. }
  12. ?>

Aki figyelt, észrevehette, hogy még ezt is lehet rövidíteni a már ismert ternáris operátorral.

  1. <?php
  2. $oldal = isset($_GET['oldal']) ? $_GET['oldal'] : '';
  3. $oldal = 'oldalak/'.basename($oldal).'.php';
  4. include (file_exists($oldal)) ? $oldal : 'oldalak/fooldal.php';
  5. ?>

Azért ahhoz képest, hogy milyen hosszú volt az első kód, egész rövid lett és sokkal többet is tud. Ideje tehát újabb szintre lépni. Eddig is szempont volt a szép kód. De ha már szempont, akkor azt is jó megtanulni, hogy célszerű a php kódokat a fájlok elejére gyűjteni. És a html kód belsejében főleg csak kiírásokat alkalmazni, esetleg includeokat. Tehát az include helyett előbb még csak változóba kellene tenni a fájlnevet, és egy html dokumentum-ban includeolni.

  1. <?php
  2. $oldal = isset($_GET['oldal']) ? $_GET['oldal'] : '';
  3. $oldal = 'oldalak/'.basename($oldal).'.php';
  4. $oldal = (file_exists($oldal)) ? $oldal : 'oldalak/fooldal.php';
  5. ?>
  6. <!DOCTYPE html>
  7. <html>
  8.     <head>
  9.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  10.         <title>PHP paraméterek</title>
  11.     </head>
  12.     <body>
  13.                 <ul>
  14.                         <li><a href="index.php">Főoldal</a></li>
  15.                         <li><a href="index.php?oldal=elso">Első</a>
  16.                                 <ul>
  17.                                         <li><a href="index.php?oldal=elso&amp;aloldal=koszon">Köszön</a></li>
  18.                                 </ul>
  19.                         </li>
  20.                         <li><a href="index.php?oldal=masodik">Második</a></li>
  21.                         <li><a href="index.php?oldal=harmadik">Harmadik</a></li>
  22.                 </ul>
  23.                 <?php include $oldal ?>
  24.         </body>
  25. </html>

Ezzel egy menüt is gyártottam, hogy könnyebb legyen a tesztelés. Megfigyelhető, hogy az & helyén én &amp; -ot írtam. Ez azért van, mert bár az url-ben & ként kell szerepelnie, html-ben vannak bizonyos karakterkódok, amik pont & -el kezdődnek. Maga az & jel is éppen ilyen, aminek a kódja az &amp;. Egyébként is működhetne, de adott esetben akár hibát is okozhatna. Minden esetre a html szintaktika hibás lenne tőle akkor is, ha jól jelenik meg az eredmény.

Az html-ből látszik, hogy van az elso.php-ben egy aloldal is. Annak forráskódja ugyanúgy épülhet fel, mint a főoldal kiválasztása. Csupán más változónevet kell használni. És például ha van egy oldalak/elso.php, akkor annak aloldalai az oldalak/elso/ mappába kerülnek. És ebből a mappából lesznek includeolva. Ott már persze nem kell a komplett html "körítés", mert egyszer már megvolt. Tehát így nézne ki az elso.php, ha további fájlokra osztott aloldalakat használna:

  1. <?php
  2. $aloldal = isset($_GET['aloldal']) ? $_GET['aloldal'] : '';
  3. $aloldal = 'oldalak/elso/'.basename($aloldal).'.php';
  4. $aloldal = (file_exists($aloldal)) ? $aloldal : 'oldalak/elso/fooldal.php';
  5. include $aloldal;
  6. ?>

De jelenleg a példámban én csak a következő egyszerű forrást használtam, mivel csak egy rövid szöveg kiírásának eldöntéséről volt szó:

  1. <?php
  2. $aloldal = isset($_GET['aloldal']) ? $_GET['aloldal'] : '';
  3. if ($aloldal == 'koszon')
  4. {
  5.         print 'Szia';
  6. }
  7. else
  8. {
  9.         print 'Nem köszönök';
  10. }
  11. ?>

Bármelyik megoldást is választjuk, jobb mindenképp más változóneveket használni, mint a főoldalban. Mert jelen felépítés mellett felülírhatja az aloldal változója a főoldalét, és ezt nem biztos, hogy szeretnénk.

Ha már szépség, ne aprózzuk el! Apache szervernél használhatók .htaccess fájlok, amennyiben a szerveren engedélyezve van a használata. Ez nem kiterjesztés, hanem a fájl neve. Ponttal kezdődik, ami linux rendszereken a rejtett fájlok jellemzője. Ebbe lehet olyanokat írni, amit az apache szerver értelmezni tud.

Htaccess engedélyezése:
Általában egy httpd.conf nevű fájlt kell keresni a szerver mappájában. Ez nem a webgyökér könyvtárat jelenti, hanem ahova a szerver program lett feltelepítve! Nálam viszont ubuntu rendszeren ez a /etc/apache2/sites-available/default nevű, kiterjesztés nélküli fájl. amiben a következőhöz hasonlót kell keresni:

  1. <Directory /var/www>
  2.         Options Indexes FollowSymLinks MultiViews
  3.         AllowOverride none
  4.         Order allow,deny
  5.         allow from all
  6. </Directory>

Az AllowOverride direktíva felel a htaccess-ek engedélyezéséért. Ha none van megadva, akkor tiltja. Át kell írni All -ra, és akkor a Directory tag-ben megadott mappában engedélyezi őket.

  1. <Directory /var/www>
  2.         Options Indexes FollowSymLinks MultiViews
  3.         AllowOverride All
  4.         Order allow,deny
  5.         allow from all
  6. </Directory>

Persze, hogy ne legyen egyszerű az életünk, magát a mod_rewrite modult is engedélyezni kell, ha még nem volt. Ugyanis erre szükség lesz. Ehhez ubuntun a /etc/apache2/mods-enabled mappába kell tenni egy rewrite.load nevű fájlt, aminek a tartalma a következő.

LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so

Máshol is hasonló módon kell, csak máshol lehet nincs külön fájl erre, hanem a httpd.conf -ban van egy hasonló sor, és az elől kell kivenni a kettős kereszt jelet. Természetesen ezek után a beállítások után újra kell indítani a webszervert.

A mod_rewrite modul használata
Hozz létre az index.php mellett egy .htaccess nevű fájlt. A tartalma a következő legyen

  1. <IfModule mod_rewrite.c>
  2. RewriteEngine On
  3. RewriteBase /phpparams/
  4.  
  5. RewriteCond %{REQUEST_FILENAME} !-f
  6. RewriteCond %{REQUEST_FILENAME} !-d
  7.  
  8. RewriteRule ^([^/]+)(/([^/]+))? index.php?oldal=$1&aloldal=$3&%{QUERY_STRING}
  9. </IfModule>

A RewriteBase után a saját mappád útvonalát írd, ahol az index.php-d van. Innentől fog számítódni a RewriteRule-ban a minta. Az egész egy IfModule tag-ben van, hogy csak akkor próbálja értelmezni az apache, ha a modul engedélyezve van. És miután engedélyezve van, még be is kell kapcsolni. Ezt végzi a RewriteEngine On sor. A két RewriteCond utasítás sorban a következőket jelenti: Csak akkor, ha nem létezik ilyen fájl, és nem létezik ilyen a mappa sem. A RewriteRule után először egy reguláris kifejezés jön, majd az az url, amire le kell cserélni, ami illeszkedik a kifejezésre. Itt konkrétan annyit tesz, hogy az első mappanevet az $1 változóba teszi, és ha van második mappa, akkor azt a $2 változóba. Majd a %{QUERY_STRING} változót még az url végére kell tenni, így a továbbra is változókként átadott értékek is megmaradnak. Az url most ilyesmi lesz:

http://hoszt/phpparams/elso/koszon

Az index.php

  1. <?php
  2. $host = 'http://domained.hu';
  3. $base = '/phpparams/';
  4. $oldal = isset($_GET['oldal']) ? $_GET['oldal'] : '';
  5. $oldal = 'oldalak/'.basename($oldal).'.php';
  6. $oldal = (file_exists($oldal)) ? $oldal : 'oldalak/fooldal.php';
  7. ?>
  8. <!DOCTYPE html>
  9. <html>
  10.     <head>
  11.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  12.         <title>PHP paraméterek</title>
  13.                 <base href="<?php print $host.$base ?>" />
  14.     </head>
  15.     <body>
  16.                 <ul>
  17.                         <li><a href=".">Főoldal</a></li>
  18.                         <li><a href="elso">Első</a>
  19.                                 <ul>
  20.                                         <li><a href="elso/koszon">Köszön</a></li>
  21.                                 </ul>
  22.                         </li>
  23.                         <li><a href="masodik">Második</a></li>
  24.                         <li><a href="harmadik">Harmadik</a></li>
  25.                 </ul>
  26.                 <?php include $oldal ?>
  27.         </body>
  28. </html>

Egy másik megoldás a következő:

  1. <IfModule mod_rewrite.c>
  2. RewriteEngine On
  3. RewriteBase /phpparams/
  4.  
  5. RewriteCond %{REQUEST_FILENAME} !-f
  6. RewriteCond %{REQUEST_FILENAME} !-d
  7.  
  8. RewriteRule . index.php
  9.  
  10. </IfModule>

Itt a szabály már sokkal rövidebb. A pont jelenti, hogy az egész fájlnevet cserélje le az index.php-re jelen esetben. Ezzel viszont nem kerülnek a $_GET -be az oldal és aloldal változók. Viszont az index.php-ben a $_SERVER['REQUEST_URI'] változóból továbbra is kinyerhetők. A következő kódot kell írni a fájl elejére még:

  1. $host = 'http://domained.hu';
  2. $base = '/phpparams/';
  3. $baselength = strlen($base);
  4. $url = substr($_SERVER['REQUEST_URI'],$baselength);
  5. $url = array_merge(array('path'=>''),parse_url($url));
  6. $get = explode('/',$url['path']);
  7. $_GET['oldal'] = ($get) ? array_shift($get) : '';
  8. $_GET['aloldal'] = ($get) ? array_shift($get) : '';

A $base változóba ugyanazt kell írni, mint a .htaccess fájlban a RewriteBase után. Ezt majd a html-ben is meg kell adni a <base> tag-ben a fejlécben. A

  1. $baselength = strlen($base);
  2. $url = substr($_SERVER['REQUEST_URI'],$baselength);

sorok vágják le a project gyökérkönyvtár előtti útvonalat. Vagyis marad az elso/koszon Persze még a végén ott maradhatnak a ?valami=akármi -ként átadott paraméterek is, de most csak az útvonal kell. És annak minden mappáját egy tömbbe tenni. Ezt teszi a következő két sor:

  1. $url = array_merge(array('path'=>''),parse_url($url));
  2. $get = explode('/',$url['path']);

Az array_shift függvénnyel a tömb első elemét lehet kivenni és egyben visszaadni változóba. Az oldal és aloldal változónál is meg kell tenni. Ezt teszi az utolsó két sor, amit már nem idézek újra.

Tehát az index.php most:

  1. <?php
  2. $host = 'http://domained.hu';
  3. $base = '/phpparams/';
  4. $baselength = strlen($base);
  5. $url = substr($_SERVER['REQUEST_URI'],$baselength);
  6. $url = array_merge(array('path'=>''),parse_url($url));
  7. $get = explode('/',$url['path']);
  8. $_GET['oldal'] = ($get) ? array_shift($get) : '';
  9. $_GET['aloldal'] = ($get) ? array_shift($get) : '';
  10.  
  11.  
  12. $oldal = isset($_GET['oldal']) ? $_GET['oldal'] : '';
  13. $oldal = 'oldalak/'.basename($oldal).'.php';
  14. $oldal = (file_exists($oldal)) ? $oldal : 'oldalak/fooldal.php';
  15. ?>
  16. <!DOCTYPE html>
  17. <html>
  18.     <head>
  19.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  20.         <title>PHP paraméterek</title>
  21.                 <base href="<?php print $host.$base ?>" />
  22.     </head>
  23.     <body>
  24.                 <ul>
  25.                         <li><a href=".">Főoldal</a></li>
  26.                         <li><a href="elso">Első</a>
  27.                                 <ul>
  28.                                         <li><a href="elso/koszon">Köszön</a></li>
  29.                                 </ul>
  30.                         </li>
  31.                         <li><a href="masodik">Második</a></li>
  32.                         <li><a href="harmadik">Harmadik</a></li>
  33.                 </ul>
  34.                 <?php include $oldal ?>
  35.         </body>
  36. </html>

A html <base> tag -ben lehet megadni azt az url-t, amit a weblapon minden url elé be kell tenni.

Azt hiszem, erről a témáról ennyit tudtam beszélni. Remélem segítettem valakinek vele. Éppen lehetne még felturbózni a megoldást, hogy ki- és bekapcsolható legyen a szép url-ek használata, de egyelőre talán ez is elég lesz :)

Kategóriák:

Hozzászólások

neogeo2 képe

Hasznos infó volt!
KÖSZÖNÖM!

Új hozzászólás