PHP counter with flock - php

I have a problem with a counter. I need to count two variables, separated with a |, but sometimes the counter doesn't increase a variable's value.
numeri.txt (the counter):
6122|742610
This is the PHP script:
$filename="numeri.txt";
while(!$fp=fopen($filename,'c+'))
{
usleep(100000);
}
while(!flock($fp,LOCK_EX))
{
usleep(100000);
}
$contents=fread($fp,filesize($filename));
ftruncate($fp,0);
rewind($fp);
$contents=explode("|",$contents);
$clicks=$contents[0];
$impressions=$contents[1]+1;
fwrite($fp,$clicks."|".$impressions);
flock($fp,LOCK_UN);
fclose($fp);
I have another counter that is a lot slower but counts both values (clicks and impressions) exactly. Sometimes the counter numeri.txt counts more impressions than the other counter. Why? How can I fix this?

We're using the following at our high-traffic site to count impressions:
<?php
$countfile = "counter.txt"; // SET THIS
$yearmonthday = date("Y.m.d");
$yearmonth = date("Y.m");;
// Read the current counts
$countFileHandler = fopen($countfile, "r+");
if (!$countFileHandler) {
die("Can't open count file");
}
if (flock($countFileHandler, LOCK_EX)) {
while (($line = fgets($countFileHandler)) !== false) {
list($date, $count) = explode(":", trim($line));
$counts[$date] = $count;
}
$counts[$yearmonthday]++;
$counts[$yearmonth]++;
fseek($countFileHandler, 0);
// Write the counts back to the file
krsort($counts);
foreach ($counts as $date => $count) {
fwrite($countFileHandler, "$date:$count\n");
fflush($countFileHandler);
}
flock($countFileHandler, LOCK_UN);
} else {
echo "Couldn't acquire file lock!";
}
fclose($countFileHandler);
}
?>
The results are both daily and monthly totals:
2015.10.02:40513
2015.10.01:48396
2015.10:88909

Try performing a flush before unlocking. You're unlocking before the data might even be written, allowing another execution to clobber.
http://php.net/manual/en/function.fflush.php

Related

Problem getting last chunk in a while loop

So it's been awhile since i did any PHP and to be honest, this question feels kinda dumb. But my head is just stuck thinking about how to get last chunk in a file.
My while loop reads a file, line by line and after 10 lines it should execute a code. Problem occures when there's 51 lines. How do i reach the last line?
The file is over 300 mb so I cannot load it into memory (array).
while ($row = fgets($handle))
{
$chunk[] = array_combine($feed_product_arraykeys, explode("\t", $row));
if(count($chunk) == 10)
{
echo count($chunk) . '<br>';
// Initiate code
unset($chunk);
}
}
Best Regards
Here's an alternate way. Just read the file into an array and chunk it into chunks of 10 The remaining will be in the last chunk:
foreach(array_chunk(file('/path/to/file'), 10) as $row) {
$chunk[] = array_combine($feed_product_arraykeys, explode("\t", $row));
echo count($chunk) . '<br>';
}
So i actually fixed it by counting number of rows in the file. I thought it would be slow but its actually fast, even on a 300 mb file with 130k rows.
// Count number of lines in feed
$feed_row_count = count_lines_in_file("tmp/56.csv");
$row_counter = 0;
$feed_handle = fopen("tmp/56.csv", "r");
while ($row = fgets($feed_handle))
{
$row_counter++;
$chunk[] = array_combine($feed_product_arraykeys, explode("\t", $row));
if(count($chunk) == 25 || $feed_row_count == $row_counter)
{
echo count($chunk) . '<br>';
// Initiate SQL
unset($chunk);
}
}

Checking for partial duplications in a CSV in PHP

