Kirjautuminen

Haku

Tehtävät

Keskustelu: Nettisivujen teko: PHP last modified-jatkoa

Sivun loppuun

Flatologi [26.09.2009 10:45:39]

#

Yli kuukauden vanha, joten jatkan tässä hieman aiheesta

Antti Laaksonen kirjoitti:

Seuraava koodi ilmoittaa, milloin jotain tiedostoa on muokattu viimeksi hakemistossa ja alihakemistoissa.

<?php

$uusin = getlastmod();

function haku($hak) {
    global $uusin;
    foreach (glob("$hak/*") as $tied) {
        if (is_dir($tied)) {
            haku($tied);
        } else {
            $uusin = max($uusin, filemtime($tied));
        }
    }
}

haku(".");

echo "Viimeisin päivitys: " . date("j.n.Y G:i:s", $uusin);

?>

Kokeilin tuota koodia, mutta se antaa seuraavanlaisen virheilmoituksen:
Warning: Invalid argument supplied for foreach() in /var/www/vhosts/oma.domain/httpdocs/lastmodified.php on line 7

Oma taitotasoni php-koodauksesta onneton, joten en ymmärrä mistä johtuu moinen.

Mod. lisäsi kooditagit

Flatologi [26.09.2009 10:56:00]

#

Parse error: syntax error, unexpected '{' in /var/www/vhosts/oma.domain/httpdocs/index.php on line 74
oli vastaus palvelimelta...

Edit.
Nopeita vastauksia...

MIB [26.09.2009 10:56:19]

#

Edit. Omani ei toiminutkaan, mutta kyllä tuo Antin versio toimii.
En tiedä, pystyykö tuon globin ottamaan pois päältä.

Flatologi [26.09.2009 11:03:09]

#

Hmmm.

Mikähän tuossa sitten oikein on? Koodi antaa kyllä sen päiväyksen, mutta ennen sitä herjaa tuosta invalidista argumentista.

MIB [26.09.2009 11:10:55]

#

<?php
$uusin = getlastmod();

function haku($hakemisto) {
	global $uusin;

	while($tiedot == readdir(opendir($hakemisto))) {
		if (is_dir($tied)) {
			haku($tiedot);
		} else {
			$uusin = max($uusin, filemtime($tiedot));
		}
	}
}

haku(".");

echo "Viimeisin päivitys: " . date("j.n.Y G:i:s", $uusin);

?>

Koitappas tätä.

Flatologi [26.09.2009 11:19:44]

#

Nyt pelittää. Kiitos!

Metabolix [26.09.2009 12:45:30]

#

No ei kyllä pelitä.

Tulostettu muokkausaika on aivan väärä, ja lisäksi tulee tällainen huomautus:

PHP Notice: Undefined variable: tiedot in /tmp/mib-koe/koe.php on line 6

MIBin koodi siis avaa hakemiston ja hakee ensimmäisen tiedostonimen ja vertaa sitä muuttujaan $tiedot, jota ei ole. Vertailun tulos on väitämättä false, joten silmukkaa ei ajeta kertaakaan.

Korjaus: vaihdetaan ==:n tilalle yksi =.

Nyt päästään jo silmukkaan asti, mutta ongelmat eivät lopu tähän.

Notice: Undefined variable: tied in /tmp/mib-koe/koe.php on line 8
Notice: Undefined variable: tied in /tmp/mib-koe/koe.php on line 8
Notice: Undefined variable: tied in /tmp/mib-koe/koe.php on line 8
jne.

Korjaus: vaihdetaan nimi oikeaksi ($tiedot).

Mutta mitä ihmettä? Skripti pyörii kauan ja päättyy virheeseen!

Fatal error: Maximum execution time of 2 seconds exceeded in /tmp/mib-koe/koe.php on line 7

Koodia katselemalla voi huomata, että opendir-kutsu on while-silmukan ehdossa, jolloin hakemisto avataan joka kerta uudestaan! Niinpä readdir palauttaa joka kerta hakemiston ensimmäisen tiedostonimen eikä silmukka lopu koskaan.

Korjaus: avataan hakemisto vain kerran, sijoitetaan tulos muuttujaan ja käytetään tätä readdirin parametrina.

