[Alku]
Testaa CSS-oppaan navigoinnin toimivuutta!
   
 
Hae sivuiltani:
[Apua]
Normaalille sivulle

Dynaaminen rakenne PHP:llä

Aiheet

Tämä sivu on jaettu jaksoihin, jotka sisältävät seuraavia aiheita:

Sisältö yhtenä isona sivuna

Yleistä

PHP:llä ei mielestäni kannata tuottaa kaikkea dokumentiin liittyviä seikkoja. CSS:llä kannattaa hallita dokumenttien esitysasullisia yksityiskohtia. CSS:n avulla voidaan myös jossakin määrin erottaa esitysasu ja sisältö toisistaan. Tavanomaisiin Web-dokumentteihin kuuluu kuitenkin aina varsinaiseen sisältöön kuulumattomia välttämättömiä rakenne-osia. Itse sisältöön eivät kuuluu myöskään perusnavigointielementit. Dokumentti voidaan kuitenkin jakaa katkelmiin (fragments), joista palvelin kasaa lopullisen dokumentin.

PHP on yksi palvelinpuolen mahdollinen kieli tämän toteuttamiseksi. PHP tunnettiin alunperin Rasmus Lerdorfin Personal Home Page Tools. PHP on HTML:n upotettu skriptikieli. Suuri osa syntaksista on lainattu C ja Perl kielistä lisättynä PHP-spesifisillä piirteillä. Päämääränä on kieli, joka antaa Web-kehittelijöille mahdollisuuden kirjoittaa nopeasti dynaamisia sivuja.

Alla on lista joistakin muiden kielten kanssa yhteisistä piirteistä, joiden tietäminen on välttämätöntä tämän sivun esimerkkien ymmärtämisen suhteen.:

PHP koodi erotetaan muusta sisällöstä yleensä <?php + ?> tai <? + ?> merkkauksilla (ainakin XML-dokumenteille pitäisi käyttää pidempää muotoa). Myös Microsoftin ASP (= Active Server Pages) tyylisiä merkkauksia (<% + %> ja <%= + %>) voi käyttää. Vaikka tiedostossa ei oli mitään muuta kuin PHP-koodia, PHP-koodi tulee ympäröidä PHP-merkkauksilla. PHP:tä voi käyttää kaikilla yleisillä palvelinohjelmistoilla.

Palvelimen tulee tunnistaa sivut, jotka käyttävät PHP:tä. Siksi lopullisen sivun tiedostopääte tulee olla tietynlainen. Yleensä palvelin on määritelty käsittelemään *.php, *.php3, *.php4 ja *.phml tiedostot PHP-jäsennintä tarvitsevina sivuina.

Palvelin- ja editorikohtaisia huomautuksia:

  1. Myös <style language="php">...</style> merkkauksia voi käytää, mutta ne voivat olla harmillisia editoitaessa koodia, sillä editori ei välttämättä tunnista niitä PHP-merkkauksiksi.
  2. Monet palvelimet eivät tue oletusarvoisesti ASP-tyylisiä merkkauksia. Jos haluaa niitä käyttää, niiden tuki täytyy pyytää palvelun tarjoajalta. Funktio phpinfo() ilmoittaa palvelimen asetukset.
  3. Jos käytetään muuta tiedostopäätettä kuin .php, tuetut tiedostopäätteet tulee tarkistaa palvelun tarjoajalta tai niitä tulee kokeilla ennen laajempaa käyttöä. Ainakin Apache-palvelinohjelmella ne määritellään palvelinohjelman asetustiedostossa. Tiedostopäätteistä php3, php4 ja phtml ei suositella käytettäväksi, sillä ne ovat poistumassa olevia (deprecated) tiedostopäätteitä.

Mikäli PHP:tä käyttävä tiedosto upotetaan sivuun, joka käyttää PHP:tä, upotettavan tiedoston tiedostopäätteen voi määrittää vapaasti. Tällaisia sivuja voitaisiin kutsua "orjadokumenteiksi", joita ei koskaan käytetä irrallisina vaan ainoastaan toisten dokumenttien osasina. Sivut, joita vierailijat näkevät internetissä olisivat tällöin "johtajasivuja". orjadokumenteille on yleisenä tapana antaa tiedostopääte inc (esim. includeTop.inc).

Kun käytetään palvelinpuolen skriptejä, niistäon sivujen latumisajoille sekä hyötyä että haittaa. Palvelinpuolen skriptien käyttö on hitaille tietokoneille etu, sillä palvelin suorittaa skriptit. Toisaalta nopealla koneella ja ruuhkaisella palvelimella skriptit voisivat toimia nopeammin selaimen suorittamana. Yleisesti ottaen palvelinpuolen skriptit ovat luotettavampia ja selainten väliset erot voi unohtaa.

Itselleni PHP on perustoiminnoiltaan ilmainen. Useille amatööreille PHP on ainoa mahdollisuus tehdä edullisesti suhteellisen dynaamisia sivuja. Mikäli haluaisin käyttää tietokantoja, siitä tulisi lisäkuluja. Olen omalla tietokoneellani hieman kokeillut tietokantojen käyttöä. Yritän soveltaa tietokantapohjaisen Web-suunnittelun perusideoita. En käsittele PHP:tä mitenkään perusteellisesti. Tämänkin sivun esimerkit saattavat tarvita perusteellisten PHP-oppaiden lukemista. Olen opetellut useimmat piirteet PHP Manual sivuista, jotka on editoinut Stig Sæther Bakken. Olen opetellut asioita myös hieman Rami Heinisuon PHP ja MySQL - Tietokantapohjaiset verkkopalvelut -kirjan avulla.

PHP: Download documentation; PHP ja MySQL - Tietokantapohjaiset verkkopalvelut (Rami Heinisuo).

[Alku]

Katkelmien hakeminen

Aiheet

Katkemat hakevat funktiot

Katkelmat kootaan yhteen joko readfile(), require() tai include() funktioilla ja jokainen rivi päätetään puolipisteeseen (;). Haettava tiedosto on yleensä sulkumerkkien sisällä, mutta require kohdalla pelkät lainausmerkitkin (yksinkertaiset tai kaksinkertaiset) riittävät (esim. <?php require 'joku.inc';?>). Lisäksi on huomattava, että include() pitää lisäksi olla kaarisulkujen ({ + }) rajoittaman käskylohkon sisällä, mikäli se on ehtolauseen sisällä.

Niiden lisäksi tarvitaan perustulostuksissa joko print() tai echo() funktio, joka joko kirjoittaa uutta koodia tai kirjoittaa haetun koodin jonnekin toiseen paikkaan. Jälkimmäisessä tapauksessa haettu koodi on määritelty muuttujaksi. Alla on esim. muuttujan käyttämisestä tietyn merkkijonon tulostamiseksi:

