PHP ieteikumi
Mar 02, 2005
Pa laikam gadās redzēt citu cilvēku rakstītus PHP skriptus, un tad parasti sanāk skrāpēt galvu. Redzot, līdz kādiem absurdiem "risinājumiem" dažkārt nonāk, domāju, jāpadalās ar to, ko pats esmu iemācījies/sapratis par PHP koda rakstīšanu. Sākumā domāju, ka varētu rakstīt apjomīgu pamācību trīs sējumos, bet tam tomēr esmu par slinku. Savu stilu un saprašanu uzspiedīšu īsos padomos un piemēros.
Vēlos pastāstīt par visu ko, bet nu sākšu ar sāpīgāko - rezultāta dokumenta formēšana, datu izvade. Ar šo domāta PHP skripta izpildes pēdējā daļa - tā, kurā skripta sagatavotie rezultāti tiek pārvērsti kaut kādā izejas dokumentā - kas visbiežāk ir html formātā.
Jāatdala rezultātu sagatavošana un formēšana
Līdzīgi kā visapkārt runā, ka saturu jāatdala no izskata (html & css), PHP skriptos ir ļoti ieteicams rezultātu sagatavošanu strikti atdalīt no gala dokumenta formēšanas. Dokumenta formēšanai izmantot sagatavi, kas atrodas savā, atsevišķā sagatavju mapē un ir atbildīga tikai par datu attēlošanu nevis apstrādi. Lietojot šādu nodalījumu, lielākais ieguvums ir - gala dokumenta izskatu var labot, neaiztiekot skripta "loģiku", to var darīt arī cilvēks, kam nav liela saprašana no PHP. Un otrādi - datu atlasi, apstrādi var labot, nejaucoties dokumenta izskatā. Viss kārtīgi un kā pie cilvēkiem.
Sagataves
Datu apstrādes un dokumenta formēšanas atdalīšanai, kamēr nekas modernāks nav izgudrots, lieto sagataves. Lietošanai ar PHP ir sarakstīti daudzi sagatavju dzinēji, viens no pazīstamākajiem ir Smarty.
Der atcerēties, ka pats PHP, visā visumā, ir sagatavju dzinējs. Vismaz man dokumentu formēšanai vienmēr ir atliku likām pieticis ar PHP iespējām un nav bijis vajadzības pēc kāda papildus sagatavju dzinēja. Turpinājumā vēlos nodemonstrēt dažas ērtas, datu izvadam piemērotas PHP iespējas, kā arī dažus lielus no-no.
Tipiskais dokumenta un sagataves izskats
dokuments www_root/userlist.php:
<?php require_once('config.php'); // konstantes, celji, DB savienojums u.c. // piesleedzu konkreetajai reizei nepiecieshamaas "biblioteekas" require_once(DB.'/users.php'); // skripta galvenaa dalja - sagatavoju rezultaatus $userlist = get_userlist(); // piesleedzu sagatavi, kas saformees lapu, tajaa ieklaujot datus // no masiiva $userlist require(TEMPLATES.'/userlist.php'); ?>
dokuments templates/userlist.php
<?php include('global/header.php') ?> <?php if (isset($title)): ?> <h1><?= $title ?></h1> <?php else: ?> <h1>List of users</h1> <?php endif; ?> <table> <tr> <th>Name</th><th>E-mail</th> </tr> <?php foreach($userlist as $user): ?> <tr> <td><?= $user['name'] ?></td> <td><?= $user['email'] ?></td> </tr> <?php endforeach ?> </table> <?php include('global/footer.php') ?>
Pirmais dokuments atrodas publiski pieejamajā mapē un tas ir tas, kurš saņem pieprasījumu. Viņš sagatavo datus un tad izsauc atbilstošo sagatavi. Sagatave pati neglabājas publiski pieejamā vietā.
Kā grāmatās: Un tagad es dzirdu jūs saucam - kas tas? Un ko nozīmē šis? Kas tie par ķeburiem?
Par visu pēc kārtas! :)
Teksti lielajos burtos (DB, TEMPLATES) ir konstantes. Par php konstantēm var palasīt šeit PHP manual - constants. Abas lietotās konstantes ir nodefinētas failā config.php un tās šajā gadījumā norāda ceļus uz vietām, kur var atrast datubāzes bibliotēkas un sagataves. config.php arī izveido datubāzes savienojumu, definē dažas bieži izmantotas funkcijas u.c.
Funkcija get_userlist() manā piemērā atgriež masīvu, kura katrs elements ir asociatīvs masīvs ar datiem par lietotāju. Tā saturs izskatās apmēram šādi:
$userlist = [ ["name" => "Jānis", "email" => "j@one.lv"], ["name" => "Jānis", "email" => "j@one.lv"], ... ]
Asociatīvie masīvi ir ērti gan datubāzes atlasēs, gan vēlāk sagatavēs
Sagatave pamatā sastāv no html koda, ar šur tur iespraustiem PHP gabaliņiem, atšķirībā no galvenā skripta, kas bija viss viens PHP gabals. Interesantākais, ko varam ievērot sagatavē, ir neparastā kontroles struktūru sintakse. Par to varam palasīt šeit - PHP manual - Alternative syntax for control structures. Labums, salīdzinot ar parasto pierakstu, ir vieglāk lasāmāks izskats, ja pa vidu nāk ne-php gabali. Salīdzinām:
<?php if (isset($title)) { ?> <h1><?= $title ?></h1> <?php } else { ?> <h1>List of users</h1> <?php }; ?>
un,
<?php if (isset($title)): ?> <h1><?= $title ?></h1> <?php else: ?> <h1>List of users</h1> <?php endif; ?>
Pieraksts <?= $title ?>
pasaka - izvadiet man mainīgā $title
vērtību un ir līdzvērtīgs šādam - <?php print $title; ?>
, tikai ir īsāks un lasāmāks.
Kopš es izlasīju par foreach, liekas, vairs nav bijis vajadzības pēc parastā for
cikla. Ērti, lasāmi. Lasām šeit - PHP manual - foreach.
Lai apskatītais piemērs būtu iztirzāts visā pilnībā, vēl jāpiebilst, ka failā /templates/global/header.php
ir dokumenta sākums ar visām pienācīgajām lietām, un, attiecīgi, failā footer.php
- dokumentas beigas.
Sliktais piemērs - divi vienā
<?php if (!isset($without_close)) print "</div> <!-- closing div id=\"content\" -->\n";//FIXME uncomment as soon as ready print "<div id=\"footer\" class=\"$TEXT_DIRECTION\">"; print "\n\t<br /><div align=\"center\" style=\"width:99%;\">"; print_contact_links(); print "\n\t<br /><a href=\"http://www.phpgedview.net\" target=\"_blank\"><img src=\"".$PGV_IMAGE_DIR."/".$PGV_IMAGES["gedview"]["other"]."\" width=\"100\" height=\"45\" border=\"0\" alt=\"PhpGedView Version $VERSION $VERSION_RELEASE - $PGV_DATABASE\" title=\"PhpGedView Version $VERSION $VERSION_RELEASE - $PGV_DATABASE\" /></a><br />"; print "\n\t<br /><a href=\"$PHP_SELF?view=preview&$QUERY_STRING\">".$pgv_lang["print_preview"]."</a>"; print_help_link("preview_help", "qm"); print "<br />"; if ($SHOW_STATS) print_execution_stats(); if ($buildindex) print " ".$pgv_lang["build_error"]." <a href=\"editgedcoms.php\">".$pgv_lang["rebuild_indexes"]."</a>\n"; if ((count($pgv_changes) > 0) and (userCanAccept(getUserName()))) { print "<br />".$pgv_lang["changes_exist"]. " <a href=\"#\" onclick=\"window.open('gdbi_changes.php','','width=600,height=600,resizable=1,scrollbars=1'); return false;\">". $pgv_lang["accept_changes"]."</a>\n"; } print "</div>"; print "</div> <!-- close div id=\"footer\" -->\n"; ?>
Šausmīgākā lieta - kods tiek izvadīts ar print
operatoru. Izskatās pēc kakas, redaktors neiekrāsos html sintaksi, strādā lēnāk.
Otra lieta - šajā fragmentā pa vidu pavīd funkcijas print_contact_links()
,
print_execution_stats()
u.c. Te uzskatāmi redzams, no kā jācenšas izvairīties - datu izvade saputrota kopā ar ieguvi un apstrādi. Ar to var sadzīvot mazos, vienkāršos skriptiņos, bet lielākās sistēmās, šādi rakstot, ar laiku kods paliek nelasāms, grūti pārvaldāms, izmētāts, un kļūdas arī grūti atrast.
Pārrakstot šo fragmentu, lai izbēgtu no pirmās problēmas (un šo to izmetot), ieguvu:
<?php if (!isset($without_close)): ?> </div> <!-- closing div id="content" --> <?php endif; ?> <div id="footer_line"> </div> <div id="footer" class="<?= $TEXT_DIRECTION ?>"> <?php print_contact_links(); ?> <?php if ($SHOW_STATS) print_execution_stats(); ?> <?php if ($buildindex): ?> <?= $pgv_lang["build_error"] ?> <a href="editgedcoms.php"> <?= $pgv_lang["rebuild_indexes"] ?> </a> <?php endif; ?> <?php if ((count($pgv_changes) > 0) and (userCanAccept(getUserName()))): ?> <?= $pgv_lang["changes_exist"] ?> <a href="#" onclick="window.open('gdbi_changes.php','','width=600,height=600,resizable=1,scrollbars=1'); return false;"> <?= $pgv_lang["accept_changes"]; ?> </a> <?php endif; ?> </div> <!-- close div id="footer" -->
Noslēdzošās piezīmes
Necenšos mācīt dzīvot, kā arī neapgalvoju, ka veids, kā es datu TO, ir vienīgais pareizais. Gaidīšu komentārus!