Ongelma ei ratkennutkaan tällä kokonaan vaan muutti muotoaan:

PHP Warning: opendir(.): failed to open dir: Too many open files in /tmp/mib-koe/koe.php on line 6
PHP Warning: readdir() expects parameter 1 to be resource, boolean given in /tmp/mib-koe/koe.php on line 7
PHP Warning: opendir(..): failed to open dir: Too many open files in /tmp/mib-koe/koe.php on line 6
PHP Warning: readdir() expects parameter 1 to be resource, boolean given in /tmp/mib-koe/koe.php on line 7
jne., kunnes
Fatal error: Maximum execution time of 2 seconds exceeded in /tmp/mib-koe/koe.php on line 7

Miten voi olla liikaa tiedostoja auki? On vain yksi selitys: ikuinen silmukka. Virheistä nähdään myös, että opendir on yrittänyt avata monta kertaa hakemistoa . (piste) eli nykyistä hakemistoa. Niinpä kyseessä tosiaan on ikuinen silmukka.

Korjaus: lisätään silmukan alkuun if-lause, jolla nimien "." ja ".." käsittely jätetään väliin.

Nyt koodi ajetaan järkevässä ajassa loppuun, mutta tulos on silti väärä. Lisäksi tulee mm. tällainen varoitus:

PHP Warning: filemtime(): stat failed for koe.txt in /tmp/mib-koe/koe.php on line 11

Tiedän, että tiedosto "koe.txt" sijaitsee hakemistossa "alihakemisto". Selvästi siis filemtime-funktion kutsusta puuttuu se hakemiston nimi. Koodista voi päätellä, että se varmaan puuttuu muistakin vastaavista kohdista (is_dir ja haku).

Korjaus: vaihdetaan silmukassa tiedostonimeksi $tiedot-muuttujan sijaan "$hakemisto/$tiedot" kaikissa kolmessa kohdassa.

Hei, se toimii! Piti noinkin lyhyestä koodista korjata viisi asiaa, ja sitä ennen ruudulle tulostui tuhansia rivejä virheilmoituksia, joita koodin tekijä tai käyttäjä eivät ehkä olisi itse ymmärtänyt.

Tarinan opetus

MIB: Kun et vielä osaa kovin hyvin koodata, testaa jokainen koodisi huolellisesti ennen lähettämistä. Avunpyytäjä ei voi tietää ohjelmointitaidoistasi ja luultavasti olettaa, että koodisi toimii, ja on aika ikävää, kun puolet koodista onkin päin honkia. Itse taas olisin aivan hyvin voinut korjata tuonkin koodin kokeilematta sitä kertaakaan – eikä siihen tarvita mitään laajempaa tietämystä, vaan pitää vain ymmärtää, miten koodin suoritus etenee ja mitä arvoja funktiot eri tilanteissa palauttavat. Se on sitä ohjelmointitaitoa, josta tuolla C++-alueellakin puhuttiin. Se ei tule lukemalla, kyselemällä ja kopioimalla vaan harjoituksen kautta.

Flatologi: Kun saat koodia henkilöltä, joka poistaa ensimmäisen vastauksensa kommentilla "oho ei se toiminutkaan", pidä varasi: koodi voi olla vaarallista (jumittaa palvelimen pitkäksi aikaa) tai toimia yllättävällä tavalla väärin. Testaa siis ainakin pari kertaa, että lopputulos on oikea. Esimerkiksi tässähän tuloksen pitäisi vaihtua, jos muokkaat jotain toista tiedostoa.

Tässä luettelemieni korjausten tekeminen MIBin viestin koodiin jääköön teille harjoitusta kaipaaville. Kyllä sen pitäisi noilla ohjeilla onnistua. Lisäksi otatte toivottavasti oppia pitkässä selostuksessa esitetyistä päättelytavoista, joissa ei sinänsä ole mitään ihmeellistä mutta joilla saa korjattua valtaosan perusvirheistä.

reca [26.09.2009 13:38:38]

#

Metabolix: Aivan mahtavaa :-D

Flatologi [26.09.2009 13:53:00]

#

