4

I have a problem with deleting files through unlink() function. When the file is with a cyrillic name the function doesn't work.

[24-Jul-2012 00:33:35 UTC] PHP Warning: unlink(/home/gtsvetan/public_html/мениджър.doc) [function.unlink]: No such file or directory in /home/gtsvetan/public_html/deleter.php on line 114

So how to delete the file when the name is cyrillized?

The code is:

$dir = is_array($dir) ? $dir : explode(',', $dir);
foreach($dir as $dirv) {
    if(is_dir($dirv)) {
        $objects = scandir($dirv);
        foreach($objects as $object) {
            if($object != "." && $object != "..") {
                if(filetype($dirv."/".$object) == "dir") {
                    $this->delete($dirv."/".$object); 
                }
                else {
                    unlink($dirv."/".$object);
                }
            }
        }
    reset($objects);
    rmdir($dirv);
    }
    else {
        unlink($dirv);
    }
}

The solution:

public function delete($dir) {
        $dir = is_array($dir) ? $dir : explode(',', $dir);
        foreach($dir as $dirv) {
            if(is_dir($dirv)) {
                $d = @dir($dirv) or die();
                while(false !== ($entry = $d->read())) {
                    if($entry[0] == ".") {
                        continue;
                    }
                    if(is_dir($dirv.$entry.'/')) {
                        $this->delete($dirv.$entry.'/');
                        @rmdir($dirv.$entry);
                    }
                    elseif(is_readable($dirv.$entry)) {
                        @unlink($dirv.$entry);
                    }
                }
                $d->close();
            }
            else {
                @unlink($dirv);
            }
            @rmdir($dirv);
        }
    }

And here is the ajax.php which make a instance of the class :)

case 'delete':
$location = $_POST['location'];
if(is_array($location)) {
    foreach($location as $v) {
    $loc[] = iconv('utf-8', 'cp1251', $v);
    }
    $pfm->delete($loc);
}
else {
    $location = iconv('utf-8', 'cp1251', $location);
    $pfm->delete($location);
}
break;

It works perfect for me :)

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
  • Have you checked it exists already with `is_file()` ? – alex Jul 24 '12 at 00:42
  • @alex: Does it make sense? If you check before delete — you have a race condition anyhow. It may gone in between of those two calls. –  Jul 24 '12 at 00:44
  • @alex, its a .doc file so - it's a file :) –  Jul 24 '12 at 00:44
  • 1
    @T0m3kk: `is_file()` checks if file exists or not, and not only whether it is a file or not.. That's what @alex meant. –  Jul 24 '12 at 00:46
  • @VladLazarenko and @alex, I tried with `is_file()` and it doesn't detect it as a file. –  Jul 24 '12 at 00:50
  • @VladLazarenko It shouldn't be a race condition if the `unlink()` is performed with the `is_file()` as its condition. – alex Jul 24 '12 at 00:50
  • 1
    @alex: No, it is. These are two different system calls. The file may be gone in between of those calls (i.e. removed by some other application). –  Jul 24 '12 at 00:51
  • @VladLazarenko I guess so. You'd just need to suppress the warning from the hitting the end user. – alex Jul 24 '12 at 00:52
  • Anyway - it doesn't work any ideas or just spamming ? –  Jul 24 '12 at 00:53
  • @T0m3kk See [this answer](http://stackoverflow.com/a/11704090/892493) I think the same thing applies to you trying to use unlink. unlink is just not Unicode aware like most PHP functions. – drew010 Jul 28 '12 at 19:43
  • @drew010, thank you, mate but now I wrote my own function to do this job. The function list folders and subfolders not like this function and before deletion I change encoding of string from utf-8 to cp1251 and then everything works fine :) –  Jul 29 '12 at 09:55
  • @T0m3kk Nice, if you posted it as an answer I'd upvote it and you could later accept it. I'd love to see the solution if you wouldn't mind posting it. – drew010 Jul 29 '12 at 18:37
  • @drew010, the solution is added :) –  Aug 06 '12 at 15:59

4 Answers4

0

I'd suggest renaming it first if it not plays well.

sanusart
  • 1,527
  • 1
  • 11
  • 12
0

i have found that sanitizing file names is always a good idea. personally i like to have my scripts name files itself, not users (esp if it's an uploaded file). create a cleaning function that converts cyrillic characters. take a look at convert_cyr_string :: http://php.net/manual/en/function.convert-cyr-string.php

another idea, does renaming the file have the same problem as deleting them? if not, rename it to something like tobedeleted.ext then unlink it.

xero
  • 4,077
  • 22
  • 39
0

unlink from PHP just forwards to the corresponding system call. The file name will be passed to that function as-is, since PHP strings are just opaque sequences of bytes. This means that the name needs to be in an encoding that the system call understands. In other words, it depends on your OS. You also need to know what is the current encoding of the file name; this depends on where the input is coming from.

If you know that the system call wants UTF-8 (which is true on Linux) and that currently the name is in ISO-8859-5, then a solution using iconv would look like

unlink(iconv('iso-8859-5', 'utf-8', $dirv."/".$object));

Of course you can do the same with mb_convert_encoding as well. The same treatment is also necessary for all the other filesystem-related calls.

Jon
  • 428,835
  • 81
  • 738
  • 806
0

Hmm, I made this, It might come in useful.

    <?php
function delete($link) {
    foreach($link as $u) {
        if(is_dir($u)) {
            delete(glob($u . DIRECTORY_SEPARATOR . "*"));
            rmdir($u);
        } else; unlink($u);
    }
    return;
}

delete(glob(__DIR__ . DIRECTORY_SEPARATOR . "*"));
?>