Корпоративный интернет-счётчик

© 2003 Рудюк С.А, Лутай Д.А
http://www.rudjuk.kiev.ua

Аннотация

В данной статье рассказывается как создать интернет-счётчик для корпоративного портала.

Задача

Счётчик должен вести подробную информацию о посетителях: адрес откуда пришёл посетитель, адрес страницы, IP-адрес посетителя, дата, браузер пользователя, операционная система.

По IP-адресу можно определить страну и регион откуда пришёл посетитель.

Кроме того мы должны иметь возможность просматривать статистику посещений по дням, количество новых посетителей (и их IP-адреса), количество уникальних посетителей (и их IP-адреса), самые лучшие ссылки на на нашу интернет-страницу, самые популярные страницы и т.п..

Схема выполнения

Для начала, рассмотрим схему выполнения счётчика.

  1. Пользователь из интернет (Пользователь1, Пользователь2, Пользователь3) заходит на нашу интернет-страницу, расположенную на сервере провайдера.
  2. На интернет-странице выполняется код на PHP, который определяет IP-адрес пользователя, URL откуда пришёл, URL страницы, браузер и операционную систему пользователя. Эти данные программа передаёт на корпоративный интернет-сервер.
  3. На корпоративном интернет-сервере вызывается хранимая процедура InterBase для передачи данных о пользователе и получении статистической информации для интернет-страницы.
  4. Статистически данные передаются в программу на Интернет-странице и выводятся для посетителя.

Со временем, администратор подключается к Interbase-серверу с помощью клиенской программы (написанной на Delphi) и обрабатывает информацию.

Примечание: Корпоративный сервер и InterBase-сервер можно совместить, но тогда уменьшается уровень защиты информации. Так как зная пароль и имя пользователя можно будет подключиться к базе данных из интернет.

 

Структура базы данных

Теперь создадим структуру базы данных.

Таблица Project предназначена для хранения названия проекта. Таким образом, мы можем создавать сколько угодно счётчиков.

В таблице CountData накапливается информация о посетителях:

URLFrom – откуда пришёл посетитель,
URLCurr – какую страницу посетили,
DateHint – дата (без времени) когда посетили,
FullDate – полная дата,
IPUser – IP-адрес посетителя,
Browser – браузер,
SystemUser – операционная система пользователя,
SystemVer – версия операционной системы,
CompName – название компьютера посетителя.

В таблице IPTable хранятся адреса уникальных IP-адресов.

Скрипт базы данных

Сгенерируем скрипт базы данных по данной структуре:


CREATE TABLE CountData (
       CountDataID          INTEGER NOT NULL,
       ProjID               INTEGER,
       URLFrom              VARCHAR(1024),
       URLCurr              VARCHAR(1024),
       DateHint             DATE,
       FullDate             DATE,
       IPUser               VARCHAR(15),
       Browser              VARCHAR(255),
       SystemUser           VARCHAR(255),
       SystemVer            VARCHAR(255)
);

CREATE INDEX IF318CountData ON CountData
(
       ProjID
);

CREATE INDEX ak_IP ON CountData
(
       IPUser
);

CREATE INDEX ak_DateHint ON CountData
(
       DateHint
);

 
ALTER TABLE CountData
       ADD CONSTRAINT PKCountData PRIMARY KEY (CountDataID);

 
CREATE TABLE IPTable (
       IPUser               VARCHAR(15) NOT NULL,
       DateAdd              DATE
);

CREATE INDEX akDateAdd ON IPTable
(
       DateAdd
);

 
ALTER TABLE IPTable
       ADD CONSTRAINT PKIPTable PRIMARY KEY (IPUser);

 
CREATE TABLE Project (
       ProjID               INTEGER NOT NULL,
       ProjName             VARCHAR(255),
       Comment              BLOB SUB_TYPE 1
);

 
ALTER TABLE Project
       ADD CONSTRAINT PKProject PRIMARY KEY (ProjID);

 
ALTER TABLE CountData
       ADD CONSTRAINT Project_CountData
              FOREIGN KEY (ProjID)
                             REFERENCES Project;

 
 
