Classe php la gestione delle visite

criric

Super Moderatore
Membro dello Staff
SUPER MOD
MOD
21 Ago 2010
5.606
54
48
TN
Ciao a tutti,
inauguro questa sezione pubblicando una classe per la gestione delle visite
La classe può essere implementata con la ricerca per data o con la geolocalizzazione per esempio
Manca completamente una gestione degli errori
La classe cmq è abbastanza commentata non dovreste avere difficoltà, al massimo chiedete pure

Ho preparato un esempio funzionante (spero) online a questo indirizzo, la stessa pagina registrerà la visita e mostrerà la tabella statistiche

Iniziamo, occorrono tre tabelle
- agent
memorizziamo gli user agent degli utenti per rispoarmiare spazio sul database
Codice:
CREATE TABLE IF NOT EXISTS `agent` (
  `idAgent` int(11) NOT NULL AUTO_INCREMENT,
  `agent` varchar(255) NOT NULL,
  PRIMARY KEY (`idAgent`),
  KEY `agent` (`agent`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;
- pagine
per lo stesso motivo cui sopra memorizziamo i nomi delle pagine. Ho gia inserito una pagina di test
Codice:
CREATE TABLE IF NOT EXISTS `pagine` (
  `idPagina` int(11) NOT NULL AUTO_INCREMENT,
  `nome` varchar(155) NOT NULL,
  PRIMARY KEY (`idPagina`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

INSERT INTO `pagine` (`idPagina`, `nome`) VALUES
(1, 'Pagina di test');
- visite
verranno memorizzati tutti i dati necessari della visita ricevuta.
Codice:
CREATE TABLE IF NOT EXISTS `visite` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(155) NOT NULL,
  `idAgent` int(11) NOT NULL,
  `data` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `lingua` varchar(2) NOT NULL,
  `referer` varchar(255) NOT NULL,
  `idPagina` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `data` (`data`),
  KEY `idPagina` (`idPagina`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;
Passiamo alla classe. Vi ricordo che non è completa e che probabilmente la aggiornerò piu avanti seguendo i vostri consigli
PHP:
<?php

/**
 * 03 settembre 2013
 * @criric
 * licenza free
 * fatene quello che volete, tenete conto che la classe non è completa 
 */
class Visite {

    // oggetto mysqli
    var $sql;
    // date per la ricerca
    var $dal;
    var $al;

    /**
     * Nel costruttore istanzio la classe per la connessione al database
     * Setto le proprietà per il periodo di ricerca
     * Non ancora implementato ma prevede date ricevute tramite POST
     */
    public function __construct() {
        // istanzio la classe per la connessione al database
        $this->sql = new mysqli("localhost", "root", "", "database");        
        // Setto le proprietà per il periodo di ricerca
        $this->dal = date("Y-m-") . "01";
        // se settata la data in POST
        if (isset($_POST['dal'])) {
            $this->dal = $_POST['dal'];
        }
        $this->al = date("Y-m-d");
        // se settata la data in POST
        if (isset($_POST['al'])) {
            $this->al = $_POST['al'];
        }
    }

    /**
     * sfrutto il distruttore per chiudere la connessione al db
     */
    public function __destruct() {
        $this->sql->close();
    }

    /**
     * Questo metodo registra la visita nella tabella visite
     * Prende come parametro l'id della pagina
     * Non implementata la gestione degli errori
     * @param type $idPagina
     * @return type boolean
     */
    public function salva_visita($idPagina) {
        // recupero il link da dove è arrivato il visitatore
        $referer = (isset($_SERVER["HTTP_REFERER"])) ? $_SERVER["HTTP_REFERER"] : "n/d";
        // richiamo il metodo per l'agent
        $idAgent = $this->id_agent();
        // query di inserimento
        $query = "INSERT INTO visite 
                              SET ip = '" . $_SERVER["REMOTE_ADDR"] . "',
                                  idAgent = '" . $idAgent . "',
                                  lingua = '" . substr($_SERVER["HTTP_ACCEPT_LANGUAGE"], 0, 2) . "',
                                  referer = '" . $referer . "',
                                  idPagina = '" . $idPagina . "'";
        // eseguo
        if (!$this->sql->query($query)) {
            // da implementare la gestione degli errori
            $error = $this->sql->error;
            return false;
        }
        // tutto ok
        return true;
    }

    /**
     * Per risparmiare spazio sul db 
     * questo metodo verifica se l'agent del visitatore è gia memorizzato nella tabella agent
     * Restituisce l'id corrispondente
     * @return type int
     */
    private function id_agent() {
        // cerco l'agent nella tabella agent
        $query = "SELECT idAgent FROM agent WHERE agent = '" . $_SERVER["HTTP_USER_AGENT"] . "'";
        // eseguo la query
        $result = $this->sql->query($query);
        // se trovo un risultato
        if ($result && $result->num_rows > 0) {
            $row = $result->fetch_row();
            // restituisco l'id corrispondente
            return $row[0];
        }
        // altrimenti inserisco in tabella il nuovo agent
        $query = "INSERT INTO agent SET agent = '" . $_SERVER["HTTP_USER_AGENT"] . "'";
        if ($this->sql->query($query)) {
            // restituisco l'id corrispondente
            return $this->sql->insert_id;
        }
        // qualcosa è andato storto : da implementare uina gestione per gli errori
        return false;
    }

    /**
     * Questo metodo restituisce il numero di record per la ricerca passata nei parametri
     * Usa le proprieta per il periodo di ricerca
     * @param type $pagina boolean 
     * @param type $uniche boolean se true raggruppa per ip
     * @return type int
     */
    public function conta_visite($pagina = false, $uniche = false) {
        $query = "SELECT id
                        FROM visite 
                        WHERE data BETWEEN '" . $this->dal . "' AND DATE_ADD('" . $this->al . "', INTERVAL 1 DAY)";
        if ($pagina) {
            $query .= " && idPagina = '" . $pagina . "'";
        }
        if ($uniche) {
            $query .= " GROUP BY ip";
        }
        $result = $this->sql->query($query);
        if ($result) {
            return $result->num_rows;
        }
        // qualcosa è andato storto : da implementare uina gestione per gli errori
        return false;
    }

    /**
     * Potrebbe essere necessario nascondere parte dell'ip
     * questo metodo nasconde le ultime due parti dell'ip passato come parametro
     * @param type $ip
     * @return string 
     * 
     */
    private function nascondi_ip($ip) {
        $ex = explode(".", $ip);
        $return = $ex[0] . "." . $ex[1] . ".";
        for ($i = 0; $i < strlen($ex[2]); $i++) {
            $return .= "*";
        }
        $return .= ".";
        for ($i = 0; $i < strlen($ex[3]); $i++) {
            $return .= "*";
        }
        return $return;
    }

    /**
     * Questo metodo stampa la tabella delle visite
     * Usa le proprieta per il periodo di ricerca
     * Usa i dati ricevuti in post per filtrare la ricerca
     * Implementata la paginazione
     * @param type $pagina 
     */
    public function tabella_visite() {
        // imposto l'id pagina a false per visualizzare tutti i record
        // non implementata la ricerca per altre pagine
        $idPagina = false;
        // verifico se è settata una pagina in particolare
        if (isset($_POST['id_pag'])) {
            $idPagina = (int) $_POST['id_pag'];
            // se id pagina è arrivato in forma corretta
            if ((int) $_POST['id_pag'] > 0) {
                // setto l'id pagina per la ricerca
                $idPagina = (int) $_POST['id_pag'];
            }
        }
        // imposto la prima pagina da visualizzare
        $num_pag = 1;
        // verifico che sia settata la pagina da visualizzare
        if (isset($_POST['num_pag'])) {
            $num_pag = (int) $_POST['num_pag'];
            // se la pagina è arrivata in forma corretta
            if ((int) $_POST['num_pag'] > 0) {
                // setto la prima pagina
                $num_pag = (int) $_POST['num_pag'];
            }
        }
        // imposto il limite di risultati per pagina
        $limite_pag = 10;
        // verifico se settato il limite per pagina
        if (isset($_POST['limit'])) {
            // se il limite è arrivato in forma corretta
            if ((int) $_POST['limit'] > 0) {
                // reimposto il limite di risultati per pagina
                $limite_pag = (int) $_POST['limit'];
            }
        }
        // imposto il limite minimo da usare nella query
        if ($num_pag <= 1) {
            $limite_min = 0;
        } else {
            $limite_min = ($num_pag - 1) * $limite_pag;
        }
        // imposto il limite massimo sul quale finirà la ricerca
        $limite_max = $limite_min + $limite_pag;
        // recupero il numero totale di visite
        $totali = $this->conta_visite($pagina);
        // recupero il numero di visite uniche
        $uniche = $this->conta_visite($pagina, true);
        // se il limite massimo supera i record totali lo fermo al numero di record totali
        if ($totali <= $limite_max) {
            $limite_max = $totali;
        }
        // recupero il numero di pagine totali
        $pagine = ceil($totali / $limite_pag);
        // query di ricerca
        $query = "SELECT V.lingua, V.ip, A.agent, V.referer,
                IF(P.nome IS NULL, 'n/d', P.nome) as nomePagina,
                DATE_FORMAT(V.data, '%d/%m/%Y %H:%i:%s') as data
                FROM visite V
                LEFT JOIN pagine P USING(idPagina)
                JOIN agent A USING(idAgent)
                WHERE data BETWEEN '" . $this->dal . "' AND DATE_ADD('" . $this->al . "', INTERVAL 1 DAY)";
        if ($idPagina) {
            $query .= " && pagina = '" . $idPagina . "'";
        }
        $query .= " ORDER BY data DESC LIMIT $limite_min, $limite_pag";
        // eseguo
        $result = $this->sql->query($query);

        echo "<form method='post' action='" . $_SERVER['PHP_SELF'] . "' > ";
        echo "<table>";
        echo "<thead>";
        echo "<tr class='filtri'>";
        echo "<td colspan='7'>";
        echo "Report visite dal " . $this->format_data_it($this->dal) . " al " . $this->format_data_it($this->al);
        echo "<input type='hidden' name='num_pag' value='1'/>";
        echo "</td>";
        echo "</tr>";
        // se la query ha prodotto dei riultati
        if ($result->num_rows > 0) {
            echo "<tr><td colspan='7'>";
            echo "<span class='fleft'>";
            echo "Totale visite  :  $totali Uniche : " . $uniche;
            echo "</span>";
            echo "<span class='fright'>";
            echo "Record per pagina <select name='limit' onchange='this.form.submit()'>";
            for ($i = 5; $i <= 50; $i+=5) {
                echo "<option value='$i' ";
                if ($i == $limite_pag)
                    echo " selected='selected'";
                echo ">$i</option>";
            }
            echo "</select>";
            echo "</span>";
            echo "</td></tr>";
            echo "<tr>";
            echo "<th>Data</th>";
            echo "<th>Pagina</th>";
            echo "<th>Sistema operativo</th>";
            echo "<th>Browser</th>";
            echo "<th>Lingua</th>";
            echo "<th>Ip</th>";
            echo "<th>Referer</th>";
            echo "</tr>";
            echo "</thead>";
            echo "<tbody>";
            $i = 0;
            while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
                $alternate = ($i % 2 == 0) ? "class='alternate'" : "";
                echo "<tr $alternate>";
                echo "<td>" . $row["data"] . "</td>";
                echo "<td>" . $row["nomePagina"] . "</td>";
                echo "<td>" . $this->sistema_operativo($row["agent"]) . "</td>";
                echo "<td>" . $this->browser($row["agent"]) . "</td>";
                echo "<td class='center'>" . $row["lingua"] . "</td>";
                echo "<td>" . $this->nascondi_ip($row["ip"]) . "</td>";
                echo "<td class='center info' title='" . $row["referer"] . "'> ? </td>";
                echo "</tr>";
                $i++;
            }
            echo "<tr>";
            echo "<td colspan='7' class='infopag'>";

            if ($num_pag <= 1) {
                $click = "onclick='return false'";
            } else {
                $click = "onclick='ImpostaPagina(" . ($num_pag - 1) . "," . $limite_pag . ")'";
            }
            echo "<span onclick='ImpostaPagina(1," . $limite_pag . ")' title='prima' class='navpag'>&lt;&lt;</span>";
            echo "<span title='precedente' class='navpag' $click>&lt;</span>";
            echo "<span class='navinfo'>Pagina $num_pag / $pagine</span>";
            if ($num_pag >= $pagine) {
                $click = "onclick='return false'";
            } else {
                $click = "onclick='ImpostaPagina(" . ($num_pag + 1) . "," . $limite_pag . ")'";
            }
            echo "<span title='sucessiva' class='navpag' $click>&gt;</span>";
            echo "<span onclick='ImpostaPagina(" . $pagine . "," . $limite_pag . ")' title='ultima' class='navpag'>&gt;&gt;</span>";
            echo "</td>";
            echo "</tr>";
            echo "</tbody>";
            // altrimenti
        } else {
            // nessun riultato
            echo "<tr><td colspan='7'>Nessun record trovato</td></tr>";
        }
        echo "</table>";
        echo "</form>";
    }

    /**
     * Restituisce la data formattata in italiano 
     * @param stringa $data yyyy-mm-dd hh:ii:ss
     * @param int $ora 1 o 0 mostra o meno l'ora
     * @return stringa 
     */
    private function format_data_it($data, $ora = 1) {

        $split = explode(" ", $data);
        $d = explode("-", $split[0]);
        $d = array_reverse($d);

        if ($ora == 1) {
            return implode("/", $d) . " " . $split[1];
        } else {
            return implode("/", $d);
        }
    }

    /**
     * Estrae dall'agent il sistema operativo
     * è possibile inserire nell'array i sistemi mancanti seguendo la stessa loogica
     * @param type $agent
     * @return type 
     */
    private function sistema_operativo($agent) {
        $so = array(
            'Windows NT 6.2' => 'Windows 8',
            'Windows NT 6.1' => 'Windows 7',
            'Windows NT 6.0' => 'Windows vista',
            'Windows NT 5.1' => 'Windows XP',
            'Windows NT 5.0' => 'Windows 2000',
            'Windows NT 4.90' => 'Windows ME',
            'Win95' => 'Windows 95',
            'Win98' => 'Windows 98',
            'Windows NT 5.2' => 'Windows NET',
            'WinNT4.0' => 'Windows NT',
            'Mac|PPC' => 'Mac',
            'Linux' => 'Linux',
            'FreeBSD' => 'FreeBSD',
            'SunOS' => 'SunOS',
            'Irix' => 'Irix',
            'BeOS' => 'BeOS',
            'OS/2' => 'OS/2',
            'AIX' => 'AIX'
        );
        foreach ($so as $chiave => $valore) {
            if (strstr($agent, $chiave)) {
                return $valore;
            }
        }
        return 'n/d';
    }

    /**
     * Estrae dall'agent il browser
     * è possibile inserire nell'array i browser mancanti seguendo la stessa loogica
     * @param type $agent
     * @return type 
     */
    private function browser($agent) {
        $browser = array(
            "Internet Explorer 9" => "MSIE 9",
            "Internet Explorer 8" => "MSIE 8",
            "Internet Explorer 7" => "MSIE 7",
            "Internet Explorer 6" => "MSIE 6",
            "Internet Explorer" => "MSIE",
            "Chrome" => "Chrome",
            "Mozilla Firefox 23" => "Firefox/23",
            "Mozilla Firefox 20" => "Firefox/20",
            "Mozilla Firefox 8" => "Firefox/8",
            "Mozilla Firefox 7" => "Firefox/7",
            "Mozilla Firefox 6" => "Firefox/6",
            "Mozilla Firefox 5" => "Firefox/5",
            "Mozilla Firefox 4" => "Firefox/4",
            "Mozilla Firefox 3" => "Firefox/3",
            "Mozilla Firefox" => "Firefox",
            "Lynx" => "Lynx",
            "Opera 10" => "Opera/10",
            "Opera 9" => "Opera/9",
            "Opera" => "Opera",
            "Konqueror" => "Konqueror",
            "Safari" => "Safari",
            "GoogleBot" => "Googlebot",
            "BingBot" => "bingbot",
            "W3C" => "W3C"
        );

        foreach ($browser as $chiave => $valore) {
            if (strstr($agent, "$valore")) {
                return $chiave;
            }
        }
        return "n/d";
    }

}

?>
Per far funzionare la paginazione è necessaria una piccola funzione javascript che reimposta i valori della pagina e dei record da visualizzare
HTML:
<script type="text/javascript">
            function ImpostaPagina(pagina,limite){
                document.forms[0].elements['limit'].value = limite;
                document.forms[0].elements['num_pag'].value = pagina;
                document.forms[0].submit();
            }
        </script>
Per registrare correttamente la visita bisogna inserire nella tabella pagine il nome della pagina soggetta alla registrazione delle visite e richiamare il metodo salva_visita(id) passandogli l'id
PHP:
<?php
// includo il file che contiene la classe
include 'visite_class.php';
// istanzio la classe
$visite = new Visite();
// richiamo il metodo passandogli l'id della pagina
$visite->salva_visita(1);
?>
Per visualizzare la tabella delle statistiche è sufficiente richiamare in una pagina il metodo tabella_visite()
PHP:
<?php
// includo il file che contiene la classe
include 'visite_class.php';
// istanzio la classe
$visite = new Visite();
// stampo la tabella visite
$visite->tabella_visite();
?>
Per l'esempio online ho usato il seguente codice css
Codice:
html body {
    text-align: center;
    font-family: Tahoma;
    font-size: 13px;
    color:#456;
}
a {                
    color:#456;
}
table {
    border-collapse: collapse;
    margin:auto;
    box-shadow: 10px 10px 5px #888;
}
table th {
    text-align: left;
    background-color: #4A4A4A;
    color: #FFFFFF;
    font-weight: bold;
    padding: 10px;
}
table td {
    border: 1px solid #D2D2D2;
    padding: 8px 10px 8px 10px;
}
table tr.alternate {
    background-color: #ECECEC;
}
.center {
    text-align: center;
}
.left {
    text-align: left;
}
table td.infopag {
    text-align: center;
    border: none;
    padding: 10px;
}
span.navpag {
    border: 1px solid #D2D2D2;
    border-radius: 4px;
    padding:2px 6px 2px 6px;
    text-decoration: none;
    letter-spacing: -3px;
    cursor:pointer;
    margin: 0 1px 0 1px;
}
span.navinfo {
    margin: 0 8px 0 8px;
}
.info {
    cursor: help;
    font-weight: bold;
}
.fright {
    float: right;
}
.fleft {
    float: left;
}
.filtri {
    font-weight: bold;
}
Che altro aggiungere? :byebye:
 
Ultima modifica:
Ho aggiornato l'esempio e la classe aggiungendo due metodi per la geolocalizzazione sfruttando un servizio gratuito per webmaster
Ho modificato leggermente la tabella e ho aggiornato i metodi per il riconoscimento del browser e del s.o
Per la geolocalizzazione ho creato una nuova tabella
Codice:
CREATE TABLE IF NOT EXISTS `geoloc` (
  `ip` varchar(155) NOT NULL,
  `data` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `geoloc` text NOT NULL,
  KEY `ip` (`ip`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Mi sono accorto di non aver indicizzato le tabelle sarebbe convienente metter degli indici per velocizzare le ricerche
Riposto la classe aggiornata
PHP:
<?php

/**
 * 03 settembre 2013
 * @criric
 * 05 settembre 2013
 * aggiunti ddue metodi per la geolocalizzazione aggiornati i metodi per riconoscere so e browser
 * 
 * licenza free
 * fatene quello che volete, tenete conto che la classe non è completa 
 */
class Visite {

    // oggetto mysqli
    var $sql;
    // date per la ricerca
    var $dal;
    var $al;

    /**
     * Nel costruttore istanzio la classe per la connessione al database
     * Setto le proprietà per il periodo di ricerca
     * Non ancora implementato ma prevede date ricevute tramite POST
     */
    public function __construct() {
        // istanzio la classe per la connessione al database
        $this->sql = new mysqli("localhost", "root", "", "database");        
        // Setto le proprietà per il periodo di ricerca
        $this->dal = date("Y-m-") . "01";
        // se settata la data in POST
        if (isset($_POST['dal'])) {
            $this->dal = $_POST['dal'];
        }
        $this->al = date("Y-m-d");
        // se settata la data in POST
        if (isset($_POST['al'])) {
            $this->al = $_POST['al'];
        }
    }

    /**
     * sfrutto il distruttore per chiudere la connessione al db
     */
    public function __destruct() {
        $this->sql->close();
    }

    /**
     * Questo metodo registra la visita nella tabella visite
     * Prende come parametro l'id della pagina
     * Non implementata la gestione degli errori
     * @param type $idPagina
     * @return type boolean
     */
    public function salva_visita($idPagina) {
        // richiamo il metodo geoloc
        $this->geoloc();
        // recupero il link da dove è arrivato il visitatore
        $referer = (isset($_SERVER["HTTP_REFERER"])) ? $_SERVER["HTTP_REFERER"] : "n/d";
        // richiamo il metodo per l'agent
        $idAgent = $this->id_agent();
        // query di inserimento
        $query = "INSERT INTO visite 
                              SET ip = '" . $_SERVER["REMOTE_ADDR"] . "',
                                  idAgent = '" . $idAgent . "',
                                  lingua = '" . substr($_SERVER["HTTP_ACCEPT_LANGUAGE"], 0, 2) . "',
                                  referer = '" . $referer . "',
                                  idPagina = '" . $idPagina . "'";
        // eseguo
        if (!$this->sql->query($query)) {
            // da implementare la gestione degli errori
            $error = $this->sql->error;
            return false;
        }
        // tutto ok
        return true;
    }

    /**
     * Verifica se l'ip è gia presente da meno di una settimana nella tabella geoloc
     * Se non presente
     * frutta un servizio gratuito (http://www.geoplugin.com/) per la geolocalizzazione dell'indirizzo ip
     * inserisce in tabella il nuovo ip con il json ricevuto
     * 
     */
    private function geoloc() {

        // cerco l'ip nella tabella geoloc
        $query = "SELECT ip FROM geoloc 
                         WHERE ip = '" . $ip . "' && data >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)";
        // eseguo la query
        $result = $this->sql->query($query);
        // se trovo un risultato esco
        if ($result && $result->num_rows > 0) {
            return true;
        }
        // richiamo il servizio di http://www.geoplugin.com/
        $ip = $_SERVER['REMOTE_ADDR'];
        $geo = file_get_contents('http://www.geoplugin.net/json.gp?ip=' . $ip);
        // inserisco i nuovi dati
        $query = "INSERT INTO geoloc SET ip ='$ip',geoloc='" . $this->sql->real_escape_string($geo) . "'";
        // eseguo
        if (!$this->sql->query($query)) {
            // da implementare la gestione degli errori
            $error = $this->sql->error;
            return false;
        }
        // tutto ok
        return true;
    }

    /**
     * Codifica il json in tabella e restituisce la regione o la nazione
     */
    private function info_geoloc($ip) {
        $query = "SELECT geoloc FROM geoloc 
                         WHERE ip = '" . $ip . "' && data >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)";
        $result = $this->sql->query($query);
        if ($result && $result->num_rows > 0) {
            $row = $result->fetch_row();
            $json = json_decode($row[0]);
            $return = $json->geoplugin_countryName;
            if (!empty($json->geoplugin_region)) {
                $return = $json->geoplugin_region;
            }
            return $return;
        }
        return "n/d";
    }

    /**
     * Per risparmiare spazio sul db 
     * questo metodo verifica se l'agent del visitatore è gia memorizzato nella tabella agent
     * Restituisce l'id corrispondente
     * @return type int
     */
    private function id_agent() {
        // cerco l'agent nella tabella agent
        $query = "SELECT idAgent FROM agent WHERE agent = '" . $_SERVER["HTTP_USER_AGENT"] . "'";
        // eseguo la query
        $result = $this->sql->query($query);
        // se trovo un risultato
        if ($result && $result->num_rows > 0) {
            $row = $result->fetch_row();
            // restituisco l'id corrispondente
            return $row[0];
        }
        // altrimenti inserisco in tabella il nuovo agent
        $query = "INSERT INTO agent SET agent = '" . $_SERVER["HTTP_USER_AGENT"] . "'";
        if ($this->sql->query($query)) {
            // restituisco l'id corrispondente
            return $this->sql->insert_id;
        }
        // qualcosa è andato storto : da implementare uina gestione per gli errori
        return false;
    }

    /**
     * Questo metodo restituisce il numero di record per la ricerca passata nei parametri
     * Usa le proprieta per il periodo di ricerca
     * @param type $pagina boolean 
     * @param type $uniche boolean se true raggruppa per ip
     * @return type int
     */
    public function conta_visite($pagina = false, $uniche = false) {
        $query = "SELECT id
                        FROM visite 
                        WHERE data BETWEEN '" . $this->dal . "' AND DATE_ADD('" . $this->al . "', INTERVAL 1 DAY)";
        if ($pagina) {
            $query .= " && idPagina = '" . $pagina . "'";
        }
        if ($uniche) {
            $query .= " GROUP BY ip";
        }
        $result = $this->sql->query($query);
        if ($result) {
            return $result->num_rows;
        }
        // qualcosa è andato storto : da implementare uina gestione per gli errori
        return false;
    }

    /**
     * Potrebbe essere necessario nascondere parte dell'ip
     * questo metodo nasconde le ultime due parti dell'ip passato come parametro
     * @param type $ip
     * @return string 
     * 
     */
    private function nascondi_ip($ip) {
        $ex = explode(".", $ip);
        $return = $ex[0] . "." . $ex[1] . ".";
        for ($i = 0; $i < strlen($ex[2]); $i++) {
            $return .= "*";
        }
        $return .= ".";
        for ($i = 0; $i < strlen($ex[3]); $i++) {
            $return .= "*";
        }
        return $return;
    }

    /**
     * Questo metodo stampa la tabella delle visite
     * Usa le proprieta per il periodo di ricerca
     * Usa i dati ricevuti in post per filtrare la ricerca
     * Implementata la paginazione
     * @param type $pagina 
     */
    public function tabella_visite() {
        // imposto l'id pagina a false per visualizzare tutti i record
        // non implementata la ricerca per altre pagine
        $idPagina = false;
        // verifico se è settata una pagina in particolare
        if (isset($_POST['id_pag'])) {
            $idPagina = (int) $_POST['id_pag'];
            // se id pagina è arrivato in forma corretta
            if ((int) $_POST['id_pag'] > 0) {
                // setto l'id pagina per la ricerca
                $idPagina = (int) $_POST['id_pag'];
            }
        }
        // imposto la prima pagina da visualizzare
        $num_pag = 1;
        // verifico che sia settata la pagina da visualizzare
        if (isset($_POST['num_pag'])) {
            $num_pag = (int) $_POST['num_pag'];
            // se la pagina è arrivata in forma corretta
            if ((int) $_POST['num_pag'] > 0) {
                // setto la prima pagina
                $num_pag = (int) $_POST['num_pag'];
            }
        }
        // imposto il limite di risultati per pagina
        $limite_pag = 10;
        // verifico se settato il limite per pagina
        if (isset($_POST['limit'])) {
            // se il limite è arrivato in forma corretta
            if ((int) $_POST['limit'] > 0) {
                // reimposto il limite di risultati per pagina
                $limite_pag = (int) $_POST['limit'];
            }
        }
        // imposto il limite minimo da usare nella query
        if ($num_pag <= 1) {
            $limite_min = 0;
        } else {
            $limite_min = ($num_pag - 1) * $limite_pag;
        }
        // imposto il limite massimo sul quale finirà la ricerca
        $limite_max = $limite_min + $limite_pag;
        // recupero il numero totale di visite
        $totali = $this->conta_visite($pagina);
        // recupero il numero di visite uniche
        $uniche = $this->conta_visite($pagina, true);
        // se il limite massimo supera i record totali lo fermo al numero di record totali
        if ($totali <= $limite_max) {
            $limite_max = $totali;
        }
        // recupero il numero di pagine totali
        $pagine = ceil($totali / $limite_pag);
        // query di ricerca
        $query = "SELECT V.lingua, V.ip, A.agent, V.referer,
                IF(P.nome IS NULL, 'n/d', P.nome) as nomePagina,
                DATE_FORMAT(V.data, '%d/%m/%Y %H:%i:%s') as data
                FROM visite V
                LEFT JOIN pagine P USING(idPagina)
                JOIN agent A USING(idAgent)
                WHERE data BETWEEN '" . $this->dal . "' AND DATE_ADD('" . $this->al . "', INTERVAL 1 DAY)";
        if ($idPagina) {
            $query .= " && pagina = '" . $idPagina . "'";
        }
        $query .= " ORDER BY data DESC LIMIT $limite_min, $limite_pag";
        // eseguo
        $result = $this->sql->query($query);
        echo "<form method='post' action='" . $_SERVER['PHP_SELF'] . "' > ";
        echo "<p class='filtri'>";
        echo "Report visite dal " . $this->format_data_it($this->dal) . " al " . $this->format_data_it($this->al);

        echo "</p>";

        echo "<input type='hidden' name='num_pag' value='1'/>";
        echo "<table>";
        echo "<thead>";

        // se la query ha prodotto dei riultati
        if ($result->num_rows > 0) {
            echo "<tr><td colspan='8'>";
            echo "<span class='fleft'>";
            echo "Totale visite  :  $totali Uniche : " . $uniche;
            echo "</span>";
            echo "<span class='fright'>";
            echo "Record per pagina <select name='limit' onchange='this.form.submit()'>";
            for ($i = 5; $i <= 50; $i+=5) {
                echo "<option value='$i' ";
                if ($i == $limite_pag)
                    echo " selected='selected'";
                echo ">$i</option>";
            }
            echo "</select>";
            echo "</span>";
            echo "</td></tr>";
            echo "<tr>";
            echo "<th>Data</th>";
            echo "<th>Pagina</th>";
            echo "<th>Sistema operativo</th>";
            echo "<th>Browser</th>";
            echo "<th>Lingua</th>";
            echo "<th>Ip</th>";
            echo "<th>Geolocation</th>";
            echo "<th>Referer</th>";
            echo "</tr>";
            echo "</thead>";
            echo "<tbody>";
            $i = 0;
            while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
                $alternate = ($i % 2 == 0) ? "class='alternate'" : "";
                echo "<tr $alternate>";
                echo "<td>" . $row["data"] . "</td>";
                echo "<td>" . $row["nomePagina"] . "</td>";
                echo "<td>" . $this->sistema_operativo($row["agent"]) . "</td>";
                echo "<td>" . $this->browser($row["agent"]) . "</td>";
                echo "<td class='center'>" . $row["lingua"] . "</td>";
                echo "<td class='left'>" . $this->nascondi_ip($row["ip"]) . "</td>";
                echo "<td>" . $this->info_geoloc($row["ip"]) . "</td>";
                echo "<td class='center info' title='" . $row["referer"] . "'> ? </td>";
                echo "</tr>";
                $i++;
            }
            echo "<tr>";
            echo "<td colspan='8' class='infopag'>";

            if ($num_pag <= 1) {
                $click = "onclick='return false'";
            } else {
                $click = "onclick='ImpostaPagina(" . ($num_pag - 1) . "," . $limite_pag . ")'";
            }
            echo "<span onclick='ImpostaPagina(1," . $limite_pag . ")' title='prima' class='navpag'>&lt;&lt;</span>";
            echo "<span title='precedente' class='navpag' $click>&lt;</span>";
            echo "<span class='navinfo'>Pagina $num_pag / $pagine</span>";
            if ($num_pag >= $pagine) {
                $click = "onclick='return false'";
            } else {
                $click = "onclick='ImpostaPagina(" . ($num_pag + 1) . "," . $limite_pag . ")'";
            }
            echo "<span title='sucessiva' class='navpag' $click>&gt;</span>";
            echo "<span onclick='ImpostaPagina(" . $pagine . "," . $limite_pag . ")' title='ultima' class='navpag'>&gt;&gt;</span>";
            echo "<span  class='fright'><a href='http://www.geoplugin.com/geolocation/' target='_new'>IP Geolocation</a> by <a href='http://www.geoplugin.com/' target='_new'>geoPlugin</a></span>";
            echo "</td>";
            echo "</tr>";
            echo "</tbody>";
            // altrimenti
        } else {
            // nessun riultato
            echo "<tr><td colspan='8'>Nessun record trovato</td></tr>";
        }
        echo "</table>";
        echo "</form>";
    }

    /**
     * Restituisce la data formattata in italiano 
     * @param stringa $data yyyy-mm-dd hh:ii:ss
     * @param int $ora 1 o 0 mostra o meno l'ora
     * @return stringa 
     */
    private function format_data_it($data, $ora = 1) {

        $split = explode(" ", $data);
        $d = explode("-", $split[0]);
        $d = array_reverse($d);

        if ($ora == 1) {
            return implode("/", $d) . " " . $split[1];
        } else {
            return implode("/", $d);
        }
    }

    /**
     * Estrae dall'agent il sistema operativo
     * è possibile inserire nell'array i sistemi mancanti seguendo la stessa loogica
     * @param type $agent
     * @return type 
     */
    private function sistema_operativo($agent) {
        $so = array(
            'Windows NT 6.2' => 'Windows 8',
            'Windows NT 6.1' => 'Windows 7',
            'Windows NT 6.0' => 'Windows vista',
            'Windows NT 5.1' => 'Windows XP',
            'Windows NT 5.0' => 'Windows 2000',
            'Windows NT 4.90' => 'Windows ME',
            'Win95' => 'Windows 95',
            'Win98' => 'Windows 98',
            'Windows NT 5.2' => 'Windows NET',
            'WinNT4.0' => 'Windows NT',
            'Mac|PPC' => 'Mac',
            'Linux' => 'Linux',
            'FreeBSD' => 'FreeBSD',
            'SunOS' => 'SunOS',
            'Irix' => 'Irix',
            'BeOS' => 'BeOS',
            'OS/2' => 'OS/2',
            'AIX' => 'AIX',
            'bot' => 'bot',
            "W3C" => "W3C"
        );
        foreach ($so as $chiave => $valore) {
            if (strstr($agent, $chiave)) {
                return $valore;
            }
        }
        return 'n/d';
    }

    /**
     * Estrae dall'agent il browser
     * è possibile inserire nell'array i browser mancanti seguendo la stessa loogica
     * @param type $agent
     * @return type 
     */
    private function browser($agent) {
        $browser = array(
            "Internet Explorer 9" => "MSIE 9",
            "Internet Explorer 8" => "MSIE 8",
            "Internet Explorer 7" => "MSIE 7",
            "Internet Explorer 6" => "MSIE 6",
            "Internet Explorer" => "MSIE",
            "Chrome" => "Chrome",
            "Mozilla Firefox 23" => "Firefox/23",
            "Mozilla Firefox 20" => "Firefox/20",
            "Mozilla Firefox 8" => "Firefox/8",
            "Mozilla Firefox 7" => "Firefox/7",
            "Mozilla Firefox 6" => "Firefox/6",
            "Mozilla Firefox 5" => "Firefox/5",
            "Mozilla Firefox 4" => "Firefox/4",
            "Mozilla Firefox 3" => "Firefox/3",
            "Mozilla Firefox" => "Firefox",
            "Python" => "urllib",
            "Curl" => "libcurl",
            "Crawler" => "Crawler",
            "YandexBot" => "YandexBot",
            "Majestic12" => "MJ12",
            "Dataprovider " => "Dataprovider",
            "Lynx" => "Lynx",
            "Opera 10" => "Opera/10",
            "Opera 9" => "Opera/9",
            "Opera" => "Opera",
            "Konqueror" => "Konqueror",
            "Safari" => "Safari",
            "GoogleBot" => "Googlebot",
            "BingBot" => "bingbot",
            "W3C" => "W3C"
        );

        foreach ($browser as $chiave => $valore) {
            if (strstr($agent, "$valore")) {
                return $chiave;
            }
        }
        return "n/d";
    }

}

?>
 
Ultima modifica di un moderatore:
Rileggendo mi sono accorto che la geolocalizzazione non funzionava correttamente per due piccoli errori nei metodi
riposto i due metodi corretti
PHP:
private function geoloc() {
    // recupero l'ip
    $ip = $_SERVER['REMOTE_ADDR'];
    // cerco l'ip nella tabella geoloc
    $query = "SELECT ip FROM geoloc 
                     WHERE ip = '" . $ip . "' && data >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)";
    // eseguo la query
    $result = $this->sql->query($query);
    // se trovo un risultato esco
    if ($result && $result->num_rows > 0) {
        return true;
    }
    // richiamo il servizio di http://www.geoplugin.com/

    $geo = file_get_contents('http://www.geoplugin.net/json.gp?ip=' . $ip);
    // inserisco i nuovi dati
    $query = "INSERT INTO geoloc SET ip ='$ip',geoloc='" . $this->sql->real_escape_string($geo) . "'";
    // eseguo
    if (!$this->sql->query($query)) {
        // da implementare la gestione degli errori
        $error = $this->sql->error;
        return false;
    }
    // tutto ok
    return true;
}

private function info_geoloc($ip) {
    $query = "SELECT geoloc FROM geoloc 
                     WHERE ip = '" . $ip . "' ORDER BY data DESC LIMIT 1";
    $result = $this->sql->query($query);
    if ($result && $result->num_rows > 0) {
        $row = $result->fetch_row();
        $json = json_decode($row[0]);
        $return = $json->geoplugin_countryName;
        if (!empty($json->geoplugin_region)) {
            $return = $json->geoplugin_region;
        }
        return $return;
    }
    return "n/d";
}
 
Ciao Nefyt, questa mi mancava
per ora ho ripulito manualmente anche perchè non ho ben capito come hai fatto :-)
in teoria dovrebbe essere sufficiente utilizzare strip_tag() prima dell'inserimento del referer
 
Beh in pratica li gestisci come $_GET o $_POST, quando vai ad inserire un header per intero rendilo sicuro nella query e poi quando lo vai a stampare utilizza htmlspecialchars() oppure anche strip_tags()



Se vuoi provare da te io ho utilizzato il composer di fiddler2 per mandare la richiesta con parametri personalizzati, comunque puoi tranquillamente farlo anche con curl o jquery o qualunque libreria http
 
A seguito dell' "hacking" subito :evil: ho aggiunto un controllo prima dell'inserimento della visita nel metodo salva_visita()
PHP:
// recupero il link da dove è arrivato il visitatore
        $referer = (isset($_SERVER["HTTP_REFERER"])) ? $this->sql->real_escape_string($_SERVER["HTTP_REFERER"]) : "n/d";
e ho ripulito la stringa prima di mandarla in output nel metodo tabella_visite()
PHP:
echo "<td class='center info' title='" . strip_tags($row["referer"]) . "'> ? </td>";
Lascio a voi eventuali altre modifiche per proteggere al meglio il codice
Grazie Nefyt :fonzie:
 

Discussioni simili