Code Newbie
News     Forums     Search     Members     Sign Up    

My Code Newbie
Username

Password

Articles/Snippets
ASP Classic
ASP.NET
C
C#
C++
HTML / CSS
Java
Javascript
Linux / BSD
Perl
PHP
Python
Ruby
SQL
VB 6
VB.NET

C.N. Friends
  Planet Rome

Link to Us!
Code Newbie
  Code Newbie
    forums
Old 05-17-2005, 02:30 PM   #1 (permalink)
sarah31
Code Monkey
 
sarah31's Avatar
 
Join Date: May 2002
Location: canada
Posts: 55
sarah31 is on a distinguished road
Hitcounter problem

Hello,

I am using a very basic hit counter script that just uses a flat .txt file but is a unique hit counter. the problem is that every once in awhile the flat file recording the IP's is resetting itself. Does anyone know how I can prevent this or what may be causing it?

Code:
<?php //hitcounter.php adapted from http://www.phpfreaks.com/quickcode/code/77.php

//Core Variables

$filename = "hitcounter.txt" ;
$startdate = "February 7, 2005" ;
$ip = getenv("REMOTE_ADDR") ;
$currentip = "$ip";
$file = file($filename);
$file = array_unique($file);

//This is the guts of the counter
  
$file = "$filename";
$fd = fopen ($file, "r");
$fget= fread ($fd, filesize ($file));
fclose ($fd);
$totalips = htmlspecialchars($fget);
if (preg_match ("/$ip/i", "$totalips"))
{$file = file($filename);
$file = array_unique($file);
$hits = count($file);
echo "<b>Visitors since, ";
echo "$startdate:</b><font color=red> $hits</font>";}  
else
{
$fd = fopen ($filename , "r");
$fstring = fread ($fd , filesize ($filename)) ;
fclose($fd) ;
$fd = fopen ($filename , "w");
$fcounted = $fstring.$currentip."\n";
$fout= fwrite ($fd , $fcounted);
fclose($fd);
$file = file($filename);
$file = array_unique($file);
$hits = count($file);
echo "Visitors since,";
echo "$startdate: $hits";
}

?>
sarah31 is offline   Reply With Quote
Old 05-18-2005, 07:02 AM   #2 (permalink)
DJMaze
Senior Contributor
 
DJMaze's Avatar
 
Join Date: Mar 2005
Posts: 651
DJMaze is on a distinguished road
Hmm that script opens the file 4-5 times and only counts unique ip's
So let's make it a little easier.

PHP Code:
<?php

//Core Variables
$filename 'hitcounter.txt';
$startdate 'February 7, 2005';

// Now get ip 
$ip $_SERVER['REMOTE_ADDR');
$file file($filename);

//This is the guts of the counter
if (!in_array($ip$file) && $fd fopen($filename "w")) {
    
$file[] = $ip;
    
$fstring implode("\n"$file);
    
fwrite ($fd$fstring);
    
fclose($fd);
}
$hits count($file);
echo 
"<b>Visitors since, $startdate:</b> <font color=red>$hits</font>";
A little more advanced:
PHP Code:
<?php

//Core Variables
$filename 'hitcounter.php';
$startdate 'February 7, 2005';

// Now get ip
$ip $_SERVER['REMOTE_ADDR');
// Load the file
include($filename);

//This is the guts of the counter
if ((empty($file) || !in_array($ip$file)) && $fd fopen($filename "w")) {
    
$file[] = $ip;
    
$hits count($file);
    
$fstring "<?php\n\$hits = $hits;\n\$file = array(\n'".implode("',\n'"$file)."'\n);";
    
fwrite ($fd$fstring);
    
fclose($fd);
}
echo 
"<b>Visitors since, $startdate:</b> <font color=red>$hits</font>";
This second scripts write the file as a PHP file and that way if someone tries to fetch your IP list he can't.
For example look on your site at http://mydomain.tld/hitcounter.txt
DJMaze is offline   Reply With Quote
Old 05-18-2005, 01:50 PM   #3 (permalink)
sarah31
Code Monkey
 
sarah31's Avatar
 
Join Date: May 2002
Location: canada
Posts: 55
sarah31 is on a distinguished road
Thanks DLMaze i believe i like that second script and will give it a test.