CREATE EXCEPTION ERWIN_PARENT_INSERT_RESTRICT "Cannot INSERT Parent table because Child table exists.";
CREATE EXCEPTION ERWIN_PARENT_UPDATE_RESTRICT "Cannot UPDATE Parent table because Child table exists.";
CREATE EXCEPTION ERWIN_PARENT_DELETE_RESTRICT "Cannot DELETE Parent table because Child table exists.";
CREATE EXCEPTION ERWIN_CHILD_INSERT_RESTRICT "Cannot INSERT Child table because Parent table does not exist.";
CREATE EXCEPTION ERWIN_CHILD_UPDATE_RESTRICT "Cannot UPDATE Child table because Parent table does not exist.";
CREATE EXCEPTION ERWIN_CHILD_DELETE_RESTRICT "Cannot DELETE Child table because Parent table does not exist.";

SET TERM ^;

CREATE TRIGGER tI_CountData FOR CountData AFTER INSERT AS
DECLARE VARIABLE numrows INTEGER;
BEGIN
    select count(*)
      from Project
      where
        NEW.ProjID = Project.ProjID into numrows;
    IF (
      NEW.ProjID is not null and
      numrows = 0
    ) THEN
    BEGIN
      EXCEPTION ERWIN_CHILD_INSERT_RESTRICT;
    END

 
END ^

CREATE TRIGGER tU_CountData FOR CountData AFTER UPDATE AS
DECLARE VARIABLE numrows INTEGER;
BEGIN
  select count(*)
    from Project
    where
      NEW.ProjID = Project.ProjID into numrows;
  IF (
    NEW.ProjID is not null and
    numrows = 0
  ) THEN
  BEGIN
    EXCEPTION ERWIN_CHILD_UPDATE_RESTRICT;
  END

 
END ^

CREATE TRIGGER tD_Project FOR Project AFTER DELETE AS
DECLARE VARIABLE numrows INTEGER;
BEGIN
    select count(*)
      from CountData
      where
        CountData.ProjID = OLD.ProjID into numrows;
    IF (numrows > 0) THEN
    BEGIN
      EXCEPTION ERWIN_PARENT_DELETE_RESTRICT;
    END

END ^

CREATE TRIGGER tU_Project FOR Project AFTER UPDATE AS
DECLARE VARIABLE numrows INTEGER;
BEGIN
  IF
    (OLD.ProjID <> NEW.ProjID) THEN
  BEGIN
    update CountData
      set
        CountData.ProjID = NEW.ProjID
      where
        CountData.ProjID = OLD.ProjID;
  END

END ^

Триггера и хранимые процедуры

Уникальные IP-адреса

Уникальные IP-адреса будут фиксироваться автоматически базой данных. Для этого мы напишем пару триггеров.

Триггер вставки уникальных IP-адресов:


CREATE TRIGGER RTI_COUNTDATA FOR COUNTDATA BEFORE INSERT POSITION 0 AS
DECLARE VARIABLE CNT INTEGER;
BEGIN
   NEW.COUNTDATAID=GEN_ID(GCountDataID,1);
   NEW.DATEHINT="TODAY";
   NEW.FULLDATE="NOW";

   SELECT COUNT(*)
   FROM IPTABLE
   WHERE (IPUSER=NEW.IPUSER)
      AND(PROJID=NEW.PROJID)
   INTO CNT;

   IF (CNT=0) THEN
     INSERT INTO IPTABLE (IPUSER, PROJID)
     VALUES (NEW.IPUSER, NEW.PROJID);
END
^

Триггер заполнения даты уникального IP-адреса:


CREATE TRIGGER RTI_IPTABLE FOR IPTABLE BEFORE INSERT POSITION 0 AS
BEGIN
   NEW.DATEADD="TODAY"
END

Регистрация пользователя в базе данных

Теперь создадим хранимую процедуру для регистрации пользователя в базе данных и получения статистической информации о посещениях.


