Im trying to, once a button is clicked, delete a directory with any files and additional directories within that however I am encountering a problem.
The error I receive is -
PHP Fatal error: Uncaught exception 'UnexpectedValueException' with
message
'RecursiveDirectoryIterator::__construct(http://...#alink.co.uk/):
failed to open dir: not implemented' in
And here is the section of code this relates to -
$dir = 'http://www.thisismylink.co.uk/userfolder/' . $row['email'] . "/";
$it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($it,
RecursiveIteratorIterator::CHILD_FIRST);
foreach($files as $file) {
if ($file->isDir()){
rmdir($file->getRealPath());
} else {
unlink($file->getRealPath());
}
}
rmdir($dir);
Now currently the structure is -
User Email ($dir)
pictures
picture.png
videos
video.mp4
What I want to do is delete everything inside the 'User Email' directory as well as the 'User Directory' itself aswell
Seems like you're using the full URL as you top directory. Shouldn't it be:
$dir = 'userfolder/' . $row['email'] . "/"; //Relative to your script
$it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($it,
RecursiveIteratorIterator::CHILD_FIRST);
foreach($files as $file) {
if ($file->isDir()){
rmdir($file->getRealPath());
} else {
unlink($file->getRealPath());
}
}
rmdir($dir);
This is really a rather dangerous way to handle the issue; I'm not trying to circumvent your actual question, however please consider keeping a database full of "directories" and who they are associated to. When you remove a user, set a flag in the db to "toBeRemoved" or something of the like.
Then every so often, have a cron / scheduled task come through and query a list of directories to remove, then delete the database entry / mark it updated.
The problem is that no matter how secure you believe it to be, a script can be exploited, or produce unpredictable results from web/user based input.
In the event these errors are permission based, using the above mentality would allow this to be done at the "root" user level without fear of system files being deleted at the hand of your code being mangled by a bad user (or well intended user hitting the wrong combination of events).
Related
I'm new to using OPcache on php 8 and I have some questions. So my folder structure looks like this:
https://i.stack.imgur.com/vb93u.png
Within each folder is the exact same thing, it's the structure of my website.
Why does OPcache generate multiple folders with the same content?
What is the best way to keep only the most recent folder and delete the others? Is there a check that can be done every so often or a setting that overwrites older files with new ones?
I'm fast approaching the file limit with my hosting and need to clear up some space.
I've read the docs but I don't have a lot of knowledge working with servers so any help is greatly appreciated!
Oh and these are the settings in my php.ini:
zend_extension=opcache.so;
opcache.enable=1;
opcache.memory_consumption=32;
opcache.interned_strings_buffer=8;
opcache.max_accelerated_files=3000;
opcache.revalidate_freq=180;
opcache.fast_shutdown=0;
opcache.enable_cli=0;
opcache.revalidate_path=0;
opcache.validate_timestamps=1;
opcache.max_file_size=0;
opcache.file_cache=/mywebsitepath/.opcache;
opcache.file_cache_only=1;
Just in case anyone else has the same problem, this is what I ended up doing. First I tried to use the Wordpress cron manager but I was having issues getting a simple function to work. Instead, in my hosting you can create a cron job and link it to a php file, so I went that route instead. Here's the contents of the cron-jobs.php file which I put in my OPcache folder. Basically it sorts the folders by date modified then deletes the old ones while keeping the fresh one. If anyone has suggestions for improvements, be my guest!
function opcache_clean($dir) {
$folders = array();
foreach (scandir($dir) as $file) {
//we only want the folders, not files
if ( strpos($file, '.') === false ) {
$folders[$file] = filemtime($dir . '/' . $file);
}
}
//only delete the old folders if there is more than one
if (count($folders) > 1) {
arsort($folders);
$folders = array_keys($folders);
//keep the first folder (most recent directory at index 0)
$deletions = array_slice($folders, 1);
foreach($deletions as $delete) {
echo "deleting $delete <br>";
system("rm -rf ".escapeshellarg($delete));
}
}
else {
echo "No folders to delete!";
}
}
//clean the current directory
opcache_clean ( dirname(__FILE__) );
Strange behaviour, exentially:
(the name of the folder depends on the date - the purpose is a hit counter of the website, broken down by day)
if (!is_dir($folder)) { // first access in the day
mkdir($folder);
}
Well: on the server in internet all works well.
But when i try in local, with the server simulator of Easy PHP, happens that:
(a) The first time, no problem. The folder doesn't exists and it is created.
(b) subsequently, for example to a page refresh, the program flow again goes in the IF (!!!) generating the error (at line of mkdir) of kind: "Warning: mkdir(): No such file or directory in [...]".
All parent part of the directory $folder exists.
Thanks
.
Try using a recursive directory creation function:
function mkdir_r($dirName, $rights = 0777)
{
$dirs = explode(DIRECTORY_SEPARATOR , $dirName);
$dir = '';
if (strpos($dirs[count($dirs) - 1], '.')) {
array_pop($dirs);
}
foreach ($dirs as $part) {
$dir .= $part . DIRECTORY_SEPARATOR ;
if (!is_dir($dir) && strlen($dir) > 0) {
mkdir($dir, $rights);
}
}
}
This way all directories up to the directry you wanted to create are created if they don't exist.
mkdir doesn't work recursively unfortunately.
If anyone faces the issue; Use the native clearstatcache() function after you delete the file.
I'm quoting the interesting part of the original documentation
You should also note that PHP doesn't cache information about non-existent files. So, if you call file_exists() on a file that doesn't exist, it will return false until you create the file. If you create the file, it will return true even if you then delete the file. However unlink() clears the cache automatically.
For further information here is the documentation page: https://www.php.net/manual/en/function.clearstatcache.php
I have an HTML form and one of the inputs creates a folder. The folder name is chosen by the website visitor. Every visitor creates his own folder on my website so they are randomly generated. They are created using PHP Code.
Now I would like to write a PHP code to copy a file to all of the child directories regardless the quantity of directories being generated.
I do not wish to stay writing a PHP line for every directory that is created - i.e. inserting the filename name manually (e.g. folder01, xyzfolder, folderabc, etc...) but rather automatically.
I Googled but I was unsuccessful. Is this possible? If yes, how can I go about it?
Kindly ignore security, etc... I am testing it internally prior to rolling out on a larger scale.
Thank you
It is sad I cannot comment so go on...
//get the new folder name
$newfolder = $_POST['newfoldername'];
//create it if not exist
if(!is_dir("./$newfolder")) {
mkdir("./$newfolder", 0777, true);
}
//list all folder
$dirname = './';
$dir = opendir($dirname);
while($file = readdir($dir)) {
if(($file != '.' OR $file != '..') AND is_dir($dirname.$file))
{
//generate a randomname
$str = 'yourmotherisveryniceABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$randomname = str_shuffle($str);
$actualdir = $dirname.$file;
//copy of the file
copy($uploadedfile['tmp_name'], $actualdir.$randomname);
}
}
closedir($dir);
I just want to say, you seem to be lazy by looking for what you want to do. because when I read "I would like to write a PHP code to copy" the answer is in your sentence: copy PHP and list of folders regarless how many? Then just simply list it !
Maybe you need to learn how to use google... If you search "I would like to write a PHP code to copy a file to all of the child directories regardless the quantity of directories being generated" Sure you will never find.
I have a directory with a number of subdirectories that users add files to via FTP. I'm trying to develop a php script (which I will run as a cron job) that will check the directory and its subdirectories for any changes in the files, file sizes or dates modified. I've searched long and hard and have so far only found one script that works, which I've tried to modify - original located here - however it only seems to send the first email notification showing me what is listed in the directories. It also creates a text file of the directory and subdirectory contents, but when the script runs a second time it seems to fall over, and I get an email with no contents.
Anyone out there know a simple way of doing this in php? The script I found is pretty complex and I've tried for hours to debug it with no success.
Thanks in advance!
Here you go:
$log = '/path/to/your/log.js';
$path = '/path/to/your/dir/with/files/';
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
$result = array();
foreach ($files as $file)
{
if (is_file($file = strval($file)) === true)
{
$result[$file] = sprintf('%u|%u', filesize($file), filemtime($file));
}
}
if (is_file($log) !== true)
{
file_put_contents($log, json_encode($result), LOCK_EX);
}
// are there any differences?
if (count($diff = array_diff($result, json_decode(file_get_contents($log), true))) > 0)
{
// send email with mail(), SwiftMailer, PHPMailer, ...
$email = 'The following files have changed:' . "\n" . implode("\n", array_keys($diff));
// update the log file with the new file info
file_put_contents($log, json_encode($result), LOCK_EX);
}
I am assuming you know how to send an e-mail. Also, please keep in mind that the $log file should be kept outside the $path you want to monitor, for obvious reasons of course.
After reading your question a second time, I noticed that you mentioned you want to check if the files change, I'm only doing this check with the size and date of modification, if you really want to check if the file contents are different I suggest you use a hash of the file, so this:
$result[$file] = sprintf('%u|%u', filesize($file), filemtime($file));
Becomes this:
$result[$file] = sprintf('%u|%u|%s', filesize($file), filemtime($file), md5_file($file));
// or
$result[$file] = sprintf('%u|%u|%s', filesize($file), filemtime($file), sha1_file($file));
But bare in mind that this will be much more expensive since the hash functions have to open and read all the contents of your 1-5 MB CSV files.
I like sfFinder so much that I wrote my own adaption:
http://www.symfony-project.org/cookbook/1_0/en/finder
https://github.com/homer6/altumo/blob/master/source/php/Utils/Finder.php
Simple to use, works well.
However, for your use, depending on the size of the files, I'd put everything in a git repository. It's easy to track then.
HTH
I've built a system whereby users can start a project and upload files to this project. When they create the project, I create a directory specifically for that project and all uploads fill this directory. However, I have implemented a system that allows the user to remove this project if they wish, deleting all the files within the directory and then the directory itself.
Locally (on MAMP), this worked a charm; on a live server however, it doesn't. For the directory removal I used a stock piece of code from a tutorial website (posted below) and as I said, works fine on a local webserver.
$name = $_POST['projectName'];
rrmdir("../../project/$name");
function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (filetype($dir."/".$object) == "dir") rrmdir($dir."/".$object); else unlink($dir."/".$object);
}
}
reset($objects);
rmdir($dir);
echo "Directory Removed";
}
Be very careful with this:
$name = $_POST['projectName'];
rrmdir("../../project/$name");
That's like an SQL injection for your server's file system, imagine if someone types this in to their browser: http://www.yoursite.com/this-script.php?projectName=../../../../var/www You'll probably want to look at escapeshellarg() to help close this gaping security hole and realpath() to convert the relative path in to an absolute one. If the target dir isn't empty then rmdir won't work, you need to remove all of the subdirs and files first before rmdir will work.
Try this one instead:
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/path/to/project/directory'),
RecursiveIteratorIterator::CHILD_FIRST);
foreach($iterator as $fileObject) {
if($fileObject->isFile()) {
echo 'Removing File: ', $fileObject->getRealpath(), PHP_EOL;
// unlink($fileObject->getRealpath());
} elseif($fileObject->isDir()) {
echo 'Removing Dir: ', $fileObject->getRealpath(), PHP_EOL;
// rmdir($fileObject->getRealpath());
}
}
Uncomment the rmdir and unlink lines to actually perform the removals.
Noticing your use of relative paths please see if this comment helps:
http://php.net/manual/en/function.unlink.php#85938