Jump to content
php.lv forumi
Sign in to follow this  
codez

Kā izveidot vienkāršu Routing Framework-u

Recommended Posts

Neliela pamācība par to, kā izveidot vienkāršu smuko urļu routing sistēmu, bez MVC freimworka un OOP.

 

Sākam ar .htaccess faila izveidi:

 

.htaccess

Options +FollowSymlinks 

RewriteEngine On 
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !^(.+)\.(css|js|jpg|gif|png|ico)$
RewriteRule ^(.*) index.php [QSA,L]

 

Šis .htaccess fails redirektos visus requestus, kuri nav uz .css, .js, ..., failiem, uz index.php.

Tātad index.php būs visu php pieprasījumu ieejas punkts.

 

Izveidojam failu index.php:

 

index.php

<?php
$p=explode('/',$_SERVER['REQUEST_URI']);
if ($p[1]=='') {$p[1]='index';}

if (file_exists($m=$p[1].'.ctrl')) {
 ob_start(); 
 require $m;
 $content = ob_get_contents(); 
 ob_end_clean(); 
} else {
 $content = 'page not found';
}
include 'layout.php';

 

index.php ar komentāriem

<?php
//nolasa pieprasīto adresi un ar explode sadalā daļās. 
//Piemeram, adrese /article/100-Hello/4, izveidos $p masīvu ['','article','100-Hello','4']
//Tātad izvēlētā lapa atradīsies masīva 1. elementā.
$p=explode('/',$_SERVER['REQUEST_URI']);

//pārbauda, ja nu gadījumā tiek pieprasīta pamatlapa ar uri '/' tad $p[1] būs '', tāpēc piešķiram viņam 'index'.
if ($p[1]=='') {$p[1]='index';}

//pārbaudam vai fails, kurā tālāk glabāsies katra no sadaļām, eksistē. Tie būs, piemēram, faili:index.ctrl, user.ctr, login.ctrl
if (file_exists($m=$p[1].'.ctrl')) {
 //ja eksistē, tad palaižam failu un tā uzģenerēto saturu nevis izvadam uzreiz ārā, bet saglabājam mainīgajā $content
 ob_start(); 
 require $m;
 $content = ob_get_contents(); 
 ob_end_clean(); 
} else {
 //ja neeksistē, tad ierakstām kontentā vienkārši, ka lapa nav atrasta. 
 $content = 'page not found';
}
//šijā pozīcijā mums ir uzģenerēts katras lapas mainīgais saturs, izsaucam layout.php, kurš izvadīs visu lapas saturu, ievietojot tur $content
include 'layout.php';

 

Kopumā šis index.php nodrošina, ka ja tiks izsaukta saite /modulis/param1/param2/, tad tiks izsaukt modulis.ctrl fails un tā uzģenērētais saturs ievietots layout.php failā. Atliek tikai uzrakstīt sev vēlamo layout.php un vēlamos .ctrl failus

 

layout.php

<div>Header</div>
<?php echo $content; ?>
<div>Footer</div>

 

index.ctrl

<b>Jūs esat nonācis sākumlapā</b>

 

user.ctrl

Sveiki, es esmu <?php echo $p[2]; ?>. lietotājs.

 

tagad varat pamēģināt izsaukt /user/123 saiti.

 

Tālāk katras jaunas lapas izveidošanai atliek tikai izveidot jaunu .ctrl failu, kurā uzreiz var rakstīt attiecīgajai sadaļai atbilstošo kodu.

Edited by codez

Share this post


Link to post
Share on other sites

Nedaudz uzlabota versija.

.htaccess failu atstājam to pašu:

 

index.php:

<?php
function load($_name,$_data=array()){
 extract($_data);
 ob_start(); 
 require $_name;
 $c = ob_get_contents(); 
 ob_end_clean();
 return $c; 
}

$p=explode('/',$_SERVER['REQUEST_URI']);
if ($p[1]=='') {$p[1]='index';}

if (file_exists($m=$p[1].'.ctrl')) {
 $content = include $m;
} else {
 $content = 'page not found';
}
include 'layout.php';

 

Šijā index.php atšķirībā no pirmā .ctrl faili netiek ielādēti kā tepleiti, bet tiek gaidīts, ka tie atgreizīs savu uzģenerēto saturu.

Tagad .ctrl fails izskatīsies šādi:

 

user.ctrl

<?php
$data=array(
 'id'=>(int)@$p[2],
 'name'=>'Jonh' //šo nolasa no db, piemēram. 
);
return load('user.tpl',$data);

 