/***********************************************************************/
/* Процедура регистрации посетителя интернет странички                 */
/***********************************************************************/
CREATE PROCEDURE PROC_COUNT_DATA
  (PROJID INTEGER,
   URLFROM VARCHAR(1024),
   URLCURR VARCHAR(1024),
   IPUSER VARCHAR(15),
   BROWSER VARCHAR(255),
   SYSTEMUSER VARCHAR(255),
   SYSTEMVER VARCHAR(255),
   COMPNAME VARCHAR(255)
   )
RETURNS (
  MINDATE DATE,                  /* Дата установки счётчика */
  NUMBER_ZAGRUZK INTEGER,        /* Количество загрузок */
  NUMBER_UN_ZAGRUZK INTEGER,     /* Количество уникальных загрузок */
  NUMBER_ZAGR_DATE INTEGER,      /* Количество загрузок сегодня */
  NUMBER_ZAGR_UN_DATE INTEGER)   /* Количество уникальных загрузок */
AS
BEGIN

   INSERT INTO COUNTDATA (PROJID, URLFROM, URLCURR, IPUSER,
      BROWSER, SYSTEMUSER, SYSTEMVER, COMPNAME)
   VALUES (:PROJID, :URLFROM, :URLCURR, :IPUSER,
      :BROWSER, :SYSTEMUSER, :SYSTEMVER, :COMPNAME);

   /* Время установки счётчика */
   SELECT MIN(DATEHINT)
   FROM COUNTDATA
   INTO :MINDATE;

   IF (MINDATE IS NULL) THEN MINDATE="TODAY";

   /* Колчичество загрузок */
   SELECT COUNT(*)
   FROM COUNTDATA
   INTO :NUMBER_ZAGRUZK;

   /* Количество уникальных загрузок */
   SELECT COUNT(*)
   FROM IPTABLE
   INTO :NUMBER_UN_ZAGRUZK;

   /* Колчичество загрузок сегодня */
   SELECT COUNT(*)
   FROM COUNTDATA
   WHERE DATEHINT="TODAY"
   INTO :NUMBER_ZAGR_DATE;

   /* Количество уникальных загрузок сегодня */
   SELECT COUNT(*)
   FROM IPTABLE
   WHERE DATEADD="TODAY"
   INTO :NUMBER_ZAGR_UN_DATE;

   SUSPEND;

END
^

Расшифруем передаваемые поля процедуре:

PROJID – ID проекта (счётчика),
URLFROM – откуда пришли,
URLCURR – куда пришли,
IPUSER – IP-адрес пользователя,
BROWSER - браузер,
SYSTEMUSER – операционная система пользователя,
SYSTEMVER – версия операционной системы пользователя,
COMPNAME – название компьютера.

Процедура возвращает статистическую информацию:

MINDATE - дата установки счётчика,
NUMBER_ZAGRUZK INTEGER – общее количество загрузок,
NUMBER_UN_ZAGRUZK INTEGER – общее количество уникальных загрузок,
NUMBER_ZAGR_DATE INTEGER - количество загрузок за сегодня,
NUMBER_ZAGR_UN_DATE INTEGER) - Количество уникальных загрузок за сегодня.

Получение списка повторных посетителей

Хранимая процедура для получения списка повторных посетителей


/***********************************************************************/
/* Процедура для извлечения повторных посетителей                      */
/***********************************************************************/
CREATE PROCEDURE POVTOR_POSETIT
  (PROJID INTEGER,
   DATEOT DATE,
   DATEDO DATE
   )
RETURNS (
  IPUSER VARCHAR(15)
  )
AS
BEGIN
   FOR SELECT C.IPUSER
       FROM COUNTDATA C
       WHERE (C.DATEHINT>=:DATEOT)
          AND(C.DATEHINT<=:DATEDO)
          AND(C.PROJID>=:PROJID)
          AND(C.IPUSER NOT IN (SELECT IT.IPUSER
                            FROM IPTABLE IT
                            WHERE (IT.DATEADD>=:DATEOT)
                               AND(IT.DATEADD<=:DATEDO)
                               AND(IT.PROJID=:PROJID)
                            ))
       GROUP BY C.IPUSER
       INTO :IPUSER
   DO
    BEGIN
      SUSPEND;
    END

END
^