I'm having an issue with a memory leak in this code. What I'm attempting to do is to temporarily upload a rather large CSV file (at least 12k records), and check each record for a partial duplication against other records in the CSV file. The reason why I say "partial duplication" is because basically if most of the record matches (at least 30 fields), it is going to be a duplicate record. The code I've written should, in theory, work as intended, but of course, it's a rather large loop and is exhausting memory. This is happening on the line that contains "array_intersect".
This is not for something I'm getting paid to do, but it is with the purpose of helping make life at work easier. I'm a data entry employee, and we are having to look at duplicate entries manually right now, which is asinine, so I'm trying to help out by making a small program for this.
Thank you so much in advance!
if (isset($_POST["submit"])) {
if (isset($_FILES["sheetupload"])) {
$fh = fopen(basename($_FILES["sheetupload"]["name"]), "r+");
$lines = array();
$records = array();
$counter = 0;
while(($row = fgetcsv($fh, 8192)) !== FALSE ) {
$lines[] = $row;
}
foreach ($lines as $line) {
if(!in_array($line, $records)){
if (count($records) > 0) {
//check array against records for dupes
foreach ($records as $record) {
if (count(array_intersect($line, $record)) > 30) {
$dupes[] = $line;
$counter++;
}
else {
$records[] = $line;
}
}
}
else {
$records[] = $line;
}
}
else {
$counter++;
}
}
if ($counter < 1) {
echo $counter." duplicate records found. New file not created.";
}
else {
echo $counter." duplicate records found. New file created as NEWSHEET.csv.";
$fp = fopen('NEWSHEET.csv', 'w');
foreach ($records as $line) {
fputcsv($fp, $line);
}
}
}
}
A couple of possibilities, assuming the script is reaching the memory limit or timing out. If you can access the php.ini file, try increasing the memory_limit and the max_execution_time.
If you can't access the server settings, try adding these to the top of your script:
ini_set('memory_limit','256M'); // change this number as necessary
set_time_limit(0); // so script does not time out
If altering these settings in the script is not possible, you might try using unset() in a few spots to free up memory:
// after the first while loop
unset($fh, $row);
and
//at end of each foreach loop
unset($line);

PHP CSVImporter

I had a script called CSVimporter V3 for PHP that I used to run on a website and it worked fine. A couple of years later I've now dug out the same script to use on another project, all works okay except the CSV files are being read as one long line, as opposed to header row and multiple lines.
Here is part of the script.
Any ideas why it would be being read as a long line?
<?php
// Reference session variable for short-hand
$f = &$_SESSION['csv_file'];
// Open file - fp = file pointer
if (!$fp = #fopen($f, 'r')) {
error(L_ERROR_PREVIEW_NO_FILE);
} else {
// Array to store each row to be inserted
$batch = array();
// Row counter
$rc = 0;
// Work out starting row
switch ($_SESSION['csv_first_row']) {
case 'data':
$start_row = 0;
break;
default:
$start_row = 1;
}
// Get contents, while below preview limit and there's data to be read
while ($data = fgetcsv($fp, 1024, delimiter_to_char($_SESSION['csv_delimiter']))) {
if ($rc < $start_row) {
// Incremement counter
$rc++;
// Go to next loop
continue;
} else {
// Array to store data to be inputted
$values = array();
// Loop data
for ($i = 0; $i < count($data); $i++) {
// If data is wanted, put data into array
if (array_key_exists($i, $column_index)) {
$values[$column_index[$i]] = $data[$i];
}
}
// Sort array into correct order by index
ksort($values);
// Join values together and store in array
$batch[] = '("' . implode('", "', str_replace('"', '\"', $values)) . '","'.$accti.'","'.$impt.'")';
}
}
}
// Close the file
fclose($fp);
I added this at the top of the code and it all works now!
ini_set('auto_detect_line_endings', true);

File loop in PHP

