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++;
}
Related
main folder is home - with subfolders and txt files - on various levels
I need the list of entire folders tree - and count txt files inside each of them
This code gives the folders but count is always - 0
I suppose paths to folders and not only folder names - are required, but can't see - how to get them.
function rscan ($dir) {
$all = array_diff(scandir($dir), [".", ".."]);
foreach ($all as $ff) {
if(is_dir($dir . $ff)){
echo $ff . "\n"; // it works
$arr = glob($ff . "/*.txt");
echo count($arr) . "\n"; // always 0
rscan("$dir$ff/");
}
}
}
rscan("home/");
Line 6
$arr = glob($ff . "/*.txt");
Change to the code below:
$arr = glob($dir.$ff . "/*.txt");
An alternate implementation:
<?php
function glob_recursive($pattern, $flags = 0): Int {
$files = glob($pattern, $flags);
$count = count($files);
foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) {
$count += glob_recursive($dir.'/'.basename($pattern), $flags);
}
return $count;
}
var_dump(glob_recursive('home/*.txt'));
The output is something like:
int(10)
I have a folder with 5 html files. The files are called:
page-1.html
page-2.html
page-hello.html
page-32.html
yo-page-text.html
I wish to use glob function to return an array with only the files formatted: "page[number 0 - 1000]".
So essentially I wish the glob function to return the following pages from the example above:
page-1.html
page-2.html
page-32.html
This is the code that I have managed to write so far:
<?php
$directory = "testfolder/";
foreach (glob($directory . "page-*.html") as $filename) {
echo basename($filename);
}
?>
The glob pattern is not as flexible as RegEx, so unless someone has some other magic, you can filter after the glob:
$files = preg_grep('/page-[0-9]+\.html/', glob($directory . '*.*'));
If the glob pattern supported repetition + then it would be easy. Bash and Korn shells offer it with +([0-9]) but PHP doesn't.
You could also check for one number and trust that what follows matched by * will be numbers with: glob($directory . 'page-[0-9]*.html'), but this could match page-0-hello.html as well.
Assuming that your code works and that you get the basename of the files:
$name = basename($filename); //do not echo the basename
for ($i = 0; $i <= 1000; $i++) {
$trname = "page-$i";
if ($name == $trname) {
echo "$name";
}
}
Put one of these codes inside the foreach and get rid of echo basename($filename);. I'd recommend using the second one as nesting loops may prove to be a problem.
Note: This code can only work if your one works.
IMPORTANT EDIT:
My guess id that you want to do something (not echoing) the names, in which case:
function foo(bar) {
$arr = array();
$name = basename($filename);
$name = str_replace("page-", "", $name);
if (intval($name) < 1000 && intval($name) >= 0) {
$arr[] = $name;
}
}
Then, you can call on the function this way:
function foo() {
$arr = array();
$directory = "testfolder/";
foreach (glob($directory . "page-*.html") as $filename) {
$name = basename($filename);
$name = str_replace("page-", "", $name);
if (intval($name) < 1000 && intval($name) >= 0) {
$arr[] = $name;
}
}
}
$bar = foo();
foreach ($bar as $obj) {
//do something
}
I've come up with a basic file navigator that accepts user input to jump to different directories. The only problem I have with it is that I'm basically looping over the data three times:
Get a valid list of all directories for comparison against user input
Build a "sorted" list of directories and files
Output final list
Any tips on optimizing or improving this code?
define('ROOT', '/path/to/somewhere');
// get a list of valid paths
$valid = array();
$dir = new RecursiveDirectoryIterator(ROOT);
$dir->setFlags(RecursiveDirectoryIterator::SKIP_DOTS);
$iter = new ParentIterator($dir);
foreach(new RecursiveIteratorIterator($iter, RecursiveIteratorIterator::SELF_FIRST) as $file) {
$path = str_replace(ROOT, '', $file->getPathname());
$valid[] = $path;
}
// user input
$subpath = isset($_GET['path']) && in_array($_GET['path'], $valid) ? $_GET['path'] : NULL;
$cwd = isset($subpath) ? ROOT.$subpath : ROOT;
// build and sort directory tree
$files = array();
foreach(new DirectoryIterator($cwd) as $file) {
if($file->isDot()) {
continue;
}
if($file->isDir()) {
$path = str_replace(ROOT, '', $file->getPathname());
$count = iterator_count(new RecursiveDirectoryIterator($file->getRealPath(), FilesystemIterator::SKIP_DOTS));
$files[$path]['name'] = $file->getFilename();
$files[$path]['count'] = $count;
} else {
$files[] = $file->getFilename();
}
asort($files);
}
// output directory tree
if(!empty($files)) {
foreach($files as $key=>$value) {
if(is_array($value)) {
echo "{$value['name']} ({$value['count']})<br />";
} else {
echo "$value<br />";
}
}
}
How often is the directory structure going to change? Can it be cached and the whitelist be re-generated only when there are changes instead of on each request? This would be my approach depending on requirements and load factors.Beyond that probably lies the realm of micro-optimizations.
I am using PHP and I need to script something like below:
I have to compare two folder structure
and with reference of source folder I
want to delete all the files/folders
present in other destination folder
which do not exist in reference source
folder, how could i do this?
EDITED:
$original = scan_dir_recursive('/var/www/html/copy2');
$mirror = scan_dir_recursive('/var/www/html/copy1');
function scan_dir_recursive($dir) {
$all_paths = array();
$new_paths = scandir($dir);
foreach ($new_paths as $path) {
if ($path == '.' || $path == '..') {
continue;
}
$path = $dir . DIRECTORY_SEPARATOR . $path;
if (is_dir($path)) {
$all_paths = array_merge($all_paths, scan_dir_recursive($path));
} else {
$all_paths[] = $path;
}
}
return $all_paths;
}
foreach($mirror as $mirr)
{
if($mirr != '.' && $mirr != '..')
{
if(!in_array($mirr, $original))
{
unlink($mirr);
// delete the file
}
}
}
The above code shows what i did..
Here My copy1 folder contains extra files than copy2 folders hence i need these extra files to be deleted.
EDITED:
Below given output is are arrays of original Mirror and of difference of both..
Original Array
(
[0] => /var/www/html/copy2/Copy (5) of New Text Document.txt
[1] => /var/www/html/copy2/Copy of New Text Document.txt
)
Mirror Array
(
[0] => /var/www/html/copy1/Copy (2) of New Text Document.txt
[1] => /var/www/html/copy1/Copy (3) of New Text Document.txt
[2] => /var/www/html/copy1/Copy (5) of New Text Document.txt
)
Difference Array
(
[0] => /var/www/html/copy1/Copy (2) of New Text Document.txt
[1] => /var/www/html/copy1/Copy (3) of New Text Document.txt
[2] => /var/www/html/copy1/Copy (5) of New Text Document.txt
)
when i iterate a loop to delete on difference array all files has to be deleted as per displayed output.. how can i rectify this.. the loop for deletion is given below.
$dirs_to_delete = array();
foreach ($diff_path as $path) {
if (is_dir($path)) {
$dirs_to_delete[] = $path;
} else {
unlink($path);
}
}
while ($dir = array_pop($dirs_to_delete)) {
rmdir($dir);
}
First you need a recursive listing of both directories. A simple function like this will work:
function scan_dir_recursive($dir, $rel = null) {
$all_paths = array();
$new_paths = scandir($dir);
foreach ($new_paths as $path) {
if ($path == '.' || $path == '..') {
continue;
}
if ($rel === null) {
$path_with_rel = $path;
} else {
$path_with_rel = $rel . DIRECTORY_SEPARATOR . $path;
}
$full_path = $dir . DIRECTORY_SEPARATOR . $path;
$all_paths[] = $path_with_rel;
if (is_dir($full_path)) {
$all_paths = array_merge(
$all_paths, scan_dir_recursive($full_path, $path_with_rel)
);
}
}
return $all_paths;
}
Then you can compute their difference with array_diff.
$diff_paths = array_diff(
scan_dir_recursive('/foo/bar/mirror'),
scan_dir_recursive('/qux/baz/source')
);
Iterating over this array, you will be able to start deleting files. Directories are a bit trickier because they must be empty first.
// warning: test this code yourself before using on real data!
$dirs_to_delete = array();
foreach ($diff_paths as $path) {
if (is_dir($path)) {
$dirs_to_delete[] = $path;
} else {
unlink($path);
}
}
while ($dir = array_pop($dirs_to_delete)) {
rmdir($dir);
}
I've tested things and it should be working well now. Of course, don't take my word for it. Make sure to setup your own safe test before deleting real data.
For recursive directories please use:
$modified_directory = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('path/to/modified'), true
);
$modified_files = array();
foreach ($modified_directory as $file)
{
$modified_files []= $file->getPathname();
}
You can do other things like $file->isDot(), or $file->isFile(). For more file commands with SPLFileInfo visit http://www.php.net/manual/en/class.splfileinfo.php
Thanks all for the precious time given to my work, Special Thanks to erisco for his dedication for my problem, Below Code is the perfect code to acomplish the task I was supposed to do, with a little change in the erisco's last edited reply...
$source = '/var/www/html/copy1';
$mirror = '/var/www/html/copy2';
function scan_dir_recursive($dir, $rel = null) {
$all_paths = array();
$new_paths = scandir($dir);
foreach ($new_paths as $path) {
if ($path == '.' || $path == '..') {
continue;
}
if ($rel === null) {
$path_with_rel = $path;
} else {
$path_with_rel = $rel . DIRECTORY_SEPARATOR . $path;
}
$full_path = $dir . DIRECTORY_SEPARATOR . $path;
$all_paths[] = $path_with_rel;
if (is_dir($full_path)) {
$all_paths = array_merge(
$all_paths, scan_dir_recursive($full_path, $path_with_rel)
);
}
}
return $all_paths;
}
$diff_paths = array_diff(
scan_dir_recursive($mirror),
scan_dir_recursive($source)
);
echo "<pre>Difference ";print_r($diff_paths);
$dirs_to_delete = array();
foreach ($diff_paths as $path) {
$path = $mirror."/".$path;//added code to unlink.
if (is_dir($path)) {
$dirs_to_delete[] = $path;
} else {
if(unlink($path))
{
echo "File ".$path. "Deleted.";
}
}
}
while ($dir = array_pop($dirs_to_delete)) {
rmdir($dir);
}
First do a scandir() of the original folder, then do a scandir on mirror folder. start traversing the mirror folder array and check if that file is present in the scandir() of original folder. something like this
$original = scandir('path/to/original/folder');
$mirror = scandir('path/to/mirror/folder');
foreach($mirror as $mirr)
{
if($mirr != '.' && $mirr != '..')
{
if(in_array($mirr, $original))
{
// do not delete the file
}
else
{
// delete the file
unlink($mirr);
}
}
}
this should solve your problem. you will need to modify the above code accordingly (include some recursion in the above code to check if the folder that you are trying to delete is empty or not, if it is not empty then you will first need to delete all the file/folders in it and then delete the parent folder).
I want to use a function to recursively scan a folder, and assign the contents of each scan to an array.
It's simple enough to recurse through each successive index in the array using either next() or foreach - but how to dynamically add a layer of depth to the array (without hard coding it into the function) is giving me problems. Here's some pseudo:
function myScanner($start){
static $files = array();
$files = scandir($start);
//do some filtering here to omit unwanted types
$next = next($files);
//recurse scan
//PROBLEM: how to increment position in array to store results
//$next_position = $files[][][].... ad infinitum
//myScanner($start.DIRECTORY_SEPARATOR.$next);
}
any ideas?
Try something like this:
// $array is a pointer to your array
// $start is a directory to start the scan
function myScanner($start, &$array){
// opening $start directory handle
$handle = opendir($start);
// now we try to read the directory contents
while (false !== ($file = readdir($handle))) {
// filtering . and .. "folders"
if ($file != "." && $file != "..") {
// a variable to test if this file is a directory
$dirtest = $start . DIRECTORY_SEPARATOR . $file;
// check it
if (is_dir($dirtest)) {
// if it is the directory then run the function again
// DIRECTORY_SEPARATOR here to not mix files and directories with the same name
myScanner($dirtest, $array[$file . DIRECTORY_SEPARATOR]);
} else {
// else we just add this file to an array
$array[$file] = '';
}
}
}
// closing directory handle
closedir($handle);
}
// test it
$mytree = array();
myScanner('/var/www', $mytree);
print "<pre>";
print_r($mytree);
print "</pre>";
Try to use this function (and edit it for your demands):
function getDirTree($dir,$p=true) {
$d = dir($dir);$x=array();
while (false !== ($r = $d->read())) {
if($r!="."&&$r!=".."&&(($p==false&&is_dir($dir.$r))||$p==true)) {
$x[$r] = (is_dir($dir.$r)?array():(is_file($dir.$r)?true:false));
}
}
foreach ($x as $key => $value) {
if (is_dir($dir.$key."/")) {
$x[$key] = getDirTree($dir.$key."/",$p);
}
}
ksort($x);
return $x;
}
It returns sorted array of directories.