Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional File functions #4712

Merged
merged 2 commits into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions system/Helpers/filesystem_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,57 @@ function directory_map(string $sourceDir, int $directoryDepth = 0, bool $hidden

// ------------------------------------------------------------------------

if (! function_exists('directory_mirror'))
{
/**
* Recursively copies the files and directories of the origin directory
* into the target directory, i.e. "mirror" its contents.
*
* @param string $originDir
* @param string $targetDir
* @param bool $overwrite Whether individual files overwrite on collision
*
* @return void
*
* @throws InvalidArgumentException
*/
function directory_mirror(string $originDir, string $targetDir, bool $overwrite = true): void
{
if (! is_dir($originDir = rtrim($originDir, '\\/')))
{
throw new InvalidArgumentException(sprintf('The origin directory "%s" was not found.', $originDir));
}

if (! is_dir($targetDir = rtrim($targetDir, '\\/')))
{
@mkdir($targetDir, 0755, true);
}

$dirLen = strlen($originDir);

/** @var SplFileInfo $file */
foreach (new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($originDir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
) as $file)
{
$origin = $file->getPathname();
$target = $targetDir . substr($origin, $dirLen);

if ($file->isDir())
{
mkdir($target, 0755);
}
elseif (! is_file($target) || ($overwrite && is_file($target)))
{
copy($origin, $target);
}
}
}
}

// ------------------------------------------------------------------------

if (! function_exists('write_file'))
{
/**
Expand Down Expand Up @@ -448,6 +499,24 @@ function octal_permissions(int $perms): string

// ------------------------------------------------------------------------

if (! function_exists('same_file'))
{
/**
* Checks if two files both exist and have identical hashes
*
* @param string $file1
* @param string $file2
*
* @return bool Same or not
*/
function same_file(string $file1, string $file2): bool
{
return is_file($file1) && is_file($file2) && md5_file($file1) === md5_file($file2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use hash_equals

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems like an unnecessary measure since there are no cryptography concerns here and we already know the string lengths will match. How does this improve on ===?

}
}

// ------------------------------------------------------------------------

if (! function_exists('set_realpath'))
{
/**
Expand Down
98 changes: 98 additions & 0 deletions tests/system/Helpers/FilesystemHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
use InvalidArgumentException;
use org\bovigo\vfs\vfsStream;

use org\bovigo\vfs\vfsStreamDirectory;
use org\bovigo\vfs\visitor\vfsStreamStructureVisitor;

class FilesystemHelperTest extends CIUnitTestCase
{

Expand Down Expand Up @@ -104,6 +107,58 @@ public function testDirectoryMapHandlesNotfound()

//--------------------------------------------------------------------

public function testDirectoryMirror()
{
$this->assertTrue(function_exists('directory_mirror'));

// Create a subdirectory
$this->structure['foo']['bam'] = ['zab' => 'A deep file'];

$vfs = vfsStream::setup('root', null, $this->structure);
$root = rtrim(vfsStream::url('root') . DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;

directory_mirror($root . 'foo', $root . 'boo');

$this->assertFileExists($root . 'boo/bar');
$this->assertFileExists($root . 'boo/bam/zab');
}

public function testDirectoryMirrorOverwrites()
{
$this->assertTrue(function_exists('directory_mirror'));

// Create duplicate files
$this->structure['foo']['far'] = 'all your base';
$this->structure['foo']['faz'] = 'are belong to us';

$vfs = vfsStream::setup('root', null, $this->structure);
$root = rtrim(vfsStream::url('root') . DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;

directory_mirror($root . 'foo', $root . 'boo', true);
$result = file_get_contents($root . 'boo/faz');

$this->assertEquals($this->structure['foo']['faz'], $result);
}

public function testDirectoryMirrorNotOverwrites()
{
$this->assertTrue(function_exists('directory_mirror'));

// Create duplicate files
$this->structure['foo']['far'] = 'all your base';
$this->structure['foo']['faz'] = 'are belong to us';

$vfs = vfsStream::setup('root', null, $this->structure);
$root = rtrim(vfsStream::url('root') . DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;

directory_mirror($root . 'foo', $root . 'boo', false);
$result = file_get_contents($root . 'boo/faz');

$this->assertEquals($this->structure['boo']['faz'], $result);
}

//--------------------------------------------------------------------

public function testWriteFileSuccess()
{
$vfs = vfsStream::setup('root');
Expand Down Expand Up @@ -379,6 +434,49 @@ public function testGetFileNotThereInfo()
}

//--------------------------------------------------------------------

public function testSameFileSame()
{
$file1 = SUPPORTPATH . 'Files/able/apple.php';
$file2 = SUPPORTPATH . 'Files/able/apple.php';

$this->assertTrue(same_file($file1, $file2));
}

public function testSameFileIdentical()
{
$file1 = SUPPORTPATH . 'Files/able/apple.php';
$file2 = SUPPORTPATH . 'Files/baker/banana.php';

$this->assertTrue(same_file($file1, $file2));
}

public function testSameFileDifferent()
{
$file1 = SUPPORTPATH . 'Files/able/apple.php';
$file2 = SUPPORTPATH . 'Images/ci-logo.gif';

$this->assertFalse(same_file($file1, $file2));
}

public function testSameFileOrder()
{
$file1 = SUPPORTPATH . 'Files/able/apple.php';
$file2 = SUPPORTPATH . 'Images/ci-logo.gif';

$this->assertFalse(same_file($file2, $file1));
}

public function testSameFileDirectory()
{
$file1 = SUPPORTPATH . 'Files/able/apple.php';
$file2 = SUPPORTPATH . 'Images/';

$this->assertFalse(same_file($file1, $file2));
}

//--------------------------------------------------------------------

public function testOctalPermissions()
{
$this->assertEquals('777', octal_permissions(0777));
Expand Down
42 changes: 39 additions & 3 deletions user_guide_src/source/helpers/filesystem_helper.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The following functions are available:

:param string $source_dir: Path to the source directory
:param int $directory_depth: Depth of directories to traverse (0 = fully recursive, 1 = current dir, etc)
:param bool $hidden: Whether to include hidden directories
:param bool $hidden: Whether to include hidden paths
:returns: An array of files
:rtype: array

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

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

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

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

Expand Down Expand Up @@ -79,6 +80,28 @@ The following functions are available:

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

.. php:function:: directory_mirror($original, $target[, $overwrite = true])

:param string $original: Original source directory
:param string $target: Target destination directory
:param bool $overwrite: Whether individual files overwrite on collision

Recursively copies the files and directories of the origin directory
into the target directory, i.e. "mirror" its contents.

Example::

try
{     
directory_mirror($uploadedImages, FCPATH . 'images/');
}
catch (Throwable $e)
{     
echo 'Failed to export uploads!';
}

You can optionally change the overwrite behavior via the third parameter.

MGatner marked this conversation as resolved.
Show resolved Hide resolved
.. php:function:: write_file($path, $data[, $mode = 'wb'])

:param string $path: File path
Expand Down Expand Up @@ -216,6 +239,19 @@ The following functions are available:

echo octal_permissions(fileperms('./index.php')); // 644

.. php:function:: same_file($file1, $file2)

:param string $file1: Path to the first file
:param string $file2: Path to the second file
:returns: Whether both files exist with identical hashes
:rtype: boolean

Compares two files to see if they are the same (based on their MD5 hash).

::

echo same_file($newFile, $oldFile) ? 'Same!' : 'Different!';

.. php:function:: set_realpath($path[, $check_existence = FALSE])

:param string $path: Path
Expand Down