Skip to content

Commit c0a7f22

Browse files
committed
Add file helpers
1 parent 7eef1f7 commit c0a7f22

3 files changed

Lines changed: 197 additions & 3 deletions

File tree

system/Helpers/filesystem_helper.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,61 @@ function directory_map(string $sourceDir, int $directoryDepth = 0, bool $hidden
7575

7676
// ------------------------------------------------------------------------
7777

78+
if (! function_exists('directory_mirror'))
79+
{
80+
/**
81+
* Recursively copies the files and directories of the origin directory
82+
* into the target directory, i.e. "mirror" its contents.
83+
*
84+
* @param string $originDir
85+
* @param string $targetDir
86+
* @param bool $overwrite Whether individual files overwrite on collision
87+
*
88+
* @return void
89+
*
90+
* @throws InvalidArgumentException
91+
*/
92+
function directory_mirror(string $originDir, string $targetDir, bool $overwrite = true): void
93+
{
94+
$originDir = rtrim($originDir, '\\/');
95+
$targetDir = rtrim($targetDir, '\\/');
96+
97+
if (! is_dir($originDir))
98+
{
99+
throw new InvalidArgumentException(sprintf('The origin directory "%s" was not found.', $originDir));
100+
}
101+
102+
if (! is_dir($targetDir))
103+
{
104+
@mkdir($targetDir, 0755, true);
105+
}
106+
107+
108+
$dirLen = strlen($originDir);
109+
110+
/** @var SplFileInfo $file */
111+
foreach (new RecursiveIteratorIterator(
112+
new RecursiveDirectoryIterator($originDir, FilesystemIterator::SKIP_DOTS),
113+
RecursiveIteratorIterator::SELF_FIRST
114+
) as $file)
115+
{
116+
$origin = $file->getPathname();
117+
$target = $targetDir . substr($origin, $dirLen);
118+
119+
if ($file->isDir())
120+
{
121+
mkdir($target, 0755);
122+
}
123+
elseif (! file_exists($target) || ($overwrite && is_file($target)))
124+
{
125+
copy($origin, $target);
126+
}
127+
}
128+
}
129+
}
130+
131+
// ------------------------------------------------------------------------
132+
78133
if (! function_exists('write_file'))
79134
{
80135
/**
@@ -448,6 +503,24 @@ function octal_permissions(int $perms): string
448503

449504
// ------------------------------------------------------------------------
450505

506+
if (! function_exists('same_file'))
507+
{
508+
/**
509+
* Checks if two files both exist and have identical hashes
510+
*
511+
* @param string $file1
512+
* @param string $file2
513+
*
514+
* @return bool Same or not
515+
*/
516+
function same_file(string $file1, string $file2): bool
517+
{
518+
return is_file($file1) && is_file($file2) && md5_file($file1) === md5_file($file2);
519+
}
520+
}
521+
522+
// ------------------------------------------------------------------------
523+
451524
if (! function_exists('set_realpath'))
452525
{
453526
/**

tests/system/Helpers/FilesystemHelperTest.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
use InvalidArgumentException;
77
use org\bovigo\vfs\vfsStream;
88

9+
use org\bovigo\vfs\vfsStreamDirectory;
10+
use org\bovigo\vfs\visitor\vfsStreamStructureVisitor;
11+
912
class FilesystemHelperTest extends CIUnitTestCase
1013
{
1114

@@ -104,6 +107,58 @@ public function testDirectoryMapHandlesNotfound()
104107

105108
//--------------------------------------------------------------------
106109

110+
public function testDirectoryMirror()
111+
{
112+
$this->assertTrue(function_exists('directory_mirror'));
113+
114+
// Create a subdirectory
115+
$this->structure['foo']['bam'] = ['zab' => 'A deep file'];
116+
117+
$vfs = vfsStream::setup('root', null, $this->structure);
118+
$root = rtrim(vfsStream::url('root') . DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
119+
120+
directory_mirror($root . 'foo', $root . 'boo');
121+
122+
$this->assertFileExists($root . 'boo/bar');
123+
$this->assertFileExists($root . 'boo/bam/zab');
124+
}
125+
126+
public function testDirectoryMirrorOverwrites()
127+
{
128+
$this->assertTrue(function_exists('directory_mirror'));
129+
130+
// Create duplicate files
131+
$this->structure['foo']['far'] = 'all your base';
132+
$this->structure['foo']['faz'] = 'are belong to us';
133+
134+
$vfs = vfsStream::setup('root', null, $this->structure);
135+
$root = rtrim(vfsStream::url('root') . DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
136+
137+
directory_mirror($root . 'foo', $root . 'boo', true);
138+
$result = file_get_contents($root . 'boo/faz');
139+
140+
$this->assertEquals($this->structure['foo']['faz'], $result);
141+
}
142+
143+
public function testDirectoryMirrorNotOverwrites()
144+
{
145+
$this->assertTrue(function_exists('directory_mirror'));
146+
147+
// Create duplicate files
148+
$this->structure['foo']['far'] = 'all your base';
149+
$this->structure['foo']['faz'] = 'are belong to us';
150+
151+
$vfs = vfsStream::setup('root', null, $this->structure);
152+
$root = rtrim(vfsStream::url('root') . DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
153+
154+
directory_mirror($root . 'foo', $root . 'boo', false);
155+
$result = file_get_contents($root . 'boo/faz');
156+
157+
$this->assertEquals($this->structure['boo']['faz'], $result);
158+
}
159+
160+
//--------------------------------------------------------------------
161+
107162
public function testWriteFileSuccess()
108163
{
109164
$vfs = vfsStream::setup('root');
@@ -379,6 +434,49 @@ public function testGetFileNotThereInfo()
379434
}
380435

381436
//--------------------------------------------------------------------
437+
438+
public function testSameFileSame()
439+
{
440+
$file1 = SUPPORTPATH . 'Files/able/apple.php';
441+
$file2 = SUPPORTPATH . 'Files/able/apple.php';
442+
443+
$this->assertTrue(same_file($file1, $file2));
444+
}
445+
446+
public function testSameFileIdentical()
447+
{
448+
$file1 = SUPPORTPATH . 'Files/able/apple.php';
449+
$file2 = SUPPORTPATH . 'Files/baker/banana.php';
450+
451+
$this->assertTrue(same_file($file1, $file2));
452+
}
453+
454+
public function testSameFileDifferent()
455+
{
456+
$file1 = SUPPORTPATH . 'Files/able/apple.php';
457+
$file2 = SUPPORTPATH . 'Images/ci-logo.gif';
458+
459+
$this->assertFalse(same_file($file1, $file2));
460+
}
461+
462+
public function testSameFileOrder()
463+
{
464+
$file1 = SUPPORTPATH . 'Files/able/apple.php';
465+
$file2 = SUPPORTPATH . 'Images/ci-logo.gif';
466+
467+
$this->assertFalse(same_file($file2, $file1));
468+
}
469+
470+
public function testSameFileDirectory()
471+
{
472+
$file1 = SUPPORTPATH . 'Files/able/apple.php';
473+
$file2 = SUPPORTPATH . 'Images/';
474+
475+
$this->assertFalse(same_file($file1, $file2));
476+
}
477+
478+
//--------------------------------------------------------------------
479+
382480
public function testOctalPermissions()
383481
{
384482
$this->assertEquals('777', octal_permissions(0777));

user_guide_src/source/helpers/filesystem_helper.rst

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ The following functions are available:
2626
2727
:param string $source_dir: Path to the source directory
2828
:param int $directory_depth: Depth of directories to traverse (0 = fully recursive, 1 = current dir, etc)
29-
:param bool $hidden: Whether to include hidden directories
29+
:param bool $hidden: Whether to include hidden paths
3030
:returns: An array of files
3131
:rtype: array
3232

@@ -42,8 +42,9 @@ The following functions are available:
4242

4343
$map = directory_map('./mydirectory/', 1);
4444

45-
By default, hidden files will not be included in the returned array. To
46-
override this behavior, you may set a third parameter to true (boolean)::
45+
By default, hidden files will not be included in the returned array and
46+
hidden directories will be skipped. To override this behavior, you may
47+
set a third parameter to true (boolean)::
4748

4849
$map = directory_map('./mydirectory/', FALSE, TRUE);
4950

@@ -79,6 +80,28 @@ The following functions are available:
7980

8081
If no results are found, this will return an empty array.
8182

83+
.. php:function:: directory_mirror($original, $target[, $overwrite = true])
84+
85+
:param string $original: Original source directory
86+
:param string $target: Target destination directory
87+
:param bool $overwrite: Whether individual files overwrite on collision
88+
89+
Recursively copies the files and directories of the origin directory
90+
into the target directory, i.e. "mirror" its contents.
91+
92+
Example::
93+
94+
try
95+
{     
96+
directory_mirror($uploadedImages, FCPATH . 'images/');
97+
}
98+
catch (Throwable $e)
99+
{     
100+
echo 'Failed to export uploads!';
101+
}
102+
103+
You can optionally change the overwrite behavior via the third parameter.
104+
82105
.. php:function:: write_file($path, $data[, $mode = 'wb'])
83106
84107
:param string $path: File path

0 commit comments

Comments
 (0)