Skip to content

Don't fail, if filenames are too long, shorten them instead #42

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

Open
wants to merge 4 commits into
base: 2.0-dev
Choose a base branch
from
Open
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
64 changes: 37 additions & 27 deletions Tests/FileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function dataTestStripExt(): \Generator
*
* @dataProvider dataTestStripExt
*/
public function testStripExt($fileName, $nameWithoutExt)
public function testStripExt(string $fileName, string $nameWithoutExt): void
{
$this->assertEquals(
File::stripExt($fileName),
Expand Down Expand Up @@ -101,6 +101,14 @@ public function dataTestMakeSafe(): \Generator
'.gitignore',
'Files starting with a fullstop should be allowed when strip chars parameter is empty',
];

$longName = str_repeat('0123456789abcdef', 16);
yield [
$longName . '.png',
[],
substr($longName, 0, 236) . '.png',
'Filenames with more than 240 characters should be shortened to 240 characters preserving the extension',
];
}

/**
Expand All @@ -111,18 +119,18 @@ public function dataTestMakeSafe(): \Generator
* @param string $expected The expected safe file name
* @param string $message The message to show on failure of test
*
* @covers Joomla\Filesystem\File::makeSafe
* @covers \Joomla\Filesystem\File::makeSafe
* @dataProvider dataTestMakeSafe
*/
public function testMakeSafe($name, $stripChars, $expected, $message)
public function testMakeSafe(string $name, array $stripChars, string $expected, string $message): void
{
$this->assertEquals(File::makeSafe($name, $stripChars), $expected, $message);
$this->assertEquals($expected, File::makeSafe($name, $stripChars), $message);
}

