File loop in PHP - 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++;
}

Related

Sorting files in an array by the ocurrences of a word in it, php

I'm making a search bar that searches files in a directory that have the word searched, then I want it to be added to an array by order of which one has more times the word asked to the one with less.
I'm working on PHP this is my code:
<?php
if(isset($_POST['busqueda'])){
$variable = utf8_encode($_POST['busqueda']);
}
$Array1 = array();
foreach(glob("*.txt") as $filename) {
$contents = file_get_contents($filename);
if (strpos($contents, $variable)){
$Array1[] = $filename;
}
}
I don't know how to do it exactly, I think that I should use substr_count(file_get_contents($Array1[$position1])) or something like that but I'm unsure how to make the sorting system, can someone help me!
print_r($Array1);
for($var1=0; $var1<sizeof($Array1); $var1++){
echo "times on the file: ".$Array1[$var1]."<br>";
echo substr_count(file_get_contents($Array1[$var1]));
}
?>
You can use the substr_count itself. Then you need to use arsort to sort the array.
$Array1 = array();
foreach (glob("*.txt") as $filename) {
$contents = file_get_contents($filename);
if ( ($count = substr_count($contents, $variable)) ) {
$Array1[$filename] = $count;
}
}
arsort($Array1) ;
print_r($Array1);
foreach ($Array1 as $file => $count) {
echo "times on the file($file): $count <br>";
}
Bash (available on at least Linux and Mac operating systems) makes it extremely easy to accomplish your task, because you can call commands through PHP's exec function, assuming it is not disabled by an administrator. If you're on Windows, then this will probably not work, but most people are using Linux for a production environment, so I thought this answer would be worthy of posting.
The following function is taken from CodeIgniter's file helper and only serves to fetch an array of filenames from a specified directory. If you don't need a function like this because you are getting your filenames from somewhere else, just note that this function can include the full file path for each file, and that's why I used it.
function get_filenames($source_dir, $include_path = FALSE, $_recursion = FALSE)
{
static $_filedata = array();
if ($fp = #opendir($source_dir))
{
// reset the array and make sure $source_dir has a trailing slash on the initial call
if ($_recursion === FALSE)
{
$_filedata = array();
$source_dir = rtrim(realpath($source_dir), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
}
while (FALSE !== ($file = readdir($fp)))
{
if (#is_dir($source_dir.$file) && strncmp($file, '.', 1) !== 0)
{
get_filenames($source_dir.$file.DIRECTORY_SEPARATOR, $include_path, TRUE);
}
elseif (strncmp($file, '.', 1) !== 0)
{
$_filedata[] = ($include_path == TRUE) ? $source_dir.$file : $file;
}
}
return $_filedata;
}
else
{
return FALSE;
}
}
Now that I can fetch an array of filenames easily, I'd do this:
/**
* Here you can see that I am searching
* all of the files in the script-library
* directory for the word "the"
*/
$searchWord = 'the';
$directory = '/var/www/htdocs/script-library';
$filenames = get_filenames(
$directory,
TRUE
);
foreach( $filenames as $file )
{
$counts[$file] = exec("tr ' ' '\n' < " . $file . " | grep " . $searchWord . " | wc -l");
}
arsort( $counts );
echo '<pre>';
print_r( $counts );
echo '</pre>';
For a good explaination of how that works, see this: https://unix.stackexchange.com/questions/2244/how-do-i-count-the-number-of-occurrences-of-a-word-in-a-text-file-with-the-comma
I tested this code locally and it works great.

PHP - get single file based on user

I'm having trouble getting my code to work the way I want.
I'm using scandir to get all files from the directory. This gives me a list with pdf files linked to a product, but the problems comes with the posibllity of pdf files multiple languages. Like so:
1096_EN.pdf
867_PT.pdf
914_EN.pdf
914_NL.pdf
Before _ is ID and after language. And I want the user to only see one file per product.
my code looks likes this:
$files = scandir($dir);
foreach ($files as $file)
{
$exp_file = explode("_", $file);
// check file for given ID
if($exp_file[0] == $_GET['iD']){
// check file for userlanguage
if($exp_file[1] == $lang){
echo $file;
}
// check file in english
elseif($exp_file[1] == "EN"){
echo $file;
}
// return available file in other language
else{
echo $file;
}
}
}
In case of 914 and NL the code returns two files. In case of 914 and PT i only get 1 file, 914_EN.pdf and in case of 867 and NL there will be zero files.
What is the best way to filter my files and return the best matched file? I personally think the error is in the for loop, but I cant find a proper way out..
thanks
If you want to have just the single items, you should keep a backlog of which you have already processed, as the foreach loop will go from for example 914_EN.pdf to 914_NL.pdf, while the checks have already been completed for 914_EN.pdf, so when you get to 914_NL.pdf, it just reruns the checks and thinks it is okay.
if working with multiple same values, you can first cleanse the array to get what you wanted. You can take a look at this, if this what you want. Cheers!
$files = array("1096_EN.pdf", "867_PT.pdf", "914_EN.pdf", "914_NL.pdf");
$new_exp_file = array();
foreach ($files as $file) {
$exp_file = explode("_", $file);
$new_exp_file[] = $exp_file[0];
}
$new_exp_file_arr_ = array_values(array_unique($new_exp_file));
for($i = 0, $file_ctr = count($new_exp_file_arr_); $i < $file_ctr; $i++) {
if($new_exp_file_arr_[$i] == "914") {
echo $new_exp_file_arr_[$i] . "<br>";
echo "<ul>";
foreach ($files as $file) {
$exp_file = explode("_", $file);
if($new_exp_file_arr_[$i] == $exp_file[0]) {
echo "<li>" . $exp_file[1] . "</li>";
}
}
echo "</ul>";
}
}
this seems to work for me? Using a regex probably not as efficient as the above methods though.
$_GET['iD'] = 1096;
$ptn = "^((\d+)\_([a-zA-Z]+)\.([a-zA-Z]+))^";
$aFiles = array('1096_EN.pdf','867_PT.pdf','914_EN.pdf','914_NL.pdf');
$lang = "EN";
foreach ($aFiles as $sFileName)
{
preg_match($ptn, $sFileName, $aFileParts);
var_dump($aFileParts);
// check file for given ID
if($aFileParts[2] == $_GET['iD']){
// check file for userlanguage
if(strtolower($aFileParts[3]) == strtolower($lang)){
echo $sFileName;
break;
}
// return available file in other language
else{
echo $sFileName;
}
}
}
I've solved my problem by the following:
if(glob($_GET['iD']."_".$_GET['t']."*.pdf"))
{
$file = glob($_GET['iD']."_".$_GET['t']."*.pdf");
echo $file[0];
}
else
{
if(glob($_GET['iD']."_EN*.pdf"))
{
$file = glob($_GET['iD']."_EN*.pdf");
echo $file[0];
}
else
{
$file = glob($_GET['iD']."*.pdf");
echo $file[0];
}
}
No more looping, just checking for different files with wildcards. Works like a charm. I.m.o. much cleaner with larger lists of files..

Increment number in text file

I want to record downloads in a text file
Someone comes to my site and downloads something, it will add a new row to the text file if it hasn't already or increment the current one.
I have tried
$filename = 'a.txt';
$lines = file($filename);
$linea = array();
foreach ($lines as $line)
{
$linea[] = explode("|",$line);
}
$linea[0][1] ++;
$a = $linea[0][0] . "|" . $linea[0][1];
file_put_contents($filename, $a);
but it always increments it by more than 1
The text file format is
name|download_count
You're doing your incrementing outside of the for loop, and only accessing the [0]th element so nothing is changing anywhere else.
This should probably look something like:
$filename = 'a.txt';
$lines = file($filename);
// $k = key, $v = value
foreach ($lines as $k=>$v) {
$exploded = explode("|", $v);
// Does this match the site name you're trying to increment?
if ($exploded[0] == "some_name_up_to_you") {
$exploded[1]++;
// To make changes to the source array,
// it must be referenced using the key.
// (If you just change $v, the source won't be updated.)
$lines[$k] = implode("|", $exploded);
}
}
// Write.
file_put_contents($filename, $lines);
You should probably be using a database for this, though. Check out PDO and MYSQL and you'll be on your way to awesomeness.
EDIT
To do what you mentioned in your comments, you can set a boolean flag, and trigger it as you walk through the array. This may warrant a break, too, if you're only looking for one thing:
...
$found = false;
foreach ($lines as $k=>$v) {
$exploded = explode("|", $v);
if ($exploded[0] == "some_name_up_to_you") {
$found = true;
$exploded[1]++;
$lines[$k] = implode("|", $exploded);
break; // ???
}
}
if (!$found) {
$lines[] = "THE_NEW_SITE|1";
}
...
one hand you are using a foreach loop, another hand you are write only the first line into your file after storing it in $a... it's making me confuse what do you have in your .txt file...
Try this below code... hope it will solve your problem...
$filename = 'a.txt';
// get file contents and split it...
$data = explode('|',file_get_contents($filename));
// increment the counting number...
$data[1]++;
// join the contents...
$data = implode('|',$data);
file_put_contents($filename, $data);
Instead of creating your own structure inside a text file, why not just use PHP arrays to keep track? You should also apply proper locking to prevent race conditions:
function recordDownload($download, $counter = 'default')
{
// open lock file and acquire exclusive lock
if (false === ($f = fopen("$counter.lock", "c"))) {
return;
}
flock($f, LOCK_EX);
// read counter data
if (file_exists("$counter.stats")) {
$stats = include "$counter.stats";
} else {
$stats = array();
}
if (isset($stats[$download])) {
$stats[$download]++;
} else {
$stats[$download] = 1;
}
// write back counter data
file_put_contents('counter.txt', '<?php return ' . var_export($stats, true) . '?>');
// release exclusive lock
fclose($f);
}
recordDownload('product1'); // will save in default.stats
recordDownload('product2', 'special'); // will save in special.stats
personally i suggest using a json blob as the content of the text file. then you can read the file into php, decode it (json_decode), manipulate the data, then resave it.

PHP_Count total lines in all files under a given folder

Just wanted to count total number of line from all the files from the folder. following php function helps me to count line num for only particular file. just wondering what is the way to cont total number of lines from the folder.
$lines = COUNT(FILE($file));
Thank you.!
You could iterate the directory and count each file and sum them all. And you are using file() function, which will load the whole content into memory, if the file is very large, your php script will reach the memory limit of your config.
If you could use external command, there is a solution with one line. (If you are using windows, just omit it.)
$total = system("find $dir_path -type f -exec wc -l {} \; | awk '{total += $1} END{print total}'");
Same as one above (salathe's answer), except this one prints the number of lines (now in php7) rather than a bunch of error messages.
$files = new RecursiveIteratorIterator(new
RecursiveDirectoryIterator(__DIR__));
$lines = 0;
foreach ($files as $fileinfo) {
if (!$fileinfo->isFile()) {
continue;
}
$read = $fileinfo->openFile();
$read->setFlags(SplFileObject::READ_AHEAD);
$lines += iterator_count($read) - 1; // -1 gives the same number as "wc -l"
}
echo ("Found :$lines");
Something like this perhaps:
<?php
$line_count = 0;
if ($handle = opendir('some/dir/path')) {
while (false !== ($entry = readdir($handle))) {
if (is_file($entry)) {
$line_count += count(file($entry));
}
}
closedir($handle);
}
var_dump($line_count);
?>
Check out the Standard PHP Library (aka SPL) for DirectoryIterator:
$dir = new DirectoryIterator('/path/to/dir');
foreach($dir as $file ){
$x += (isImage($file)) ? 1 : 0;
}
(FYI there is an undocumented function called iterator_count() but probably best not to rely on it for now I would imagine. And you'd need to filter out unseen stuff like . and .. anyway.)
or try this:--
see url :- http://www.brightcherry.co.uk/scribbles/php-count-files-in-a-directory/
$directory = "../images/team/harry/";
if (glob($directory . "*.jpg") != false)
{
$filecount = count(glob($directory . "*.jpg"));
echo $filecount;
}
else
{
echo 0;
}
A very basic example of counting the lines might look something like the following, which gives the same numbers as xdazz's answer.
<?php
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__));
$lines = $files = 0;
foreach ($files as $fileinfo) {
if (!$fileinfo->isFile()) {
continue;
}
$files++;
$read = $fileinfo->openFile();
$read->setFlags(SplFileObject::READ_AHEAD);
$lines += iterator_count($read) - 1; // -1 gives the same number as "wc -l"
}
printf("Found %d lines in %d files.", $lines, $files);
See also
RecursiveDirectoryIterator
SplFileInfo
SplFileObject
RecursiveIteratorIterator
iterator_count()

PHP Help with "if" statement to dynamically include files

I have these files:
"id_1_1.php", "id_1_2.php", "id_1_3.php" etc
"id_2_1.php", "id_2_2.php", "id_2_3.php" etc
the number of files is not known because will always grow..
all the files are in same directory..
I want to make a if statement:
to include the files only if their name ends with "_1"
another function to load all the files that start with "id_1"
How can I do this? Thank you!
edit1: no the numbers will not be skipped, once I have another item for id_1_ collection of products I will add new ones as id_1_1, id_1_2 etc.. so no skipping..
// Each of these:
// - scans the directory for all files
// - checks each file
// - for each file, does it match the pattern described
// - if it does, expand the path
// - include the file once
function includeFilesBeginningWith($dir, $str) {
$files = scandir($dir);
foreach ($files as $file) {
if (strpos($file, $str) === 0) {
$path = $dir . '/' . $file;
include_once($path);
}
}
}
function includeFilesEndingWith($dir, $str) {
$files = scandir($dir);
foreach ($files as $file) {
if (strpos(strrev($file), strrev($str)) === 0) {
$path = $dir . '/' . $file;
include_once($path);
}
}
}
/* To use: - the first parameter is ".",
the current directory, you may want to
change this */
includeFilesBeginningWith('.', 'id_1');
includeFilesEndingWith('.', '_1.php');
Loosely based on Svisstack's original answer (untested):
function doIncludes($pre='',$post=''){
for ($i=1;1;$i++)
if (file_exists($str=$pre.$i.$post.'.php'))
include($str);
else
return;
}
function first_function(){
doIncludes('id_','_1');
}
function second_function(){
doIncludes('id_1_');
}
function my_include($f, $s)
{
#include_once("id_" . $f . "_" . $s . ".php");
}
function first_function($howmany = 100, $whatstart = '1')
{
for ($i=1; $i <= $howmany; $i++)
{
my_include('1', $i)
}
}
function second_function($howmany = 100, $whatend = '1')
{
for ($i=1; $i <= $howmany; $i++)
{
my_include($i, '1');
}
}
This will parse through every file incrementing by one until it finds a file that doesn't exist. Assuming contiguous numbers it should catch every existing file. If you want to include files with a number other then 1 in the name, just change $lookingfor as appropriate.
$lookingfor = 1;
$firstnum=1;
while ($firstnum>0) {
$secondnum=1;
while ($secondnum>0) {
$tempfilename = "id_".$firstnum."_".$secondnum.".php";
if file_exists($tempfilename) {
if (($firstnum==$lookingfor)||($secondnum==$lookingfor)) {include $tempfilename; }
$secondnum++;
} else {
$secondnum=-1;
}
}
$firstnum++;
}

Categories