PHP hit Counter text file value resets frequently? - php

I am Using this simple hit counter script in my website. but its value resets to zero or changes to a lower value frequently (no specific time interval i noticed). is there any problem in scripts. please help ?
<?php
// SETUP YOUR COUNTER
// Detailed information found in the readme.htm file
// Count UNIQUE visitors ONLY? 1 = YES, 0 = NO
$count_unique = 1;
// Number of hours a visitor is considered as "unique"
$unique_hours = 1;
// Minimum number of digits shown (zero-padding). Set to 0 to disable.
$min_digits = 5;
#############################
# DO NOT EDIT BELOW #
#############################
/* Turn error notices off */
error_reporting(E_ALL ^ E_NOTICE);
/* Get page and log file names */
$page = input($_GET['page']) or die('ERROR: Missing page ID');
$logfile = 'logs/' . $page . '.txt';
/* Does the log exist? */
if (file_exists($logfile))
{
/* Get current count */
$count = intval(trim(file_get_contents($logfile))) or $count = 0;
$cname = 'tcount_unique_'.$page;
if ($count_unique==0 || !isset($_COOKIE[$cname]))
{
/* Increase the count by 1 */
$count = $count + 1;
$fp = #fopen($logfile,'w+') or die('ERROR: Can\'t write to the log file ('.$logfile.'), please make sure this file exists and is CHMOD to 666 (rw-rw-rw-)!');
flock($fp, LOCK_EX);
fputs($fp, $count);
flock($fp, LOCK_UN);
fclose($fp);
/* Print the Cookie and P3P compact privacy policy */
header('P3P: CP="NOI NID"');
setcookie($cname, 1, time()+60*60*$unique_hours);
}
/* Is zero-padding enabled? */
if ($min_digits > 0)
{
$count = sprintf('%0'.$min_digits.'s',$count);
}
/* Print out Javascript code and exit */
echo 'document.write(\''.$count.'\');';
exit();
}
else
{
die('ERROR: Invalid log file!');
}
/* This functin handles input parameters making sure nothing dangerous is passed in */
function input($in)
{
$out = htmlentities(stripslashes($in));
$out = str_replace(array('/','\\'), '', $out);
return $out;
}
?>

As far as my assumption, this reset occurs when there are simultaneous visits from users leading bad I/O of file, thus resetting it.
Solution:
Create a timely backup of txt file and compare values to publish the value from the file which has higher counter.
Switch to database!

Related

How to cache remote video

I want to play video from a remote server. so I write this function.
$remoteFile = 'blabla.com/video_5GB.mp4';
play($remoteFile);
function play($url){
ini_set('memory_limit', '1024M');
set_time_limit(3600);
ob_start();
if (isset($_SERVER['HTTP_RANGE'])) $opts['http']['header'] = "Range: " . $_SERVER['HTTP_RANGE'];
$opts['http']['method'] = "HEAD";
$conh = stream_context_create($opts);
$opts['http']['method'] = "GET";
$cong = stream_context_create($opts);
$out[] = file_get_contents($url, false, $conh);
$out[] = $httap_response_header;
ob_end_clean();
array_map("header", $http_response_header);
readfile($url, false, $cong);
}
The above function works very well in playing videos. But I don't want to burden the remote server
My question is how can I cache video files every 5 hours to my server. if possible, the cache folder contains small files (5MB / 10MB) from remote video
As mentioned in my comment, the following code has been tested only on a small selection of MP4 files. It could probably do with some more work but it does fill your immediate needs as it is.
It uses exec() to spawn a separate process that generates the cache files when they are needed, i.e. on the first request or after 5 hours. Each video must have its own cache folder because the cached chunks are simply called 1, 2, 3, etc. Please see additional comments in the code.
play.php - This is the script that will be called by the users from the browser
<?php
ini_set('memory_limit', '1024M');
set_time_limit(3600);
$remoteFile = 'blabla.com/video_5GB.mp4';
play($remoteFile);
/**
* #param string $url
*
* This will serve the video from the remote url
*/
function playFromRemote($url)
{
ob_start();
$opts = array();
if(isset($_SERVER['HTTP_RANGE']))
{
$opts['http']['header'] = "Range: ".$_SERVER['HTTP_RANGE'];
}
$opts['http']['method'] = "HEAD";
$conh = stream_context_create($opts);
$opts['http']['method'] = "GET";
$cong = stream_context_create($opts);
$out[] = file_get_contents($url, false, $conh);
$out[] = $http_response_header;
ob_end_clean();
$fh = fopen('response.log', 'a');
if($fh !== false)
{
fwrite($fh, print_r($http_response_header, true)."\n\n\n\n");
fclose($fh);
}
array_map("header", $http_response_header);
readfile($url, false, $cong);
}
/**
* #param string $cacheFolder Directory in which to find the cached chunk files
* #param string $url
*
* This will serve the video from the cache, it uses a "completed.log" file which holds the byte ranges of each chunk
* this makes it easier to locate the first chunk of a range request. The file is generated by the cache script
*/
function playFromCache($cacheFolder, $url)
{
$bytesFrom = 0;
$bytesTo = 0;
if(isset($_SERVER['HTTP_RANGE']))
{
//the client asked for a specific range, extract those from the http_range server var
//can take the form "bytes=123-567" or just a from "bytes=123-"
$matches = array();
if(preg_match('/^bytes=(\d+)-(\d+)?$/', $_SERVER['HTTP_RANGE'], $matches))
{
$bytesFrom = intval($matches[1]);
if(!empty($matches[2]))
{
$bytesTo = intval($matches[2]);
}
}
}
//completed log is a json_encoded file containing an array or byte ranges that directly
//correspond with the chunk files generated by the cache script
$log = json_decode(file_get_contents($cacheFolder.DIRECTORY_SEPARATOR.'completed.log'));
$totalBytes = 0;
$chunk = 0;
foreach($log as $ind => $bytes)
{
//find the first chunk file we need to open
if($bytes[0] <= $bytesFrom && $bytes[1] > $bytesFrom)
{
$chunk = $ind + 1;
}
//and while we are at it save the last byte range "to" which is the total number of bytes of all the chunk files
$totalBytes = $bytes[1];
}
if($bytesTo === 0)
{
if($totalBytes === 0)
{
//if we get here then something is wrong with the cache, revert to serving from the remote
playFromRemote($url);
return;
}
$bytesTo = $totalBytes - 1;
}
//calculate how many bytes will be returned in this request
$contentLength = $bytesTo - $bytesFrom + 1;
//send some headers - I have hardcoded MP4 here because that is all I have developed with
//if you are using different video formats then testing and changes will no doubt be required
header('Content-Type: video/mp4');
header('Content-Length: '.$contentLength);
header('Accept-Ranges: bytes');
//Send a header so we can recognise that the content was indeed served by the cache
header('X-Cached-Date: '.(date('Y-m-d H:i:s', filemtime($cacheFolder.DIRECTORY_SEPARATOR.'completed.log'))));
if($bytesFrom > 0)
{
//We are sending back a range so it needs a header and the http response must be 206: Partial Content
header(sprintf('content-range: bytes %s-%s/%s', $bytesFrom, $bytesTo, $totalBytes));
http_response_code(206);
}
$bytesSent = 0;
while(is_file($cacheFolder.DIRECTORY_SEPARATOR.$chunk) && $bytesSent < $contentLength)
{
$cfh = fopen($cacheFolder.DIRECTORY_SEPARATOR.$chunk, 'rb');
if($cfh !== false)
{
//if we are fetching a range then we might need to seek the correct starting point in the first chunk we look at
//this check will be performed on all chunks but only the first one should need seeking so no harm done
if($log[$chunk - 1][0] < $bytesFrom)
{
fseek($cfh, $bytesFrom - $log[$chunk - 1][0]);
}
//read and send data until the end of the file or we have sent what was requested
while(!feof($cfh) && $bytesSent < $contentLength)
{
$data = fread($cfh, 1024);
//check we are not going to be sending too much back and if we are then truncate the data to the correct length
if($bytesSent + strlen($data) > $contentLength)
{
$data = substr($data, 0, $contentLength - $bytesSent);
}
$bytesSent += strlen($data);
echo $data;
}
fclose($cfh);
}
//move to the next chunk
$chunk ++;
}
}
function play($url)
{
//I have chosen a simple way to make a folder name, this can be improved any way you need
//IMPORTANT: Each video must have its own cache folder
$cacheFolder = sha1($url);
if(!is_dir($cacheFolder))
{
mkdir($cacheFolder, 0755, true);
}
//First check if we are currently in the process of generating the cache and so just play from remote
if(is_file($cacheFolder.DIRECTORY_SEPARATOR.'caching.log'))
{
playFromRemote($url);
}
//Otherwise check if we have never completed the cache or it was completed 5 hours ago and if so spawn a process to generate the cache
elseif(!is_file($cacheFolder.DIRECTORY_SEPARATOR.'completed.log') || filemtime($cacheFolder.DIRECTORY_SEPARATOR.'completed.log') + (5 * 60 * 60) < time())
{
//fork the caching to a separate process - the & echo $! at the end causes the process to run as a background task
//and print the process ID returning immediately
//The cache script can be anywhere, pass the location to sprintf in the first position
//A base64 encoded url is passed in as argument 1, sprintf second position
$cmd = sprintf('php %scache.php %s & echo $!', __DIR__.DIRECTORY_SEPARATOR, base64_encode($url));
$pid = exec($cmd);
//with that started we need to serve the request from the remote url
playFromRemote($url);
}
else
{
//if we got this far then we have a completed cache so serve from there
playFromCache($cacheFolder, $url);
}
}
cache.php - This script will be called by play.php via exec()
<?php
//This script expects as argument 1 a base64 encoded url
if(count($argv)!==2)
{
die('Invalid Request!');
}
$url = base64_decode($argv[1]);
//make sure to use the same method of obtaining the cache folder name as the main play script
//or change the code to pass it in as an argument
$cacheFolder = sha1($url);
if(!is_dir($cacheFolder))
{
die('Invalid Arguments!');
}
//double check it is not already running
if(is_file($cacheFolder.DIRECTORY_SEPARATOR.'caching.log'))
{
die('Already Running');
}
//create a file so we know this has started, the file will be removed at the end of the script
file_put_contents($cacheFolder.DIRECTORY_SEPARATOR.'caching.log', date('d/m/Y H:i:s'));
//get rid of the old completed log
if(is_file($cacheFolder.DIRECTORY_SEPARATOR.'completed.log'))
{
unlink($cacheFolder.DIRECTORY_SEPARATOR.'completed.log');
}
$bytesFrom = 0;
$bytesWritten = 0;
$totalBytes = 0;
//this is the size of the chunk files, currently 10MB
$maxSizeInBytes = 10 * 1024 * 1024;
$chunk = 1;
//open the url for binary reading and first chunk for binary writing
$fh = fopen($url, 'rb');
$cfh = fopen($cacheFolder.DIRECTORY_SEPARATOR.$chunk, 'wb');
if($fh !== false && $cfh!==false)
{
$log = array();
while(!feof($fh))
{
$data = fread($fh, 1024);
fwrite($cfh, $data);
$totalBytes += strlen($data); //use actual length here
$bytesWritten += strlen($data);
//if we are on or passed the chunk size then close the chunk and open a new one
//keeping a log of the byte range of the chunk
if($bytesWritten>=$maxSizeInBytes)
{
$log[$chunk-1] = array($bytesFrom,$totalBytes);
$bytesFrom = $totalBytes;
fclose($cfh);
$chunk++;
$bytesWritten = 0;
$cfh = fopen($cacheFolder.DIRECTORY_SEPARATOR.$chunk, 'wb');
}
}
fclose($fh);
$log[$chunk-1] = array($bytesFrom,$totalBytes);
fclose($cfh);
//write the completed log. This is a json encoded string of the chunk byte ranges and will be used
//by the play script to quickly locate the starting chunk of a range request
file_put_contents($cacheFolder.DIRECTORY_SEPARATOR.'completed.log', json_encode($log));
//finally remove the caching log so the play script doesn't think the process is still running
unlink($cacheFolder.DIRECTORY_SEPARATOR.'caching.log');
}

I Want to get the page count using cookie

I want to get the page count in my index page,using cookie.So far I have done like.Now My question is:If I refresh the page, The browser shows with count 2 and it doesnot increase for next refresh.I don't know what is wrong in my code.Further I want to know how to handle cookie in next page or shall I can handle cookie in same page? can any one guide please.but this is my requirement.
<?php
$cookie = 1;
setcookie("count", $cookie);
if (!isset($_COOKIE['count']))
{
}
else
{
$cookie = ++$_COOKIE['count'];
}
echo "The Total visit is".$cookie;
?>
I decided to use local storage for this as I really dislike cookies, some users block them completely.
You can setup the echo. To work with this
Here is what I cam up with: http://jsfiddle.net/azrmno86/
// Check browser support
if (typeof(Storage) != "undefined") {
//check if the user already has visited
if (localStorage.getItem("count") === "undefined") {
//set the first time if it dfoes not exisit yet
localStorage.setItem("count", "1");
}
//get current count
var count = localStorage.getItem("count");
//increment count by 1
count++;
//set new value to storage
localStorage.setItem("count", count);
//display value
document.getElementById("result").innerHTML = count
} else {
document.getElementById("result").innerHTML = "Sorry, your browser does not support";
}
UPDATE After a bit more clarification
This style uses a .txt file stored on the server. Cookies are not reliable. If someone clears them, your done. if you use variables any server restart will kill your count. Eith use database or this method.
<?php
//very important
session_start();
$counter_name = "counter.txt";
// Check if a text file exists. If not create one and initialize it to zero.
if (!file_exists($counter_name)) {
$f = fopen($counter_name, "w");
fwrite($f,"0");
fclose($f);
}
// Read the current value of our counter file
$f = fopen($counter_name,"r");
$counterVal = fread($f, filesize($counter_name));
fclose($f);
// Has visitor been counted in this session?
// If not, increase counter value by one
if(!isset($_SESSION['hasVisited'])){
$_SESSION['hasVisited']="yes";
$counterVal++;
$f = fopen($counter_name, "w");
fwrite($f, $counterVal);
fclose($f);
}
echo "You are visitor number $counterVal to this site";

get specific text from .txt file in php

I am having a log file like this :
2013-04-08-03-17-52: Cleaning Up all data for restart operation
2013-04-08-03-18-02: Creating new instance of app before closing
2013-04-08-03-18-03: New instance created and running
2013-04-08-03-18-03: Application started
Currently, i am loading full log file every second to show it to the user using jquery ajax, As this being soo inefficient i am trying to figure out some way to load only the updated lines from log file.
Is their any way to get lines only after a particular time stamp 2013-04-08-03-18-03
, For this i will be managing a variable with last timestamp and will be updating it every time i get new lines.
I am kind of New to Php and know only the basics of reading and writing files.
You might want to check the file modification time first to see whether or not you need to reload the log file. You can use the filemtime-function for that.
Furthermore, you can use the file_get_contents-function using an offset to read the file from a certain point.
Edit: So, how does it work?
Suppose you have stored the last modification time in a session variable $_SESSION['log_lastmod'] and the most recent offset in $_SESSION['log_offset'].
session_start();
// First, check if the variables exist. If not, create them so that the entire log is read.
if (!isset($_SESSION['log_lastmod']) && !isset($_SESSION['log_offset'])) {
$_SESSION['log_lastmod'] = 0;
$_SESSION['log_offset'] = 0;
}
if (filemtime('log.txt') > $_SESSION['log_lastmod']) {
// read file from offset
$newcontent = file_get_contents('log.txt', NULL, NULL, $_SESSION['log_offset']);
// set new offset (add newly read characters to last offset)
$_SESSION['log_offset'] += strlen($newcontent);
// set new lastmod-time
$_SESSION['log_lastmod'] = filemtime('log.txt');
// manipulate $newcontent here to what you want it to show
} else {
// put whatever should be returned to the client if there are no updates here
}
You can try
session_start();
if (! isset($_SESSION['log'])) {
$log = new stdClass();
$log->timestamp = "2013-04-08-03-18-03";
$log->position = 0;
$log->max = 2;
$_SESSION['log'] = &$log;
} else {
$log = &$_SESSION['log'];
}
$format = "Y-m-d-:g:i:s";
$filename = "log.txt";
// Get last date
$dateLast = DateTime::createFromFormat($format, $log->timestamp);
$fp = fopen($filename, "r");
fseek($fp, $log->position); // prevent loading all file to memory
$output = array();
$i = 0;
while ( $i < $log->max && ! feof($fp) ) {
$content = fgets($fp);
// Check if date is current
if (DateTime::createFromFormat($format, $log->timestamp) < $dateLast)
continue;
$log->position = ftell($fp); // save current position
$log->timestamp = strstr($content, ":", true); // save curren time;
$output[] = $content;
$i ++;
}
fclose($fp);
echo json_encode($output); // send to ajax
Try something like this:
<?php
session_start();
$lines = file(/*Insert logfile path here*/);
if(isset($_SESSION["last_date_stamp"])){
$new_lines = array();
foreach($lines as $line){
// If the date stamp is newer than the session variable, add it to $new_lines
}
$returnLines = array_reverse($newLines); // the reverse is to put the lines in the right order
} else {
$returnLines = $lines;
}
$_SESSION['last_date_stamp'] = /* Insert the most recent date stamp here */;
// return the $returnLines variable
?>
What this does is reads a file line by line and, if a session variable already exists, add all of the lines where the date stamp is newer than the one in the session variable to a return variable $returnLines, if no session variable exists it will put all of the lines in the file in $returnLines.
After this, it create.updates the session variable for use the next time the code is executed. Finally it returns the log file data by using the $returnLines variable.
Hope this helps.

Need help resetting a php counter

I'm really new to php. I decided to make a counter based off a script I've seen. I've made changes to it. I'm trying to figure out how to reset the counter.
$userCount = file_get_contents("count.txt");
$userCount = trim($userCount);
$userCount = $userCount + 1;
$countReset = $userCount - $userCount;
$file = fopen("count.txt","w+");
fwrite($file,$userCount);
fclose($file);
print "The number of visitors is: $userCount";
if ($userCount < 20){
echo 'not yet';
}
else {
echo 'done!';
}
if ($userCount > 40){
fwrite($file,$countReset);
fclose($file);
}
I tried subtracting the counter from itself to get it back to 0.
$countReset = $userCount - $userCount;
However, it doesn't seem to work. the counter itself works, but I am unable to get it to reset back to 0.
This is just an impression like script I'm doing as a way to learn php. Also, sorry about the format, struggling with this post editor.
Any help with the script?
You've closed the file before trying to write to it again. Before your second fwrite, add:
$file = fopen("count.txt","w+");
Try to simply set value 0 to the var:
$countReset = 0;
Couldn't you just edit the count.txt file?
Doing it in PHP, you could do
fwrite($file,'0');
EDIT: Like CanSpice said, you shouldn't close the file before you're done with it. Remove the first fclose, and it should work.
I wouldn't mix fopen() and file_get_content() functions in this context, either use fopen(), fread() and fwrite() or use file_get_contents() and file_put_contents().
If you need just reset counter and you don't need previous value, than use:
file_put_contents('count.txt', '0');
If you need update value you may use either:
$count = file_get_contents( 'count.txt');
$count++;
// Reset?
if( $reset){
$count = 0;
}
file_put_contents( 'count.txt', "$count");
Or rather:
$fp = fopen( 'count.txt', 'r+') or die( 'Cannot use counter');
$count = trim( fread( $fp, 1024));
$count++;
// Reset?
if( $reset){
$count = 0;
}
ftruncate( $fp, 0);
fseek( 0, SEEK_SET)
fwrite( $fp, "$count");
fclose( $fp);
Here are the manual pages for ftruncate() and fseek() + you should probably study flock() so two scripts wouldn't overwrite the content at the same time.
/****************************************************************************
* read the current count from the counter file, increment
* it by 1, and re-save the new value off to the file
***************************************************************************/
function getAndIncrementCount(){
// attempt to open the file
// a+ means keep the contents so we can read it at least once,
// but allow us to overwrite the value once we increment it
if (($fHandle = fopen('count.txt','a+')) !== FALSE){
// read in the count (also cast to an int so if there is
// an invalid (or absent) value it will default to a 0
$count = (int) fread($fHandle, 100);
// increase the counter
$count++;
// go back to the beginning of the file
fseek($fHandle, 0);
// re-write the new count back to the file
fwrite($fHandle, $count);
// cose the file now that we're done with it
fclose($fHandle);
// return back the count
return $count;
}
// we couldn't get to the file, so return an error flag
return FALSE;
}
/****************************************************************************
* write the specified value to the counter file
***************************************************************************/
function setCount($count = 0){
// attempt to open the file with over-write permissions
// w+ will open the file and clear it
if (($fHandle = fopen('count.txt','w+')){
// write the counter to the file
fwrite($fHandle, $count);
// close the file now that we're done
fclose($fHandle);
// return the newly saved count
return $count;
}
// we couldn't get to the file, so return an error flag
return FALSE;
}
And applied in practice:
$userCount = getAndIncrementCount();
echo "The number of visitors is: {$userCount}";
if ($userCount < 20){
echo "Not Yet";
}else{
echo "Done!";
}
if ($userCount > 40){
setCount(0);
}
It's because you are not rewriting the file contents but adding to them when you use the fwrite second time, so $countReset gets appended to the content already in the file. Try this:
$userCount = file_get_contents("count.txt");
$userCount = $userCount + 1;
$countReset = $userCount - $userCount;
file_put_contents("count.txt", $userCount);
print "The number of visitors is: $userCount\n";
if ($userCount < 20){
echo 'not yet';
}
else {
echo 'done!';
}
if ($userCount > 40){
file_put_contents("count.txt", $countReset);
}

How to implement a Yahoo currency cache?

I have a Yahoo currency script in my site but they are taking too much time to load and are slowing my site. How can I cache them and refreshing cache every 3600 minutes?
You need some place to store these results. MySQL is a popular choice, but if the data does not need to stick around or have historical values, using memcache would be easier. Depending on your host, both of these options may be available.
The idea is:
create some sort of cache dir and set the defined cache age
then, at the very beginning of your function, check for cache
if it exists, check it's age.
If within range, get it
if cache too old
use live data and set that data into cache file.
Something like this should do the trick:
define(CACHE_DIR, 'E:/xampp/xampp/htdocs/tmp');
define(CACHE_AGE, 3600);
/**
* Adds data to the cache, if the cache key doesn't aleady exist.
* #param string $path the path to cache file (not dir)
* #return false if there is no cache file or the cache file is older that CACHE_AGE. It return cache data if file exists and within CACHE_AGE
*/
function get_cache_value($path){
if(file_exists($path)){
$now = time();
$file_age = filemtime($path);
if(($now - $file_age) < CACHE_AGE){
return file_get_contents($path);
} else {
return false;
}
} else {
return false;
}
}
function set_cache_value($path, $value){
return file_put_contents($path, $value);
}
function kv_euro () {
$path = CACHE_DIR . '/euro.txt';
$kveuro = get_cache_value($path);
if(false !== $kveuro){
echo "\nFROM CACHE\n";
return round($kveuro, 2);
} else {
echo "\nFROM LIVE\n";
$from = 'EUR'; /*change it to your required currencies */
$to = 'ALL';
$url = 'http://finance.yahoo.com/d/quotes.csv?e=.csv&f=sl1d1t1&s='. $from . $to .'=X';
$handle = #fopen($url, 'r');
if ($handle) {
$result = fgets($handle, 4096);
fclose($handle);
}
$allData = explode(',',$result); /* Get all the contents to an array */
$kveuro = $allData[1];
set_cache_value($path, $kveuro);
return $kveuro;
}
}
Also, rather than fgets which reads file line by line rather slower and since you are not manipulating a line, you should consider using file_get_contents function instead.

Categories