10. Tiedostojen käsittely 173 OPPITUNTI 10 Tiedostojen käsittely Tiedostojen testaaminen, lukeminen ja kirjoittaminen ovat yleisiä toimenpiteitä käytännön ohjelmissa ja niitä vastaava tekniikka on mukana kaikissa todellisissa ohjelmointikielissä. PHP ei ole poikkeus vaan tarjoaa käyttöön useita funktioita, joilla tiedostoja voidaan käsitellä suoraviivaisesti. Tällä tunnilla opit seuraavia asioita: Kuinka tiedostoja sisällytetään asiakirjoihin Kuinka tiedostoja ja kansioita testataan Kuinka tiedosto avataan ennen käyttöä Kuinka tietoa luetaan tiedostoista Kuinka tiedostoon kirjoitetaan ja lisätään tietoa Kuinka tiedosto lukitaan Kuinka kansioilla työskennellään
174 10. Tiedostojen käsittely Tiedoston sisällyttäminen include()-funktiolla Tiedostoja voi liittää PHP-asiakirjoihin include()-funktiolla. Noissa tiedostoissa oleva PHP-koodi voidaan suorittaa aivan kuin se olisi osa pääasiakirjaa. Tämä voi olla hyödyllistä, kun samaa kirjastokoodia laitetaan useille sivuille. Jos olet luonut vaikkapa lopetusfunktion, voit tietenkin liittää sen jokaiseen asiakirjaan, jossa sitä tarvitaan. Tietenkin tuollainen funktio tulee testata perusteellisesti ennen liittämisiä. Muutoin työmäärä kasvaa suureksi, kun virhettä korjataan eri paikoissa. Kun teet saman include()-funktiolla, riittää, kun korjaat alkuperäisen funktion koodin. Oma funktio sijoitetaan yhdelle sivulle ja sisällytetään muille sivuille tarpeen mukaan. PHP:n include()-funktio ottaa yhden argumentin, joka on polku sijoitettavaan tiedostoon. Listaus 10.1 luo yksinkertaisen PHP-skriptin, joka käyttää include()-funktiota tulostamiskoodin sisällyttämiseen. Listaus 10.1 Tiedoston sijoittaminen include()-funktiolla 3: <title>listing 10.1 Using include()</title> 4: </head> 5: <body> 6: <?php 7: include("eg10.2.php"); 8:?> 9: </body> 10: </html> Listauksen 10.1 include()-funktio sijoittaa asiakirjaan tiedoston eg10.2.php, jonka sisältö on listauksessa 10.2. Listauksemme tulostaa nyt tekstin I have been included!!, mikä voi tuntua kummalliselta, koska olemme sisällyttäneet pelkän tekstin PHP-koodilohkoon. Eikö siinä synny virhettä? Itse asiassa sisällytettävän tiedoston sisältö esitetään oletuksena HTML-koodattuna. Jos haluat suorittaa sisällytettävän tiedoston PHP-koodin, sinun on laitettava mukaan PHP-koodin alku- ja lopputagit. Listauksissa 10.3 ja 10.4 edellistä esimerkkiä muutetaan niin, että sisällytettävän tiedoston PHP-koodi suoritetaan. Listaus 10.2 Tiedosto, joka sisällytettiin listaukseen 10.1 1: I have been included!! Listaus 10.3 Toisessa tiedostossa olevan PHP-koodin suorittaminen include()- funktiolla 3: <title>listing 10.3 Using include to execute PHP in another file</title>
10. Tiedostojen käsittely 175 4: </head> 5: <body> 6: <?php 7: include("eg10.4.php"); 8:?> 9: </body> 10: </html> Listaus 10.4 Sisällytettävä tiedosto, jossa on PHP-koodi 1: <?php 2: print "I have been included!!<br>"; 3: print "But now I can add up... 4 + 4 =".(4 + 4); 4:?> PHP4:ssä voivat sisällytettävät tiedostot palauttaa arvon samalla lailla kuin funktiot. Palauttamiseen käytetään return-lausetta samalla lailla kuin funktioiden kohdalla. Mitään muuta HTML:ää ei enää sisällytetä. Listauksissa 10.5 ja 10.6 käytetään sisällyttämisessä tiedostoa, joka palauttaa arvon; tuo arvo sijoitetaan muuttujaan. Listaus 10.5 Sisällytettävä tiedosto palauttaa arvon, joka otetaan talteen 3: <title>listing 10.5 Using include() to execute PHP and assign the return Avalue</title> 4: </head> 5: <body> 6: <?php 7: $addresult = include("eg10.6.php"); 8: print "The include file returned $addresult"; 9:?> 10: </body> 11: </html>
176 10. Tiedostojen käsittely Listaus 10.6 Sisällytettävä tiedosto 1: <?php 2: $retval = ( 4 +4 ); 3: return $retval; 4:?> 5: Tätä HTML:ää ei esitetä, koska se on return-lauseen jälkeen. PHP3:ssa return-lause toimii vain ollessaan funktiossa. Listauksen 10.6 koodi aiheuttaisi virheen PHP3-ympäristössä. Voit käyttää include()-lausetta ehdollisessa lauseessa ja viitattua tiedostoa voi lukea vain, jos ehto on täytetty. Esimerkiksi seuraavan koodin include()-funktiota ei koskaan kutsuta: $test = false; if ( $test ) { include( "a_file.txt" ); // ei sisällytetä } Jos käytät include()-lausetta silmukassa, se korvataan viitatun tiedoston sisällöllä joka kerta, kun include()-lause suoritetaan. Tuo sisältö suoritetaan jokaisen kutsun yhteydessä. Listaus 10.7 havainnollistaa edellä kerrottua. Nyt include() viittaa jokaisella kierroksella eri tiedostoon. Listaus 10.7 Include() silmukan sisällä 3: <title>listing 10.7 Using include() within a loop</title> 4: </head> 5: <body> 6: <?php 7: for ( $x = 1; $x<=3; $x++ ) 8: { 9: $incfile = "incfile$x".".txt";
10. Tiedostojen käsittely 177 10: print "Attempting include $incfile<br>"; 11: include( "$incfile" ); 12: print "<p>"; 13: } 14:?> 15: </body> 16: </html>.black plate (183,1) Kun listaus 10.7 ajetaan, se sisällyttää kolmen eri tiedoston sisällön itseensä. Noiden tiedostojen nimet ovat "incfile1.txt", "incfile2.txt" ja "incfile3.txt". Olettaen, että kukin näistä tiedostoista sisältää pelkästään oman nimensä tulostamisen, tulostus näyttää seuraavalta: This is incfile1.txt Attempting to include incfile2.txt This is incfile2.txt Attempting to include incfile3.txt PHP sisältää myös require()-lauseen, joka suorittaa samanlaisen toiminnon. Voit sijoittaa require()-lauseen silmukkaan kuten include()- lauseenkin. Tuloksena oleva tiedosto ei kuitenkaan voi palauttaa arvoa. Tiedostojen testaaminen Ennen kuin aloitamme tiedostojen ja hakemistojen käsittelyn, on hyvä opiskella aihetta enemmän. PHP4 tarjoaa käyttöön useita funktioita, joilla voidaan saada selville tiedostoja koskevaa tietoa. Tässä osassa käsittelemme tärkeimpiä noista funktioista. Tiedoston olemassaolon testaaminen file_exists()-funktiolla Funktiolla file_exists() voidaan tarkistaa, onko jokin tiedosto olemassa. Se vaatii merkkijonon, joka edustaa kohteena olevan tiedoston tiedostopolkua. Jos tiedosto löytyy, funktio palauttaa arvon true; muutoin funktio palauttaa arvon false. if ( file_exists("test.txt") ) print "The file exists!";
178 10. Tiedostojen käsittely Tiedosto vai hakemisto? Voit varmistaa, että testattava kohde on tiedosto eikä hakemisto käyttämällä is_file()-funktiota. Tuo funktio vaatii argumentikseen tiedostopolun ja palauttaa totuusarvon: if ( is_file( "test.txt" ) ) print "test.txt is a file!"; Joskus on hyvä testata, onko kohde hakemisto. Se tehdään is_dir()-funktiolla, joka ottaa argumentikseen hakemistopolun ja palauttaa totuusarvon: if ( is_dir( "/tmp" ) ) print "/tmp is a directory"; Tiedoston tilan tarkistaminen Kun tiedät, että tiedosto on olemassa, voit ottaa selville enemmänkin tiedostoa koskevaa tietoa. Yleensä tiedostoa luetaan, se ajetaan tai siihen kirjoitetaan. PHP voi auttaa kaikissa noissa toiminnoissa. Se, onko tiedosto luettavissa, selvitetään is_readable()-funktiolla. UNIX-järjestelmissä voit ehkä nähdä tiedoston, mutta et silti pääse lukemaan sitä, koska oikeutesi eivät riitä sen lukemiseen. Funktio is_readable() ottaa argumentikseen tiedostopolun ja palauttaa totuusarvon: if ( is_readable( "test.txt" ) ) print "test.txt is readable"; Funktiolla is_writable() voidaan tarkistaa, voiko tiedostoon kirjoittaa. Se ottaa argumentikseen tiedostopolun ja palauttaa totuusarvon: if ( is_writable( "test.txt" ) ) print "test.txt is writable"; Funktio is_executable() kertoo, voiko tiedoston suorittaa. Tiedoston suorittaminen voi riippua käyttöoikeuksista tai tiedostotunnuksesta. Tämäkin funktio ottaa argumentikseen tiedostopolun ja palauttaa totuusarvon: if ( is_executable( "test.txt" ) print "test.txt is executable";
10. Tiedostojen käsittely 179 Tiedoston koon määrittäminen filesize()-funktiolla Funktio nimeltä filesize() kertoo argumenttina olevan tiedoston koon tavuina. Se palauttaa arvon false, jos ongelmia ilmenee. print "The size of test.txt is.. "; print filesize( "test.txt" ); Tiedostoa koskevan päivämäärätiedon selvittäminen Joskus ohjelmissa halutaan tietää, milloin tiedostoon on viimeksi kirjoitettu tai milloin sitä on viimeksi käsitelty. PHP tarjoaa käyttöön useita funktioita, jotka antavat noita tietoja. Se, milloin tiedostoa tutkittiin viimeksi, saadaan selville fileatime()-funktiolla. Se ottaa argumentikseen tiedostopolun ja palauttaa päivämäärän, jolloin tiedostoa viimeksi käsiteltiin. Tiedoston käsitteleminen tarkoittaa joko tiedoston lukemista tai tiedostoon kirjoittamista. Näiden funktioiden palauttamat päivämäärät ovat UNIX-muodossa: päivämäärää kuvaava luku on sekuntimäärä, joka on kulunut päivämäärästä 1.1.1970. Esimerkeissämme käytämme date()-funktiota kääntämään tuon sekuntiluvun sopivampaan muotoon. Saat lisätietoa päivämääräfunktioista luvusta 15, Päivämäärien käsittely. $atime = fileatime( "test.txt" ); print "test.txt was last accessed on "; print date("d d M Y g:i A", $atime); // Tulostusnäyte: Thu 13 Jan 2000 2:26 PM Voit ottaa selville tiedoston muutospäivämäärän filemtime()-funktiolla, joka ottaa argumentikseen tiedostopolun ja palauttaa päivämäärän. Tiedoston muuttaminen tarkoittaa tiedoston sisällön muokkaamista jollakin tavalla. $mtime = filemtime( "test.txt" ); print "test.txt was last modified on "; print date("d d M Y g:i A", $mtime); // Tulostusnäyte: Thu 13 Jan 2000 2:26 PM] PHP mahdollistaa myös asiakirjan muutosajan tarkistamisen filectime()-funktiolla. UNIX-järjestelmissä tiedoston muutosaika asetetaan, kun tiedoston sisältöä muokataan tai kun tehdään muutoksia tiedoston oikeuksiin tai omistajuuteen. Toisilla alustoilla filectime() palauttaa luomispäivämäärän. $ctime = filectime( "test.txt" ); print "test.txt was last changed on "; print date("d d M Y g:i A", $ctime);
180 10. Tiedostojen käsittely // Tulostusnäyte: Thu 13 Jan 2000 2:26 PM] Funktio, joka suorittaa useita tiedostotestejä Listaus 10.8 luo funktion, joka käyttää kaikkia edellä kuvattuja testausfunktioita. Listaus 10.8 Funktio tulostaa useiden tiedostotestien tulokset 3: <title>listing 10.8 A function to output the results of multiple file tests</ title> 4: </head> 5: <body> 6: <?php 7: $file = "test.txt"; 8: outputfiletestinfo( $file ); 9: function outputfiletestinfo( $f ) 10: { 11: if (! file_exists( $f ) ) 12: { 13: print "$f does not exist<br>"; 14: return; 15: } 16: print "$f is ".(is_file( $f )?"":"not ")."a file<br>"; 17: print "$f is ".(is_dir( $f )?"":"not ")."a directory<br>"; 18: print "$f is ".(is_readable( $f )?"":"not ")."readable<br>"; 19: print "$f is ".(is_writable( $f )?"":"not ")."writable<br>"; 20: print "$f is ".(is_executable( $f )?"":"not ")."executable<br>"; 21: print "$f is ".(filesize($f))." bytes<br>"; 22: print "$f was accessed on ".date( "D d M Y g:i A", fileatime( $f ) )."<br>"; 23: print "$f was modified on ".date( "D d M Y g:i A", filemtime( $f ) )."<br>";
10. Tiedostojen käsittely 181 24: print "$f was changed on ".date( "D d M Y g:i A", filectime( $f ) )."<br>"; 25: } 26: 27:?> 28: </body> 29: </html> Huomaa, että käytimme ehto-operaattoria tiivistämään koodia. Katsokaamme yhtä tuollaista lausetta hieman tarkemmin: print "$f is ".(is_file( $f )?"":"not ")."a file<br>"; Käytämme nyt is_file()-funktiota ehto-operaattorin ehtolausekkeessa. Jos se palauttaa arvon true, palautetaan tyhjä merkkijono. Muutoin palautetaan merkkijono Not. Ehto-operaattorin palauttama merkkijono lisätään tulostettavaan tekstiin. Seuraavat lauseet olisivat kuvanneet asiaa selvemmin, mutta koodi ei olisi ollut yhtä tiivistä: $is_it = is_file( $f )?"":"not "; print "$f is $isit"."a file"; Voisimme tietenkin käyttää selvempiä if-lauseita, mutta kuvittele, kuinka suureksi funktion koodi olisi kasvanut, jos olisimme käyttäneet seuraavanlaista koodia: if ( is_file( $f ) ) print "$fi is a file<br>"; else print "$f is not a file<br>"; Koska kaikkien näiden lähestymistapojen tulos on aivan sama, on kyseessä pitkälti makuasia. Tiedostojen luominen ja tuhoaminen Jos tiedosto ei ole vielä olemassa, voit luoda sen touch()-funktiolla. Funktio ottaa argumentikseen tiedostopolun ja luo tiedoston, jos sellaista ei ole olemassa. Jos tiedosto on jo olemassa, sisältöä ei tuhota, mutta muutospäivämäärä päivitetään ajan tasalle.
182 10. Tiedostojen käsittely touch("myfile.txt"); Voit tuhota olemassa olevan tiedoston unlink()-funktiolla. Sekin ottaa argumentikseen tiedostopolun: unlink("myfile.txt"); Kaikki funktiot, jotka luovat, tuhoavat, lukevat tai muokkaavat tiedostoja tai kirjoittavat niihin, vaativat UNIXjärjestelmissä, että tiedostoihin ja hakemistoihin on oikeat oikeudet. Tiedoston avaaminen kirjoittamista, lukemista tai lisäämistä varten Ennen kuin alat käyttää tiedostoa, sinun on avattava se. Avaaminen tapahtuu tiettyä toimintoa varten. Toiminto voi olla lukeminen, kirjoittaminen tai molemmat. PHP tarjoaa fopen()-funktion tiedoston avaamiseen; se ottaa argumenteikseen tiedostopolun ja avausmoodin. Yleisimmät avausmoodit ovat lukeminen ( r ), kirjoittaminen ( w ) ja lisääminen ( a ). Funktio palauttaa kokonaisluvun, jota voidaan käyttää myöhemmin tiedoston käsittelyyn. Tuo kokonaisluku tunnetaan tiedosto-osoittimena ja se tulisi sijoittaa muuttujaan. Tiedoston avaaminen lukemista varten tapahtuu seuraavalla koodilla: $fp = fopen( "test.txt", 'r' ); Voisit käyttää seuraavaa koodia avataksesi tiedoston kirjoittamista varten: $fp = fopen( "test.txt", 'w' ); Tiedosto avataan lisäämistä varten (tällöin uutta tietoa lisätään tiedoston loppuun) seuraavalla koodilla: $fp = fopen( "test.txt", 'a' ); Funktiot palauttavat arvon false, jos tiedostoa ei voitu avata. On aina hyvä testata tuo palautusarvo, joka kertoo, onko avaaminen onnistunut. Seuraavassa on testikoodi: if ( $fp = fopen( "test.txt", "w" ) ) { // tee jotain $fp-muuttujalla }
10. Tiedostojen käsittely 183 Tai voit käyttää loogista operaattoria lopettamaan suoritus, jos kohdetiedostoa ei voitu avata: ( $fp = fopen( "test.txt", "w" ) ) or die ("Couldn't open file, sorry"); Jos fopen() palauttaa arvon true, lausekkeen loppuosaa ei jäsennellä eikä die()-funktiota (joka palauttaa viestin selaimelle ja lopettaa skriptin) koskaan suoriteta. Muutoin or-operaattorin oikealla puolella oleva lauseke jäsennellään ja die()-funktio suoritetaan. Olettaen, että kaikki on hyvin ja siirryt työskentelemään avatulla tiedostollasi, sinun tulisi muistaa sulkea se, kun tiedoston käsittely päättyy. Sulkeminen voidaan tehdä kutsumalla fclose()-funktiota, joka vaatii argumentikseen tiedosto-osoittimen, joka on saatu työssään onnistuneelta fopen()-funktiolta: fclose( $fp ); Tiedostojen lukeminen PHP tarjoaa käyttöön useita funktioita, joilla luetaan tietoa tiedostoista. Ne mahdollistavat lukemisen tavuittain, riveittäin tai jopa merkeittäin. Rivien lukeminen fgets()- ja feof()-funktioilla Kun olet avannut tiedoston lukemista varten, haluat yleensä lukea sitä rivi riviltä. Tiedostosta luetaan sisältö rivi kerrallaan fgets()-funktiolla, joka ottaa argumentikseen fopen()-funktion palauttaman tiedostoosoittimen. Toinen argumentti on kokonaisluku, joka määrittää luettavien tavujen määrän, jos rivin tai tiedoston loppua ei kohdata ennen sitä. Funktio lukee tiedostoa, kunnes se kohtaa rivinvaihtomerkin ( \n ), argumenttina oleva tavujen määrä täyttyy tai tiedoston loppu saavutetaan. $line = fgets( $fp, 1024 ); // $fp on fopen()-funkt. palautt. tiedosto-osoitin Vaikka voitkin lukea rivejä fgets()-funktiolla, sinun on kerrottava sille, milloin tiedosto loppuu. Tiedoston loppu saadaan selville feof()-funktiolla, joka palauttaa arvon true, kun tiedoston loppu saavutetaan. Tämäkin funktio ottaa argumentikseen tiedosto-osoittimen: feof( $fp ); // $fp on fopen()-funkt. palautt. tiedosto-osoitin Nyt sinulla on tarpeeksi tietoa lukeaksesi tiedostoa rivi kerrallaan, kuten listauksessa 10.9 tehdäänkin.
184 10. Tiedostojen käsittely Listaus 10.9 Tiedoston avaaminen ja lukeminen rivi riviltä 3: <title>listing 10.9 Opening and reading a file line by line</title> 4: </head> 5: <body> 6: <?php 7: $filename = "test.txt"; 8: $fp = fopen( $filename, "r" ) or die("couldn't open $filename"); 9: while (! feof( $fp ) ) 10: { 11: $line = fgets( $fp, 1024 ); 12: print "$line<br>"; 13: } 14:?> 15: </body> 16: </html> Kutsumme fopen()-funktiota ja käytämme or-operaattoria varmistamaan, että skripti lopetetaan, jos tiedostoa ei voi lukea. Avaaminen lukemistarkoituksessa yleensä epäonnistuu, jos tiedostoa ei ole olemassa tai tiedoston oikeudet (UNIX-järjestelmissä) eivät salli lukemista. Lukeminen tapahtuu while-lauseessa; siinä testiehtona on feof()-funktion kutsu. Kun tiedosto on loppunut, feof() palauttaa arvon true. Itse silmukassa fgets() erottaa rivin tiedostosta (tai 1024 tavua). Tulos sijoitetaan $line-muuttujaan ja tulostetaan selaimelle; tulostuksessa lisätään <BR>-tagi mukaan parantamaan luettavuutta. Vaihtelevan tietomäärän lukeminen tiedostosta fread()-funktiolla Tiedostoa joudutaan joskus lukemaan muullakin tavalla kuin rivi kerrallaan. Joskus tavujen määrä olisi hyvä antaa heti ja siihen sopii fread()-funktio hyvin: se ottaa argumenteikseen tiedosto-osoittimen ja luettavien tavujen määrän. Se palauttaa tietoa joko tarvittavan määrän tai tiedoston loppuun saakka. $chunk = fread( $fp, 16 ); Listaus 10.10 on aikaisempi esimerkkimme muutettuna siten, että se lukee rivien sijaan 16 tavun lohkoja.
10. Tiedostojen käsittely 185 Listaus 10.10 Tiedoston lukeminen fread()-funktiolla 3: <title>listing 10.10 Reading a file with fread()</title> 4: </head> 5: <body> 6: <?php 7: $filename = "test.txt"; 8: $fp = fopen( $filename, "r" ) or die("couldn't open $filename"); 9: while (! feof( $fp ) ) 10: { 11: $chunk = fread( $fp, 16 ); 12: print "$chunk<br>"; 13: } 14:?> 15: </body> 16: </html> Vaikka fread() antaakin sinun määritellä tiedon määrän, se ei salli alkuosoitteen määrittämistä. Sen voit asettaa manuaalisesti fseek()-funktiolla, joka mahdollistaa nykyisen aloituspisteen määrittämisen. Se ottaa argumentikseen tiedosto-osoittimen ja siirtymän tiedoston alusta (tavuina): fseek( $fp, 64 ); Listaus 10.11 käyttää fseek()- ja fread()-funktiota ja lukee puoli tiedostoa kerrallaan. Listaus 10.11 Tiedostossa liikkuminen fseek()-funktiolla 3: <title>listing 10.11 Moving around a file with fseek()</title> 4: </head> 5: <body> 6: <?php 7: $filename = "test.txt";
186 10. Tiedostojen käsittely 8: $fp = fopen( $filename, "r" ) or die("couldn't open $filename"); 9: $fsize = filesize($filename); 10: $halfway = (int)( $fsize / 2 ); 11: print "Halfway point: $halfway <BR>\n"; 12: fseek( $fp, $halfway ); 13: $chunk = fread( $fp, ($fsize - $halfway) ); 14: print $chunk; 15:?> 16: </body> 17: </html>.black plate (191,1) Listauksessa puolitettiin ensin tiedoston koko, joka saatiin filesize()-funktiolla. Saatua arvoa käytettiin fseek()-funktion toisena argumenttina; nyt funktio siirsi lähtöpisteeksi tiedoston puolivälin. Esimerkin lopussa fread() lukee tiedoston toisen puoliskon ja tiedot tulostetaan selaimelle. Merkkien lukeminen tiedostosta fgetc()-funktiolla Fgetc()-funktio on samanlainen kuin fgets(),paitsi että se palauttaa yksittäisen merkin jokaisen kutsun yhteydessä. Koska merkin koko on yksi tavu, fgetc() ei vaadi pituusargumenttia. Sille viedään pelkästään tiedosto-osoitin: $char = fgetc( $fp ); Listaus 10.12 luo silmukan, joka lukee tiedoston test.txt merkin kerrallaan ja tulostaa kunkin merkin omalle rivilleen. Listaus 10.12 Tiedoston lukeminen merkki kerrallaan fgetc()-funktiolla 3: <title>listing 10.12: fgetc()</title> 4: </head> 5: <body> 6: <?php 7: $filename = "test.txt"; 8: $fp = fopen( $filename, "r" ) or die("couldn't open $filename"); 9: while (! feof( $fp ) )
10. Tiedostojen käsittely 187 10: { 11: $char = fgetc( $fp ); 12: print "$char<br>"; 13: } 14:?> 15: </body> 16: </html> Tiedostoon kirjoittaminen ja lisääminen Tiedostoon kirjoittaminen ja siihen lisääminen ovat samantapaisia toimintoja. Ero on vain fopen()-funktion kutsussa. Kun tiedosto avataan kirjoittamista varten, annetaan avaamismoodiksi w, kun kutsutaan fopen()-funktiota: $fp = fopen( "test.txt", "w" ); Kirjoittaminen tapahtuu tiedoston alkuun. Jos tiedostoa ei ole olemassa, se luodaan. Jos tiedosto on jo olemassa, sen mahdollinen sisältö tuhotaan ja sisältö korvataan uudella tiedolla. Kun tiedostoon lisätään, se avataan moodilla a : $fp = fopen( "test.txt", "a" ); Kirjoittaminen tapahtuu tiedoston loppuun, aiemman sisällön perään. Tiedostoon kirjoittaminen fwrite()- tai fputs()-funktiolla Funktio fwrite() ottaa argumenteikseen tiedosto-osoittimen ja merkkijonon, jonka se kirjoittaa tiedostoon. Samalla lailla toimii myös fputs(). fwrite( $fp, "hello world" ); fputs( $fp, "hello world" ); Tiedostoihin kirjoittaminen on noin suoraviivaista. Listaus 10.13 käyttää fwrite()-funktiota tiedostoon kirjoittamiseen. Sitten sama tehdään fputs()-funktiolla.
188 10. Tiedostojen käsittely Listaus 10.13 Tiedostoon kirjoittaminen ja lisääminen 3: <title>listing 10.13 Writing and appending to a file</title> 4: </head> 5: <body> 6: <?php 7: $filename = "test.txt"; 8: print "Writing to $filename<br>"; 9: $fp = fopen( $filename, "w" ) or die("couldn't open $filename"); 10: fwrite( $fp, "Hello world\n" ); 11: fclose( $fp ); 12: print "Appending to $filename<br>"; 13: $fp = fopen( $filename, "a" ) or die("couldn't open $filename"); 14: fputs( $fp, "And another thing\n" ); 15: fclose( $fp ); 16:?> 17: </body> 18: </html>.black plate (193,1) Tiedostojen lukitseminen flock()-funktiolla Edellä esitetyt tekniikat, joilla tiedostoja luettiin tai muutettiin, toimivat hyvin yksittäisen käyttäjän kohdalla. Todellisuudessa käyttäjiä saattaa kuitenkin olla useitakin samalla kertaa. Kuvittele, mitä tapahtuu, jos kaksi käyttäjää suorittaa skriptin, joka kirjoittaa tiettyyn tiedostoon juuri samalla hetkellä. Tiedosto saattaa vahingoittua. PHP4 tarjoaa käyttöön flock()-funktion, joka ehkäisee edellä kerrotun uhan toteutumisen. Funktio lukitsee tiedoston varoittaen näin muita prosesseja kirjoittamasta tiedostoon tai lukemasta tiedostoa niin kauan kuin toinen prosessi käsittelee tiedostoa. Funktio ottaa argumentikseen tiedosto-osoittimen ja kokonaisluvun, joka edustaa lukitsemistyyppiä. Taulukko 10.1 luettelee kolme eri lukitustapaa, joita voidaan kohdistaa tiedostoon. Taulukko 10.1 flock()-funktion kokonaislukuargumentit Kokonaisluku Lukitustyyppi Kuvaus 1 Jaettu Sallii muiden prosessien lukea tiedostoa, mutta ei kirjoittaa tiedostoon (käytetään luettaessa tiedostoa) 2 Laajennettu Estää muita prosesseja lukemasta tiedostoa tai kirjoittamasta tiedostoon (käytetään kirjoitettaessa tiedostoon) 3 Vapautus Vapauttaa jaetun tai laajennetun lukon
10. Tiedostojen käsittely 189 flock()-funktiota tulisi käyttää heti fopen()-funktion jälkeen. Lukitus tulee sitten myös vapauttaa tiedoston käsittelyn jälkeen. $fp = fopen( "text.txt", "a" ); flock( $fp, 2 ); // ulkoinen lukko // write to the file flock( $fp, 1 ); // vapauta lukko fclose( $fp ); Lukitseminen flock()-funktiolla on suositeltavaa. Vain toiset flock()- funktiota käyttävät skriptit hyväksyvät asettamasi lukituksen. Hakemistojen käsittely Nyt kun osaat testata tiedostoja, lukea niitä ja kirjoittaa niihin, voimmekin kiinnittää huomiomme kansioihin. PHP sisältää useita funktioita, jotka käsittelevät hakemistoja. Katsomme nyt hakemistojen luomista, tuhoamista ja lukemista. Hakemistojen luominen mkdir()-funktiolla Funktiolla mkdir() luodaan hakemisto. Funktio ottaa argumenteikseen merkkijonon, joka kuvaa luotavan hakemiston polkua sekä oktaalinumeron, joka edustaa hakemistolle asetettavaa moodia. Oktaaliluvussa käytetään aina etunollaa. Tuo moodiargumentti toimii vain Unix-järjestelmissä. Moodi koostuu kolmesta numerosta, joista kukin on väliltä 0-7. Nuo moodinumerot kuvaavat hakemiston käyttöoikeuksia, joita annetaan omistajalle, ryhmälle ja muille käyttäjille. Nämä käyttöoikeudet ovat lukuoikeus, kirjoitusoikeus ja suoritusoikeus. Funktio palauttaa arvon true, jos luominen onnistuu. Yleensä mkdir() epäonnistuu siksi, että isäntäkansion käyttöoikeudet eivät salli alikansion luomista. mkdir( "testdir", 0777 ); // kaikki oikeudet annetaan uuteen kansioon Hakemiston poistaminen rmdir()-funktiolla Rmdir() mahdollistaa hakemiston poistamisen tiedostojärjestelmästä, jos prosessilla, joka ajaa skriptiäsi, on oikeus tehdä niin ja hakemisto on tyhjä. Funktio ottaa argumentikseen polkua edustavan merkkijonon. rmdir( "testdir" ); Hakemiston avaaminen lukemista varten opendir()-funktiolla Ennen kuin voit lukea hakemiston sisältöä, on sinun saatava käyttöösi hakemisto-osoitin funktiolla opendir(). Funktio ottaa argumentikseen polkua edustavan merkkijonon ja palauttaa onnistuessaan hakemisto-osoittimen. Jos avaaminen ei onnistu, funktio palauttaa arvon false.
190 10. Tiedostojen käsittely $dh = opendir( "testdir" ); Hakemiston sisällön lukeminen readdir()-funktiolla Funktio readdir() lukee tiedoston tai hakemiston nimen hakemistosta (vrt. fgets(), joka lukee rivin kerrallaan tiedostosta). Funktio ottaa argumentikseen hakemisto-osoittimen ja palauttaa merkkijonon, jossa on kohteen sisältö. Jos hakemiston loppu on kohdattu, readdir() palauttaa arvon false. Huomaa, että readdir() palauttaa vain kohteiden nimet, ei täydellisiä polkuja. Listaus 10.14 näyttää hakemiston sisällön. Listaus 10.14 Hakemiston sisällön lukeminen readdir()-funktiolla 3: <title>listing 10.14 Listing the contents 4: of a directory with readdir()</title> 5: </head> 6: <body> 7: <?php 8: $dirname = "testdir"; 9: $dh = opendir( $dirname ); 10: while ( gettype( $file = readdir( $dh ))!= boolean ) 11: { 12: if ( is_dir( "$dirname/$file" ) ) 13: print "(D)"; 14: print "$file<br>"; 15: } 16: closedir( $dh ); 17:?> 18: </body> 19: </html> Listauksessa avataan ensin hakemisto lukemista varten opendir()-funktiolla. Sisältöä luetaan whilesilmukan sisällä kohde kerrallaan: readdir()-kutsu on osana while-silmukan ehtoa; funktion palautusarvo sijoitetaan muuttujaan $file. While-silmukan rungossa käytetään $dirname-muuttujaa yhdessä $file-
10. Tiedostojen käsittely 191 muuttujan kanssa luomaan täysi tiedostopolku, jota voidaan sitten testata. Jos polku johtaa hakemistoon, tulostetaan (D) selaimelle. Lopuksi tulostetaan tiedostonimi. Olemme käyttäneet nyt turhankin monimutkaista rakennetta while-silmukan ehto-osassa. Useimmat PHPohjelmoijat (mukaan lukien minä itse) käyttäisivät jokseenkin seuraavanlaista rakennetta: while ( $file = readdir( $dh ) ) { print "$file<br>\n"; } Koodi testaa readdir()-funktion palauttaman arvon. Koska mikä tahansa muu merkkijono kuin 0 vastaa arvoa tosi (true), ei ongelmia ilmene. Kuvittele kuitenkin, että hakemisto sisältää neljä tiedostoa, joiden nimet ovat 0, 1, 2 ja 3. Koodimme tulostaisikin nyt tyhjää! Kun silmukka kohtaa tiedoston nimeltä 0, readdir() palauttaa siten arvon, joka on sama kuin false, jolloin silmukka päättyy. Listauksessa 10.14 otetaan tuokin seikka huomioon ja ehdossa testataan, että palautusarvon tyyppi on todellinen totuusarvo. Yhteenveto Tällä oppitunnilla opit käyttämään include()-funktiota, jonka avulla voidaan skriptiin sisällyttää tiedostoja. Luvussa tutkittiin myös tiedostojen testaamisfunktioita. Opit myös lukemaan tiedostoja rivi riviltä, merkki kerrallaan ja vaihtelevissa lohkoissa. Opit myös kirjoittamaan ja lisäämään tietoa tiedostoihin. Kirjoittamismoodissa mahdollinen vanha tieto korvataan uudella tiedolla ja lisäämismoodissa taas uusi tieto kirjoitetaan vanhan tiedon perään. Luvun lopussa opit luomaan, tuhoamaan ja lukemaan hakemistoja. K&V K Hidastaako include() skriptejäni? V Koska sisällytettävä tiedosto on avattavat ja jäsenneltävä tulkin toimesta, teho heikkenee hieman. Kuitenkin haitta on vähäinen etuihin verrattuna. K Tulisiko minun aina lopettaa skriptin suoritus, jos tiedostoa ei voida avata kirjoittamista tai lukemista varten? V Sinun tulisi aina sallia tämä mahdollisuus. Jos skriptisi riippuu ehdottomasti tiedostosta, jolla haluat työskennellä, saatat haluta käyttää die()-funktiota, joka kirjoittaa virheilmoituksen selaimelle. Vähemmän kriittisissä tilanteissa sinun tulisi silti sallia virhe ja ehkäpä kirjoittaa tiedot virheestä lokitiedostoon. Saat lisätietoa aiheesta luvusta 22, Vianhaku.
192 10. Tiedostojen käsittely Työpaja Työpaja tarjoaa pikakysymyksiä, joiden avulla voit kerrata läpikäytyä materiaalia. Yritä ymmärtää vastaukset ennen kuin jatkat seuraavaan lukuun. Vastaukset annetaan liitteessä A. Kysymyksiä 1. Millä funktiolla voisit lisätä kirjastokoodin skriptiisi? 2. Millä funktiolla voisit selvittää, onko tietty tiedosto olemassa järjestelmässäsi? 3. Kuinka voisit määrittää tiedoston koon? 4. Millä funktiolla voisit avata tiedoston lukemista tai kirjoittamista varten? 5. Millä funktiolla voit lukea rivillisen tietoa tiedostosta? 6. Mistä saadaan selville, onko tiedoston loppu saavutettu? 7. Millä funktiolla voisit kirjoittaa rivin tietoa tiedostoon? 8. Kuinka voisit avata hakemiston lukemista varten? 9. Millä funktiolla voisit lukea hakemiston sisällä olevan kohteen nimen, kun hakemisto on ensin avattu lukemista varten? Toiminta 1. Luo lomake, johon voi kirjoittaa käyttäjän etunimen ja sukunimen. Luo sitten skripti, joka tallentaa tiedot tiedostoon. 2. Luo skripti, joka lukee tietoa tiedostosta, jonka loit edellä. Käytä rivinvaihtotageja tulostuksessa ja tulosta myös tiedostossa olevien rivien määrä sekä tiedoston koko.