I'm writing a json-file inside a generated folder. After an hour I want to delete the folder with its content automatically.
I tried:
$dir = "../tmpDir";
$cdir = scandir($dir);
foreach ($cdir as $key => $value)
{
if (!in_array($value,array(".","..")))
{
if (is_dir($dir.'/'.$value))
{
if(filectime($dir.'/'.$value)< (time()-3600))
{ // after 1 hour
$files = glob($dir.'/'.$value); // get all file names
foreach($files as $file)
{ // iterate files
if(is_file($file))
{
unlink($file); // delete file
}
}
rmdir($dir.'/'.$value);
/*destroy the session if the folder is deleted*/
if(isset($_SESSION["dirname"]) && $_SESSION["dirname"] == $value)
{
session_unset(); // unset $_SESSION variable for the run-time
session_destroy(); // destroy session data in storage
}
}
}
}
}
I get: rmdir(../tmpDir/1488268867): Directory not empty in /Applications/MAMP/htdocs/.... on line 46
if I remove the
if(is_file($file))
{
}
I get a permission error
Maybe someone knows why I get this error
It's much easier to use your native O/S to delete the directory when it comes to things like these, so you don't have to write a horrible loop that may have some edge cases that you could miss and then end up deleting things you shouldn't have!
$path = 'your/path/here';
if (PHP_OS !== 'WINDOWS')
{
exec(sprintf('rm -rf %s', $path));
}
else
{
exec(sprintf('rd /s /q %s', $path));
}
Of course, tailor the above to your needs. You can also use the backtick operator if you want to avoid the overhead of a function call (negligible in this case, anyway). It's also probably a good idea to use escape_shell_arg for your $path variable.
For a one-liner:
exec(sprintf((PHP_OS === 'WINDOWS') ? 'rd /s /q %s' : 'rm -rf %s', escape_shell_arg($path)));
Regardless, sometimes it's easier to let the O/S of choice perform file operations.
rmdir() removes directory if u want remove file then you should use unlink() function
Proper aporach would be using DirectoryIterator or glob() and looping through files then deleting them and after you do it you can remove empty directory.
You can also call system command rm -rf direcory_name with exec() or shell_exec()
useful links:
Delete directory with files in it?
How to delete a folder with contents using PHP
PHP: Unlink All Files Within A Directory, and then Deleting That Directory
I've found pretty useful function on php.net that removes hidden files as well
public function delTree($dir) {
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file");
}
return rmdir($dir);
}
Related
I have a specific directory which may contain zip files.
I would like to loop through each sub-element of my directory to check if this is a zip. And unzip that. Then process the others files.
I'm using flysystem to work with my files.
So I went for this
$contents = $this->manager->listContents('local://my_directory , true);
foreach ($contents as $file) {
if( $file['extension'] == 'zip')
//Unzip in same location
}
The problem is that the files unziped are not in the loop and if the zip file, contain another zip. The second one will be never be unziped.
So I thought about it
function loopAndUnzip(){
$contents = $this->manager->listContents('local_process://' . $dir['path'] , true);
foreach ($contents as $file) {
if( $file['extension'] == 'zip')
//Unzip and after call loopAndUnzip()
}
}
But the initial function will never be finished and be called over and over if there are zip inside zip.
Isn't it a performance issue?
How to manage this kind of thing?
You can use glob to find them, and make the function recursive. You can do this by starting at a certain dir, unzip all the files into it & check if there are new zips.
I recommend using recursive directories as well. If A.zip and B.zip both have a file called example.txt, it overwrites. With dirs it wont:
function unzipAll(string $dirToScan = "/someDir", $depth=0):void {
if($depth >10 ){
throw new Exception("Maximum zip depth reached");
}
$zipfiles = glob($dirToScan."*.zip");
// Unzip all zips found this round:
foreach ($zipfiles as $zipfile) {
$zipLocation = "/".$zipname;
// unzip here to $zipLocation
// and now check if in the zip dir there is stuff to unzip:
unzipAll($dirToScan.$zipLocation, ++$depth);
}
}
The $depth is optional, but this way you cant zipbomb yourself to death.
loopAndUnzip will do all files again, so you will just again unpack the same zipfile and start over with the entire folder, ad infinitum.
Some possibilities:
Keep a list of items that was already processed or skipped and don't process those again, so while iterating over $contents, keep a separate array, and have something like:
PHP:
foreach ($contents as $file) {
if (!array_search($processedFiles, $file) {
if( $file['extension'] == 'zip')
//Unzip in same location
}
$processedFiles[] = $file;
}
Use an unzipper that returns a list of files/folders created, so you can explicitly process those instead of the full directory contents.
If the unzipper can't do it, you could fake it by extracting to a separate location, get a listing of that location, then move all the files in the original location, and process the list you got.
We have a sessions folder in outdated Magento installation,
that need to be manually cleaned from older files.
This is the current code:
private function _rrmdirContent($dir)
{
$items = array_diff(scandir($dir), array('..', '.'));
foreach ($items as $item) {
$path = $dir . DIRECTORY_SEPARATOR . $item;
is_dir($path) ? $this->_rrmdir($path) : unlink($path);
}
}
It loads a ton of resources if the file list is to long (1000 000 000 files -> 4gb memory limit exception)
Is there a way to remove the files one by one (ideally with some date created check), .. without loading them all at once?
IMHO the best way is to use DirectoryIterator, RecursiveDirectoryIterator is also available but it's quite poorly documented so I stick with DirectoryIterator.
Function to recursively delete all files in directory could look like this
function clearDirectory($path, $rmDir = false)
{
$iterator = new DirectoryIterator($path);
foreach ($iterator as $item) {
if ($item->isDot()) {
continue; // skip dot dirs
}
if ($item->isDir()) {
clearDirectory($item->getPathname(), true);
} else {
unlink($item->getPathname());
}
}
if ($rmDir) {
rmdir($iterator->getPathname());
}
}
To clear directory PATH call
clearDirectory(PATH);
If you want to delete root directory at the end, use second parameter
clearDirectory(PATH, true);
Use with caution, this will actually delete files
I am making a Cron that will delete a folder older than 15days. I already made a function to delete the folder and it's content, what I don't have is to loop inside a folder then check each folder's age then if that is 15days old or above I will executue my delete function.
I want to loop inside public/uploads
in my uploads directory I store folders with content ex.
public/
uploads/
Test/
Test2/
I want to check how old those folder then delete it by calling
function Delete($path)
{
if (is_dir($path) === true)
{
$files = array_diff(scandir($path), array('.', '..'));
foreach ($files as $file)
{
Delete(realpath($path) . '/' . $file);
}
return rmdir($path);
}
else if (is_file($path) === true)
{
return unlink($path);
}
return false;
}
How do I do that? Thanks
The function you are looking for is filemtime(). This lets you determine the last modified date of a file (or directory). That in combination with the various directory functions will allow you to loop through them and check their dates.
This is something mocked up off the top of my head to give you a rough idea of how you may go about this:
$dir = '/path/to/my/folders';
$folders = scandir($dir);
foreach ($folders as $folder) {
$lastModified = filemtime($folder);
// Do a date comparison here and call your delete if necessary
}
Function:
function scandir_recursive($dir) {
$items = scandir($dir);
foreach($items as $item) {
if ($item == '.' || $item == '..') {
continue;
}
$file = $dir . '/' . $item;
echo "$file<br />";
if (is_dir($file)) {
scandir_recursive($file);
}
}
}
scandir_recursive($path);
Output:
EEAOC/EN/1001/data
EEAOC/EN/1001/data/New Text Document.txt
EEAOC/EN/1001/data/troll.txt
EEAOC/EN/1002/data
EEAOC/EN/1002/data/New Text Document.txt
EEAOC/EN/1002/data/troll.txt
EEAOC/EN/1003/data
EEAOC/EN/1003/data/New Text Document.txt
EEAOC/EN/1003/data/troll.txt
EEAOC/EN/1004/data
EEAOC/EN/1004/data/New Text Document.txt
EEAOC/EN/1004/data/troll.txt
Is it possible to delete those empty directories and keep files?
such deleting EEAOC/EN/1001/data &EEAOC/EN/1002/data& EEAOC/EN/1003/data &EEAOC/EN/1004/data
I want to keep remaining how?
As shown from your function-output, the directories are not empty.
I suppose that the data is of low value and just needs to be backed up. I assume you are running Linux from your choice of a slash rather than a backslash. Naturally directories incur links or inodes, with metadata, which in linux can be checked via
df -hi
The Inode-Ids of files can be shown via the -i flag of ls
ls -i filename
ls -iltr
ls -ltri `echo $HOME`
Inodes take up space, and introduce overhead in file-system operations. So much to the motivation to remove the directories.
PHP's rmdir function to remove directories requires that The directory must be empty, and the relevant permissions must permit this. It is also non-recursive.
Approach 1: flatten filenames and move the files to a backup directory, then remove the empty directories
Approach 2: incrementally archive and remove the directories
#Approach 1
Your choice should depend on how easy and frequent your file access occurs.
function scandir_recursive($dir) {
$items = scandir($dir);
foreach($items as $item) {
if ($item == '.' || $item == '..') {
continue;
}
$file = $dir . '/' . $item;
$fnameflat = $dir . '_' . $item;
echo "$file<br />\n";
if (is_dir($file)) {
scandir_recursive($file);
}
if(is_file($file)){
rename($file, '~/backup/'.$fnameflat);
}
}
}
scandir_recursive($path);
Afterwards use this function by SeniorDev, to unlink files and directories, or run
`exec("rmdir -rf $path")`
This is not recommended.
#Approach 2
Archive the directory using exec("tar -cvzf backup".date().".tar.gz $path"); or using php.
Then remove the directory aferwards, as described in #1.
$value can = a folder structure to the language file. Example: languages/english.php
$value can also = the files name. Example: english.php
So I need to get the current folder that $value is in and delete the folder ONLY if there are no other files/folders within that directory (after deleting the actual file as I am doing already, ofcourse).
foreach($module['languages'] as $lang => $langFile)
{
foreach ($langFile as $type => $value)
{
#unlink($module_path . '/' . $value);
// Now I need to delete the folder ONLY if there are no other directories inside the folder where it is currently at.
// And ONLY if there are NO OTHER files within that folder also.
}
}
How can I do this?? And wondering if this can be done without using a while loop, since a while loop within a foreach loop could take some time, and need this to be as quick as possible.
And just FYI, the $module_path should never be deleted. So if $value = english.php, it should never delete the $module_path. Ofcourse, there will always be another file in there, so checking for this is not necessary, but won't hurt either way.
Thanks guys :)
EDIT
Ok, now I'm using this code here and it is NOT working, it is not removing the folders or the files, and I don't get any errors either... so not sure what the problem is here:
foreach($module['languages'] as $lang => $langFile)
{
foreach ($langFile as $type => $value)
{
if (#unlink($module_path . '/' . $value))
#rmdir(dirname($module_path . '/' . $value));
}
}
NEVERMIND, this works a CHARM!!! Cheers Everyone!!
The easyest way is try to use rmdir. This don't delete folder if it is not empty
rmdir($module_path);
also you can check is folder empty by
if(count(glob($module_path.'*'))<3)//delete
2 for . and ..
UPD: as I reviewed maybe you should replace $module_path by dirname($module_path.'.'.$value);
Since the directory you care about might be part of the $value, you need to use dirname to figure out what the parent directory is, you can't just assume that it's $module_path.
$file_path = $module_path . '/' . $value;
if (#unlink($file_path)) {
#rmdir(dirname($file_path));
}
if (is_file($value)) {
unlink($value);
} else if (is_dir($value)) {
if (count(scandir($value)) == 2) }
unlink($value)
}
}
http://php.net/manual/en/function.is-dir.php
http://www.php.net/manual/en/function.scandir.php
The code below will take a path, check if it is a file (i.e. not a directory). If it is a file, it will extract the directory name, then delete the file, then iterate over the dir and count the files in it, if the files are zero it'll delete the dir.
Code is as an example and should work, however privileges and environment setup may result in it not working.
<?php
if(!is_dir ( string $filename )){ //if it is a file
$fileDir = dirname ( $filename );
if ($handle = opendir($fileDir)) {
echo "Directory handle: $handle\n";
echo "Files:\n";
$numFiles=0;
//delete the file
unlink($myFile);
//Loop the dir and count the file in it
while (false !== ($file = readdir($handle))) {
$numFiles = $numFiles + 1;
}
if($numFiles == 0) {
//delete the dir
rmdir($fileDir);
}
closedir($handle);
}
}
?>