Процедуре мы передаём диапазон за который хотим посмотреть статистику, а она возвращает IP-адреса пользователей, которые повторно посетили наш сайт.

Статистика посещений по дням

Хранимая процедура для расчёты статистики посещений по дням:


/***********************************************************************/
/* Статистика посещений по дням                                        */
/***********************************************************************/
CREATE PROCEDURE STAT_DAY
  (PROJID INTEGER,
   DATEOT DATE,
   DATEDO DATE
   )
RETURNS (
  DATE_POSETIT DATE,  /* Дата посещения */
  KOL_POSESCH INTEGER,/* Количество посещений */
  KOL_UNIKALN INTEGER, /* Количество уникальных загрузок */
  KOL_POVTORN_POSET INTEGER /* Количество повторных посетителей */
  )
AS
BEGIN

   FOR SELECT DATEHINT
       FROM COUNTDATA
       WHERE (PROJID=:PROJID)
          AND(DATEHINT>=:DATEOT)
          AND(DATEHINT<=:DATEDO)
       GROUP BY DATEHINT
       INTO :DATE_POSETIT
   DO
    BEGIN

      /* Колчичество загрузок */
      SELECT COUNT(*)
      FROM COUNTDATA
      WHERE DATEHINT=:DATE_POSETIT
      INTO :KOL_POSESCH;

      /* Количество уникальных загрузок */
      SELECT COUNT(*)
      FROM IPTABLE
      WHERE DATEADD=:DATE_POSETIT
      INTO :KOL_UNIKALN;

 
      SELECT COUNT(*)
      FROM POVTOR_POSETIT(:PROJID, :DATE_POSETIT, :DATE_POSETIT)
      INTO KOL_POVTORN_POSET;

 
      SUSPEND;
    END

END
^

Лучшие ссылки на сайт

Хранимая процедура для получения лучших ссылок на сайт


/***********************************************************************/
/* Лучшая ссылка на наш сайт                                           */
/***********************************************************************/
CREATE PROCEDURE BEST_LINK
  (PROJID INTEGER,
   DATEOT DATE,
   DATEDO DATE
   )
RETURNS (
  URLFROM VARCHAR(1024),
  COUNT_URLS INTEGER
  )
AS
BEGIN
  FOR SELECT URLFROM, COUNT(*) COUNT_URLS
  FROM COUNTDATA
  where (PROJID=:PROJID)
      And(DATEHINT>=:DateOt)
      And(DATEHINT<=:DateDo)
  GROUP BY URLFROM
  INTO :URLFROM, :COUNT_URLS
  DO
   BEGIN
     SUSPEND;
   END  
END
^

Лучшая интернет-страница

Хранимая процедура для получения статистики по посещаемости наших страниц:


/***********************************************************************/
/* Лучшая наша страничка                                               */
/***********************************************************************/
CREATE PROCEDURE BEST_PAGE
  (PROJID INTEGER,
   DATEOT DATE,
   DATEDO DATE
   )
RETURNS (
  URLCURR VARCHAR(1024),
  COUNT_URLS INTEGER
  )
AS
BEGIN
  FOR SELECT URLCURR, COUNT(*) COUNT_URLS
  FROM COUNTDATA
  where (PROJID=:PROJID)
      And(DATEHINT>=:DateOt)
      And(DATEHINT<=:DateDo)
  GROUP BY URLCURR
  INTO :URLCURR, :COUNT_URLS
  DO
   BEGIN
     SUSPEND;
   END  
END
^

На нашем сервере (код на PHP)

 

Прежде всего, необходимо настроить Apache на нашем сервере. Как его устанавливать и настраивать Вы можете во многочисленных статьях интернета.

Скрипт для подключения к нашей базе данных InterBase, передачи информации о пользователе и получения статистической информации.

acounter.PHP


<?PHP
/*include("config.PHP");*/
include "classdb.PHP3"; 
class acounter {
    var $config = array();
	var $conn;
	var $dbname;
	var $dbuser;
	var $dbpass;
    var $NUMBER_ZAGRUZK;
    var $NUMBER_UN_ZAGRUZK;
	var	$NUMBER_ZAGR_DATE;
	var	$NUMBER_ZAGR_UN_DATE;
	var $MINDATE;
	var $okrugl;	