Itselle ei kylläkään tullut noita herjauksia, mutta päiväys on kylläkin väärin. Aikaisempi Antin laittama koodi tulostaa päiväyksen oikein, mutta jostain syystä se alkoi herjaamaan päiväyksen ohella, vaikka toimikin ensin täysin ok.
Täytyy perehtyä tuohon php-koodaukseen paremmin, tai siis opetella se, ei tästä muuten mitään tule. Toisaalta en sitä hirveästi tarvitse vielä, muttei siitä haittaakaan ole.

Metabolix: Kiitos huomioista.

Metabolix [26.09.2009 14:08:46]

#

Flatologi kirjoitti:

Itselle ei kylläkään tullut noita herjauksia, mutta päiväys on kylläkin väärin.

Tuo voi johtua siitä, että yleensä palvelimilla notice-tason herjat on laitettu täysin pois päältä ja varoituksetkin saattavat ohjautua lokitiedostoon. Tässähän vakavampia virheitä alkoi tulla vasta, kun tuon ensimmäisen virheen korjasi; aiemmin suurinta osaa koodista ei edes ajettu kertaakaan.

Mikä tarkalleen on Antin koodista tuleva ilmoitus?

Flatologi [26.09.2009 15:30:42]

#

Metabolix kirjoitti:

Mikä tarkalleen on Antin koodista tuleva ilmoitus?

Se on tuo ekassa viestissä oleva:
Warning: Invalid argument supplied for foreach() in /var/www/vhosts/oma.domain/httpdocs/lastmodified.php on line 7

Tuo tulee kahdesti peräkkäin ja alimmaisena oikea päiväys.

Metabolix [26.09.2009 16:56:02]

#

Glob saattaa palauttaa tyhjästä hakemistosta arvon false. Lisää siis koodiin tarkistus tämän varalta:

<?php

$lista = glob("$hak/*");
if (!empty($lista)) foreach ($lista as $tied) {
  //...
}

T.M. [26.09.2009 17:26:18]

#

eikös is_array() olisi loogisempi? sillä foreach syö vain taulukoita.

Metabolix [26.09.2009 17:28:57]

#

Samahan se tuossa on. Glob ei kuitenkaan palauta kuin taulukoita tai falsen, ja tyhjästä taulukosta ei ole foreachissa mitään iloa.

Flatologi [26.09.2009 17:29:28]

#

Tuo lisäys ei kylläkään muuttanut tilannetta.
Koodi toimi ennen, mutta se oli /testaus/ alihakemistossa. Nyt päähakemistossa se herjaa...

T.M. [26.09.2009 23:26:23]

#

Metabolix kirjoitti:

Samahan se tuossa on. Glob ei kuitenkaan palauta kuin taulukoita tai falsen, ja tyhjästä taulukosta ei ole foreachissa mitään iloa.

käsittääkseni glob() ei koskaan palauta tyhjää taulukkoa

Chiman [26.09.2009 23:35:44]

#

T.M. kirjoitti:

käsittääkseni glob() ei koskaan palauta tyhjää taulukkoa

Manuaali sanoo että voi palauttaa. Jos tiedät olevasi oikeassa, voit lähettää kommentin tuonne:
https://www.php.net/manual/en/function.glob.php

Taidat kuitenkin olla väärässä:

$ php -r 'print_r(glob("ihanjotainmuuta*"));'
Array
(
)

Edit: Ellet tarkoittanut aloitusviestin koodin tilannetta, jolloin voit olla oikeassa ajoympäristöstä riippuen. Windowsissa tyhjästä hakemistosta palautuisi tyhjä taulukko?

Metabolix [27.09.2009 14:28:37]

#

T.M. kirjoitti:

käsittääkseni glob() ei koskaan palauta tyhjää taulukkoa

Käsityksesi on manuaalin ja Chimanin esimerkin perusteella väärä.

https://www.php.net/glob:

Returns an array containing the matched files/directories, an empty array if no file matched or FALSE on error.

Note: On some systems it is impossible to distinguish between empty match and an error.

T.M. [27.09.2009 15:50:31]

#

joo pieni ajatusvirhe tuli, tietysti se palauttaa tyhjän taulukon jos kansio on esim tyhjä.

pointtini oli se että foreach toimii kuten pitää, kun sinne ei päästä kuin taulukoita.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta