PHP recursively delete empty parent directories - php

Problem Background
I have a multimedia web applications where users can save and delete files. The saved files are used for various application wide functionalities.
When files are saved, they are put deep inside directory tree (usually 6 to 9 folders deep).
The files are put at such deep level to make sure that they are easily distinguishable by admin/superuser looking at file structure and to match with existing manual system.
For Example,
"UploadedAssets" is the root directory and can contain following folder structure : \ith\sp\cookery\ckpe\interactives\timestep\blahblah\item1\image01.png
\ith\sp\cookery\ckpe\interactives\timestep\blah_only\item1\image02.png
\ith\sp\cookery\ckpe\interactives\timestep\blah_only\item2\image03.png
\ith\sp\cookery\abcd\interactives\timestep\blahblah\item1\image66.png
The first 5 names are user selectable (from dynamic dropdown menus) form approx 20-25 options each.
The last 3 depends on whatever user inputs (e.g. category , title, etc.). This is received from user using text input fields. These can be 1 or up to 4 directories.
The last directory "item1" can have one or more files but no sub directories
Each directory EXCEPT the last one (item1) can have other sub directories
When I remove/delete a file, obviously program knows the full path of the file location.
Because of number of possible directory name options, even in the testing phase, the root "UploadedAssets" directory has exploded and there are plenty of empty unused directories and dead branches.
My question is
Once the user deletes one/more files from a directory (e.g. image01.png from item1),
How can I traverse up the tree from deleted file (going only to straight parents) and delete the parent node if its empty.
While deleting if one of the directoy has other children/files then not to delete that directory and finish the process.
While deleting stop at pre-defined root directory OR
Stop after going UP n directory levels
E.g. in the above given example directory structure,
if user deletes image01.png then it should delete item1 and blahblah directories
if user deletes image02.png then it should delete only its parent item1 directory
if user deletes image66.png then it should delete all parent directories including abcd
My Attempts / research
I know how to remove single directory using php's rmdir. But couldn't think on how can I use it recursively to solve my problem.
I have tried to get my head around following stuff, but I don't know if any of those can fit my problem
PHP: Unlink All Files Within A Directory, and then Deleting That Directory
Delete files then directory
http://php.net/manual/en/function.glob.php
https://stackoverflow.com/a/4490706/2337895

Because this has some overhead, I'm going to suggest that you only do this from PHP if absolutely needed. You can use the Linux find command for this:
find /path/to/dir -empty -type d -delete
I would put that in a cron job. If you like you can use the php system command to do this though:
system('find /path/to/dir -empty -type d -delete', $retval);
Using this you would simply delete the file and then let this run only every day or so to go through and take care of any empty directories. This may seem more hackish than making it all in PHP but it'll run much faster. It is less portable but that shouldn't matter too much for most sites. Save this as rdel.bat (I walways make sure you have the [Show hidden file extensions][2] explorer folder option turned on but if you don't then use the drop-down in your text editors Save As... dialog to ensure it has the proper extension).
UPDATE:
To do the same thing in Windows use a batch file with just this one line:
for /f "delims=" %%d in ('dir /s /b /ad ^| sort /r') do rd "%%d"
Test this by changing to a directory and running your new file. It should remove any empty directories below the current.
To schedule this just add an entry to your task scheduler. That is a little different depending on which version of Windows you use. Be sure to set the working directory correctly (this is how the batch file knows where to start).
The problem with this is that in Windows a lot of junk files get created in empty directories (ie Thumbs.db). You can handle that by adding code to your batch file to remove any of those files too:
del /s /q Thumbs.db
Add this above the other line and repeat for anything else which may be unneeded.

Related

What is the difference between / addressing and ./ addressing? [duplicate]

This question already has answers here:
What is double dot(..) and single dot(.) in Linux?
(4 answers)
Closed 4 years ago.
I am working on a PHP website where I needed to get a list of the files in the Images directory. The example in PHP.net shows the addressing with just a forward slash as shown in the example. This addressing pattern did not work. Instead the address needs to start with ./ . What is the difference?
<?php
$dir = '/tmp';
$files1 = scandir($dir);
$files2 = scandir($dir, 1);
print_r($files1);
print_r($files2);
?>
A search for other questions have addressed single dot and double dot, but I don't see posts for a forward slash with no dot.
the . indicates the current directory. Assume, for instance, that your current path is /var/www:
If you say ./tmp the directory you are looking for is /var/www/tmp
If you say /tmp you are looking for /tmp (starting from the root)
Maybe you're a Windows user and you are not familiar with the way Linux addresses files, but essentially there are no drive letters. / is the root of the file system, basically equivalent to C:\ on Windows (though not quite). It also works on Windows except it will refer to the root of the partition your script is running in as Windows separates each partition on a different drive letter.
There's a standard folder in the root of the file system called tmp, and if you want to refer to it you want to specify full path, /tmp.
Just using tmp or ./tmp will refer to a tmp folder on your local path, which is a completely different folder just with the same name (that might not even exist).
When you don't want to specify a full path but somewhere relative or local path instead, you don't put / in the beginning. You might just put nothing, or you can put ./ to explicitate this is a path relative to your current working directory which is ..
It doesn't matter in which folder your script working, . always represents it. It is actually unnecessary to put ./ in most cases since relative paths are implicit.
On a relate note, .. represents the parent folder.
Example, my script is /var/www/script.php, when I run it:
. is the folder /var/www.
fopen('note.txt', 'r') will open file /var/www/note.txt for reading.
fopen('./note.txt', 'r') will do the exact same thing.
.. is the folder /var.
../../tmp is the same as /tmp.
Note the current working directory represented by . remains constant even if you include() a script from a subfolder.
This might get things confusing because . may not be the folder the script you included is in. To workaround this problem, use __DIR__ to get the folder your script is in, it will get the current folder of your script even if you are calling it through a include() or calling it from a different directory.
/ denotes the root directory of your server. As in /var/www/.
./ denotes the current directory that the script is executing in. This is the equivalent of getcwd(). For example, you're currently in /php/scripts, it will refer to /var/www/php/scripts.
../ denotes the parent folder, and can be chained to move up multiple levels.
The use of each depends on which file or folder you're trying to manipulate from which script.
If you're trying to access /var/www/tmp from /var/www/php/scripts/script.php, you can use either:
../../tmp
/tmp
Note that if you're unsure how many levels you need to 'move up', you can affix an infinite number of ../; it won't 'break out' of the root. Having said that, referencing directly from the root is often much more straight-forward.

Creating directories with regex same in url structure

I will mass download thousands of images from a server.
My problem is : filenames are same and they are located in different directories.
Ex:
http://domain.com/images/upload/2014/09/SKU00123/1.jpg
http://domain.com/images/upload/2014/09/SKU1501/1.jpg
I want to download them with the same directory structure.
c:\images\upload\2014\09\SKU00123\1.jpg
I can take the file name with basename command but i couldn't find a way to get the directory structure. I need php to create directories and save the files to that destination.
Is there a way to change the url structure to directory structure? Maybe with regex?
For the next time, please show us some PHP code. Have you already tried something?!
...You can easily do this in 2 steps:
Use parse_url to find the path(/images/upload/2014/etc..) of the URL.
Use mkdir with the recursive parameter to create these directories on your own system.

Referring to the root directory

Before I start, this question may have been asked before, but I either don't know what to type to find it specifically and this is a slightly more specific case.
I'm developing a website with many dynamic ties, some of which is at the beginning of every php file there is the line require("global.php"); which does what the name states, among others such as the css file and whatever else is found on the root level. Problem is however, upon entering down into another directory, the link to this file is broken.
Traditionally what I've done (which is an absolutely stupid beginner workaround) is create a variable just before the require called $up which contained the string '../' which recurred respectively depending on how many directories deep the file is from the root. So the line would then appear require($up."global.php");.
I've realised how stupid this is and have tried to find ways around this. One of which is using the variable $_SERVER['DOCUMENT_ROOT'] to print out the root directory but the reason why this won't work 100% is because a) I'm developing the website on a local machine where the 'root' of the website in development is located in a subdirectory on the server and b) it returns the entire path based upon the location on the drive starting from D:\ and working its location that way, which I don't particularly like to work with when it comes to developing websites and would rather remain within the hosted directories of the web server if possible.
So, based on what I've explained, is there an easy way to get the root location of a website (regardless if the project is in a subridectory or the real root of the web server) within a short string that is extremely easy to append to the begining of every file reference in every php file regardless of it's directory level?
Thanks! <3
Suppose you have the following directory:
/srv/website/index.php
/srv/website/lib/functions.php
You could then do this:
define("MY_ROOT", $_SERVER['DOCUMENT_ROOT']);
And use it in every (included) file relatively:
require (MY_ROOT . "lib/functions.php" );