<?php $Muuttuja="\"Tämä on muuttujan sisältämä teksti\"";
print $Muuttuja;?>
tulostaa seuraavan tekstin:
"Tämä on muuttujan sisältämä teksti"

Merkkijono voi sisältää tavallista tekstiä tai vaikka HTML-koodia. Mikäli merkkijonossa on yksin- tai kaksinkertaiset lainausmerkkejä, niiden eteen täytyy laittaa etukenoviiva (\) pakokirjaimeksi (escape character). Muutoin jäsennin tulkitsee lainausmerkkien joko päättävän muuttujan tai katkovan sitä osiin. Jos merkkijono jaettaisiin lainausmerkeillä osiin, välissä tulisi olla piste (esim. "jono"."jono"), jolloin kyseessä olisi konkatenointi.

Toisinaan tarvitaan ns. taulukkomuuttujia (arrays), joissa samalla muuttujalla on useita jäsenmuuttujia, esim.:

$aLinks = array('Linkki1.inc','Linkki2.inc','Linkki3.inc');

Funktioita, jotka sisällyttävät katkelmat toisiin dokumentteihin kannattaa käyttää siten, että palvelin toimisi mahdollisimman nopeasti, mutta funktioilla on myös omat ongelmansa. Alla hieman perustietoa:

Palvelimiin liittyviä huomautuksia:

  1. Kaikki palvelimet eivät tue readfile() funktiota.

  2. Jos require()-funktion kanssa tulee tilanne, että haettua sivua ei löydy, sivun käsittely pysähtyy virheeseen. Mikäli käytettäisiin include() funktiota palvelin pystyisi ohittamaan virheen. Funktion include() käyttö on siten kaikkein turvallisinta.

  3. Palvelimet antavat vaihtelevassa määrin virheilmoituksia. Online-tilassa voi olla mahdotonta paikallistaa virhettä.

  4. Tietyillä toimintamoodi- ja open_basedir-asetuksilla suhteelliset polut eivät toimi. Mikäli sivuja täytyy testata offline-tilassa, on syytä luoda hyvin systemaattinen nimeämiskäytäntö ja laittaa pääosa tiedostoista samaa hakemistoon. Kullekin tietotyypille tulee laittaa nimi, jota voi etsiä helposti tiedostomaskeja käyttäen (esim. *index.inc).

  5. Palvelimet voivat estää myös absoluuttisten (http://...) polkujen käytön, jolloin PHP-tiedostoja ei voi jakaa useiden hakemiston kesken.

[Alku]

Mitkä laitetaan katkelmiksi

Ainakin seuraavat katkelmat olisivat mielekkäitä:

Itselläni esim. monijaksoisen liitesivun aloitussivun DynamicMenus.php3 rakenne on joskus ollut seuraava (olen lisännyt kommentteja):

<?php
/* Sivun yläosa: */
readfile('AdviceMeta.inc'); /* Alkuosa, jossa yhteiseen HEAD-osaan asti. */
require ('DynamicMenus0Top.inc'); /* Sivukohtainen HEAD-osa ja BODY-elementin alkumerkkaus.
/* BODY-osan alku: */
readfile('topSection.inc'); /* Välittömästi BODY-elementin jälkeen tuleva yhteinen BODY-osan koodaus. */
require ('dynamicMenusButtons.inc'); /* Sivukohtaiset navigointinappulat. */
readfile('AdviceOldInfo.inc'); /* Vanhoja selaimia koskeva yleinen tiedoite. */
/* BODY-osan yläosassa olevat yleiset navigointielementit: */
readfile('./include/AdviceHideInst.inc');
readfile('./include/allPages4.inc');
require ('AdviceNavigation.inc');
/* BODY-osan asiasisältöön liityvät osat: */
print "<div class=\"doc\" id=\"docSection\">"; /* Asiasisällön ympärille kirjoitettavan perusrakenne-elementin alkumerkkaus. Joillakin sivuilla tämä on osana varsinaista asiasisältöä. */
require ('./appendixesContents/DynamicMenus0Content.inc'); /* Sivun varsinainen asiasisältö. */
/* Sivun päättämiseen liityvät osat: */
readfile('./include/nextSection.inc'); /* Kaksi navigointinappia (linkki sivun alkuun ja seuraavalle sivulle). */
print "<br /></div>"; /* Asiasisällön ympärille kirjoitetun perusrakenne-elementin loppumerkkaus. */
readfile('bottomShort.inc'); /* Sivun lopullinen päätös, joka sisältää BODY ja HTML elementtien päätösmerkkaukset. Joillakin sivuilla ennen päätösmerkkauksia on mainoksia ja tiedotteita. */
?>

Jotkin katkelmat voi ajatella ikään kuin moduuleiksi. Yllä olleessa tapauksissa kaikki selaimet saavat samat moduulit, mutta niitä voisi myös yksilöisi selainkohtaisesti (käsittelen selaintunnistuksia edempänä.

Yksityiskohtia

Sivukohtaisissa META elementeissä osa on yleistä ja osa sivukohtaista. Esim. sivukuvauksen alkuun haen alisivuston yleisen kuvauksen ja loppuun kirjoitan sivukohtaisen tarkennuksen:

<meta name="description" content="<?php readfile('metaContent.inc');?>..." />

Olen tehnyt PHP + JavaScript koodauksella hyvin tehokkaan tavan kontrolloida relatiivisia navigaatiolinkkejä (Takaisin, edelliselle sivulle, seuraavalle sivulle ja sivun alkuun; engl. up, prev ja next) laittamalla sivun HEAD-osaan (mallina on CSS-sivujen englanninkielinen versio):

<link rel="prev" href="javascript:prev();" />
<link rel="next" href="javascript:next();" />
<link rel="up" href="javascript:up();" />
...
<script language="JavaScript1.1" type="text/javascript">

<!--
function print $up{
open("index1.php3","_self");}
function prev(){
open("Linkkeja.php3","_self");}
function next(){
open("index2a.php3","_self");}
//-->
</script><style type="text/css">
...

Ja sivun BODY-osaan:

<body...>
...
<td align="left" style="width:16px !important">
<a href="javascript:prev();" onmouseover="window.status='The\ previous\ page\ or\ anchor';return true;" onmouseout="window.status='';return true;" class="left" title="The previous revious page tai anchor"><img
src="../Kuvat/buttons/Left.gif" border="0" alt="[Prev]" align=
"middle" style="height:16px; width:16px" /></a>
</td>
<td align="center" class="up">
<a href="javascript:up();" onmouseover="window.status='To\ the\ upper\ page\ level';return true;" onmouseout="window.status='';return true;" class="up" title="To the upper page level"><img
src="../Kuvat/buttons/TopSmallDouble.gif" border="0" alt="[Up]" align=
"middle" style="height:16px; width:16px" class="up" /></a></td>
<td align="right" style="width:16px !important">
<a href="javascript:next();" onmouseover="window.status='The\ next\ page\ or\ section';return true;" onmouseout="window.status='';return true;" title="the next page tai section" class="right"><img
src="../Kuvat/buttons/Right.gif" border="0" alt="[Next]" align="middle" style="height:16px; width:16px" /></a>
</td>

...
<?php readfile('./include/nextSection.inc'); ?>
</div>
</body>
</html>

Tiedostossa nextSection.inc on seuraava HTML-koodi:

<div style="margin-top:1.0em"><div class="cNoPrint"><a href="#Top"><img
src="../Kuvat/buttons/Top.gif" border="0" alt="[Top]" align="middle" title="To the top of this page"/></a><br /><br /></div>
<div class="noPrint noBig" align="right"><a href="javascript:up()"><img
src="../Kuvat/buttons/TopSmallDouble.gif" border="0" alt="[Up]" align="middle" title="To the upper page level" /></a> <a href="javascript:next();"><img
src="../Kuvat/buttons/Right.gif" border="0" alt="[Next]" align="right" title="The next page tai section" /></a></div><br /></div>

Kun käytetään JavaScript-koodausta ongelmana on se, että JavaScript-tuki täytyy olla päällä. Homma voitaisiin koodata pelkästään PHP:n avulla esim. seuraavalla tavalla (esimerkkinä siirtyminen sivulta Elementit0.php3 sivulle Elementit.php3):

  1. Dokumenttiin laitetaan ennen linkkejä sivukohtainen tieto muuttujiin (laittaisin sen dokumentin HEAD-osaan):
    <?php
    $next = "Elementit.php3";
    $up = ...
    ?>
  2. Jos käytetään elementtiä LINK, PHP:llä laitetaan muuttujien avulla saadut attribuuttien arvot, esim. rel="up" href="<?php print $up;?>"
  3. Sivun lopussa oleva linkkikokonaisuus haetaan samalla tavalla kuin yleensä <?php require('...');?> tai <?php include('...');?> käyttäen.
  4. Haettu tiedosto sisältää linkit muualla määriteltyjen muuttien avulla haettuina attribuuttien arvoina. Se voisi olla kahden alla olevan esimerkin kaltainen:
    <?php print "...<a href=\"".$next."\"><img src=\"../Kuvat/buttons/Right.gif\" border=\"0\" alt=\"[Next]\" align=\"right\" title=\"The next section\" /></a>"; ?>
    tai
    ...<a href="<?php print $next;?>"><img src="../Kuvat/buttons/Right.gif" border="0" alt="[Next]" align="right" title="The next section" /></a>

Muuttujat voidaan sijoittaa myös funktioihin ja sitten kutsua niihin liittyviä funktioita, esim.:

<?php
function nextSection(){
$next=...
print $next; /* Muuttujat eivät toimi, jos ne määritellään funktion ulkopuolella paitsi siten, että funktion sisällä ilmoitetaan, että käytetään yleismuuttujia (esim. global $next, ...;). */
...
}

...
<a href="
<?php nextSection();?>"> ...

Helpoiten selviää, jos muuttujille määrittelee oletusarvot, jotka sivukohtaisesti tarvittaessa määrittää uudelleen. Kaikissa tapauksissa osaa muuttujista ei tarvitse määritellä lainkaan sivukohtaisesti. Kun sivukohtaisia muuttujia käytetään, on muistettava, että muuttujat ottavan sen arvon, joka niille on viimeksi määritelty ennen muuttujiin liittyvää funktiota. Seuraava koodi ei toimi missään selaimessa:

<link rel="up" href="<?php up(); ?>" />
<?php
$up = "Content.php";
function print $up{
global $up;
print $up;
}
?>

Edellä olleessa esimerkissä linkit määritellään muuttujiksi. Käsittelen muuttujien käyttöä seuraavassa jaksossa hieman enemmän.

[Alku]

Muuttujien tehokäyttö

Aiheet

Muuttujien käytön perusidea ja nimeämiskäytännöt

Muuttujien käytön perusidea on siinä, että muuttujat voidaan määritellä muualla kuin käyttökohteessaan. Tässä mielessä PHP-muuttujien käyttö muistuttaa class attribuutin käyttöä. Edellisessä jaksossa olevassa esimerkissä määrittelin niitä samalla sivulla, mikä muistuttaa STYLE elementin käyttöä.

Jos muuttujat määritellään johtajasivujen ulkopuolella, käyttö muistuttaa CSS:n @import at-säännön käyttöä, jossa tuontitiedosto on ikään kuin kirjoitettu kyseessä olevalle sivulle.

Koska tarvitaan sekä tavanomaisia muuttujia että taulukkomuuttujia eräs s-postiystäväni ehdotti, että taulukkomuuttujat ja muut muuttujatyypit kannattaisi nimetä seuraavaa käytäntöä noudattaen, jotta jatkossa heti näkisi minkä tyyppisestä muuttujasta on kyse:

Koska merkkijonomuuttuja on useimmin käytetty muuttujatyyppi, en laita sille mitään etuliitettä. Sekakirjasinten (engl. mixed case, leikillisesti camel case) eli "kamelikirjainten" käyttö puolestaan lisää nimien luettavuutta (suosittelen sekakirjasinten käyttöä alaviivan sijasta, esim. $aMinunTaulukkomuuttuja pikemmin kuin $a_minun_taulukkomuuttuja).

[Alku]

Toistuvat merkkijonot

Kaikki tiedostokatkelmat voidaan kirjoittaa erillistiedostoihin, mutta pienet useaan kertaan käytettävät seikat kannattaa laittaa muuttujiksi. Jos sivut listaa aakkosilla, mutta ei ole varma, että sivujen keskinäinen järjestys tai sivumäärä säilyy samana, kannattaa luoda sivun järjestyskirjaimille merkkijonomuuttujat. Järjestyksen muuttuessa täytyy korjata paljon vähemmän sivuja. Muuttujaa voidaan käyttää samalla sivulla useaan otteeseen. Esim. dynaamisia valikoita käsittävän sivun aloitussivulla on samaa muuttujaa käytetty neljä kertaa:

[Alku]

Linkkilistat

Aiemmassa esimerkissä mainitsin sivukohtaisista muuttujista. Myös linkkilistat kannattaa luoda muuttujiksi. Linkit voi lajitella valmiiksi. Funktioilla sort() (ensimmäisestä viimeiseen) ja asort() (viimeisestä ensimmäiseen) aakkosten mukaan tapahtuva lajittelu tapahtuu muuttujan sisältämän merkkijonon eikä muuttujan nimen mukaan.

Jotta lajittelu menisi oikein olen laittanut linkeille class-attribuutti. Esim.

<?php
$CodeStyle ="<a class=\"CodeStyle\" href=\"http://www.codestyle.org/\" target=\"new\">CodeStyle</a>";
...
$aLinks = array($..., $..., $CodeStyle, $...);
sort($aLinks); /* Linkki http://www.codestyle.org tulee nyt ensimmäiseksi. */
?>
voidaan kutsua valmiiksi lajiteltuna toisessa yhteydessä, jossa PHP:llä luodaan lopullinen linkkilista, esim.:
<ul>
<?php
include('tutorials.inc');

for ($i = 0; $i < count($aLinks); $i++) /* Silmukan laskeminen aloitetaan nollasta, ja joka kierroksella lukemaa kasvatetaan yhdellä kunnes laskuri huomaa, että päästiin viimeisen muuttujan kohdalle. Taulukkomuuttujille on mahdollista käyttää myös foreach funktioita, joka on taulukkomuuttujille tarkoitettu esimääritelty silmukointi. Se luo taulukkomuuttujista tavanomaisia muuttujia (esim. foreach($aLinks as $jokuMuuttuja)), jolloin laskuria ei tarvita. */

{
print ("<li>".$aLinks[$i]."</li>\n"); /* Jos käytettäisiin foreach funktiota muuttujan nimenä oli annettu uusi muuttujanimi, esim. $jokuMuuttuja. Tässä tapauksessa on välttämätöntä käyttää kaksinkertaisia lainausmerkkejä. Jos käytetään "..." voidaan hakea muuttuja jonon keskelle siten, että muuttujan arvo tulostuu. Jos käytettäisiin '...' vain tarkat kirjaimet tulostuisivat, ei muuttujan arvo. Kohta voitaisiin toteuttaa myös ilman konkatenointioperaattoria muodossa ("<li>$aLinks[$i]</li>\n"). */
}
?>

</ul>

Attribuutin id käyttö olisi loogisesti mielekkäämpää, mutta käytön ongelmana on se, että samalla sivulla ei saisi koskaan olla kahta saman arvon omaavaa id-attribuuttia. Saattaa olla olla tilanteita, joissa samaan linkkiin viitataan samalla sivulla useaan otteeseen.

Jo en olisi käyttänyt luokka-attribuuttia, aakkostaminen olisi edellyttänyt erityisten avainten luomista. Olen kuitenkin pyrkinyt pitämään systeemin mahdollisimman yksinkertaisena. Alla on kuitenkin eräältä s-postiystävältä saadun mallin mukaan tehty esimerkki:

$aLinks = array(
"CodeStyle" => "<a href=\"http://www.codestyle.org/\" target=\"new\">CodeStyle</a>",
...
);

Esittämälläni tavalla voidaan saada luotua yksinkertaisia helposti hallittavia oikeita tietokantoja muistuttavia linkkitiedostoja. Listojen järjestely ja lopullinen tuottaminen muistuttavat tietokantatyöskentelyä. Edellä esitetyn ratkaisun ongelma on se, että linkkeihin liittyvät yksittäiset muuttujat täytyy manuaalisesti listata taulukkomuuttujiksi.

Mikäli linkkeihin liittyvät muuttujat listaa alla olevalla tavalla suoraan taulukkomuuttujiksi voidaan hakea joko koko taulukkomuuttuja tai sen yksittäisiä jäseniä. Taulukkomuuttujan päivittäminen ja uuden linkin lisääminen toteutuvat yhtä aikaa ilman, että taulukkomuuttujan päivittäminen aiheuttaa lisätöitä.

<?php
$aLinks = array(
($CodeStyle ="<a class=\"CodeStyle\" href=\"http://www.codestyle.org/\" target=\"new\">CodeStyle</a>"),
($IndexDOTCss="<a class=\"IndexDOTCss\" href=\"http://www.blooberry.com/indexdot/css/\" target=\"new\">Index DOT Css</a>"),
...
);
?>

Mikäli on tarpeen luoda uusi taulukkomuuttuja, jossa on enemmän muuttujia kuin jossakin olemassa olevassa taulukkomuuttujassa, uudet muuttuja voidaan lisätä olemassa olevaan taukkomuuttujaan, josta tulee uuden taulukkomuuttujan osa. Alla esimerkki array_push() funktion avulla luodusta uudesta taulukkomuuttujasta. Uudessa taulukkomuuttujassa on yksi jäsen enemmän kuin aikaisemmin luodussa taulukkomuuttujassa.

<?php
$aLinksFi = $aLinks;
array_push($aLinksFi,
($KeijoKortelainen="<a class=\"KeijoKort\" href=\"http://www.2kmediat.com/\" target=\"new\">Keijo Kortelainen, 2K mediat</a>")); /* Käytetty funktio lisää uuden muuttujan olemassa olevan taulukkomuuttujan loppuun. */
?>

Minun tapauksessani sama lopputulos olisi saatu funktiolla array_unshift(), joka lisää uuden muuttujan taulukkomuuttujan alkuun. Funktiolla array_merge() voidaan puolestaan yhdistää taulukkomuuttujia. Esimerkkitapauksessa muuttuja $KeijoKortelainen olisi ensin tarvinnut sijoittaa uuteen taulukkomuuttajaan.

Sillä että muuttujat määritellään taulukkomuuttujan sisällä on yksi haitta. Jos yksittäisiä muuttujia määritellään jossakin kohtaa uudelleen, muutos ei koske taulukkomuuttujia.

Jotta muuttujien uudet arvot saadaan myöskin taulukkomuuttujiin, taulukkomuuttujien sisällä olevat muuttujat täytyy poistaa ja niiden tilalle tulee laittaa muutetut muuttujat array_splice() funktion avulla, esim.:

$TopStyle=$TopStyle." (".$BradSoft.")";
$PageBuilder=$PageBuilder." (".$TafWS.")";
$DHTMLMenuB=$DHTMLMenuB." (".$xFX.")"; /* Alkuperäisessä $aApplications taulukkomuuttujassa edellä listatuilla muuttujilla on eri arvot. */

array_splice($aApplications,2,3,array($TopStyle,$PageBuilder,$DHTMLMenuB));
/* Parametrit 2,3 poistavat alkuperäiset muuttujat. */

Funktio array_splice() poistaa elementtejä alkuperäisestä taulukkomuuttujasta ns. siirtymän (offset) eli sijainnin ja pituuden (length) eli peräkkäisten muuttujien lukumäärän perusteella. Muuttujan nimen perusteella muuttien poistaminen ei ole mahdollista. Siksi alkupeäisten poistettavien muuttujien paikka täytyy tarkistaa ja poistettavien muuttujien tulee olla peräkkäin, jotta poisto ja uusien muuttujien antaminen voisi tapahtua yhdellä komennolla. Tämä on mielestäni suurin haittatekijä.

Oikeita tietokantoja käyttäen yksittäiset linkit voidaan hakea kenttien nimien perusteella. Koska muuttujien arvoja ei kirjoiteta suoraan taulukkomuuttijien sisään, paikalliset muutokset koskevat myös taulukkomuuttujia. Esittämässäni ratkaisussa uusien linkkien lisääminen linkkilistaan on kuitenkin suurin piirtein yhtä helppoa kuin tietokantoja käytettäessä. Alla on esimerkki tietokantojen käytöstä, jolla saavutettaisiin sama lopputulos kuin aikaisemmin esitetyssä esimerkissä noudetun inc-tiedoston käytöllä:

<ul>
<?php
result = mysql($conn, "SELECT class, osoite, nimi FROM tutorials"); /* Esimerkissä oletetaan, että tietokantataulukossa on kentät id, class, osoite nimi ja kuvaus, joista tässä esimerkissä noudetaan tietoa kolmesta kentästä. */
while (list($class, $osoite, $nimi) = mysql_fetch_row($result)) {
print("<li><a class=\"$class\" href=\"$osoite\" target=\"new\">$nimi</a></li>");
}
?>

</ul>
[Alku]

Dynaamiset valikot

Olen tehnyt dynaamisia valikoita staattisesti. Niitä voidaan tuottaa myös PHP:n dynaamisesti. Verrattuna JavaScript-koodauksella tuotettuihin valikoihin etuna on se, että valikot ovat olemassa, vaikka JavaScript-tuki oli pois päältä.

Dynaamisten valikoiden alku ja loppu kannattaa joko laittaa tavanomaisena HTML-koodina haettavaan tiedostoon tai kirjoituttaa koodia funktioiden avulla. Funktiot eivät ole tässä kohdassa tarpeellisia.

Valikkokohdat

Funktiota tarvitaan aina itse valikkokohtien kirjoittamiseen, jolloin valikkokohtien rakennetta voidaan myöhemmin muunnella. Valikkokohtien asiatieto tulee jakaa selkeästi määriteltäviin muuttujiin, esim. alla alivalikkokohdan tuottava funktio mItem():

function mItem(){

global $classD,$idD,$classD2,$classA,$idA,$title,$href,$mOver,$name;

print "<div class=\"".$classD."\" ".$idD." onmouseover=\"".$mOver."\"><div class=\"".$classD2."\"><a class=\"".$classA."\" id=\"".$idA."\" title=\"".$title."\" href=\"".$href."\">".$name."</a></div></div>";
}
Yksittäisten yleismuuttujien käyttäminen

Tässä koodissa on käytetty yksittäisiä yleismuuttujia. Muuttujia ei tarvitse laittaa argumenttilistaan sen paremmin funktiota määriteltäessä tai sitä kutsuttaessa. Kaikkia muuttujia ei tarvitse määritellä, mutta jos jonkin kohdan määrittelee, se koskee automaattisesti kaikkia jäljessä tulevia valikkokohtia mikäli samaa muuttujaa ei määritellä uudelleen. Funktiotiedoston loppuun voi laittaa joidenkin muuttujien oletusarvot ja funktio, jolla voi palauttaa oletusarvot, esim.:

$classD="noArrow";
$classD2="itemD";
$idD=""; /* Muuttujan arvo täytyy kirjoittaa muodossa $idD="id=\"idArvo"\";, sillä arvon tulee olla uniikki eikä tyhjä arvo kelpaa. Oletusarvoksi ei siten voi luoda tyhjää attribuuttia vaan oletusarvona on, että attribuuttia ei ole lainkaan. Muilla muuttujilla tuotetut id-attribuutit ovat sellaisia, että ne tuotetaan kaikille elementeille ja jokaisessa kohteessa täytyy antaa uniikki arvo. Tämä ongelma olisi vältettävissä käyttämällä lisäfunktiota (annan edempänä mallin sen toteuttamiseksi).*/
$classA="itemA";
$mOver="";

function setDefaults(){

global $classD,$idD,$classD2,$classA,$mOver;

$classD="noArrow";
$idD="";
$classD2="itemD";
$classA="itemA";
$mOver="";
}

Alla on esimerkki, jossa määritellään yksi alivalikkokohta. Koska esimerkissä on tavallisuudesta poikkeava luokkamääritys lopussa on oletusasetusten palautusfunktio:

$idA="uniikkiId";
$title="Jokin linkki";
$href="link1.php";
$classD2="jokinLuokka";
$name="Linkki 1";

smItem();setDefaults();

Tavallisesti määrittelen yhdeksästä muuttujien määrittelemistä arvosta neljä. Esimerkiksi

$idA="CSSCover";
$title="CSS-oppaan etusivu";
$href="
index1.php3";
$name="Aloitussivu";

mItem();
tuottaa funktion mItem() kanssa seuraavan lopullisen lähdekoodin:
<div class="noArrow" ><div class="itemD"><a class="itemA" id="CSSCover" title="CSS-oppaan etusivu" href=" index1.php3" onmouseover="">CSS-oppaan etusivu</a></div></div>

Sen asemasta että muuttuja listataan ennen funktiokutsua, muuttujat voitaisiin määritellä myös funktiokutsun sisään pilkuilla toisistaan erotettuna (mItem($idA="CSSCover",...)).

Edellä olleen koodin ongelmana on se, että se luo paljon tyhjiä onmouseover attribuutteja. Ne saa pois monimutkaisemmalla koodauksella, mikä ei kuitenkaan ole mitenkään välttämätöntä. Em. attribuuttien ja joidenkin id attribuuttien suhteen voisi tarkistaa, ovatko muuttujat tyhjiä. Jos ne olisivat tyhjiä ei luotaisi attribuutteja lainkaan. Muussa tapauksessa tuotettaisiin attribuutti ja sen arvo. Alla koodi tämän toteuttamiseksi:

checkIfEmpty(){
global $mOver,$idD;
    
    if (empty($idD)){ } /* Funktion määrittely voi olla tyhjä. */
    
    else {
        print " id=\"".$idD."\"";
    }
    
    if (empty($mOver)){ }
    
    else {
        print " onmouseover=\"".$mOver."\"";
    }   
}

function mItem(){
...
    print "<div class=\"".$classD."\"";
    checkIfEmpty();
    print "> class=\"".$classD2."...";

Taulukkomuuttujien käyttäminen

Yksittäisten muuttujien käyttäminen ei yhdessä suhteessa edusta yleisesti suositeltavaa ohjelmointitapaa. Muuttujien tiedot "vuotavat" yhdestä kohteesta seuraaviin kohteisiin. Yksittäisten muuttujien sijaan voitaisiin luoda yksi taulukkomuuttuja (esim. esim. $aItem) ja sille yksittäisiä tavanomaisia muuttujia vastaavat alimuuttujat seuraavaan tapaan:

function mItem(){

global $aItem;

... ".$aItem["classD"]." ... ".$aItem["href"]." ... ".$aItem["name"]"...

Alimuuttujien arvot voitaisiin määrittää seuraavalla tavalla => operaattoria käyttäen:

mItem($aItem = array("classD" => "noArrow", ..., "href" => " index1.php3", "name" => "Aloitussivu", ... ));

Nyt yksittäisten muuttujien arvot eivät vuoda, mutta ne täytyy aina määritellä taulukkomuuttujan sisällä uusiksi. On toki mahdollista, että tarkistetaan onko attribuutin arvo tyhjä (se on tyhjä, jos muuttujaa ei ole määritelty). Jos arvo havaitaan tyhjäksi, annetaan esimääritelty oletusarvo. Mielestäni järkevämpää on ei-tyhjille oletusarvoille oma taulukkomuuttuja, esim.:

$aItemDefaults = array("classD" => "noArrow", "classD2" => "itemD", "classA" => "itemA", "href" => "#");

Nämä oletusarvot voidaan yhdistetään funktion array_merge() avulla taulukkomuuttujan $aItem kanssa. Alla esimerkkitoteutus:

mItem(array_merge($aItemDefaults,$aItem = array(
"idD" => "GenericItem",
"idA" => "GenericA",
"classD" => $arrow,
"title" => "Yleistä",
"mOver" => "hideThlMenus(); MM_showHideLayers('indexPages','','hide','allSites','','hide','Generic','','show'...);"
)));

Jos arvot ovat erilaisia, jälkimmäisen yhdistetyn taulukkomuuttujat voittavat. Yllä olleessa tapauksessa "classD" => $arrow voittaa oletusasetuksen "classD" => "noArrow".

Koodia tarvitaan kaikissa edellä esittämissäni taulukkomuuttujien käyttötavoissa hieman enemmän kuin yksittäisiä muuttujia käytettäessä. Olen tosin nähnyt tiivistettyjä JavaScript taulukkomuuttujia, joissa yksittäiset arvot laitetaan pilkkujen väliin (esim. ,,,,noArrow,,,). Uskoisin, että myös PHP:n kanssa päästäisiin samankaltaiseen ratkaisuun. Ongelma on siinä, että on vaikea muista mikä arvo tulee kunkin pilkun väliin. Siksi en suosittelen niin tiiviin koodin käyttämistä. Mielestäni kaikista helpoin tapa ei-ammattilaisille on yksittäisten muuttujien käyttäminen. Monimutkaisten taulukkomuuttujien käyttö on ammattilaisia varten.

Säilytinlohkot

Alivalikoiden säilytinelementille kannattaa ainakin laittaa muuttuja, joka nimeää sen yksilöllisesti id-attribuutin avulla. Alla olevassa esimerkkitapauksessa olen käyttänyt yksittäisiä muuttujia (myös taulukkomuuttujia voitaisiin käyttää):

function smTop(){

global $id,...;

print "<div id=\"".$id."\" class=\"pageGroup\"...
}

Siihen liittyvä funktiokutsu on esim.:

...
$id = "MainPages";

...

smTop();

Näillä ratkaisuilla saadaan automaattisesti luotuja valikkokohtia, mutta valikkolohkon ja koko valikon luomisen suhteen automatisointi on vain osittaista.

Esitysasut

Itselläni lähes kaikki ulkoasuun liittyvät seikat määritellään ulkopuolisista CSS-tiedostoista käsin. Merkittävin poikkeus on se, että olen määritellyt joidenkin elementtien korkeus määritellään PHP:n ja style attribuuttien avulla. Valikon laatija määrittelee alivalikon sisällön korkeuden muuttujan $iHeightMI (nimi tulee sanoista integer Height Menu Items = kokonaisluku Korkeus Valikko Kohtien). Tämä sisältökorkeus määrittää varjojen korkeuden (korkeusarvot saattavat olla itselläni hieman erilaisia):

function smTop(){

global ...,$iHeightMI;

print "<div id=\"".$id."\" class=\"pageGroup\" style=\"height:".($iHeightMI+10)."px;\">
...
}

function smBottom(){

global $iHeightMI;

print "</div><div class=\"shadowBase\" style=\"height:".($iHeightMI+122)."px;\"><div class=\"shadow\" style=\"height:".($iHeightMI+18)."px;\"> </div></div></div>";
}

Ratkaisuni heikkous on edelleen siinä, että sivujen laatijan täytyy käsin määritell muuttujan $iHeightMI. Mikäli kukin alivalikko olisi yksi taulukkomuuttuja, arvon voisi saada laskemalla kuinka monta valikkokohtaan kuuluu alivalikkoon ja kertomalla tuo luku valikkokohdan korkeudella (joka itselläni on 15px). Ratkaisussani on monia muitakin esitysasullisia piirteitä, jotka voisi automatisoida.

Kaikkien valikoiden asemoinnit määritellään tiedoston ../Css/dynamicMenus.css[S] alussa. Siinä määritellään myös yleiset esitysasulliset piirteet. CSS:ää on helppo editoida siten, että saa joko pysty- tai vaakatasoisen päävalikon.

Koska itselläni dynaamiset valikot toimivat myös staattisina linkkitaulukkoina, valikoiden sijoittuminen taulukkosoluihin ja taulukkoriveille täytyy määritellä ei-automaattisesti. Mikäli valikoita ei tarvita staattisina linkkitaulukkoina, kaikki valikkolohkoihin liittyvät rakenteet voidaan tuottaa PHP-funktioiden avulla.

Tein yhden valikon pohjatiedoston[S], johon haetaan funktioiden määrittelytiedosto tekstitiedostona[S]. Jos et tarvitse valikoiden ympärillä taulukkosoluja poista kaikki taulukkoelementteihin (TABLE, TR ja TD) liittyvä lähdekoodi.

Käyttämällä CSS:ää ja PHP:tä valikoiden perusrakenne, ulkoasumäärittelyt, JavaScript-koodaus ja valikoiden sisältö voidaan lähes kokonaan erottaa toisistaan.

Jos ratkaisuani kehitettäisiin edelleen, se voitaisiin muuntaa tietokantapohjaiseksi, jolloin tuotettujen valikkokohtien attribuuttien arvot haetaan tiekannoista *.inc tiedostojen sijasta. Jos jokainen valikko on oma taulu, voidaan hakea koko taulun sisältö esim. while-silmukan ja list() avulla seuraavaan tapaan:

<?php
result = mysql($conn, "SELECT classD, idD, mOver,... FROM menutableMain");

while (list($idA, $name,...) = mysql_fetch_row($result)) {

print ...
}
?>

Erityiset väliotsikot ja tyhjät valikkokohdat on kuitenkin syytä unohtaa sillä ne keskeyttäisivät silmukat. tai ne tulisi rakentaa samalla kaavalla kuin linkilliset valikkokohdat.

On myös mahdollista luoda lomake, jonka avulla päivitys voi toteutua minkä tahansa Web-selaimen avulla. Lomakkeen jotkut kentät tulisi määritellä pakollisiksi ja jotkut valinnaisiksi. Näihin ortoni - ainakaan toistaiseksi - ei riitä. Ja vaikka minulla olisikin riittävät ordot, en kertoisi näillä sivuilla tietokantojen käyttöä.

Jos dynaamiset valikot tuntuvat latautuvan verkossa liian hitaasti, valikon voi koota valmiiksi kun tekee jonkin *.php -tiedoston, johon hakee valikon tarvitsemat osat. Kasattu valikkotiedosto tulee tallentaa alkuperäisellä nimellä johonkin toiseen hakemistoon, josta sen voi lähettää verkkoon.

[Alku]

Selaintunnistukset

PHP:llä selaimia tunnistettaessa perusongelma on siinä, että selaimia voidaan tunnistaa vain selainten tunnistautumismerkkijonoista (UA strings) PHP:n sisäänrakennettujen muuttujien, esim. $HTTP_USER_AGENT avulla.

<?php print $HTTP_USER_AGENT; ?>

Yllä mainittu komento tulostaa selaimesi tunnistusmerkkijonon, mikäli Web-palvelimen asetukset sen sallivat. On kuitenkin paras luoda tähän tarkoitukseen uusi muuttuja, esim. $userBrowser = $_SERVER['HTTP_USER_AGENT']; (käytän esimerkeissäni $userBrowser-muuttujaa).

Palvelinkohtaisia huomautuksia:

  1. Palvelimen PHP-asetuksissa saattaa olla register_globals=Off. Tällöin $HTTP_USER_AGENT ei toimi. $HTTP_USER_AGENT jne. ovat sisäänrakennettuja globaaleja, joiden näkyvyysalue kattaa koko sovelluksen (ts. ne ovat sovellusmuuttujia). Ne ovat käytettävissä kaikillä sivuilla ilman sen kummempia määrittelyjä. Sen sijaan kun koodissa määrätään muuttujan global sanalla globaaliksi, määritetään sen vaikutusalueeksi ko. skriptisivun (tosin esim. include-funktion avulla tällä voidaan jäljitellä sovellusmuuttujia). $_SERVER muuttujaa käyttämällä voi luoda uusia skriptikohtaisia yleismuuttuja. Esim. koodin

    <?php
    global $userBrowser;
    $userBrowser = $_SERVER['HTTP_USER_AGENT'];
    print $userBrowser;
    ?>
    pitäisi tulostaa selaimesi tunnistusmerkkijonon, joka näkyy tämän tekstin alapuolella:
    CCBot/2.0 (http://commoncrawl.org/faq/)

Eräs ongelmallisimmista tunnistettavista on Netscape 4.x -sarjan selaimet. Ne kun antavat käytännössä hyödynnettäväksi vain Mozilla ja sen versionumeron. Alla esimerkki erään Netscape 4.x -selaimen antamasta tunnistusjonosta:

Mozilla/4.79 [en] (Win98; U)

Tunnistusjonoa Mozilla/4. käyttävät useimmat markkinoilla olevat selaimet, joten muut selaimet tulee raakata pois.Alla koodi, jossa ensin funktion stristr() avulla tunnistetaan MS IE, Mozilla Gecko ja Opera -selaimet sekä Mozilla/4 merkkijono (kyseistä funktiota käytettäessä ei ole merkitystä ovatko kirjaimet isoja vai pieniä).

$MSIE = (stristr($userBrowser, "MSIE") || stristr($userBrowser, "Internet Explorer")); /* Funktion stristr() ensimmäinen argumentti on merkkijono, josta etsitään. Jälkimmäinen on alimerkkijono, jota haetaan. */
$Opera = stristr($userBrowser, "Opera");
$Gecko = stristr($userBrowser, "Gecko");
$Moz4 = stristr($userBrowser, "Mozilla/4");
$NN4 = ($Moz4 && !$MSIE && !$Gecko && !$Opera);

Tunnistamatta jäävät sellaiset selaimet, joiden tunnistusmerkkijonossa mainitaan Mozilla/4. ja jotka eivät ole MS IE, Opera tai Netscape -selaimia. On kuitenkin mahdollista tunnistaa selaimet tietyltä väliltä. Mielekäs väli olisi tunnistaa selaimet, jotka ovat Mozilla/4.00 uudempia ja Mozilla/5.0 vanhempia. Nämä selaimet voidaan tunnistaa käyttämällä floatval() funktiota, jonka avulla merkkijonot voidaan kääntää desimaaliluvuiksi (käytetään engl. nimitystä double):

$Moz4All = ((floatval(substr(stristr($userBrowser, "Mozilla"),8, 3)) > 4.0) && (floatval(substr(stristr($userBrowser, "Mozilla"),8, 3)) < 5.0)); /* Ensimmäinen substr() funktion arvo määrittää mistä kohtaa merkkijonon alusta alimerkkijono alkaa ja jälkimmäinen määrittää merkkijonon pituuden. Jos arvon loppuun tulisi välilyönti ja kirjaimia, ne eivät haittaa, sillä niiden mahdollisuus on otettu huomioon funktiossa. ctype_digit() avulla suoritettua arvotarkistusta ei tarvita. Saamani s-postin mukaan PHP:n float-tietotyypissä (liukuluku) tulee varmistaa, että tietotyyppi on liukuluku. Vertailevan liukuluvun voi myös pilkkoa explode()-funktiolla pisteen kohdalta ja vertailemalla erikseen syntyviä kokonaislukuja (ts. pisteen etu- ja jälkipuolta) intval() funktiota käyttäen. */

Palvelinkohtaisia huomautuksia:

  1. Kaikki palvelimet eivät tue floatval() funktioita, sillä se on tuettu PHP 4.2.0 lähtien, mutta toimivuuden saa aikaiseksi vanhemmissa PHP-versiossa käyttäjän määrittelemällä funktiolla.

    php.net: floatval

Joissakin tapauksissa tarvittava käännösfunktio on intval() (esim. Gecko/20001108 varten (intval(substr(stristr($userBrowser, "Gecko"),6, 8))). Selaintarkistukseni ei ota huomioon aivan kaikkia selaimia, mutta useimmat kuitenkin. On myös huomattava, että jotkin selaimet käyttävät joko MS IE:n tai Mozilla Gecko -selainten esityskonetta, jolloin ne otetaan huomioon MS IE ja Gecko -selaimia etsittäessä. Maininnan arvoisia selaimia ovat Linux käyttöjärjestelmässä käytettävissä oleva Konqueror ja Macissa Safari. Ne voi tunnistaa aivan samaan tapaan kuin muutkin selaimet. Alla on eräs Safarin tunnistusmerkkijono:

Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/48 (like Gecko) Safari/48

JavaScript-koodauksessa voitaisiin kysyä if (document.layers), jota tukee vain Netscape 4.x -sarjan selaimet. Ongelmana olisi kuitenkin se, että kyselyjä ei voitaisi laittaa muuttujiksi (esim var 4NN4 = (...);) ja Netscape 4.x täytyy huomioida erityisellä tavalla.

Toinen ongelmakohta on tunnistamatta jääneet DOM-selaimet. JavaScript-koodauksessa voidaan kysyä if (document.getElementById) eikä selainten tunnistusmerkkijonoja tarvita. Ei ole mitään mieltä yrittää selvittää mitkä kaikki selaimet tukevat DOMia. Jos esim. halutaan määritellä DOMia tukeville selaimille tietty CSS tunnistuksessa voidaan edetä seuraavasti:

  1. Määritellään MS IE:n selainkohtaista DHTML:ää ja DOMia tukeville selaimille yhteinen CSS.
  2. Kyseinen CSS laitetaan sivulle siten, että Netscape 4.x ei sitä lue. Mielestäni helpoiten asiasta selviää kirjoituttamalla kommentit sellaisten CSS-tiedostojen ympärille, joita Netscape 4.x:n ei kuulu lukea:
    <?php
    include('...');
    /* Tiedosto sisältää muuttujamääritykset. */

    if ($NN4){echo "<!--\n";}?>

    <link rel="stylesheet" ...
    ...

    <?php if ($NN4){echo "-->\n";}?>
  3. Selainkohtaiset lisätiedostot haetaan PHP:llä (mallitiedosto[S] ja siihen liittyvät muuttujamäärittelyt[S]).

JavaScript ja PHP tunnistuksia voidaan myös yhdistellä. Ensin niin pitkälle kuin mahdollista PHP:llä ja selaimille, joille se ei sovellu tuotetaan tarvittava JavaScript-koodi tai viittaus JavaScript-tietostoon:

...
else {
echo "<script language=\"JavaScript\" type=\"text/javascript\" src=\"../Navigoinnit/AdviceBrowserSpecificCSS.js\"></script>"; }

Tavanomaiset (X)HTML-selainten ohella on myös WML-dokumenttien selailemiseen luotuja kännykkäselaimia. WML 1.x muistuttaa niin paljon XHTML:ää, että pienellä moduloinnilla on mahdollista luoda samasta materiaalista sekä WML 1.x:ää että (X)HTML:ää ymmärtäville selaimille eri versiot.

WML 1.x sisältää HTML:ään verraten vähän elementtejä. Jos sisältö on yhteinen, käytetään elementtejä ja attribuutteja, joita WML 1.x -selain ei tunnista, mutta joista ei pitäisi olla sille haittaa. WML:ssä on puolestaan elementtejä ja attribuutteja, jotka eivät kuulu XHTML:ään. Seuraavat elementit ovat yhteisiä (WML 1.2 elementeistä on myös englanninkielinen taulukko[S]):

Mikäli WML-selaimia tuetaan ainakin seuraavat asiat tulee ottaa huomioon:

Tavanomaisen selaintunnistuksen sijaan WML-selaimet voitaisiin periaatteessa tunnistaa mime-tyypin perusteella eli sen perusteella millaisia tiedostotyyppejä selaimet suoraan (ilman lisäosia) tukevat. Muuttujina käytettääisiin joko $_SERVER['HTTP_ACCEPT'] tai $HTTP_SERVER_VARS['HTTP_ACCEPT'] muuttujaa (jälkimmäinen välttämätön, jos globaaleja muuttujia ei sallita). Alla on testi, joka ilmoittaa toimiiko systeemi käyttämälläsi selaimella riittävässä määrin. Testi myös listaa selaimesi ilmoittamat mime-tyypit (alla testikoodi):

Selaimesi ilmoittaa palvelimelle riittävän määrän tuettuja mime-tyyppejä!

Selaimesi ilmoittaa seuraavat mime-tyypit:
text/html
application/xhtml+xml
application/xml;q=0.9
*/*;q=0.8
<?php
$MimeTypes = $HTTP_SERVER_VARS['HTTP_ACCEPT'];

if (stristr($MimeTypes, "application/xhtml+xml") || stristr($MimeTypes, "text/html"))
{print "...";}

else {print "...";}

print "...";

$aMimeTypes = explode(",",$_SERVER['HTTP_ACCEPT']);

for($i=0;$i<count($aMimeTypes);$i++){
echo $aMimeTypes[$i] . "<br />";
?>

Testi meni riittävässä määrin läpi eräällä Opera ja eräällä Mozilla Gecko -selaimella, mutta ei MS IE 5.5:llä. Systeemi voisi toimia, jos kaikki WML-selaimet ilmoittavat tukevansa WML:ää ja ensin kysytään stristr($MimeTypes, "wml". Mikäli WML-selaimet eivät niin toimi, WML-selaimille pitäisi keksiä jokin muu tunnistuskeino.

phpfreaks: Mime Types.

Esittämäni moduloinnin vaihtoehtona on raskas XSLT-muunnos, jossa dokumentti luodaan ensin XML:nä ja muunnetaan joko WML 1.x tai XHTML dokumenteiksi.

Uudemmat kännykkäselaimet osaavat XHTML:ään perustuvaa WML 2.0:aa. Niille voi toteuttaa optimiratkaisun yksinkertaistetuilla XHTML-dokumenteilla. Tällaisten selainten täysi huomioiminen saattaa edellyttää erilaisia naviointi- ja Web-sivujen runko-osien rakennemoduuleja.

[Alku]

Yhteenveto

Jotta dokumenttien luominen olisi mahdollisimman dynaamista PHP:tä ja CSS:ää käyttäen useille käyttöympäristöille, suosittelen laatimaan sivut seuraavia periaatteita käyttäen:

[Alku]