Another question though.... do you think that the problem of opening the file several times could have led to the issues I am having? Or is somebody likely using the fairly loose permissions on the .txt file to tinker with it?
sarah31 is offline   Reply With Quote
Old 05-18-2005, 02:00 PM   #4 (permalink)
sde
Moderator
 
sde's Avatar
 
Join Date: May 2002
Location: us.ca
Posts: 4,444
sde is on a distinguished road
i would guess the first .. perhaps it breaks when multiple people visit the page at the same time.
__________________
Mike
sde is offline   Reply With Quote
Old 05-18-2005, 02:03 PM   #5 (permalink)
teknomage1
Jack of all trades
 
teknomage1's Avatar
 
Join Date: Feb 2005
Location: Los Angeles
Posts: 596
teknomage1 is on a distinguished road
Send a message via AIM to teknomage1
More likely it's a race condition caused by one instance of the script trying to read while another is writing. If the timing is write the script won't get any useful data so it will start the counter anew.

Nevermind sde beat me to the explanation.
__________________
Stop intellectual property from infringing on me
teknomage1 is offline   Reply With Quote
Old 05-18-2005, 03:21 PM   #6 (permalink)
DJMaze
Senior Contributor
 
DJMaze's Avatar
 
Join Date: Mar 2005
Posts: 651
DJMaze is on a distinguished road
Depends.
We need some server details like:
- speed
- traffic
- bots

All of the above scripts don't use file locking.
File locking is essential when you read/write fast from a file within a milliseconds.

For example you have 4 visitiors with your old script:
#1 opens counter.txt at 0.010 seconds
#2 opens counter.txt at 0.011 seconds
#3 opens counter.txt at 0.012 seconds
#1 writes to file at 0.012 seconds
#2 writes the file data at 0.013 seconds
#3 writes the file data at 0.013 seconds

the entry of #1 is lost cos #2 was to late
#2 and #3 try to write the file at the same time.
This crashes so the file is empty.
Now #4 opens the file at 0.015 seconds and sees he's the first visitor.

