Rekurzív mappamásolás php-ben

Ismét egy olyan script, aminek a megírására más kérdése ösztönzött. A script pedig nem más, mint a mappák és fájlok rekurzív másolása. Lehetne írni rá jó kis rekurzív függvényt, ami önmagát hívja meg. Vagy egy ciklust, ami az aktuális könyvtárszintet változtatgatja és rekurzív függvényhívás nélkül dolgozik. Vagy használhatjuk a PHP 5 adta lehetőségeket. Jelen esetben az iterátorokra gondolok.

A függvény

function recursiveCopy ($sourceDir,$destDir)
{
        //Az útvonalak mind 1 db "/" jellel véződjenek.
        $sourceDir = rtrim($sourceDir, '/').'/';
        $destDir = rtrim($destDir, '/').'/';

        $len = strlen($sourceDir);

        $iter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($sourceDir));
        foreach ($iter as $v)
        {
                if ($iter->isDot()) continue; // "." és ".." (aktuális és szülő) könyvtárak nem kellenek
                $source = $iter->getPathname();
                //Forrásmappa lecserélése  célmappára az útvonalban
                $dest = substr_replace($source, $destDir, 0, $len);
                       
                if ($iter->isDir()) //Nem létező könyvtárak létrehozása
                {
                        if (is_dir($dest)) continue;
                        mkdir($dest, $iter->getPerms(), true);
                }
                else //Fájlok másolása
                {
                        $dir = dirname($dest);
                        if (!is_dir($dir)) //Ha a szülő mappa nem létezik, azt is létrehozzuk
                        {
                                mkdir($dir, fileperms($iter->getPath()), true);
                        }
                        copy($source, $dest);
                }
        }
}

Használata:

recursiveCopy('ebből a mappából','ebbe a mappába');

Rövid, és egyszerű. Némi magyarázatot azért fűzök hozzá. A substr_replace() függvény használata egyszerű módja a forrás mappa lecserélésének. Az eredeti fájl útvonala már adott lesz. És ismerjük, hogy milyen hosszú ebben a forrás mappa útvonala.

Akik már találkoztak azzal a problémával, hogy UTF-8 karakterek 2 byte-on tárolódnak, azokban felmerülhet a kérdés, nem lesz-e gond, hogy ha ékezet van az útvonalban és strlen() függvényt használtam mb_strlen() helyett. De jelen esetben nem gond, mivel az strlen() valóban byte-okat számol, viszont lényegtelen is a tényleges karakterek száma, hisz a substr_replace() is byte-okkal dolgozik.

A másik kérdés, hogy vajon miért kell egy rekurzív iterátort egy rekurzív iterátor iteráló iterátornak átadni. Valójában a RecursiveDirectoryIterator annyival több a DirectoryIterator-nál, hogy előbbi tartalmaz plusz metódusokat, amivel a rekurzió megkönnyíthető. A RecursiveIteratorIterator pedig ezeket fel is használja. És így egyetlen ciklussal megkapjuk az összes adatot.

Az isDot() metódussal vizsgálható, hogy az aktuális elem éppen az aktuális, vagy a szülő könyvtárra mutató hivatkozás-e. Így kihagyható a listából. Ez mindig működik. Viszont PHP 5.3 óta a RecursiveDirectoryIterator szülő osztálya nem közvetlenül a DirectoryIterator, hanem az utóbbit öröklő FileSystemIterator. Így már a példányosításkor megadható, hogy ezeket a könyvtárakat ugorja át. A következőképpen:

$iter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($sourceDir,FilesystemIterator::SKIP_DOTS));

Az mkdir() függvény PHP 5.0 óta tartalmazza a harmadik paraméterét. Ahol is megadható egy logikai értékkel, hogy amennyiben nem létezik a létrehozni kívánt mappa szülő könyvtára sem, hozza létre a szülőkönyvtárakat is.

Végül: A fileperms() függvény tulajdonképpen ugyanazt teszi, mint az iterátor getPerms() metódusa. Lekérdezi a fájl ( mappa ) jogosultságait. Ezt az mkdir() második paraméterében megadva mindig, a forrás mappával azonos jogosultságú mappa jön létre. A fájlok pedig másoláskor eleve megtartják a jogosultságaikat. A tulajdonos változhat.

Kategóriák: 
Megosztás/Mentés