    function acounter () {

        /*Подключение к БД*/
include "config.PHP";
		$this->okrugl = $okrugl;
        $this->dbname = $dbname;
        $this->dbuser = $dbuser;
        $this->dbpass = $dbpass;
        $this->conn=ibase_connect($this->dbname,$this->dbuser,$this->dbpass);

        /* URL to the digitset */
        $this->config['img'] = "http://MySite.com.ua/MyCounter/digits/";

        /* URL to the animated digitset */
        $this->config['animated_img'] = "http://MySite.com.ua/MyCounter/digits_ani/";

        /* How many digits to show */
        $this->config['pad'] = 6;

        /* digit width and height */
        $this->config['width']  = 16;
        $this->config['height'] = 22;

        /* timeout (minutes) */
        $this->config['block_time'] = 15;
    }

    //Получает количество записей в таблице CountData(количество посещений)
	function ibase_fetch_array($res) {
	   return get_object_vars(ibase_fetch_object($res));
 	}

	function ibase_num_rows($query) {
		$i=0;
		while (ibase_fetch_row($query)){
		$i++;}
	return $i;
	}

	function GetCounterValue() {
	    $sqlExpr="select * from CountData";
		$sth = ibase_query($this->conn,$sqlExpr);
        $CounterValue=$this->ibase_num_rows($sth);
	return $CounterValue;
    }
	//
	function InsertData($ip='',$urlFrom='',$urlCurr='',$host='') {
		//Beru vid brausera, versiu brauzera, platformu mashini;
		$info=getenv("HTTP_USER_AGENT");
//		$urlFrom=getenv("HTTP_REFERER");
		$Gateway=getenv("GATEWAY_INTERFACE");
		$Connect=getenv("SERVER_PROTOCOL");
		//Beru ip-adres;
//		$ip = getenv("REMOTE_ADDR");

	    $db = new CConnectionIBase();  
		$sqlExpr="SELECT * FROM PROC_COUNT_DATA(1, '".$urlFrom."', '".$urlCurr."', '".$ip."', '', '".$info."', '','".$host."')";

  		$sth = ibase_query($this->conn,$sqlExpr);
		$mas=$this->ibase_fetch_array($sth);
        $this->NUMBER_ZAGRUZK = $mas[NUMBER_ZAGRUZK];
        $this->NUMBER_UN_ZAGRUZK = $mas[NUMBER_UN_ZAGRUZK];
		$this->NUMBER_ZAGR_DATE = $mas[NUMBER_ZAGR_DATE];
		$this->NUMBER_ZAGR_UN_DATE = $mas[NUMBER_ZAGR_UN_DATE];
		$this->MINDATE = $mas[MINDATE];		
    }

 
    function create_output($ip='',$urlFrom='',$urlCurr='',$host='') {
		$this->InsertData($ip,$urlFrom,$urlCurr,$host);
//Vivogu Col. zagruzok

        $html_output = "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr>\n";
		$html_output .="<font size='1' color='#0000FF'>Всего загрузок:</font></tr>\n<tr align=\"center\">\n";
		$html_output .= "<font size='3' color='#800080'><b>".sprintf($this->okrugl,$this->NUMBER_ZAGRUZK)."</b></font>";
        $html_output .= "</tr></table>\n";
//Vivogu col. unikalnih zagruzok

        $html_output .= "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr>\n";
		$html_output .="<font size='1' color='#0000FF'>Всего уникальных загрузок:</font></tr>\n<tr align=\"center\">\n";
		$html_output .= "<font size='3' color='#800080'><b>".sprintf($this->okrugl,$this->NUMBER_UN_ZAGRUZK)."</b></font>";
        $html_output .= "</tr></table>\n";
//Vivogu col. zagruzok segodnia

        $html_output .= "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr>\n";
		$html_output .="<font size='1' color='#0000FF'>Загрузок за день:</font></tr>\n<tr align=\"center\">\n";
		$html_output .= "<font size='3' color='#800080'><b>".sprintf($this->okrugl,$this->NUMBER_ZAGR_DATE)."</b></font>";
        $html_output .= "</tr></table>\n";
		
//Vivogu col. unikalnih zagruzok segodnia

        $html_output .= "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr>\n";
		$html_output .="<font size='1' color='#0000FF'>Уникальных загрузок за день:</font></tr>\n<tr align=\"center\">\n";
		$html_output .= "<p align='center'><font size='3' color='#800080'><b>".sprintf($this->okrugl,$this->NUMBER_ZAGR_UN_DATE)."</b></font></p>";
        $html_output .= "</tr></table>\n";

//Vivogu datu ustanovki schetchika
        $html_output .= "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr>\n";
		$html_output .="<font size='1' color='#0000FF'>Дата установки счётчика:</font></tr>\n<tr align=\"center\">\n";
		$str=substr($this->MINDATE,0,10);
		$html_output .= "<font size='1' color='#800080'>".$str."</font>";
        $html_output .= "</tr></table>\n";

		
        return $html_output;
    }

}

