Kirjoittaja: Metabolix
Kirjoitettu: 03.11.2013 – 29.11.2013
Tagit: ohjelmointitavat, kirjasto, koodi näytille, vinkki
Nettisivut eivät toimi äärettömän nopeasti. Jos kaksi sivunlatausta tapahtuu samaan aikaan ja molemmilla käsitellään tiedostoa, lopputulos voi olla väärä tai jopa tuhoisa. Siksi tiedostot pitää joissain tilanteissa lukita. Tyypillinen tilanne on tiedoston muokkaus: lukitus on tarpeen, jotta lukijat eivät näe keskeneräistä tiedostoa vaan joko vanhan tai uuden version. Lukitusta voi käyttää myös sellaisissa tilanteissa, joissa itse toimenpide ei edes liity tiedostoon mutta halutaan vain estää samanaikainen suoritus.
Lukitusta varten tiedosto pitää ensin avata funktiolla fopen. Lukitseminen tapahtuu funktiolla flock. Funktion toiseksi parametriksi annetaan haluttu lukituksen tyyppi: LOCK_EX tarkoittaa eksklusiivista eli poissulkevaa lukitusta, jolloin kukaan muu ei voi samaan aikaan lukita tiedostoa. LOCK_SH taas on jaettu lukitus, jolloin muut voivat myös lukita tiedoston jaetusti mutta poissulkevaa lukitusta ei voi samaan aikaan olla. LOCK_UN vapauttaa nykyisen lukituksen.
Lyhyesti lukitus siis tapahtuu näin:
$f = fopen("tiedosto.txt", "r+"); if (flock($f, LOCK_EX)) { // Tässä välissä tehdään jotain. // Lopuksi avataan lukitus. flock($f, LOCK_UN); } else { echo "Lukitus epäonnistui!"; } fclose($f);
Seuraava luokka toteuttaa lukituksen helposti. Lopussa on esimerkki luokan käytöstä.
<?php /** * Luokka tiedoston lukitsemiseen flock-funktiolla. */ final class Tiedostolukko { private $tiedostonimi; private $kahva; /** * Muodostin. Tavallisesti myös lukitsee tiedoston heti. * * @param $tiedostonimi Lukittavan tiedoston nimi. * @param $lukitse Lukitaanko tiedosto heti? */ public function __construct($tiedostonimi, $lukitse = true) { $this->tiedostonimi = $tiedostonimi; if ($lukitse) { $this->lukitse(); } } /** * Tuhoaja. Avaa lukituksen. */ public function __destruct() { $this->avaa(); } /** * Lukitsee tiedoston. */ public function lukitse() { if (!$this->kahva) { $this->kahva = fopen($this->tiedostonimi, "r+"); if (!flock($this->kahva, LOCK_EX)) { throw new RuntimeException("Virhe lukituksessa."); } } } /** * Avaa lukituksen. */ public function avaa() { if ($this->kahva) { flock($this->kahva, LOCK_UN); fclose($this->kahva); $this->kahva = null; } } }
Seuraavassa esimerkissä luokkaa käytetään lukitukseen, kun koodissa tehdään jokin tärkeä temppu, jota ei saa tehdä monta kertaa päällekkäin. Lisäksi funktiolla ignore_user_abort määrätään, että skriptin suoritus jatkuu, vaikka käyttäjä painaisi selaimen stop-napista; näin tärkeä temppu ei jää kesken.
if (temput_tekemättä()) { // Ei päästetä muita tähän kohtaan samaan aikaan. $lukko = new Tiedostolukko(__FILE__); // Tarkistetaan tilanne uudestaan ja tehdään temput. if (temput_tekemättä()) { $ignore_vanha = ignore_user_abort(true); tee_temput(); ignore_user_abort($ignore_vanha); } $lukko->avaa(); }
Hieno koodivinkki kun olen itsekin tarvinnut tätä.
Kiitos paljon!