/**
* Test copy method.
*/
public function testCopyWithPathArgPassed()
public function testCopyWithPathArgPassed(): void
{
$name = 'tempFile';
$copiedName = 'tempCopiedFileName';
Expand All @@ -148,7 +156,7 @@ public function testCopyWithPathArgPassed()
/**
* Test copy method.
*/
public function testCopyWithoutPathArgPassed()
public function testCopyWithoutPathArgPassed(): void
{
$name = 'tempFile';
$copiedName = 'tempCopiedFileName';
Expand All @@ -174,7 +182,7 @@ public function testCopyWithoutPathArgPassed()
/**
* Test copy method using streams.
*/
public function testCopyWithStreams()
public function testCopyWithStreams(): void
{
$name = 'tempFile';
$copiedName = 'tempCopiedFileName';
Expand All @@ -200,7 +208,7 @@ public function testCopyWithStreams()
/**
* Test makeCopy method for an exception
*/
public function testCopySrcDontExist()
public function testCopySrcDontExist(): void
{
$this->expectException(\UnexpectedValueException::class);

Expand All @@ -213,7 +221,7 @@ public function testCopySrcDontExist()
/**
* Test delete method.
*/
public function testDeleteForSingleFile()
public function testDeleteForSingleFile(): void
{
$name = 'tempFile';
$data = 'Lorem ipsum dolor sit amet';
Expand All @@ -232,7 +240,7 @@ public function testDeleteForSingleFile()
/**
* Test delete method.
*/
public function testDeleteForArrayOfFiles()
public function testDeleteForArrayOfFiles(): void
{
$name1 = 'tempFile1';
$name2 = 'tempFile2';
Expand All @@ -257,7 +265,7 @@ public function testDeleteForArrayOfFiles()
/**
* Tests the File::move method.
*/
public function testMoveWithPathArgPassed()
public function testMoveWithPathArgPassed(): void
{
$name = 'tempFile';
$movedName = 'tempCopiedFileName';
Expand All @@ -277,7 +285,7 @@ public function testMoveWithPathArgPassed()
/**
* Tests the File::move method.
*/
public function testMoveWithoutPathArgPassed()
public function testMoveWithoutPathArgPassed(): void
{
$name = 'tempFile';
$movedName = 'tempCopiedFileName';
Expand All @@ -297,7 +305,7 @@ public function testMoveWithoutPathArgPassed()
/**
* Tests the File::move method.
*/
public function testMoveWithStreams()
public function testMoveWithStreams(): void
{
$name = 'tempFile';
$movedName = 'tempCopiedFileName';
Expand All @@ -314,11 +322,10 @@ public function testMoveWithStreams()
);
}


/**
* Test the File::move method where source file doesn't exist.
*/
public function testMoveSrcDontExist()
public function testMoveSrcDontExist(): void
{
$name = 'tempFile';
$movedName = 'tempCopiedFileName';
Expand All @@ -332,7 +339,7 @@ public function testMoveSrcDontExist()
/**
* Test write method.
*/
public function testWrite()
public function testWrite(): void
{
$name = 'tempFile';
$data = 'Lorem ipsum dolor sit amet';
Expand All @@ -352,10 +359,10 @@ public function testWrite()
/**
* Test write method when appending to a file.
*/
public function testWriteWithAppend()
public function testWriteWithAppend(): void
{
$name = 'tempFile.txt';
$data = 'Lorem ipsum dolor sit amet';
$name = 'tempFile.txt';
$data = 'Lorem ipsum dolor sit amet';
$appendData = PHP_EOL . $data;

$this->assertTrue(
Expand All @@ -378,7 +385,7 @@ public function testWriteWithAppend()
/**
* Test write method.
*/
public function testWriteCreatesMissingDirectory()
public function testWriteCreatesMissingDirectory(): void
{
$name = 'tempFile';
$data = 'Lorem ipsum dolor sit amet';
Expand All @@ -398,7 +405,7 @@ public function testWriteCreatesMissingDirectory()
/**
* Test write method.
*/
public function testWriteWithStreams()
public function testWriteWithStreams(): void
{
$name = 'tempFile';
$data = 'Lorem ipsum dolor sit amet';
Expand All @@ -420,7 +427,7 @@ public function testWriteWithStreams()
*
* @backupGlobals enabled
*/
public function testUpload()
public function testUpload(): void
{
include_once __DIR__ . '/Stubs/PHPUploadStub.php';

Expand Down Expand Up @@ -450,7 +457,7 @@ public function testUpload()
*
* @backupGlobals enabled
*/
public function testUploadWithStreams()
public function testUploadWithStreams(): void
{
include_once __DIR__ . '/Stubs/PHPUploadStub.php';

Expand Down Expand Up @@ -480,12 +487,12 @@ public function testUploadWithStreams()
*
* @backupGlobals enabled
*/
public function testUploadToNestedDirectory()
public function testUploadToNestedDirectory(): void
{
include_once __DIR__ . '/Stubs/PHPUploadStub.php';

$name = 'tempFile';
$data = 'Lorem ipsum dolor sit amet';
$name = 'tempFile';
$data = 'Lorem ipsum dolor sit amet';
$uploadedFileName = 'uploadedFileName';

if (!File::write($this->testPath . '/' . $name . '.txt', $data))
Expand All @@ -501,7 +508,10 @@ public function testUploadToNestedDirectory()
];

$this->assertTrue(
File::upload($this->testPath . '/' . $name . '.txt', $this->testPath . '/' . $name . '/' . $uploadedFileName)
File::upload(
$this->testPath . '/' . $name . '.txt',
$this->testPath . '/' . $name . '/' . $uploadedFileName
)
);
}
}
2 changes: 1 addition & 1 deletion Tests/HelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function testRemotefsize()
);

$this->assertTrue(
is_numeric(Helper::remotefsize('https://www.joomla.org')),
is_numeric(Helper::remotefsize('https://www.example.org')),
'Line:' . __LINE__ . ' for a valid remote file, returned size should be numeric.'
);

Expand Down
1 change: 1 addition & 0 deletions Tests/fixtures/tempFile.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Lorem ipsum dolor sit amet
47 changes: 45 additions & 2 deletions src/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public static function makeSafe($file, array $stripChars = ['#^\.#'])
// Remove any trailing dots, as those aren't ever valid file names.
$file = rtrim($file, '.');

// Shorten to maximum length, if necessary
$file = self::shortenIfTooLong($file);

return $file;
}

Expand Down Expand Up @@ -88,7 +91,9 @@ public static function copy($src, $dest, $path = null, $useStreams = false)

if (!$stream->copy($src, $dest, null, false))
{
throw new FilesystemException(sprintf('%1$s(%2$s, %3$s): %4$s', __METHOD__, $src, $dest, $stream->getError()));
throw new FilesystemException(
sprintf('%1$s(%2$s, %3$s): %4$s', __METHOD__, $src, $dest, $stream->getError())
);
}

self::invalidateFileCache($dest);
Expand Down Expand Up @@ -281,7 +286,9 @@ public static function upload($src, $dest, $useStreams = false)

if (!$stream->upload($src, $dest, null, false))
{
throw new FilesystemException(sprintf('%1$s(%2$s, %3$s): %4$s', __METHOD__, $src, $dest, $stream->getError()));
throw new FilesystemException(
sprintf('%1$s(%2$s, %3$s): %4$s', __METHOD__, $src, $dest, $stream->getError())
);
}

self::invalidateFileCache($dest);
Expand Down Expand Up @@ -325,4 +332,40 @@ public static function invalidateFileCache($file)
}
}
}

/**
* Shorten a filename to a maximum allowed length
*
* The directory name is not changed.
*
* @param string $filename The filename
* @param int $maxLen The maximum length including extension. Defaults to 240.
*
* @return string The original filename, if it is shorter than the maximum length, a shortened filename otherwise.
*/
private static function shortenIfTooLong(string $filename, int $maxLen = 240): string
{
$info = pathinfo($filename);

if (strlen($info['filename']) > $maxLen)
{
$path = $info['dirname'] === '.' ? '' : $info['dirname'];

if ($path > '')
{
$path .= '/';
}

$ext = $info['extension'] ?? '';

if ($ext > '')
{
$ext = '.' . $ext;
}

$filename = $path . substr($info['filename'], 0, $maxLen - strlen($ext)) . $ext;
}

return $filename;
}
}