Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

2.1 folder merge #502

Closed
wants to merge 3 commits into from

2 participants

@dereuromark
Collaborator

folder fix for merging by default to ensure the class works correctly in all cases, especially with multilevel folders. manual change to other merge schemes added.
fix for default dirs added before starting the test. this ensures that if the tmp folder didnt contain all default dirs, that they are added.

@see ticket http://cakephp.lighthouseapp.com/projects/42648/tickets/2314-folder-class-should-merge-by-default

dereuromark added some commits
@dereuromark dereuromark folder fix for merging by default to ensure the class works correctly…
… in all cases, especially with multilevel folders. manual change to other merge schemes added. fix for default dirs added.
f6e8a29
@dereuromark dereuromark coding standards
fcf0f06
@dereuromark dereuromark more standards correction
757a6d8
@lorenzo
Owner

Thanks for contributing! 2.1 is already an RC and we cannot accept new features. We will review these changes again for 2.2 as soon as current version is marked stable

@dereuromark
Collaborator

to be exact this isnt exactly a new feature but a fix for broken functionality (see my reasoning in the ticket). the default case has to make sure that the least possible file loss occurs. which is not the case. actually, quite a lot of data gets lost internally using the current behavior for multilevel directories. the "new default case" would simply leave too many files there as worst case, not deleting them forever).

@dereuromark
Collaborator

closed in favor of a clean new approach in 2.2 (respecting all new coding standards)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 21, 2012
  1. @dereuromark

    folder fix for merging by default to ensure the class works correctly…

    dereuromark authored
    … in all cases, especially with multilevel folders. manual change to other merge schemes added. fix for default dirs added.
  2. @dereuromark

    coding standards

    dereuromark authored
  3. @dereuromark

    more standards correction

    dereuromark authored
This page is out of date. Refresh to see the latest.
Showing with 308 additions and 12 deletions.
  1. +271 −7 lib/Cake/Test/Case/Utility/FolderTest.php
  2. +37 −5 lib/Cake/Utility/Folder.php