Katram .ctrl failam ar return jāatgriež savs uzģenerētais saturs. Šijā gadījumā to daru ar load funkciju ielādējot user.ctrl atbilstošu templeitu user.tpl

Load funkcijai pirmais paramters ir templeita fails, otrais ir masīvas ar parametriem, kurus nodot templeitam.

 

user.tpl

Lietotāja id:<?php echo $id; ?><br />
Lietotāja vārds:<?php echo $name; ?>

 

Šāda pieeja ļauj katras lapas PHP kodu, atdalīt atsevišķā failā no teplate koda.

Tagad, katras jaunas lapas izveidei, jāizveido .ctrl un .tpl fails un .ctrl failā ar retrun load('kautkas.tpl'); jāatgriež uzģenerētais templeits.

 

Tagad šis jau būtībā ir tāds vienkārš MVC, kurā realizēta V un C daļa.

Edited by codez

Share this post


Link to post
Share on other sites

Sveiki, patestēju, un pats iemēģināju visu!

Diagnoze man ir diezgan smaga, pats esmu iesācējs, tāpēc ir tā, ka ar šādām lietām ir jāsāk darboties KAUTKAD vismaz.

Izmantojot pirmo veidu, bez tā teiktā uzlabojuma, viss iziet laukā kā tam jābūt tā teikt.

HEADRIS
CONTENT
FOOTER

Bet atverot, kādu URL, šajā gadījumā pieminēto /user vai /user/123

Reālā veidā man viņš viņu neatver, bet ja ievada kautko citu, pasaka - Šāda lapa neeksistē. Viss kā nākas.

Ja uztaisa ctrl failu lol atverot /lol atkal tas pats, kas ar user!

Otro veidu izmantojot manuprāt aizgāja sliktāk ( MAN ).

Sākuma lapā

CONTENT
HEADER
1
FOOTER

Atverot "Šāda lapa neeksistē" viss ir bumbā, un atkal tā pati kļūda ar šiem CTRL failiem!

Tā tad mans .htaccess un pārējie faili ir sekojoši šeit:

HTACCESS

Options +FollowSymlinks 

RewriteEngine On 
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !^(.+)\.(css|js|jpg|gif|png|ico)$
RewriteRule ^(.*) index.php [QSA,L]

INDEX

<?php
$p=explode('/',$_SERVER['REQUEST_URI']);
if ($p[1]=='') {$p[1]='index';}

if (file_exists($m=$p[1].'.ctrl')) {
 ob_start(); 
 require $m;
 $content = ob_get_contents(); 
 ob_end_clean(); 
} else {
 $content = 'page not found';
}
include 'layout.php';
?>

LAYOUT

<div>Header</div>
<?php echo $content; ?>
<div>Footer</div>

index.ctrl

<b>Jus esat nonacis sakumlapa</b>

USER CTRL

Sveiki, es esmu <?php echo $p[2]; ?>. lietotajs.

 

 

PIedevām atverot - /index viss itkā ir okey :P

Share this post


Link to post
Share on other sites

Otrajā variantā tiek izmantots principā jau MVC paterns, tāpēc kontrolerim (.ctrl failos) ir nevis jāsatur html kods, bet gan jāatgriež tas.

Kontrolerī šijā gadījumā ir paredzēts apstrādāt pieprasījumu: nolasīt datus no modeļiem, padot tos templeitam, utt.

Kā redzams index.php (2.variantā) tād no kontrolera tā ģenerētais saturs tiek saņemts šādi:

$content = include $m;

 

tas nozīmē, ka kontrolerim saturs ir jāatgriež šādi:

return 'kaut kas';

 

Bet tā kā mēs gribam saturu rakstīt smukā html formātā, tad to mēs daram .tpl failā, kuru kontroleris tad arī ielādē un tā uzģenerēto saturu atgriež. Bet ielādes procesā kontroleris par parametru nodot datus, kurus templeits izmanto html koda ģenerēšanai.

