Spletni PHP krožek

5. teden

Živjo!

Ta teden si bomo pogledali delo z datotekami in mapami.

Branje iz datotek

PHP ima nekaj zelo "udobnih" funkcij za branje iz datotek, najbolje, da si kar pogledamo primer.

Imamo datoteko imena.txt, v kateri imamo v vsako vrstico zapisano po eno ime, recimo:

Marko
Peter
Mitja
Janez

Te podatke lahko v naš program preberemo z uporabo dveh funkcij - file() in file_get_contents().

Razlika med tema dvema funkcijama je, da funkcija file() prebere datoteko in jo vrne v obliki polja, funkcija file_get_contents() pa jo vrne v obliki niza.

Funkcija file()

Funkcija file() sprejme en obvezen argument - to je pot do datoteke. Ta pot je lahko podana absolutno (npr. c:\dokumenti\podatki.csv (Windows) oz. /home/marko/doc/podatki.csv (Linux)), relativno (doc/podatki.csv), lahko pa je to tudi pot do datoteke na nekem HTTP ali FTP strežniku (recimo http://www.stamcar.com/test.txt).

Če se vrnemo na zgornjo datoteko imena.txt (ki se nahaja v istem direktoriju kot naša skripta) - z ukazom

$vsebina = file("imena.txt");

vsebino datoteke shranimo v 1-dimenzionalno polje z imenom vsebina. Vsaka vrstica v tem polju odgovarja eni vrstici v datoteki, zato lahko z ukazom count($vsebina) dobimo število vrstic v datoteki.

Pomni, da je v vsaki vrstici na koncu ohranjen tudi znak za novo vrstico (\n), ki ga boš morda želel odstraniti npr. z uporabo funkcije substr().

Vsebino datoteke bi na zaslon vrstico-po-vrstico izpisali takole:

$vsebina = file("imena.txt");

for ($i = 0; $i < count($vsebina); $i++) {
    print $vsebina[$i]; // izpis trenutne vrstice
}

Uporabili smo zanko for, s pomočjo katere smo šli čez vse vrstice, začenši pri 0 in do "velikost polja - 1".

Primer branja iz spleta (prebrali bomo vremensko napoved iz strani ARSO):

$vsebina = file("http://www.arso.gov.si/podro~cja/vreme_in_podnebje/napovedi_in_podatki/napoved.html");

for ($i = 0; $i < count($vsebina); $i++) {
    print $vsebina[$i]; // izpis trenutne vrstice
}

Z izpisom še nismo zadovoljni, ker smo bili primorani zajeti celotno spletno stran. Zato moramo ugotoviti, v kateri vrstici se začne dejanska vremenska napoved. Če si pogledamo izvorno kodo strani, ugotovimo, da v 35. vrstici - zato bomo začetno vrednost števca i iz 0 spremenili v 34.

Morda bi želeli odrezati tudi spodnji del strani, kjer se vremenska napoved konča. V kodi opazimo, da se tam pojavi oznaka za konec celice - </td>. Zanko lahko z ukazom break prekinemo, ko pridemo do vrstice, v kateri se nahaja ta oznaka.

V skladu z zgornjim namigom moramo upoštevati, da je na koncu vsake vrstice dodana oznaka \n, ki pomeni prelom vrstice. Imamo dve možnosti - ko iščemo, ali je v trenutni vrstici ($vsebina[$i]) oznaka </td>, moramo na koncu dodati še znak \n, ali pa iz vseh vrstic z uporabo funkcije substr() odstranimo zadnji znak. Spodaj sta ti dve možnosti pobarvani z rdečo in modro barvo (pri kopiranju kode izberi samo eno možnost!).

Izboljšana verzija kode je tako takšna:

$vsebina = file("http://www.arso.gov.si/podro~cja/vreme_in_podnebje/napovedi_in_podatki/napoved.html");

for ($i = 34; $i < count($vsebina); $i++) {

    $vsebina[$i] = substr($vsebina[$i],0,strlen($vsebina[$i])-1);  /* iz trenutne vrstice
                                                                     odrežemo zadnji znak */
    if ($vsebina[$i] == "</td>") {
    if ($vsebina[$i] == "</td>\n") {
        break;
    }

    print $vsebina[$i];
}

Če želimo iz izpisa odstraniti HTML oznake, lahko uporabimo funkcijo strip_tags().

Če odstranimo HTML oznake, izgubimo tudi oznake <br />, ki označujejo prelome vrstic v HTML kodi (pomni, da spletni brskalniki vrstice prelomijo samo z oznako <br />, znake za prelom vrstic \n pa ignorirajo oz. jih pretvorijo v presledke.), zato je koristno uporabiti tudi funckijo nl2br(), ki znake za nove vrstice pretvori v HTML oznake <br />. V primeru, da bi želeli uporabiti to funkcijo, se moramo obvezno poslužiti modre variante zgoraj, vrstico za izpis trenutne vrstice pa spremenimo v

print nl2br(strip_tags($vsebina[$i]));

kar pomeni, da iz trenutne vrstice najprej odstranimo vse HTML oznake iz originalne spletne strani, nato pa vse znake za nove vrstice zamenjamo z HTML oznakami <br />.

Funkcija file_get_contents()

je dokaj podobna funkciji file() s to razliko, da nam namesto 1D polja vsebino datoteka vrne z niz. Tako je branje iz datoteke še lažje, če jo ne mislimo obdelovati - primer izpisa vsebine datoteke imena.txt z dodanimi oznakami <br /> za nove vrstice, s katerimi dosežemo lepši izpis:

$vsebina = file_get_contents("imena.txt");
print nl2br($vsebina);

Pisanje v datoteke

V datoteko neke podatke najpogosteje zapišemo s kombinacijo funkcij fopen(), fputs() in fclose().

Če bi v datoteko test.txt želeli napisati Živjo!, bi to storili takole:

$handler = fopen("test.txt", "w");
fputs($handler, "Živjo!");
fclose($handler);
Bodi pozoren na z rdečo pobarvano črko w. Le-ta pomeni, da se bo vsebina datoteke ob vsakem klicu funkcije prepisala. Če bi rad z vsakim pisanjem nove podatke dodal na konec datoteke, boš kot drugi argument funkciji fopen() podal črko "a".

V prvi vrstici datoteko test.txt odpremo za pisanje (ta kazalec shranimo v spremenljivko z imenom handler), nato v drugi vrstici s sklicevanjem na ta kazalec v datoteko zapišemo niz Živjo! in datoteko na koncu "zapremo" (prenehamo obdelovati) s klicem funkcije fclose().

Primer: knjiga gostov

Sedaj si oglejmo, kako bi naredili preprosto knjigo gostov. Od uporabnika bomo želeli 3 podatke - ime, priimek in kraj. Podatke o knjigi gostov bomo hranili v datoteki knjigagostov.txt, vsak vnos pa bomo shranili v novo vrstico. Za začetek si poglejmo kodo za obrazec in za shranjevanje v datoteko:

<form method="post">
Ime: <input type="text" name="ime"/><br />
Priimek: <input type="text" name="priimek"/><br />
Kraj: <input type="text" name="kraj"/><br />
<br />
<input type="submit" name="akcija" value="Vpis v knjigo gostov"/>
</form>

<?php

if ($_POST['akcija']) {
    if ($_POST['ime'] && $_POST['priimek'] && $_POST['kraj']) {

        $niz = $_POST['ime'] . "@" . $_POST['priimek'] . "@" . $_POST['kraj'] . "\n";

        $handler = fopen("knjigagostov.txt", "a");
        fputs($handler, $niz);
        fclose($handler);

        print "<br /><b>Hvala! :)</b>";

    } else {
        print "<br /><b>Napaka: niste izpolnili vseh polj!</b>";
    }
}

?>

Prvih 7 vrstic tvori kodo za standarden HTML obrazec s tremi tekstovnimi polji in gumbom za pošiljanje.

V PHP kodi imamo nato en zunanji if stavek, s katerim preverjamo, če je uporabnik kliknil na gumb ($_POST['akcija'] je okrajšava za $_POST['akcija'] != ""), z notranjim if stavkom pa preverjamo, če so bila res izpolnjena vsa 3 vnosna polja (v nasprotnem primeru vrnemo napako).

Če so bila, ustvarimo nov niz z imenom niz in vanj "sestavimo" vrstico, ki jo bomo zapisali v datoteko. Podatke ločimo z nekim znakom, ki ga v samem zapisu ne pričakujemo, recimo z @ (afno). Na koncu ne smemo pozabiti še znaka za novo vrstico - \n. (Drugače bi se vsi naslednji zapisi "nabirali" v isti vrstici, česar pa nočemo).

Z naslednjimi 3 vrsticami ta niz zapišemo v datoteko knjigagostov.txt (ker smo uporabili argument "a" se bodo podatki dodali na konec datoteke) in se uporabniku zahvalimo za vnos.

Da bi preprečili uporabnikom, da nam "pokvarijo" podatkovno bazo, lahko pred shranjevanjem iz spremenljivk $_POST['ime'], $_POST['priimek'] in $_POST['kdaj'] s funkcijo str_replace() odstranimo znake @ (tj. jih zamenjamo s praznim nizom).

Če bi sedaj podatke iz knjige gostov želeli izpisati v obliki tabele, bi uporabili funkciji file() in explode().

Funkcija explode() nam omogoča, da vrstico razdelimo na več manjših koščkov (funkcija vrne 1D polje s temi koščki), koščke pa ločuje seperator, v našem primeru je seperator znak @ (afna).

Primer uporabe funkcije explode:

Imamo niz to*je*test*hihi. Za seperator vzamemo zvezdico (*) in polje, ki ga vrne funkcija explode, shranimo pod imenom koscki. Besedica to bi se tako nahajala v $koscki[0], beseda je v $koscki[1], test v $koscki[2] in hihi v $koscki[3].

Koda za izpis podatkov v obliki tabele:

<table>
<tr><th>Ime</th><th>Priimek</th><th>Kraj</th></tr>
<?php

$podatki = file("knjigagostov.txt");
for ($i = 0; $i < count($podatki); $i++) {
    $vrstica = explode("@",$podatki[$i]);
    print "<tr><td>{$vrstica[0]}</td><td>{$vrstica[1]}</td><td>{$vrstica[2]}</td></tr>";
}

?>
</table>

V polje podatki smo shranili vsebino celotne datoteke, nato pa vsako vrstico razbijemo s funkcijo explode() in sicer na 3 dele, do katerih nato dostopamo prek $vrstica[0], $vrstica[1] in $vrstica[2].

Popularen format CSV (comma seperated values) za seperator uporablja vejico ali podpičje.

Primer: števec obiskovalcev

Poglejmo si še, kako bi naredili preprost števec obiskovalcev. Podatke bomo shranjevali v datoteko stevec.txt:

<?php

$vsebina = file("stevec.txt");
$stevilo = intval($vsebina[0]);
$stevilo++;

$handler = fopen("stevec.txt", "w");
fputs($handler, $stevilo);
fclose($handler);

print $stevilo;

?>

Prvič, ko boš skripto pognal, bo javila napako, ker datoteke stevec.txt še ni, naslednjič pa bo že lepo delovala. Ob vsakem klicu se iz datoteke prebere trenutno število (ki je shranjeno v prvi in edini vrstici datoteke), nato se to število poveča za 1 in zapiše v datoteko (ker smo v funkciji fopen() uporabili argument "w", se trenutno število v datoteki prepiše z novim) ter se na koncu še izpiše trenutno število obiskovalcev.

Ostale funkcije za delo z datotekami in mapami

Poglejmo si še nekaj koristnih funkcij za delo z datotekami in mapami:

unlink() - kot argument podamo ime datoteke, ki jo želimo izbrisati
opendir(), readdir() in closedir() - seznam datotek v neki mapi, primer:

<?php

$mapa = "."; // trenutna mapa, kjer se nahaja skripta

$handler = opendir($mapa);

while (($datoteka = readdir($handler)) !== false) {
    echo $datoteka . "<br />";
}
closedir($handler);

?>

filesize() in filectime() - vrne velikost datoteke v bajtih datum ustvaritve datoteke (samo v Windowsih)
mime_content_type() - vrne MIME tip datoteke (npr. text/plain)

Tukaj si lahko ogledaš še seznam preostalih funkcij za delo z datotekami in mapami.

Zaključek

Sedaj znamo z datotekami ustvariti preprosto podatkovno bazo. V PHP žargonu bi temu rekli "flat-text database". Z uporabo nekega neuporabljenega znaka lahko v isto vrstico "zakodiramo" več podatkov. Sedaj pa veselo na domačo nalogo in nato na forum, če imaš kakšna vprašanja :)