Given the path /books/Aaronovitch, Ben/Rivers of London/9780575097568, how could I use PHP to rename the actual folder names to remove the spaces?
You can try the following
echo renameRecrisive(__DIR__, "xx_x/yyy yyy/zz z/fff");
Output
/public_html/www/stac/xx_x/yyy_yyy/zz_z
Function
/**
*
* #param string $path Current path ending with a slash
* #param string $pathname Path you cant to rename
* #param string $sep Optional Seprator
*/
function renameRecrisive($path, $pathname, $sep = "_") {
$pathSplit = array_filter(explode("/", $pathname));
$dir = $path;
while ( $next = array_shift($pathSplit) ) {
$current = $dir . "/" . $next;
if (! is_dir($current)) {
break;
}
if (preg_match('/\s/', $next)) {
$newName = str_replace(" ", $sep, $next);
rename($current, $dir . "/" . $newName);
$dir .= "/" . $newName;
} else {
$dir .= "/" . $next;
}
}
return $dir ;
}
Php function str_replace:
$newPath = str_replace(' ', '', $path);
and then use the rename function.
rename($path, $newPath);
This will walk down each level of the hierarchy, renaming each component if it contains spaces.
$patharray = split('/', $path);
$newpatharray = str_replace(' ', '', $patharray);
$oldpath = $patharray[0];
$newpath = $newpatharray[0];
$i = 0;
while (true) {
if ($patharray[$i] != $newpatharray[$i]) {
rename($oldpath, $newpath);
}
$i++;
if ($i >= count($patharray) {
break;
}
$oldpath .= "/".$patharray[$i];
$newpath .= "/".$newpatharray[$i];
}
Related
Here is a code to search and return the existing files from the given directories:
<?php
function getDirContents($directories, &$results = array()) {
$length = count($directories);
for ($i = 0; $i < $length; $i++) {
if(is_file($directories[$i])) {
if(file_exists($directories[$i])) {
$path = $directories[$i];
$directory_path = basename($_SERVER['REQUEST_URI']);
$results[] = 'https://' . $_SERVER['SERVER_NAME'] . str_replace($directory_path, "", $_SERVER['REQUEST_URI']) .$path;
}
} else {
$files = array_diff(scandir($directories[$i]), array('..', '.'));
foreach($files as $key => $value) {
$path = $directories[$i].DIRECTORY_SEPARATOR.$value;
if(is_dir($path)) {
getDirContents([$path], $results);
} else {
$directory_path = basename($_SERVER['REQUEST_URI']);
$results[] = 'https://' . $_SERVER['SERVER_NAME'] . str_replace($directory_path, "", $_SERVER['REQUEST_URI']) .$path;
}
}
}
}
return $results;
}
echo json_encode(getDirContents($_POST['directories']));
So you can pass an array of file addresses and directories and get what ever files inside those directories, Note if you pass a file address instead of a directory address the function checks if there is such a file and if there is it returns its address in the result .
The issue is for the directories it works fine but the files repeat twice in the result and for each file the function double checks this if statement in the code:
if(is_file($directories[$i]))
Here is a result of the function note that contemporary.mp3 and Japanese.mp3
has been re checked and added to the result.
How can I solve this?
If $directories contains both a directory and a file within that directory, you'll add the file to the result for the filename and also when scanning the directory.
A simple fix is to check whether the filename is already in the result before adding it.
<?php
function getDirContents($directories, &$results = array()) {
foreach ($directories as $name) {
if(is_file($name)) {
$path = $name;
$directory_path = basename($_SERVER['REQUEST_URI']);
$new_path = 'https://' . $_SERVER['SERVER_NAME'] . str_replace($directory_path, "", $_SERVER['REQUEST_URI']) .$path;
if (!in_array($new_path, $results)) {
$results[] = $new_path;
}
} elseif (is_dir($name)) {
$files = array_diff(scandir($name), array('..', '.'));
foreach($files as $key => $value) {
$path = $name.DIRECTORY_SEPARATOR.$value;
if(is_dir($path)) {
getDirContents([$path], $results);
} else {
$directory_path = basename($_SERVER['REQUEST_URI']);
$new_path = 'https://' . $_SERVER['SERVER_NAME'] . str_replace($directory_path, "", $_SERVER['REQUEST_URI']) .$path;
if (!in_array($new_path, $results)) {
$results[] = $new_path;
}
}
}
}
}
return $results;
}
i would like to create a PHP script that delete files from multiple folders/paths.
I managed something but I would like to adapt this code for more specific folders.
This is the code:
<?php
function deleteOlderFiles($path,$days) {
if ($handle = opendir($path)) {
while (false !== ($file = readdir($handle))) {
$filelastmodified = filemtime($path . $file);
if((time() - $filelastmodified) > $days*24*3600)
{
if(is_file($path . $file)) {
unlink($path . $file);
}
}
}
closedir($handle);
}
}
$path = 'C:/Users/Legion/AppData/Local/Temp';
$days = 7;
deleteOlderFiles($path,$days);
?>
I would like to make something like to add more paths and this function to run for every path.
I tried to add multiple path locations but it didn't work because it always takes the last $ path variable.
For exemple:
$path = 'C:/Users/Legion/AppData/Local/Temp';
$path = 'C:/Users/Legion/AppData/Local/Temp/bla';
$path = 'C:/Users/Legion/AppData/Local/Temp/blabla';
$path = 'C:/Users/Legion/AppData/Local/Temp/blalbalba';
$days = 7;
deleteOlderFiles($path,$days);
Thank you for you help!
The simple solution, call the function after setting the parameter not after setting all the possible parameters into a scalar variable.
$days = 7;
$path = 'C:/Users/Legion/AppData/Local/Temp';
deleteOlderFiles($path,$days);
$path = 'C:/Users/Legion/AppData/Local/Temp/bla';
deleteOlderFiles($path,$days);
$path = 'C:/Users/Legion/AppData/Local/Temp/blabla';
deleteOlderFiles($path,$days);
$path = 'C:/Users/Legion/AppData/Local/Temp/blalbalba';
deleteOlderFiles($path,$days);
Alternatively, place the directories in an array and then call the funtion from within a foreach loop.
$paths = [];
$paths[] = 'C:/Users/Legion/AppData/Local/Temp';
$paths[] = 'C:/Users/Legion/AppData/Local/Temp/bla';
$paths[] = 'C:/Users/Legion/AppData/Local/Temp/blabla';
$paths[] = 'C:/Users/Legion/AppData/Local/Temp/blalbalba';
$days = 7;
foreach ( $paths as $path){
deleteOlderFiles($path,$days);
}
It seems that you need a recursive function, i.e. a function that calls itself. In this case it calls itself when it finds a subdirectory to scan/traverse.
function delete_files($current_path, $days) {
$files_in_current_path = scandir($current_path);
foreach($files_in_current_path as $file) {
if (!in_array($release_file, [".", ".."])) {
if (is_dir($current_path . "/" . $file)) {
// Scan found subdirectory
delete_files($current_path . "/" . $file, $days);
} else {
// Here you add your code for checking date and deletion of the $file
$filelastmodified = filemtime($current_path . "/" . $file);
if((time() - $filelastmodified) > $days*24*3600) {
if(is_file($current_path . "/" . $file)) {
unlink($current_path . "/". $file);
}
}
}
}
}
}
delete_files("your/startpath/here", 7);
This code starts in your specified start path. It scans all files in that directory. If a sub directory is found, there will be a new call to delete_files, but with that sub directory as a start.
I wrote a controller to upload files to a Directory. and I want if a file exists already in that Directory ,before moving , file name To be changed by increment one unit from last similar existent file name like this:
test.jpg
test(1).jpg
test(2).jpg
This is body of my Controller
$fileName = $file->getClientOriginalName();
$fileExt = $file->getClientOriginalExtension();
$destinationFolder = public_path('upload/userfiles/');
$num = 1;
$newName = $fileName;
while (file_exists($destinationFolder . $newName )) {
$newName = $fileName. '(' . $num . ')';
$num ++;
}
$file->move($destinationFolder, $newName . '.' . $fileExt);
But this does not work correctly and create file name like this :
test.jpg
test(1).jpg
test(1)(2).jpg
your problem is this:
$newName = $fileName;
at first is test.jpg
then you append the ($num++) so it becomes test(1).jpg
the next time its still test(1).jpg
and you append the ($num++) then it becomes test(1)(2).jpg
Thats it
This will work:
$fileName = $file->getClientOriginalName();
$fileExt = $file->getClientOriginalExtension();
$destinationFolder = public_path('upload/userfiles/');
$num = 1;
$newName = $fileName;
$appendNum = false;
while (file_exists($destinationFolder . $newName )) {
$appendNum = true;
$num ++;
}
if ($appendNum) $newName = $fileName. '(' . $num . ')';
$file->move($destinationFolder, $newName . '.' . $fileExt);
this work for me :
$current_name = $file->getClientOriginalName();
$extension = $file->getClientOriginalExtension();
$only_name = explode('.', $current_name)[0];
$new_name = $current_name;
$destination = Storage::disk($link_directory)->path($folder);
$all_files = Storage::disk($link_directory)->listContents($folder);
$searchword = $only_name;
$matches = array_filter($all_files, function($var) use ($searchword) {
if(strpos($var['filename'], $searchword) !== FALSE) {
return $var;
}
});
if( is_array($matches) ){
$new_name = $only_name. '_' . count($matches).'.'.$extension;
}
$filename = Storage::disk($link_directory)->putFileAs($folder, $file, $new_name);
I have created a simple helper function that generates file name:
if (!function_exists('generateFilename')) {
/**
* Generate filename
*
* #param string $disk
* #param string $path
* #param UploadedFile $file
* #param integer $count
* #return string $filename
*/
function generateFilename(string $disk, string $path, UploadedFile $file, int $count = 0)
{
$extension = $file->getClientOriginalExtension();
$filename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME) . ($count == 0 ? "." . $extension : "-{$count}." . $extension);
$filePath = Str::of($path)->finish(DIRECTORY_SEPARATOR)->finish($filename)->toString();
if (Storage::disk($disk)->exists($filePath)) {
$count++;
return generateFilename($disk, $path, $file, $count);
}
return $filename;
}
}
How can I get all sub-directories of a given directory without files, .(current directory) or ..(parent directory)
and then use each directory in a function?
Option 1:
You can use glob() with the GLOB_ONLYDIR option.
Option 2:
Another option is to use array_filter to filter the list of directories. However, note that the code below will skip valid directories with periods in their name like .config.
$dirs = array_filter(glob('*'), 'is_dir');
print_r($dirs);
Here is how you can retrieve only directories with GLOB:
$directories = glob($somePath . '/*' , GLOB_ONLYDIR);
The Spl DirectoryIterator class provides a simple interface for viewing the contents of filesystem directories.
$dir = new DirectoryIterator($path);
foreach ($dir as $fileinfo) {
if ($fileinfo->isDir() && !$fileinfo->isDot()) {
echo $fileinfo->getFilename().'<br>';
}
}
Almost the same as in your previous question:
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($yourStartingPath),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $file) {
if($file->isDir()) {
echo strtoupper($file->getRealpath()), PHP_EOL;
}
}
Replace strtoupper with your desired function.
Try this code:
<?php
$path = '/var/www/html/project/somefolder';
$dirs = array();
// directory handle
$dir = dir($path);
while (false !== ($entry = $dir->read())) {
if ($entry != '.' && $entry != '..') {
if (is_dir($path . '/' .$entry)) {
$dirs[] = $entry;
}
}
}
echo "<pre>"; print_r($dirs); exit;
In Array:
function expandDirectoriesMatrix($base_dir, $level = 0) {
$directories = array();
foreach(scandir($base_dir) as $file) {
if($file == '.' || $file == '..') continue;
$dir = $base_dir.DIRECTORY_SEPARATOR.$file;
if(is_dir($dir)) {
$directories[]= array(
'level' => $level
'name' => $file,
'path' => $dir,
'children' => expandDirectoriesMatrix($dir, $level +1)
);
}
}
return $directories;
}
//access:
$dir = '/var/www/';
$directories = expandDirectoriesMatrix($dir);
echo $directories[0]['level'] // 0
echo $directories[0]['name'] // pathA
echo $directories[0]['path'] // /var/www/pathA
echo $directories[0]['children'][0]['name'] // subPathA1
echo $directories[0]['children'][0]['level'] // 1
echo $directories[0]['children'][1]['name'] // subPathA2
echo $directories[0]['children'][1]['level'] // 1
Example to show all:
function showDirectories($list, $parent = array())
{
foreach ($list as $directory){
$parent_name = count($parent) ? " parent: ({$parent['name']}" : '';
$prefix = str_repeat('-', $directory['level']);
echo "$prefix {$directory['name']} $parent_name <br/>"; // <-----------
if(count($directory['children'])){
// list the children directories
showDirectories($directory['children'], $directory);
}
}
}
showDirectories($directories);
// pathA
// - subPathA1 (parent: pathA)
// -- subsubPathA11 (parent: subPathA1)
// - subPathA2
// pathB
// pathC
You can try this function (PHP 7 required)
function getDirectories(string $path) : array
{
$directories = [];
$items = scandir($path);
foreach ($items as $item) {
if($item == '..' || $item == '.')
continue;
if(is_dir($path.'/'.$item))
$directories[] = $item;
}
return $directories;
}
Non-recursively List Only Directories
The only question that direct asked this has been erroneously closed, so I have to put it here.
It also gives the ability to filter directories.
/**
* Copyright © 2020 Theodore R. Smith <https://www.phpexperts.pro/>
* License: MIT
*
* #see https://stackoverflow.com/a/61168906/430062
*
* #param string $path
* #param bool $recursive Default: false
* #param array $filtered Default: [., ..]
* #return array
*/
function getDirs($path, $recursive = false, array $filtered = [])
{
if (!is_dir($path)) {
throw new RuntimeException("$path does not exist.");
}
$filtered += ['.', '..'];
$dirs = [];
$d = dir($path);
while (($entry = $d->read()) !== false) {
if (is_dir("$path/$entry") && !in_array($entry, $filtered)) {
$dirs[] = $entry;
if ($recursive) {
$newDirs = getDirs("$path/$entry");
foreach ($newDirs as $newDir) {
$dirs[] = "$entry/$newDir";
}
}
}
}
return $dirs;
}
<?php
/*this will do what you asked for, it only returns the subdirectory names in a given
path, and you can make hyperlinks and use them:
*/
$yourStartingPath = "photos\\";
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($yourStartingPath),
RecursiveIteratorIterator::SELF_FIRST);
foreach($iterator as $file) {
if($file->isDir()) {
$path = strtoupper($file->getRealpath()) ;
$path2 = PHP_EOL;
$path3 = $path.$path2;
$result = end(explode('/', $path3));
echo "<br />". basename($result );
}
}
/* best regards,
Sanaan Barzinji
Erbil
*/
?>
This is the one liner code:
$sub_directories = array_map('basename', glob($directory_path . '/*', GLOB_ONLYDIR));
Proper way
/**
* Get all of the directories within a given directory.
*
* #param string $directory
* #return array
*/
function directories($directory)
{
$glob = glob($directory . '/*');
if($glob === false)
{
return array();
}
return array_filter($glob, function($dir) {
return is_dir($dir);
});
}
Inspired by Laravel
The following recursive function returns an array with the full list of sub directories
function getSubDirectories($dir)
{
$subDir = array();
$directories = array_filter(glob($dir), 'is_dir');
$subDir = array_merge($subDir, $directories);
foreach ($directories as $directory) $subDir = array_merge($subDir, getSubDirectories($directory.'/*'));
return $subDir;
}
Source: https://www.lucidar.me/en/web-dev/how-to-get-subdirectories-in-php/
You can use the glob() function to do this.
Here is some documentation on it:
http://php.net/manual/en/function.glob.php
Find all PHP files recursively. The logic should be simple enough to tweak and it aims to be fast(er) by avoiding function calls.
function get_all_php_files($directory) {
$directory_stack = array($directory);
$ignored_filename = array(
'.git' => true,
'.svn' => true,
'.hg' => true,
'index.php' => true,
);
$file_list = array();
while ($directory_stack) {
$current_directory = array_shift($directory_stack);
$files = scandir($current_directory);
foreach ($files as $filename) {
// Skip all files/directories with:
// - A starting '.'
// - A starting '_'
// - Ignore 'index.php' files
$pathname = $current_directory . DIRECTORY_SEPARATOR . $filename;
if (isset($filename[0]) && (
$filename[0] === '.' ||
$filename[0] === '_' ||
isset($ignored_filename[$filename])
))
{
continue;
}
else if (is_dir($pathname) === TRUE) {
$directory_stack[] = $pathname;
} else if (pathinfo($pathname, PATHINFO_EXTENSION) === 'php') {
$file_list[] = $pathname;
}
}
}
return $file_list;
}
If you're looking for a recursive directory listing solutions. Use below code I hope it should help you.
<?php
/**
* Function for recursive directory file list search as an array.
*
* #param mixed $dir Main Directory Path.
*
* #return array
*/
function listFolderFiles($dir)
{
$fileInfo = scandir($dir);
$allFileLists = [];
foreach ($fileInfo as $folder) {
if ($folder !== '.' && $folder !== '..') {
if (is_dir($dir . DIRECTORY_SEPARATOR . $folder) === true) {
$allFileLists[$folder . '/'] = listFolderFiles($dir . DIRECTORY_SEPARATOR . $folder);
} else {
$allFileLists[$folder] = $folder;
}
}
}
return $allFileLists;
}//end listFolderFiles()
$dir = listFolderFiles('your searching directory path ex:-F:\xampp\htdocs\abc');
echo '<pre>';
print_r($dir);
echo '</pre>'
?>
Find all subfolders under a specified directory.
<?php
function scanDirAndSubdir($dir, &$fullDir = array()){
$currentDir = scandir($dir);
foreach ($currentDir as $key => $filename) {
$realpath = realpath($dir . DIRECTORY_SEPARATOR . $filename);
if (!is_dir($realpath) && $filename != "." && $filename != "..") {
scanDirAndSubdir($realpath, $fullDir);
} else {
$fullDir[] = $realpath;
}
}
return $fullDir;
}
var_dump(scanDirAndSubdir('C:/web2.0/'));
Sample :
array (size=4)
0 => string 'C:/web2.0/config/' (length=17)
1 => string 'C:/web2.0/js/' (length=13)
2 => string 'C:/web2.0/mydir/' (length=16)
3 => string 'C:/web2.0/myfile/' (length=17)
I need to create a loop through all files in subdirectories. Can you please help me struct my code like this:
$main = "MainDirectory";
loop through sub-directories {
loop through filels in each sub-directory {
do something with each file
}
};
Use RecursiveDirectoryIterator in conjunction with RecursiveIteratorIterator.
$di = new RecursiveDirectoryIterator('path/to/directory');
foreach (new RecursiveIteratorIterator($di) as $filename => $file) {
echo $filename . ' - ' . $file->getSize() . ' bytes <br/>';
}
You need to add the path to your recursive call.
function readDirs($path){
$dirHandle = opendir($path);
while($item = readdir($dirHandle)) {
$newPath = $path."/".$item;
if(is_dir($newPath) && $item != '.' && $item != '..') {
echo "Found Folder $newPath<br>";
readDirs($newPath);
}
else{
echo ' Found File or .-dir '.$item.'<br>';
}
}
}
$path = "/";
echo "$path<br>";
readDirs($path);
You probably want to use a recursive function for this, in case your sub directories have sub-sub directories
$main = "MainDirectory";
function readDirs($main){
$dirHandle = opendir($main);
while($file = readdir($dirHandle)){
if(is_dir($main . $file) && $file != '.' && $file != '..'){
readDirs($file);
}
else{
//do stuff
}
}
}
didn't test the code, but this should be close to what you want.
I like glob with it's wildcards :
foreach (glob("*/*.txt") as $filename) {
echo "$filename\n";
}
Details and more complex scenarios.
But if You have a complex folders structure RecursiveDirectoryIterator is definitively the solution.
Come on, first try it yourself!
What you'll need:
scandir()
is_dir()
and of course foreach
http://php.net/manual/en/function.is-dir.php
http://php.net/manual/en/function.scandir.php
Another solution to read with sub-directories and sub-files (set correct foldername):
<?php
$path = realpath('samplefolder/yorfolder');
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $filename)
{
echo "$filename <br/>";
}
?>
Minor modification on what John Marty posted, if we can safely eliminate any items that are named . or ..
function readDirs($path){
$dirHandle = opendir($path);
while($item = readdir($dirHandle)) {
$newPath = $path."/".$item;
if (($item == '.') || ($item == '..')) {
continue;
}
if (is_dir($newPath)) {
pretty_echo('Found Folder '.$newPath);
readDirs($newPath);
} else {
pretty_echo('Found File: '.$item);
}
}
}
function pretty_echo($text = '')
{
echo $text;
if (PHP_OS == 'Linux') {
echo "\r\n";
}
else {
echo "</br>";
}
}
<?php
ini_set('max_execution_time', 300); // increase the execution time of the file (in case the number of files or file size is more).
class renameNewFile {
static function copyToNewFolder() { // copies the file from one location to another.
$main = 'C:\xampp\htdocs\practice\demo'; // Source folder (inside this folder subfolders and inside each subfolder files are present.)
$main1 = 'C:\xampp\htdocs\practice\demomainfolder'; // Destination Folder
$dirHandle = opendir($main); // Open the source folder
while ($file = readdir($dirHandle)) { // Read what's there inside the source folder
if (basename($file) != '.' && basename($file) != '..') { // Ignore if the folder name is '.' or '..'
$folderhandle = opendir($main . '\\' . $file); // Open the Sub Folders inside the Main Folder
while ($text = readdir($folderhandle)) {
if (basename($text) != '.' && basename($text) != '..') { // Ignore if the folder name is '.' or '..'
$filepath = $main . '\\' . $file . '\\' . $text;
if (!copy($filepath, $main1 . '\\' . $text)) // Copy the files present inside the subfolders to destination folder
echo "Copy failed";
else {
$fh = fopen($main1 . '\\' . 'log.txt', 'a'); // Write a log file to show the details of files copied.
$text1 = str_replace(' ', '_', $text);
$data = $file . ',' . strtolower($text1) . "\r\n";
fwrite($fh, $data);
echo $text . " is copied <br>";
}
}
}
}
}
}
static function renameNewFileInFolder() { //Renames the files into desired name
$main1 = 'C:\xampp\htdocs\practice\demomainfolder';
$dirHandle = opendir($main1);
while ($file = readdir($dirHandle)) {
if (basename($file) != '.' && basename($file) != '..') {
$filepath = $main1 . '\\' . $file;
$text1 = strtolower($filepath);
rename($filepath, $text1);
$text2 = str_replace(' ', '_', $text1);
if (rename($filepath, $text2))
echo $filepath . " is renamed to " . $text2 . '<br/>';
}
}
}
}
renameNewFile::copyToNewFolder();
renameNewFile::renameNewFileInFolder();
?>
$allFiles = [];
public function dirIterator($dirName)
{
$whatsInsideDir = scandir($dirName);
foreach ($whatsInsideDir as $fileOrDir) {
if (is_dir($fileOrDir)) {
dirIterator($fileOrDir);
}
$allFiles.push($fileOrDir);
}
return $allFiles;
}