View
278 lib/Cake/Test/Case/Utility/FolderTest.php
@@ -29,11 +29,16 @@ class FolderTest extends CakeTestCase {
protected static $_tmp = array();
/**
- * Save the directory names in TMP
+ * Save the directory names in TMP and make sure default directories exist
*
* @return void
*/
public static function setUpBeforeClass() {
+ $dirs = array('cache', 'logs', 'sessions', 'tests');
+ foreach ($dirs as $dir) {
+ new Folder(TMP . $dir, true);
+ }
+
foreach (scandir(TMP) as $file) {
if (is_dir(TMP . $file) && !in_array($file, array('.', '..'))) {
self::$_tmp[] = $file;
@@ -800,8 +805,128 @@ public function testDelete() {
$this->assertEquals($expected, $messages);
}
+ /**
+ * testCopy method
+ *
+ * Verify that subdirectories existing in both destination and source directory
+ * are merged recursivly.
+ *
+ * $path: folder_test/
+ * $folder1: folder_test/folder1/
+ * - file1.php
+ * $folder2: folder_test/folder2/
+ * - file2.php
+ * $folder3: folder_test/folder1/folder3/
+ * - file3.php
+ * $folder4: folder_test/folder2/folder4/
+ * - file4.php
+ * $folder5: folder_test/folder4/
+ */
+ public function testCopy() {
+ $path = TMP . 'folder_test';
+ $folder1 = $path . DS . 'folder1';
+ $folder2 = $path . DS . 'folder2';
+ $folder3 = $folder1 . DS . 'folder3';
+ $folder4 = $folder2 . DS . 'folder4';
+ $folder5 = $path . DS . 'folder5';
+ $file1 = $folder1 . DS . 'file1.php';
+ $file2 = $folder2 . DS . 'file2.php';
+ $file3 = $folder3 . DS . 'file3.php';
+ $file4 = $folder4 . DS . 'file4.php';
+ $file5 = $folder3 . DS . 'file5.php';
+ $file6 = $folder4 . DS . 'file6.php';
+
+ new Folder($path, true);
+ new Folder($folder1, true);
+ new Folder($folder2, true);
+ new Folder($folder3, true);
+ new Folder($folder4, true);
+ new Folder($folder5, true);
+ touch($file1);
+ touch($file2);
+ touch($file3);
+ touch($file4);
+
+ $Folder = new Folder($folder1);
+ $result = $Folder->copy($folder5);
+ $this->assertTrue($result);
+ $this->assertTrue(file_exists($folder5 . DS . 'file1.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'folder3' . DS . 'file3.php'));
+
+ $Folder = new Folder($folder2);
+ $result = $Folder->copy($folder5);
+ $this->assertTrue($result);
+ $this->assertTrue(file_exists($folder5 . DS . 'file1.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'file2.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'folder3' . DS . 'file3.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'folder4' . DS . 'file4.php'));
+
+ $Folder = new Folder($path);
+ $Folder->delete();
+ }
+
+ /**
+ * testCopyWithMerge method
+ *
+ * Verify that subdirectories existing in both destination and source directory
+ * are merged recursivly.
+ *
+ * $path: folder_test/
+ * $folder1: folder_test/folder1/
+ * - file1.php
+ * $folder2: folder_test/folder2/
+ * - file2.php
+ * $folder3: folder_test/folder1/folder3/
+ * - file3.php
+ * $folder4: folder_test/folder2/folder4/
+ * - file4.php
+ * $folder5: folder_test/folder4/
+ */
+ public function testCopyWithMerge() {
+ $path = TMP . 'folder_test';
+ $folder1 = $path . DS . 'folder1';
+ $folder2 = $path . DS . 'folder2';
+ $folder3 = $folder1 . DS . 'folder3';
+ $folder4 = $folder2 . DS . 'folder4';
+ $folder5 = $path . DS . 'folder5';
+ $file1 = $folder1 . DS . 'file1.php';
+ $file2 = $folder2 . DS . 'file2.php';
+ $file3 = $folder3 . DS . 'file3.php';
+ $file4 = $folder4 . DS . 'file4.php';
+ $file5 = $folder3 . DS . 'file5.php';
+ $file6 = $folder4 . DS . 'file6.php';
+
+ new Folder($path, true);
+ new Folder($folder1, true);
+ new Folder($folder2, true);
+ new Folder($folder3, true);
+ new Folder($folder4, true);
+ new Folder($folder5, true);
+ touch($file1);
+ touch($file2);
+ touch($file3);
+ touch($file4);
+
+ $Folder = new Folder($folder1);
+ $result = $Folder->copy($folder5);
+ $this->assertTrue($result);
+ $this->assertTrue(file_exists($folder5 . DS . 'file1.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'folder3' . DS . 'file3.php'));
+
+ $Folder = new Folder($folder2);
+ $result = $Folder->copy(array('to'=>$folder5, 'scheme'=>Folder::MERGE));
+ $this->assertTrue($result);
+ $this->assertTrue(file_exists($folder5 . DS . 'file1.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'file2.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'folder3' . DS . 'file3.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'folder4' . DS . 'file4.php'));
+
+ $Folder = new Folder($path);
+ $Folder->delete();
+ }
+
/**
- * testCopy method
+ * testCopyWithSkip method
*
* Verify that directories and files are copied recursively
* even if the destination directory already exists.
@@ -810,7 +935,7 @@ public function testDelete() {
*
* @return void
*/
- public function testCopy() {
+ public function testCopyWithSkip() {
$path = TMP . 'folder_test';
$folder1 = $path . DS . 'folder1';
$folder2 = $folder1 . DS . 'folder2';
@@ -826,7 +951,7 @@ public function testCopy() {
touch($file2);
$Folder = new Folder($folder1);
- $result = $Folder->copy($folder3);
+ $result = $Folder->copy(array('to'=>$folder3, 'scheme'=>Folder::SKIP));
$this->assertTrue($result);
$this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
$this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php'));
@@ -835,7 +960,7 @@ public function testCopy() {
$Folder->delete();
$Folder = new Folder($folder1);
- $result = $Folder->copy($folder3);
+ $result = $Folder->copy(array('to'=>$folder3, 'scheme'=>Folder::SKIP));
$this->assertTrue($result);
$this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
$this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php'));
@@ -848,14 +973,76 @@ public function testCopy() {
file_put_contents($folder3 . DS . 'folder2' . DS . 'file2.php', 'untouched');
$Folder = new Folder($folder1);
- $result = $Folder->copy($folder3);
+ $result = $Folder->copy(array('to'=>$folder3, 'scheme'=>Folder::SKIP));
$this->assertTrue($result);
$this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
$this->assertEquals(file_get_contents($folder3 . DS . 'folder2' . DS . 'file2.php'), 'untouched');
$Folder = new Folder($path);
$Folder->delete();
+ }
+
+ /**
+ * testCopyWithOverwrite
+ *
+ * Verify that subdirectories existing in both destination and source directory
+ * are overwritten/replaced recursivly.
+ *
+ * $path: folder_test/
+ * $folder1: folder_test/folder1/
+ * - file1.php
+ * $folder2: folder_test/folder2/
+ * - file2.php
+ * $folder3: folder_test/folder1/folder3/
+ * - file3.php
+ * $folder4: folder_test/folder2/folder4/
+ * - file4.php
+ * $folder5: folder_test/folder4/
+ */
+ function testCopyWithOverwrite() {
+ $path = TMP . 'folder_test';
+ $folder1 = $path . DS . 'folder1';
+ $folder2 = $path . DS . 'folder2';
+ $folder3 = $folder1 . DS . 'folder';
+ $folder4 = $folder2 . DS . 'folder';
+ $folder5 = $path . DS . 'folder5';
+ $file1 = $folder1 . DS . 'file1.php';
+ $file2 = $folder2 . DS . 'file2.php';
+ $file3 = $folder3 . DS . 'file3.php';
+ $file4 = $folder4 . DS . 'file4.php';
+
+ new Folder($path, true);
+ new Folder($folder1, true);
+ new Folder($folder2, true);
+ new Folder($folder3, true);
+ new Folder($folder4, true);
+ new Folder($folder5, true);
+ touch($file1);
+ touch($file2);
+ touch($file3);
+ touch($file4);
+
+ $Folder = new Folder($folder1);
+ $result = $Folder->copy(array('to'=>$folder5, 'scheme'=>Folder::OVERWRITE));
+
+
+ $this->assertTrue(file_exists($folder5 . DS . 'file1.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'folder' . DS . 'file3.php'));
+
+ $Folder = new Folder($folder2);
+ $result = $Folder->copy(array('to'=>$folder5, 'scheme'=>Folder::OVERWRITE));
+ $this->assertTrue($result);
+
+ $this->assertTrue(file_exists($folder5 . DS . 'file1.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'file2.php'));
+ $this->assertTrue(!file_exists($folder5 . DS . 'folder' . DS . 'file3.php'));
+ $this->assertTrue(file_exists($folder5 . DS . 'folder' . DS . 'file4.php'));
+
+ $Folder = new Folder($path);
+ $Folder->delete();
}
+
+
/**
* testMove method
@@ -863,7 +1050,7 @@ public function testCopy() {
* Verify that directories and files are moved recursively
* even if the destination directory already exists.
* Subdirectories existing in both destination and source directory
- * are skipped and not merged or overwritten.
+ * are merged recursivly.
*
* @return void
*/
@@ -925,6 +1112,83 @@ public function testMove() {
$result = $Folder->move($folder3);
$this->assertTrue($result);
$this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
+ $this->assertNotEquals(file_get_contents($folder3 . DS . 'folder2' . DS . 'file2.php'), 'untouched');
+ $this->assertFalse(file_exists($file1));
+ $this->assertFalse(file_exists($folder2));
+ $this->assertFalse(file_exists($file2));
+
+ $Folder = new Folder($path);
+ $Folder->delete();
+ }
+
+/**
+ * testMoveWithSkip method
+ *
+ * Verify that directories and files are moved recursively
+ * even if the destination directory already exists.
+ * Subdirectories existing in both destination and source directory
+ * are skipped and not merged or overwritten.
+ *
+ * @return void
+ */
+ public function testMoveWithSkip() {
+ $path = TMP . 'folder_test';
+ $folder1 = $path . DS . 'folder1';
+ $folder2 = $folder1 . DS . 'folder2';
+ $folder3 = $path . DS . 'folder3';
+ $file1 = $folder1 . DS . 'file1.php';
+ $file2 = $folder2 . DS . 'file2.php';
+
+ new Folder($path, true);
+ new Folder($folder1, true);
+ new Folder($folder2, true);
+ new Folder($folder3, true);
+ touch($file1);
+ touch($file2);
+
+ $Folder = new Folder($folder1);
+ $result = $Folder->move(array('to'=>$folder3, 'scheme'=>Folder::SKIP));
+ $this->assertTrue($result);
+ $this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
+ $this->assertTrue(is_dir($folder3 . DS . 'folder2'));
+ $this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php'));
+ $this->assertFalse(file_exists($file1));
+ $this->assertFalse(file_exists($folder2));
+ $this->assertFalse(file_exists($file2));
+
+ $Folder = new Folder($folder3);
+ $Folder->delete();
+
+ new Folder($folder1, true);
+ new Folder($folder2, true);
+ touch($file1);
+ touch($file2);
+
+ $Folder = new Folder($folder1);
+ $result = $Folder->move(array('to'=>$folder3, 'scheme'=>Folder::SKIP));
+ $this->assertTrue($result);
+ $this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
+ $this->assertTrue(is_dir($folder3 . DS . 'folder2'));
+ $this->assertTrue(file_exists($folder3 . DS . 'folder2' . DS . 'file2.php'));
+ $this->assertFalse(file_exists($file1));
+ $this->assertFalse(file_exists($folder2));
+ $this->assertFalse(file_exists($file2));
+
+ $Folder = new Folder($folder3);
+ $Folder->delete();
+
+ new Folder($folder1, true);
+ new Folder($folder2, true);
+ new Folder($folder3, true);
+ new Folder($folder3 . DS . 'folder2', true);
+ touch($file1);
+ touch($file2);
+ file_put_contents($folder3 . DS . 'folder2' . DS . 'file2.php', 'untouched');
+
+ $Folder = new Folder($folder1);
+ $result = $Folder->move(array('to'=>$folder3, 'scheme'=>Folder::SKIP));
+ $this->assertTrue($result);
+ $this->assertTrue(file_exists($folder3 . DS . 'file1.php'));
$this->assertEquals(file_get_contents($folder3 . DS . 'folder2' . DS . 'file2.php'), 'untouched');
$this->assertFalse(file_exists($file1));
$this->assertFalse(file_exists($folder2));
View
42 lib/Cake/Utility/Folder.php
@@ -26,6 +26,30 @@
class Folder {
/**
+ * Default scheme for Folder::copy
+ * Recursivly merges subfolders with the same name
+ *
+ * @constant MERGE
+ */
+ const MERGE = 'merge';
+
+/**
+ * Overwrite scheme for Folder::copy
+ * subfolders with the same name will be replaced
+ *
+ * @constant OVERWRITE
+ */
+ const OVERWRITE = 'overwrite';
+
+/**
+ * Skip scheme for Folder::copy
+ * if a subfolder with the same name exists it will be skipped
+ *
+ * @constant SKIP
+ */
+ const SKIP = 'skip';
+
+/**
* Path to Folder.
*
* @var string
@@ -598,6 +622,7 @@ public function delete($path = null) {
* - `from` The directory to copy from, this will cause a cd() to occur, changing the results of pwd().
* - `mode` The mode to copy the files/directories with.
* - `skip` Files/directories to skip.
+ * - `schema` Folder::MERGE, Folder::OVERWRITE, Folder::SKIP
*
* @param mixed $options Either an array of options (see above) or a string of the destination directory.
* @return boolean Success
@@ -612,7 +637,7 @@ public function copy($options = array()) {
$to = $options;
$options = array();
}
- $options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array()), $options);
+ $options = array_merge(array('to' => $to, 'from' => $this->path, 'mode' => $this->mode, 'skip' => array(), 'scheme'=>Folder::MERGE), $options);
$fromDir = $options['from'];
$toDir = $options['to'];
@@ -634,10 +659,10 @@ public function copy($options = array()) {
$exceptions = array_merge(array('.', '..', '.svn'), $options['skip']);
if ($handle = @opendir($fromDir)) {
- while (false !== ($item = readdir($handle))) {
- if (!in_array($item, $exceptions)) {
- $from = Folder::addPathElement($fromDir, $item);
- $to = Folder::addPathElement($toDir, $item);
+ while (($item = readdir($handle)) !== false) {
+ $to = Folder::addPathElement($toDir, $item);
+ if (($options['scheme'] != Folder::SKIP || !is_dir($to)) && !in_array($item, $exceptions)) {
+ $from = Folder::addPathElement($fromDir, $item);
if (is_file($from)) {
if (copy($from, $to)) {
chmod($to, intval($mode, 8));
@@ -647,6 +672,10 @@ public function copy($options = array()) {
$this->_errors[] = __d('cake_dev', '%s NOT copied to %s', $from, $to);
}
}
+
+ if (is_dir($from) && file_exists($to) && $options['scheme'] == Folder::OVERWRITE) {
+ $this->delete($to);
+ }
if (is_dir($from) && !file_exists($to)) {
$old = umask(0);
@@ -661,6 +690,9 @@ public function copy($options = array()) {
} else {
$this->_errors[] = __d('cake_dev', '%s not created', $to);
}
+ } elseif (is_dir($from) && $options['scheme'] == Folder::MERGE) {
+ $options = array_merge($options, array('to' => $to, 'from' => $from));
+ $this->copy($options);
}
}
}
Something went wrong with that request. Please try again.