Tāpēc 2. variantā (kurš aprakstīts komentārā #2) kontrolerim (.ctrl failam) jāizskatās šādi:

<?php
$data=array(
 'id'=>(int)@$p[2],
 'name'=>'Jonh' //šo nolasa no db, piemēram. 
);
return load('user.tpl',$data);

kur $data ir masīvs ar mainīgajiem, kurus gribam nodot templeitam.

 

 

kamēr templeitam (.tpl failam) šādi:

Padotais parametrs id: <?php echo $id; ?>
Padotais parametrs name: <?php echo $name; ?>

templeitā tiek izmantoti ar $data nodotie mainīgie.

Ja masīvā $data ir elementi a,b,c - $data=array('a'=>1,'b'=>'hi','c'=>777);

tad .tpl failā būs pieejami mainīgie $a,$b,$c ar šādām vērtībām.

 

2. variants ir nedaudz labāks par 1. jo tajā tad teiksim viss PHP kods tiek atdalīts no templeita.

.ctrl failā mēs veicam dažādas pārbaudes, ielādējam vajadzīgos failus, utt.

Bet beigās uzģenerētos datus padodam .tpl failam satura uzģenerēšanai un ar return atgriežam index.php failam.

 

Mēs pat varam no viena kontrolera izvēlēties dažādu .tpl failu ielādi.

Piemēram, ja lietotājs nav ielogojies, varam atgriezt viņam, ka lapai nav iespējams piekļūt, bet lai dāžadi templeiti nebūtu jāliek vienā failā un tad ar case vai if izvēlās, kuru tekstu izvadīt, mēs kontrolerī jau nosakām, kuru tpl failu atgreizt.

<?php
if (lietotajs_ielogojies()){
 $data=ieladejam_leitotaja_datus();
 return load('secretpage.tpl',$data);
} else {
 return load('accessdenied.tpl');
}

 

Tas mums ļauj arī izvairīties no headers already sent kļūdām, jo kontrolera izpildes laikā vēl nekāda izvade nav notikus, tāpēc varam rakstīt, piemēram, ja lietotājs nav ielogojies, redirektojam viņu uz login lapu, savadāk lādējam vajadzīgo templeitu.

<?php
if (lietotajs_ielogojies()){
 $data=ieladejam_leitotaja_datus();
 return load('secretpage.tpl',$data);
} else {
 Header('location:/login');
 exit;
}

 

Tātad #2 komentāra variantā:

- kontrolerī (.ctrl failā) ir PHP kods - aplikācijas loģika

- templeitā (.tpl failā) ir HTML templeits ar atsevieškiem PHP koda fragmentiem, kuri tiek izmantoti mainīgo izvadei, vai teiksim kādai cikliskai koda ģenerācijai.

Edited by codez

Share this post


Link to post
Share on other sites

Kemito, ja tev tomēr neiet pirmais variants, tad pārbaudi vai tev rewrite modulis apacī ir ieslēgts.

Ja izmanto wamp, tad menu, tad ir pie Apache->apache modules-> rewrite_module

Ja pliku apache, tad httpd.conf failā pie Loadmodule rewrite_module jānoņem komentārs - # zīmē.

Jāpārstartē apacis.

 

P.S.

Nokopēju kodus tieši no komentāra un saliku failos - viss strādā kā vajag.

Pārbaudi vai tev visi faili ir ar mazajiem burtiem.

Edited by codez

Share this post


Link to post
Share on other sites

Ja izmanto wamp, tad menu, tad ir pie Apache->apache modules-> rewrite_module

Ja pliku apache, tad httpd.conf failā pie Loadmodule rewrite_module jānoņem komentārs - # zīmē.

Ja džekam ir kāda Debian-like distra ar apache2, tad moduļu ir jāenablo ar a2enmod komandu šellā.

Share this post


Link to post
Share on other sites

Bet tā arī neizskaidroji, kapēc man mistiskajā veidā neatrod tādu URL, ja ir izveidots ctrl fails, bet index atrod, kā arī, ar otro veidu mistiski parādās 1nieks!, un virs header parādās saturs!

Share this post


Link to post
Share on other sites

Kemito, man pārkopējot kodus no tava komentārā, viss strādā kā vajag. Iespējams vaina ir kur citur.

Piemēram, kāda ir pilnā adrese uz kuru tu testē? http://localhost/user/123 vai http://localhost/tests1/user/123

Otrajā gadījumā viņš mēģinās meklēt tests1.ctrl failu, kura protams nebūs.

Pameklē vēl kas varētu ne tā būt.

 

Pamēģini padebugot - ko tev izvada šādi?:

<?php
$p=explode('/',$_SERVER['REQUEST_URI']);
print_r($p);
echo '<br />';
if ($p[1]=='') {$p[1]='index';}
print_r($p);
echo '<br />';

if (file_exists($m=$p[1].'.ctrl')) {
 print_r($m);
 echo '<br />';
 ob_start(); 
 require $m;
 $content = ob_get_contents(); 
 ob_end_clean(); 
} else {
 $content = 'page not found';
}
include 'layout.php';

Share this post


Link to post
Share on other sites

Kā pārveidot htaccess, lai varētu atvērt dažas izņēmumu mapes, piemēram localhost/forums ?

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
php_flag magic_quotes_gpc off
php_flag register_globals off

Es izmantoju šādu variantu, tiesa gan, nav manis rakstīts.. Bet deļ kam divreiz izgudrot divriteni.

Edited by Maaren

Share this post


Link to post
Share on other sites

Mazliet offtopiks, bet kāpēc jālieto kaut kas tik nepareizs kā (int)@$p[2] ? Divkāršs type castings un @ - sucks.

 

$data = array(
 'id' => (isset($p[2]) && is_int($p[2])) ? $p[2] : '',
);

 

Palabojiet, ja kļūdos.

Share this post


Link to post
Share on other sites

Mazliet offtopiks, bet kāpēc jālieto kaut kas tik nepareizs kā (int)@$p[2] ? Divkāršs type castings un @ - sucks.

 

1)Tāpēc, ka tas ir acīmredzami īsāk:

'id' => (isset($p[2]) && is_int($p[2])) ? $p[2] : ''
'id' => (int)@$p[2]

un viennozīmīgi ir vieglāk uztverams un tas strādā.

 

2)Tāpēc, ka cenšoties izdarīt tipa "pareizāk", kā tu centies, var pielaist kaudzi kļūdu, kā to izdarīji tu, jo $p[2] pēc explode vienmēr būs string tipa mainīgais un tavs nosacījums vienmēr būs false un izteiksme atgriezīs ''.

 

Vai palaboju?

 

 

P.S. Maaren .htaccess fails iepriekš'īsti nederēs, jo ļaus pa taisno piekļūt .ctrl un .tpl failiem.

Share this post


Link to post
Share on other sites

Īsti negribas piekrist pa to, ka vieglāk saprast.

 

Vēl viens iemesls varētu būt ātrums. Rezultāti un manas sistēmas:

Short: 3.8734s
Long: 0.8649s

 

Ar kodu:


<?php
function test_long($p) {
return isset($p[0]) ? (int) $p[0] : 0;
}

function test_short($p) {
return (int) @$p[0];
}

class Timer {
private $t;

public function __construct() {
	$this->t = microtime(true);
}

public function __toString() {
	return round(microtime(true) - $this->t, 4) . 's';
}
}

$p = array();

$t = new Timer();
for($i = 0; $i < 1000000; $i++) {
test_short($p);
}
echo "Short: $t";

echo '<br /><br />';

$t = new Timer();
for($i = 0; $i < 1000000; $i++) {
test_long($p);
}
echo "Long: $t";
?>

 

Lai vai kā - katrs dara kā ērtāk.

Share this post


Link to post
Share on other sites

ratrij,

1)Par to vieglāk saprasts, atkarīgs no tavām zināšanām, kuras veidojas no ikdienas pieredzes. Ja tu protams tādus neizmanto, tad saprast grūtāk, bet, ja zini abu variantu darbības mehānismus, tas īsāko izlasīt ir ātrāk.

 

2) Patestē klašu metožu izsaukšanu objektu instancēm pret funkcijas izsaukšanu. Varbūt teiksi, ka tas ir vērā ņemams iemesls kapēc vairs nelietot OOP?

 

Class: 2.9369s
Function: 1.783s

 

OMG, OOP is evil.

 

3)Kāpēc tu testē gadījumu, kurš pareizas aplikācijas darbība izpildīsies zem 1% gadījumu?

Patestē šo ar varaintu, kad masīva elements ir definēts - test_long(array($p)); . Starpība vairs nebūs tik liela.

Short: 2.5447s
Long: 1.5923s

 

Ja godīgi, es uzskatu, ka PHP skriptam griežoties pie nedefinēta mainīgā vajadzētu atgriez NULL, nevis saukt warning-us. Bet tā kā PHP nav tā kā es vēlētos, tad es to simulēju visērtākajā veidā, attiecīgi, @$p[0];

Edited by codez

Share this post


Link to post
Share on other sites

@codez, atvaino - ielaidu kļūdu - is_int vietā biju domājis is_numeric. Jebkurā gadījumā uzskatu, ka tavs piemērs ir nedaudz čž, vienkārši centos palabot konkrēto vietu.

 

Un tas, ka kods ir nedaudz īsāks, uzreiz nenozīmē, ka tas ir arī vieglāk lasāms un pareizāks.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...