?>

classdb.PHP3


<?PHP
  //---------------------------------------------------------------------------------------------
  class CConnection {
    var $err_logon = "Can't connect to database %s!";

    var $descriptor = 0; // database descriptor
    var $result;         // result array
    var $countRow = 0;   // number of records in result array
    var $countField = 0; // number of fields in result array

    // Clears result array. For internal use.
    function freeQuery() {
      unset($this->result);
      $this->countRow = 0;
      $this->countField = 0;
    }

    // Returns content of specified cell of result array or null if $col or $row is wrong.
    // $col can to hold the field name or field index
    function getData($col, $row) {
      if ((0 <= $row) && ($row < $this->countRow)) {
        if (!is_string($col)) {
          reset($this->result);
          for ($fno = 0; $fno < $col; $fno++) next($this->result);
          $col = current($this->result);
          return $col[$row];
        } else return $this->result[strtoupper($col)][$row];
      } else return null;
    }

    // Returns field name by field index or empty string if $col is wrong
    function getFieldName($col) {
      if (is_integer($col) && (0 <= $col) && ($col < $this->countField)) {
        reset($this->result);
        for ($fno = 0; $fno < $col; $fno++) next($this->result);
        list($key, $val) = each($this->result);
        return $key;
      } else return "";
    }
  }

  //---------------------------------------------------------------------------------------------
  class CConnectionIBase extends CConnection {
    // Constructor. Creates class.
    function CConnectionIBase() {}

    // Opens specified database.
    // Returns database descriptor.
    function open($database, $username = "sysdba", $password = "masterkey", $charset="WIN1251") {
      $this->close();
      $this->descriptor = ibase_connect($database, $username, $password, $charset);
      return $this->descriptor;
    }

    // Closes current database connection
    function close() {
      if ($this->descriptor) {
        $this->freeQuery();
        ibase_close($this->descriptor);
        $this->descriptor = 0;
      }
    }

    // Prepares data to storing in BLOBs. Used in query & execute functions
    // Returns query statement descriptor
    function execCode($code, $blobs=0) {
      $statement = 0;
      $this->freeQuery();
      if ($this->descriptor) {
        $cmd = "$"."statement = ibase_query("."$"."this->descriptor, "."$"."code";
        if (is_array($blobs) && count($blobs)) {
          reset($blobs);
          $fno = 0;
          while (list($key, $val) = each($blobs)) {
            $finfo[$fno] = array("blob_id" => ibase_blob_create(), "blob_str" => "");
            if (is_string($val)) ibase_blob_add($finfo[$fno]["blob_id"], $val);
            else ibase_blob_add($finfo[$fno]["blob_id"], "Not supported yet, sorry");
            $finfo[$fno]["blob_str"] = ibase_blob_close($finfo[$fno]["blob_id"]);
            $cmd = $cmd.", $"."finfo[$fno][\"blob_str\"]";
            $fno++;
          }
        }
        $cmd = $cmd.");";
        eval($cmd);
      }
      return $statement;
    }

    // Executes SELECT statement and fills result array by dataset contents
    // Returns number of records placed to result array
    function query($code, $blobs=0) {
      if ($statement = $this->execCode($code, $blobs)) {
        while ($row = ibase_fetch_row($statement)) {
          while(list($fno, $val) = each($row)) {
            // Getting information about fields existing in current row
            if ($this->countField < count($row)) $this->countField = count($row);
            $finfo = ibase_field_info($statement, $fno);
            $fname = $finfo["alias"];
            $ftype = $finfo["type"];
            unset($finfo);

            if (!strcmp($ftype, "BLOB")) {
              // Getting data from BLOB field
              if (($finfo = ibase_blob_info($val)) &&
                  ($finfo["length"] > 0) &&
                  ($val = ibase_blob_open($val))) {
                $blob = ibase_blob_get($val, $finfo["length"]);
                ibase_blob_close($val);
              } else $blob = "";
              $this->result[$fname][$this->countRow] = $blob;
              unset($blob);
            } else {
              // Getting data from another field
              if (isset($val)) {
                if (is_string($val))
                  $this->result[$fname][$this->countRow] = trim($val);
                else $this->result[$fname][$this->countRow] = $val;
              } else $this->result[$fname][$this->countRow] = "";
            }
          }
          $this->countRow++;
          // Cleaning temporary variables
          unset($row);
          unset($fno);
          unset($val);
          unset($finfo);
          unset($fname);
          unset($ftype);
        }
        ibase_free_result($statement);
        unset($statement);
      }
      return $this->countRow;
    }

    // Executes INSERT, DELETE or UPDATE statements. Result array is empty.
    // Returns nonzero if all OK
    function execute($code, $blobs=0) {
      if ($statement = $this->execCode($code, $blobs)) {
        @ibase_free_result($statement);
        return 1;
      }
      return 0;
    }

    // Commits current transaction
    function commit() { ibase_commit(); }

    // Rollbacks current transaction
    function rollback() { ibase_rollback(); }
  }

  //---------------------------------------------------------------------------------------------
  class CConnectionOCI extends CConnection {
    function CConnectionOCI() {}

    function open($database = "", $username = "system", $password = "manager") {
      $this->close();
      if (($database) && strlen($database))
        $this->descriptor = OCILogon($username, $password, $database);
      else $this->descriptor = OCILogon($username, $password);
      return $this->descriptor;
    }

    function close() {
      if ($this->descriptor) {
        $this->freeQuery();
        OCILogOff($this->descriptor);
        $this->descriptor = 0;
      }
    }

    function query($code) {
      $this->freeQuery();
      if ($this->descriptor) {
        if ($code && ($statement = OCIParse($this->descriptor, $code))) {
          OCIExecute($statement, OCI_DEFAULT);
          $this->countRow = OCIFetchStatement($statement, $this->result);
          $this->countField = count($this->result);
          OCIFreeStatement($statement);
        }
      }
      return $this->countRow;
    }

    function execute($code, $blob=0) {
      $res = 0;
      $this->freeQuery();
      if ($this->descriptor) {
        if ($code) {
          if ($blob) $lob = OCINewDescriptor($this->descriptor, OCI_D_LOB);
          if ($statement = OCIParse($this->descriptor, $code)) {
            if ($blob) OCIBindByName($statement, ":blob", &$lob, -1, OCI_B_CLOB);
            OCIExecute($statement, OCI_DEFAULT);
            if ($lob) {
              if ($lob->save($blob)) $res = 1;
              OCIFreeDescriptor($lob);
            } else $res = 1;
            OCIFreeStatement($statement);
          }
        }
      }
      return $res;
    }

    function commit() {
      if ($this->descriptor) OCICommit($this->descriptor);
    }

    function rollback() {
      if ($this->descriptor) OCIRollback($this->descriptor);
    }
  }

  //---------------------------------------------------------------------------------------------