"Symbolic" link to resolve requests to a folder outside public_html?

I have a lot of image and pdf files in a folder within the public_html directory, and I have a lot of scripts on public_html dynamically processing, creating and editing these files based on user requests.
Now the problem is that I've realized that having these files stored in folders in public_html isn't safe, maybe someone could just inject some script and delete them all! So what I want to do is move them outside public_html. However - since I have so very many scripts dealing with all these files, all the paths will be messed up. I am using scripts that use relative paths, (/home/public_html/designs/product1/...) as well as scripts that use absolute paths (www.mydomain.com/designs/product1/...).
I don't know much about symbolic links to be honest - I've read up some but I found it confusing since I'm not that good with Linux either.
My question is: is there any way that I can put some kind of symbolic link or connection, so that I can move all my product design files outside public_html, and yet don't have to change all my scripts to point to the new path? So maybe, some kind of code that resolves all public_html/designs/ requests from my scripts to the new path? Maybe symbolic links isn't what I need here, but something else?
create sysmbolic links is easy in linux
first go to public_html directory :
/home/public_html/
then move designs directory to upper directory [ or every where you want ]
mv designs ../
then create sysmbolic link to the real directory
ln -s ../designs designs
every thing will be work

Creating a file/folder structure and zipping it up?

I have a directory of image files and I need a php script or shell script that will rename them, create a structure of nested directories, and then insert each image into a specified place in the directory hierarchy. Ideally I would just specify a parent directory for each file and a parent directory for each directory and it would build it. And then finally, I need the script to zip up the whole thing.
There's probably not an existing php class that will do all this for me, but if anyone knows of a php class or other script available online that would handle a lot of this logic that would be great.
I know its not PHP, but you might want to investigate something like this:
a shell script for renaming files (your dialect may vary, but the man pages are very helpful).
foreach img (/path/to/directory/*.jpg)
set newimg= `echo $img | sed 's,path/to/directory/(.+)\.jpg,new/path/$1newname.jpg/,'`
cp img newimg
end
If all the files in a particular directory are going to one location, something like the above might work. Essentially it loops through the target directory getting the names of the files that have a .jpg (or whatever) extension. Then it takes those names, including their path and subs in the new directory path and some change to the original file change. I've used , for the separator in the substitution because escaping all those path separators is a pain. Lastly, it copies the old file to the new location.
Since your directory needs are probably more complex than a hardcoded path allows for, you can include a line to parse your filepath/filename and determine what its target path should be; and use that in the substitution.
A snippet for creating a directory tree in one go can be found here.
You may also decide that find is a better fit for this than foreach because it can descend a directory structure as far as you like.

Categories