Ljubitelji knjig ne prezrite: izdal sem nov spletni fantazijski roman imenovan Senca Temnega Coprnika. Preberete ga lahko popolnoma brezplačno!
MYSQL števec obiskov
Pred desetletjem so bili števci obiskov edini preprosti način za spremljanje števila obiskovalcev. Nato so se pojavile storitve za analitiko, ki so poleg štetja obiskovalcev ponujale tudi zbiranje brskalnih navad obiskovalcev. Čeprav vam lahko te storitve pomagajo pri izboljšavi vsebine na spletišču pa grobo posegajo v zasebnost obiskovalcev. V ta namen sem recimo jaz ukinil Google Analytics in se odločil, da bom število obiskovalcev spremljal zgolj preko preprostega števca obiskov, ki spoštuje njihovo zasebnost. Potrebujete PHP 5.3 ali novejši in podatkovno zbirko MYSQL z vklopljeno možnostjo CREATE.
Prejem
Izvorno kodo razreda za štetje obiskovalcev lahko prejmete s klikom na prejem. Morebitne napake in predloge za izboljšave pa mi sporočite preko knjige gostov ali mojega obrazca za stik.
Vključitev kode na vašo stran
Vso kodo sem spisal v obliki razreda Stevec, ki ga v vašo stran vključite preko ukaza include ali require. Nato morate razred začeti (in mu izbirno podati parametre), na koncu pa lahko število obiskovalcev bodisi izpišete s pomočjo ukaza echo, lahko pa tudi ne.
<?php include('Stevec.class.php'); $stevec = new Stevec(); // koda za povezovanje s podatkovno zbirko in ustvarjanje tabel // koda za izpis števila obiskovalcev in/ali napak ?>
Najprej je potrebno vzpostaviti povezavo s podatkovno zbirko. V ta namen morate podati veljavne podatke za povezavo s podatkovno zbirko kot polje v metodo prijava. Prijava mora biti izvedena takoj za ustvaritvijo primerka. Za prvi zagon je potrebno ustvariti primerne tabele. Če to ne želite narediti ročno, izvedite metodo ustvari_tabele z argumentom true. Po uspešni ustvaritvi tabel lahko to vrstico izbrišete, saj ni več zahtevana. Za ustvaritev tabel, mora imeti vaš uporabnik dovoljenje CREATE:
$stevec->prijava(array('gostitelj'=>'gostiteljsko ime', 'uporabnik'=>'uporabniško ime', 'geslo'=>'geslo za uporabnika', 'podatkovna_zbirka'=>'ime podatkovne zbirke')); $stevec->ustvari_tabele(true);
Sedaj imate dve možnosti. Prva je, da obiskovalce štejete javno in številko prikazujete nekje na strani. V ta namen lahko primerek izpišete s pomočjo ukaza echo. V primeru, da želite štetje prikriti, lahko samo izvedete metodo stej() in podatki o obiskih bodo zapisani v podatkovno zbirko, ne bodo pa prikazani. Razred shranjuje napake/izjeme v razredno spremenljivko napake, ki jo lahko tudi izpišete, če karkoli ne deluje kot bi moralo. Priporočeno je, da imate na javnosti dostopnih straneh to vrednost zakomentirano, saj lahko razkrije podrobnosti o vaši podatkovni zbirki.
echo $stevec; // ali $stevec->stej(); // echo $stevec->napaka; // odkomentirajte, če želite javni prikaz napak
Pregled razreda Stevec
Na začetku razreda imamo nastavljeno vidnost nekaterih spremenljivk. Te se naj ne bi nastavljale kar tako (to naj bi opravljale metode) ali pa sploh ne. Prilagodite lahko privzeti niz, ki se pokaže, če povezava s podatkovno zbirko ne uspe. Nastavite lahko imeni tabel, v katerih se bodo shranjevali vnosi. Na voljo je tudi nastavitev premora med štetji, ki ga sicer lahko nastavite tukaj, lažje pa je preko metode (več o tem kasneje).
<?php class Stevec { // te spremenljivke se ne nastavljajo neposredno private $db; private $obiski = 'neznano'; // privzeti niz, če povezava s podatkovno zbirko ne uspe private $tabela_ip = 'stevec_ip'; private $tabela_obiski = 'stevec_obiski'; private $premor = 86400; // privzet premor med štetji (v sekundah) // izgrajevalnik // metoda za povezovanje s podatkovno zbirko // metoda za ustvarjanje tabel // metoda za nastavljanje premora // metoda stej() // izpis števila obiskov } ?>
Izgrajevalnik sprejme polje s parametri za nastavljanje ob začenjanju razreda. V primeru vključitve na stran ste videli, da sem raje uporabil veriženje metod. Izberite tisto, kar vam bolj ustreza.
public function __construct($parametri = null) { // nastavi nekatere privzete spremenljivke in preveri obstoj vsakega parametra ter izvedi ustrezno metodo if (isset($parametri['prijava'])) { $this->prijava($parametri['prijava']); } if (isset($parametri['ustvari_tabele'])) { $this->ustvari_tabele($parametri['ustvari_tabele']); } if (isset($parametri['premor_med_stetji'])) { $this->premor_med_stetji($parametri['premor_med_stetji']); } }
Za delo s podatkovno zbirko uporabljamo vmesnik PDO (PHP Data Objects) namesto funkcij mysql_, saj bodo slednje v prihodnosti odstranjene iz kode. Primerek uspešno vzpostavljene povezave shranimo v razredno spremenljivko db. Morebitne napake/izjeme med povezovanjem s podatkovno zbirko so na voljo v razredni spremenljivki napaka.
public function prijava($podatki = null) { // vzpostavi povezavo s podatkovno zbirko if (!is_null($podatki) && isset($podatki['gostitelj']) && isset($podatki['uporabnik']) && isset($podatki['geslo']) && isset($podatki['podatkovna_zbirka'])) { try { $this->db = new PDO('mysql:host=' . $podatki['gostitelj'] . ';dbname=' . $podatki['podatkovna_zbirka'] . ';charset=utf8', $podatki['uporabnik'], $podatki['geslo']); // omogočimo izpis izjem $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { // napaka med vzpostavljanjem povezave $this->napaka = $e->getMessage(); } } }
Metoda za ustvarjanje tabel ustvari tabele za shranjevanje podatkov, če te še ne obstajajo. IP-ji se shranjujejo kot razpršilo MD5 s fiksno dolžino 32 znakov, čas dostopa kot celoštevilčni UNIX timestamp, tudi število obiskov je celoštevilčna vrednost, datum pa se zapiše ob začetku spremljanja števila obiskov. Ko so tabele uspešno ustvarjene, te metode ne potrebujete več. Morebitne napake/izjeme med ustvarjanje tabel so na voljo v razredni spremenljivki napaka.
public function ustvari_tabele($ustvari_tabele = false) { // ustvari tabele v podatkovni zbirki, če še ne obstajajo. Uporabnik mora imeti dovoljenja za CREATE, povezava s podatkovno zbirko mora biti vzpostavljena if (!is_null($this->db) && $ustvari_tabele === true) { try { $this->db->query('CREATE TABLE IF NOT EXISTS ' . $this->tabela_ip . ' ( ip char(32) NOT NULL, cas int(32) NOT NULL, UNIQUE KEY ip (ip) ) ENGINE=MyISAM DEFAULT CHARSET=utf8'); $this->db->query('CREATE TABLE IF NOT EXISTS ' . $this->tabela_obiski . ' ( obiski int(16) NOT NULL, datum date NOT NULL , UNIQUE KEY datum (datum) ) ENGINE=MyISAM DEFAULT CHARSET=utf8'); } catch (PDOException $e) { $this->napaka = 'Napaka med ustvarjanjem tabel: ' . "\n" . $e->getMessage(); } } return $this; }
Okrog metode za nastavljanje premora ni kaj preveč za filozofirati. Vi vnesete premor v urah in metoda poskrbi, da je premor med štetji veljavna celoštevilčna vrednost v sekundah. V to metodo lahko vključite tudi lastne pogoje.
public function premor_med_stetji($premor_med_stetji = null) { // nastavi premor med štetji. izhod = sekunde, vhod = ure if (!is_null($premor_med_stetji) && is_int($premor_med_stetji) && $premor_med_stetji > 0) { $this->premor = $premor_med_stetji*3600; } return $this; }
Metoda stej() je glavna metoda števca, ki vedno vrne vrednost razredne spremenljivke obiski. V njej najprej preverimo, če je povezava s podatkovno zbirko sploh vzpostavljena. Če ni, vrnemo privzeto vrednost razredne spremenljivke obiski in zaključimo. Če je povezava vzpostavljena, pridobimo trenuten čas ob obisku. Nato pridobimo IP obiskovalca in preverimo veljavnost, saj je lahko ta niz tudi prazen ali vsebuje karkoli drugega. Če zadeva ni veljavna, vrnemo privzeto vrednost razredne spremenljivke obiski in zaključimo. Sledi blok, ki skrbi za štetje. Morebitne napake/izjeme v tem bloku so na voljo v razredni spremenljivki napaka.
public function stej() { // glavna metoda števca if (is_null($this->db)) { // povezava ni vzpostavljena return $this->obiski; } $datum_cas = new DateTime(); $trenuten_cas = $datum_cas->getTimestamp(); $ip_obiskovalca = filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP); if ($ip_obiskovalca === false) { // IP obiskovalca ni veljaven, zato samo prikažemo število obiskov return $this->obiski; } try { // pridobivanje števila obiskov } catch (PDOException $e) { // zanimajo nas samo napake PDO $this->napaka = 'Napaka števca: ' . "\n" . $e->getMessage(); } return $this->obiski; }
Najprej poskušamo pridobiti število obiskov iz podatkovne zbirke. Če dobimo vsaj en vnos, potem najprej sledi štetje. V primeru, da je rezultat nič vrstic, vemo, da gre za prvi vnos v podatkovno zbirko. V ta namen pripravimo stavka SQL s pomočjo prepare in bindValue ter izvedemo s pomočjo execute. Priprava stavkov SQL v tem primeru sicer ni potrebna, saj je edini obiskovalčev vnos veljaven naslov IP, ki ga tako ali tako kodiramo z MD5, je pa znak dobre prakse.
// pridobimo število obiskov $q = $this->db->query('SELECT obiski, datum FROM ' . $this->tabela_obiski); if ($q->rowCount() > 0) { $obiski_polje = $q->fetchObject(); $datum = $obiski_polje->datum; $this->obiski = $obiski_polje->obiski; // brisanje starih vnosov // preverjanje vnosa za trenutnega obiskovalca // štetje } else { // to je krstni vnos $q = $this->db->prepare('INSERT INTO ' . $this->tabela_ip . ' (ip, cas) VALUES (:ip, :cas)'); $q->bindValue(':ip', md5($ip_obiskovalca), PDO::PARAM_STR); $q->bindValue(':cas', $trenuten_cas, PDO::PARAM_INT); $q->execute(); $this->obiski = 1; // vstavi obisk $q = $this->db->prepare('INSERT INTO ' . $this->tabela_obiski . ' (obiski, datum) VALUES (:obiski, :datum)'); $q->bindValue(':obiski', $this->obiski, PDO::PARAM_INT); $q->bindValue(':datum', $datum_cas->format('Y-m-d'), PDO::PARAM_STR); $q->execute(); }
Koda za štetje najprej izbriše vnose obiskovalcev, ki so starejši od naše omejitve. Nato poskuša iz tabele izbrati IP obiskovalca in če ji to ne uspe, vemo, da je obiskovalec v nastavljenem intervalu prvič na strani. Zato v podatkovno zbirko vstavimo njegova z MD5 kodiran IP in čas obiska. Ob uspešnem vnosu pa tudi posodobimo število obiskov in vrednost povečamo za 1. Spet uporabljamo predpripravo stavkov, čeprav tudi tukaj ni nujna.
// izbrišemo vnose obiskovalcev, starejše od naše omejitve $q = $this->db->prepare('DELETE FROM ' . $this->tabela_ip . ' WHERE cas < :cas'); $q->bindValue(':cas', ($trenuten_cas - $this->premor), PDO::PARAM_INT); $q->execute(); // sedaj poskusimo izbrati IP obiskovalca iz tabele $q = $this->db->prepare('SELECT ip FROM ' . $this->tabela_ip . ' WHERE ip = :ip'); $q->bindValue(':ip', md5($ip_obiskovalca), PDO::PARAM_STR); $q->execute(); if ($q->rowCount() == 0) { // obiskovalec v nastavljenem intervalu še ni bil na strani $q = $this->db->prepare('INSERT INTO ' . $this->tabela_ip . ' (ip, cas) VALUES (:ip, :cas)'); $q->bindValue(':ip', md5($ip_obiskovalca), PDO::PARAM_STR); $q->bindValue(':cas', $trenuten_cas, PDO::PARAM_INT); $q->execute(); // dodaj en obisk in posodobi števec $this->obiski++; $q = $this->db->prepare('UPDATE ' . $this->tabela_obiski . ' SET obiski = :obiski WHERE datum = :datum'); $q->bindValue(':obiski', $this->obiski, PDO::PARAM_INT); $q->bindValue(':datum', $datum, PDO::PARAM_STR); $q->execute(); }
Zadnja metoda skrbi za prikaz števila obiskov. Ker je vrnjena vrednost metode stej() lahko tudi celoštevilčna, jo moramo pretvoriti v niz.
public function __toString() { // pokliče števec in izpiše število obiskov return (string)$this->stej(); }