?>

Файл для конфигурирования счётчика:

config.PHP


<?

        $dbname = "Server:C:\DataBase\Counter.GDB";
        $dbuser = "SYSDBA";
        $dbpass = "masterkey";
        $okrugl = "%07s";
?>
$dbname – название базы данных,
$dbuser- имя пользователя InterBase,
$dbpass – пароль,
$okrugl – количество цифр в счётчики (например, 0000012).

Файл, который вызывается из интернет-страницы:

test.PHP


    <?PHP
    include_once "acounter.PHP";
    $ani_counter = new acounter();
    echo $ani_counter->create_output($_GET["ip"],$_GET["urlFrom"],$_GET["urlCurr"],$_GET["host"]);

    ?>

На интернет-странице (код на PHP)

Эта часть находится на сервере провайдера (там, где находится наша интернет-страница).

Создаём скрипт для определения данных о пользователе:

Counter.inc


<?PHP

//	PHPinfo();
	//Beru vid brausera, versiu brauzera, platformu mashini;
	
	$info=getenv("HTTP_USER_AGENT");
	$urlFrom=getenv("HTTP_REFERER");
	$urlCurr=$_SERVER["REQUEST_URI"];
	$host=getenv("HTTP_HOST");
	//Beru ip-adres;
	$ip = getenv("REMOTE_ADDR");
	readfile('http://client70.ukrtelebud.com.ua/RudjukCounter/test.PHP?ip='.$ip.'&urlFrom='.$urlFrom.'&urlCurr='.$urlCurr.'&host='.$host);

 