To prevent this, you could use file locking.
I will give you an example of a custom function i commonly use
PHP Code:
function file_write($filename, &$content$mode='wb') {
    if (!
$fp fopen($filename$mode)) {
        
trigger_error("Cannot open file ($filename)"E_USER_WARNING);
        return 
false;
    }
    
flock($fpLOCK_EX); // lock it
    
if (fwrite($fp$content) === FALSE) {
        
flock($fpLOCK_UN); // don't forget to unlock
        
trigger_error("Cannot write to file ($filename)"E_USER_WARNING);
        return 
false;
    }
    
flock($fpLOCK_UN); // don't forget to unlock
    
if (!fclose($fp)) {
        
trigger_error("Cannot close file ($filename)"E_USER_WARNING);
        return 
false;
    }
    return 
true;

This way you could use:
PHP Code:
$fstring = "<?phpn$hits $hits;n$file = array(n'".implode("',n'", $file)."'n);";
if (file_write($filename, $fstring)) {
    echo 'cool it worked';
}
else
{
    echo 'something screwey happend, better go look at the shown warnings';
}
databases work differently
they queue the queries into a waitingroom untill it's their turn.
The benefit is that it never goes wrong.
The disadvantage is when your site is heavily visited. Then the queue gets rather full and the script has to wait seconds before it's his turn (10 to 45 seconds is possible)

A similar function is also available in PHP, but we don't gonna use that unless you're a skilled programmer who understands the drawbacks of sleep/wait commands.

It's better to miss a count then to crash a whole server
DJMaze is offline   Reply With Quote
Old 05-18-2005, 03:54 PM   #7 (permalink)
sarah31
Code Monkey
 
sarah31's Avatar
 
Join Date: May 2002
Location: canada
Posts: 55
sarah31 is on a distinguished road
Speed of the server I am not sure. No bots that I know of .... at least that I have implimented. Traffic:

Totals for Summary Period: May 1 2005 to May 18 2005

Files Transmitted During Summary Period 5200
Bytes Transmitted During Summary Period 382046752
Average Files Transmitted Daily 289
Average Bytes Transmitted Daily 21224820
Daily Transmission Statistics

%Reqs %Byte Bytes Sent Requests Date
----- ----- ------------ -------- |------------
4.67 5.81 22181193 243 | May 1 2005
10.69 13.86 52933708 556 | May 2 2005
11.31 9.73 37171923 588 | May 3 2005
5.15 6.28 23974905 268 | May 4 2005
4.79 3.78 14460132 249 | May 5 2005
2.90 3.15 12041459 151 | May 6 2005
3.12 1.31 5008105 162 | May 7 2005
2.69 2.83 10819884 140 | May 8 2005
5.48 5.46 20860931 285 | May 9 2005
4.63 9.40 35900164 241 | May 10 2005
5.06 6.32 24161178 263 | May 11 2005
9.23 3.86 14738228 480 | May 12 2005
10.52 4.63 17674415 547 | May 13 2005
7.13 4.57 17452353 371 | May 14 2005
3.00 2.01 7680815 156 | May 15 2005
5.67 6.46 24681824 295 | May 16 2005
3.69 10.20 38967826 192 | May 17 2005
0.25 0.35 1337709 13 | May 18 2005
Hourly Transmission Statistics

%Reqs %Byte Bytes Sent Requests Time
----- ----- ------------ -------- |-----
7.29 5.66 21638242 379 | 00
4.73 11.23 42902168 246 | 01
3.00 1.73 6601079 156 | 02
1.52 1.04 3990970 79 | 03
1.54 1.02 3884424 80 | 04
3.52 8.25 31522058 183 | 05
3.29 2.59 9883960 171 | 06
3.63 4.63 17702226 189 | 07
2.96 4.60 17587772 154 | 08
4.37 10.85 41440960 227 | 09
4.10 6.52 24909596 213 | 10
6.87 7.32 27964698 357 | 11
5.13 3.93 15016906 267 | 12
2.98 0.96 3666125 155 | 13
2.48 1.86 7094027 129 | 14
5.40 4.88 18656669 281 | 15
5.92 3.94 15033550 308 | 16
4.38 2.62 10015077 228 | 17
3.19 2.57 9814138 166 | 18
12.12 2.23 8503408 630 | 19
2.02 1.56 5971813 105 | 20
3.79 4.74 18090101 197 | 21
3.06 2.64 10081921 159 | 22
2.71 2.64 10074864 141 | 23
sarah31 is offline   Reply With Quote
Old 05-18-2005, 04:10 PM   #8 (permalink)
DJMaze
Senior Contributor
 
DJMaze's Avatar
 
Join Date: Mar 2005
Posts: 651
DJMaze is on a distinguished road
630 requests in 1 hour isn't much.
But that doesn't state 2 or more requests at the same time isn't possible.

You don't have page gen logs ?
I mean somesort of script that tells you how long it took for a webpage to generate ?
DJMaze is offline   Reply With Quote
Old 05-18-2005, 06:12 PM   #9 (permalink)
idx
Senior Grasshopper
 
idx's Avatar
 
Join Date: Jun 2003
Location: FL
Posts: 317
idx is on a distinguished road
..or just convert the txt file to a sqlite database (assuming it would already be in mysql if that was available)..

-r
idx is offline   Reply With Quote
Old 05-18-2005, 07:56 PM   #10 (permalink)
sarah31
Code Monkey
 
sarah31's Avatar
 
Join Date: May 2002
Location: canada
Posts: 55
sarah31 is on a distinguished road
No gen logs.

I would convert to using mysql or sqlite but I haven't had time to familiarize myself with PHP/MySQL coding yet. In fact I am not experienced with PHP enough to have figured out the problems with the initial script.
sarah31 is offline   Reply With Quote
Old 05-19-2005, 07:50 AM   #11 (permalink)
DJMaze
Senior Contributor
 
DJMaze's Avatar
 
Join Date: Mar 2005
Posts: 651
DJMaze is on a distinguished road
You need a database table
Code:
CREATE TABLE hitcounter (
  ip VARCHAR(15) NOT NULL DEFAULT '',
  hits INT NOT NULL DEFAULT 1
);
PHP Code:
$db mysql_connect('localhost''username''password');
if (!
$db) {
    die(
mysql_error());
}
if (!
mysql_select_db('database_name'$db)) {
    die(
mysql_error($db));
}

$hits 0;
$ip $_SERVER['REMOTE_ADDR'];

if (!
mysql_query("UPDATE hitcounter SET hits=hits+1 WHERE ip='$ip'"$db) || mysql_affected_rows($db) < 1)) {
    
// IP isn't listed yet so add it
    
mysql_query("INSERT INTO hitcounter VALUES ('$ip', '1')"$db);
}
// IP is inserted so now get the ammount of ip's visited
$q_result mysql_query("SELECT COUNT(*) FROM hitcounter"$db);
if (
$q_result) {
    
$row mysql_fetch_row($q_result);
    
$hits $row[0];

MORE ADVANCED AND SECURE (less HD space abuse)
then change 1 php line into:
PHP Code:
$ip ip2long($_SERVER['REMOTE_ADDR']); 
And the database table
Code:
CREATE TABLE hitcounter (
  ip INT NOT NULL DEFAULT 0,
  hits INT NOT NULL DEFAULT 1,
  PRIMARY KEY (ip)
);
In the database the IP could use 7 to 15 bytes '0.0.0.0' to '255.255.255.255' but when we use ip2long() the IP will be converted to an 32bit integer which is only 4 bytes.
So on every table entry you save 3 to 11 bytes of HD space.
this doesn't look much untill there are 1 million entries.
As benefit there's also a "PRIMARY KEY" field which actualy protects the table from inserting duplicate IP's so each table entry has a unique ip.

As you see there's also a "hits" counter.
This actualy ads another feature. You can count the exact ammount of page hits instead of unique ip's.
PHP Code:
mysql_query("SELECT SUM(hits) FROM hitcounter"$db); 
This counts the total ammount of views you recieved.
PHP Code:
$hits $visitors 0;
$q_result mysql_query("SELECT COUNT(*) FROM hitcounter"$db);
if (
$q_result) {
    
$row mysql_fetch_row($q_result);
    
$visitors $row[0];
    
mysql_free_result($q_result);
}
$q_result mysql_query("SELECT SUM(hits) FROM hitcounter"$db);
if (
$q_result) {
    
$row mysql_fetch_row($q_result);
    
$hits $row[0];
    
mysql_free_result($q_result);
}
echo 
"We recieved $hits page views by $visitors visitors"
the output will be like
Code:
We recieved 15601215 page views by 49 visitors
DJMaze is offline   Reply With Quote
Old 05-19-2005, 09:11 AM   #12 (permalink)
teknomage1
Jack of all trades
 
teknomage1's Avatar
 
Join Date: Feb 2005
Location: Los Angeles
Posts: 596
teknomage1 is on a distinguished road
Send a message via AIM to teknomage1
Isn't a databse overkill for a hit counter? That's equivalent to driving across the street. Simply locking the file on write and checking for a lock before reading should solve the problem. Here's the php page on flock http://www.php.net/manual/en/function.flock.php .
__________________
Stop intellectual property from infringing on me
teknomage1 is offline   Reply With Quote
Old 05-19-2005, 02:32 PM   #13 (permalink)
DJMaze
Senior Contributor
 
DJMaze's Avatar
 
Join Date: Mar 2005
Posts: 651
DJMaze is on a distinguished road
Quote:
Originally Posted by teknomage1
Isn't a database overkill for a hit counter? That's equivalent to driving across the street.
Not entirely.
Say the file has 1 million entries.
Which will be faster to load: the file or a single SQL query result ?
Which will use less memory: the full file or a single SQL query result ?

Quote:
Originally Posted by teknomage1
Simply locking the file on write and checking for a lock before reading should solve the problem. Here's the php page on flock http://www.php.net/manual/en/function.flock.php.
i already explained flock here:
Hitcounter problem
The MySQL example is just a follow up to an already available solution.
DJMaze is offline   Reply With Quote
Old 05-19-2005, 10:46 PM   #14 (permalink)
sarah31
Code Monkey
 
sarah31's Avatar
 
Join Date: May 2002
Location: canada
Posts: 55
sarah31 is on a distinguished road
Thanks everyone for your input. I will try and work on the options presented above.
sarah31 is offline   Reply With Quote
Old 06-22-2005, 01:27 PM   #15 (permalink)
falsepride
Regular Contributor
 
Join Date: Oct 2004
Posts: 203
falsepride is on a distinguished road
off topic. but i never used file locking before. didnt even know it existed till i was doing random browsing on the php site. do i just gotta lock before i write and unlock after i write? is it that simple
falsepride is offline   Reply With Quote
Reply

Bookmarks

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


<
Similar Threads
Thread Thread Starter Forum Replies Last Post
JSP code problem j.gohel Java 7 04-15-2005 02:07 PM
really stuck on deallocation problem MealMan401 Standard C, C++ 6