I wrote some code to create a text file just once each time I execute the php file.
Its idea is to check all existing files with a specific name then create a text file with the previous name +1
For example, if there is a file called filetext0.txt, my code will create a file called filetext1.txt and so on...
Please help me to find the error in my code:
<?php
for ($i=0; $i=1000; $i=$i+1)
{
$handle = fopen("filetext".$i.".txt","r");
if ($handle) {
fclose($handle);
$s=$i+1
$handlex = fopen("filetext".$s.".txt","w+");
fclose($handlex);
break
}
}
?>
First of all you should use file_exists in the first step.
Then, your problem are missing semi-colon ';' at the end of lines. Check the error messages on your web pages next time ;)
And finally, your code create a file each file it found, not only one.
I'll suggest this code :
$i = 0;
while(true) {
$filename = "filetext".$i.".txt";
if(! file_exists($filename)) {
touch($filename);
break;
}
$i++
}
You do not have to open each and ever file to check if it exists. You should use PHP's directory functions.
// the maximum number
$maxnum = 0;
$d = dir(".");
while (false !== ($entry = $d->read())) {
if (preg_match ('/filetext([0-9]+)\.txt/', $entry, $matches)) {
if ($matches[1] > $maxnum) {
$maxnum = $matches[1];
}
}
}
$d->close();
echo ("The biggest number is: " . $maxnum);
// increment maxnum
$maxnum++;
// creating the file
touch ("filetext" . $maxnum . ".txt");
You need a ; after each statement.
$fileNames = glob('filetext*.txt');
$latestNumber = -1;
foreach($fileNames as $fileName) {
list($fileNumber) = sscanf($fileName,'filetext%d.txt');
$latestNumber = max($latestNumber,$fileNumber);
}
if ($fileNumber > -1) {
$fileName = 'filetext'.($fileNumber+1).'.txt';
touch($fileName);
}
Leaving aside the syntax errors, the algorithm you are using does not scale well. A better solution would be a searching method something like:
function find_next($stub)
{
$increment=1000; // depending on number of files
$offset=0;
for ($x=0; $x<500; $x++) {
$offset+=$increment;
if (file_exists($stub . $offset)) {
if ($increment<0) {
$increment=-1*((integer)($increment/2) ? $increment/2 : 1;
}
} else {
if (file_exists($stub . ($offset-1)) {
return $offset;
}
if ($increment>0) {
$increment=-1*((integer)($increment/2) ? $increment/2 : 1;
}
}
}
return false; // too many files!
}
(NB I'm just typing this stuff - the above may be a bit buggy).
But it'd be a lot better to store a sequence number and increment it each time you add a file.
However do beware that storing transactional data for a multi-user system using files with PHP is a very bad idea.
Yes used file_exists function to find next name for the file.
The above code missed the brace in if condition.
here code for your problem
$i = 0;
while(true) {
$myfile = "myfile".$i.".txt";
if(!file_exists($myfile)) {
$fh = fopen($myfile, 'w');
fclose($fh);
break;
}
$i++;
}

(PHP) Help debug tracking script to show only unique items

i have this script that i am trying to implement to work on my website and the script is suppose to track what games are being played at the moment but the only problem is that when i call the script to display the entries it would show duplicates and i want the script to actually explore the string and take only the $gamename and look for duplicates, not the entire string.
What the script does:
Records the gameID, gamename, gamethumb url, IP and time all separated by |.
Example: 1744|The Simpliest Snowboarding|The Simpliest Snowboarding|77.88.42.26|1264717552
Look for the IP and if already exists update the record with the new info.
If the IP does not exist already write a new line with the information.
If the record is older then 60min erase it.
Its just a simple script that i will use to show what people are currently playing on the website.
$dataFile = "visitors2.txt";
$numbergames = 30;
//Please do not edit bellow this line
error_reporting(E_ERROR | E_PARSE);
$users = array();
//getting
$fp = fopen($dataFile, "r");
flock($fp, LOCK_SH);
while(!feof($fp)) {
$users[] = fgets($fp, 4096);
}
flock($fp, LOCK_UN);
fclose($fp);
$i = 0;
echo '<ul>';
foreach(array_unique($users) as $key => $data) {
list($game2id , $gamename , $gamethumb , , ) = explode("|", $data);
//echo $game2id . $gamename;
if($gamename != "" && $i <= $numbergames) {
$newpageurl = str_replace(" ", "-", strip_tags(trim(str_replace($rplce, "", $gamename)))) ;
$url = $game2id .'-'. $newpageurl .'.html';
echo '<li><img src="./arcade/img/'.$gamethumb.'.png" width="35" height="35" border="0" />'.$gamename.'</li>';
}
$i++;
}
Please, help and thanks everyone in advance.
Your array_unique() call is only eliminating duplicate rows - entire rows, not just game names. You need to create an array of just the game names. Then, you can eliminate the dupes.
Something like:
$currentGames = array();
$max = 30;
$fp = fopen($dataFile, "r");
flock($fp, LOCK_SH);
while ((count($currentGames) < $max) && (($data = fgetcsv($fg, 0, '|')) !== FALSE)
{
if (!in_array($data[1], $currentGames)) $currentGames[$data[0]] = $data[1];
}
This will give you an associative array of unique game names with game ids as keys.

Categories