//	readfile('http://MySite.com.ua/MyCounter/test.PHP?ip='.$ip.'&urlFrom='.$urlFrom);
//  readfile('http://MySite.com.ua/MyCounter/test.PHP')  

?>

На самой интернет-странице прописываем скрипт, который и будет запускать весь счётчик:


<?PHP
         include ("counter_inc.PHP")
?>

Примечание: Для того, чтобы счётчик работал корректно необходимо файлы интернет-страницы называть с расширением PHP, а не htm.

Клиентская часть

Саму обработку статистических данных удобнее всего сделать на Delphi.

Реализацию на Delphi я оставляю читателю, приведу лишь SQL-запросы для получения необходимых данных.

SQL-запросы

Получение подробной информации о посетителях:


SELECT *
from COUNTDATA
where (PROJID=:PROJID)
    And(DATEHINT>=:DateOt)
    And(DATEHINT<=:DateDo)
ORDER BY COUNTDATAID

Статистика по дням:


SELECT *
FROM STAT_DAY(:ProjID, :DateOt, :DateDo) ORDER BY DATE_POSETIT

Лучшие ссылки на интернет-страницу:


SELECT *
FROM BEST_LINK(:ProjID, :DateOt, :DateDo)
ORDER BY COUNT_URLS DESC

Лучшие интернет-страницы:


SELECT *
FROM BEST_PAGE(:ProjID, :DateOt, :DateDo) ORDER BY COUNT_URLS DESC

Уникальные IP-адреса:


select *
from IPTable
where (ProjID=:ProjID)
   And(DateAdd>=:DateOt)
   And(DateAdd<=:DateDo)

IP-адреса повторных посетителей:


SELECT *
FROM POVTOR_POSETIT(:ProjID, :DateOt, :DateDo) ORDER BY IPUSER

Вот что получилось

Работу интернет-счётчика Вы можете увидеть на нашем сайте www.rudjuk.kiev.ua.

Получение подробной информации о посетителях:

Статистика по дням:

Лучшие ссылки:

Лучшие страницы:

Уникальные IP-адреса:

Повторные IP-адреса:

 

Заключение

В программе есть ряд неточностей, а так же не определяются страны по IP-адресу. Эти задачи я оставляю за читателями.

Скачать текст PHP-программ и SQL-скрипты Вы можете на http://www.rudjuk.kiev.ua/downloads/InetCounter/InetCounter.exe

С вопросами и предложениями обращайтесь по адресу:
EMail: rudjuk1@gala.net
ICQ: 131674371

2011123456789101112
2010123456789101112
2009123456789101112
2008123456789101112
2007123456789101112
2006123456789101112
2005123456789101112
2004123456789101112
2003123456789101112
2002123456789101112
2001123456789101112
2000123456789101112
1999123456789101112

Последние статьи
Литература