diff --git a/.travis.yml b/.travis.yml index 9fbe40be1..25596d03c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,8 @@ matrix: - php: 5.6 env: DB=MYSQL PHPUNIT_TEST=1 PHPCS_TEST=1 - php: 7.0 + env: DB=MYSQL PHPUNIT_TEST=1 + - php: 7.1 env: DB=MYSQL PDO=1 PHPUNIT_COVERAGE_TEST=1 - php: 5.6 env: DB=MYSQL BEHAT_TEST=1 diff --git a/src/Assets/File.php b/src/Assets/File.php index ee2d6ca72..8ca2bcb5a 100644 --- a/src/Assets/File.php +++ b/src/Assets/File.php @@ -1012,8 +1012,6 @@ class File extends DataObject implements ShortcodeHandler, AssetContainer, Thumb /** * Formats a file size (eg: (int)42 becomes string '42 bytes') * - * @todo unit tests - * * @param int $size * @return string */ @@ -1040,22 +1038,29 @@ class File extends DataObject implements ShortcodeHandler, AssetContainer, Thumb /** * Convert a php.ini value (eg: 512M) to bytes * - * @todo unit tests - * - * @param string $iniValue + * @param string $iniValue * @return int */ public static function ini2bytes($iniValue) { - switch (strtolower(substr(trim($iniValue), -1))) { + $iniValues = str_split(trim($iniValue)); + $unit = strtolower(array_pop($iniValues)); + $quantity = (int) implode($iniValues); + switch ($unit) { case 'g': - $iniValue *= 1024; + $quantity *= 1024; + // deliberate no break case 'm': - $iniValue *= 1024; + $quantity *= 1024; + // deliberate no break case 'k': - $iniValue *= 1024; + $quantity *= 1024; + // deliberate no break + default: + // no-op: pre-existing behaviour + break; } - return $iniValue; + return $quantity; } /** diff --git a/src/Security/RandomGenerator.php b/src/Security/RandomGenerator.php index 4902c0035..ffbd73ec0 100644 --- a/src/Security/RandomGenerator.php +++ b/src/Security/RandomGenerator.php @@ -24,6 +24,11 @@ class RandomGenerator { $isWin = preg_match('/WIN/', PHP_OS); + // PHP 7.1 preference, since mcrypt is deprecated + if (function_exists('random_bytes')) { + return bin2hex(random_bytes(64)); + } + // TODO Fails with "Could not gather sufficient random data" on IIS, temporarily disabled on windows if (!$isWin) { if (function_exists('mcrypt_create_iv')) { diff --git a/tests/php/Assets/FileTest.php b/tests/php/Assets/FileTest.php index d5ca613ef..dfb5a021a 100644 --- a/tests/php/Assets/FileTest.php +++ b/tests/php/Assets/FileTest.php @@ -54,8 +54,8 @@ class FileTest extends SapphireTest $fileIDs = $this->allFixtureIDs(File::class); foreach ($fileIDs as $fileID) { /** - * @var File $file -*/ + * @var File $file + */ $file = DataObject::get_by_id(File::class, $fileID); $root = ASSETS_PATH . '/FileTest/'; if ($folder = $file->Parent()) { @@ -143,7 +143,7 @@ class FileTest extends SapphireTest // because the parent folders don't exist in the database $folder = Folder::find_or_make('/FileTest/'); $testfilePath = BASE_PATH . '/assets/FileTest/CreateWithFilenameHasCorrectPath.txt'; // Important: No leading slash - $fh = fopen($testfilePath, "w"); + $fh = fopen($testfilePath, 'w'); fwrite($fh, str_repeat('x', 1000000)); fclose($fh); @@ -285,8 +285,8 @@ class FileTest extends SapphireTest public function testSetNameChangesFilesystemOnWrite() { /** - * @var File $file -*/ + * @var File $file + */ $file = $this->objFromFixture(File::class, 'asdf'); $this->logInWithPermission('ADMIN'); $file->publishRecursive(); @@ -461,8 +461,8 @@ class FileTest extends SapphireTest public function testDeleteFile() { /** - * @var File $file -*/ + * @var File $file + */ $file = $this->objFromFixture(File::class, 'asdf'); $this->logInWithPermission('ADMIN'); $file->publishSingle(); @@ -502,7 +502,7 @@ class FileTest extends SapphireTest //get folder again and see if the filename has changed $folder = DataObject::get_by_id(Folder::class, $folderID); $this->assertEquals( - $newTitle . "/", + $newTitle . '/', $folder->Filename, "Folder Filename updated after rename of Title" ); @@ -587,7 +587,6 @@ class FileTest extends SapphireTest $this->assertTrue($file->canEdit(), "Admins can edit files"); } - public function testJoinPaths() { $this->assertEquals('name/file.jpg', File::join_paths('/name', 'file.jpg')); @@ -598,6 +597,31 @@ class FileTest extends SapphireTest $this->assertEquals('', File::join_paths('/', '/')); } + /** + * Test that ini2bytes returns the number of bytes for a PHP ini style size declaration + * + * @param string $iniValue + * @param int $expected + * @dataProvider ini2BytesProvider + */ + public function testIni2Bytes($iniValue, $expected) + { + $this->assertSame($expected, File::ini2bytes($iniValue)); + } + + /** + * @return array + */ + public function ini2BytesProvider() + { + return [ + ['2k', 2 * 1024], + ['512M', 512 * 1024 * 1024], + ['1024g', 1024 * 1024 * 1024 * 1024], + ['1024G', 1024 * 1024 * 1024 * 1024] + ]; + } + /** * @return AssetStore */ diff --git a/tests/php/Assets/UploadTest.php b/tests/php/Assets/UploadTest.php index 174a3cfeb..bdad0e8dd 100644 --- a/tests/php/Assets/UploadTest.php +++ b/tests/php/Assets/UploadTest.php @@ -11,9 +11,21 @@ use SilverStripe\ORM\Versioning\Versioned; class UploadTest extends SapphireTest { - + /** + * {@inheritDoc} + * @var bool + */ protected $usesDatabase = true; + /** + * The temporary file path used for upload tests + * @var string + */ + protected $tmpFilePath; + + /** + * {@inheritDoc} + */ public function setUp() { parent::setUp(); @@ -21,30 +33,33 @@ class UploadTest extends SapphireTest TestAssetStore::activate('UploadTest'); } + /** + * {@inheritDoc} + */ public function tearDown() { TestAssetStore::reset(); parent::tearDown(); + + if (file_exists($this->tmpFilePath)) { + unlink($this->tmpFilePath); + } } public function testUpload() { // create tmp file $tmpFileName = 'UploadTest-testUpload.txt'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i=0; $i<10000; - $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'txt', 'error' => UPLOAD_ERR_OK, ); @@ -92,20 +107,16 @@ class UploadTest extends SapphireTest { // create tmp file $tmpFileName = 'UploadTest-testUpload.txt'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i=0; $i<10000; - $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'txt', 'error' => UPLOAD_ERR_OK, ); @@ -131,14 +142,14 @@ class UploadTest extends SapphireTest // check max file size set by app category $tmpFileName = 'UploadTest-testUpload.jpg'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - file_put_contents($tmpFilePath, $tmpFileContent . $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + file_put_contents($this->tmpFilePath, $tmpFileContent . $tmpFileContent); $tmpFile = array( 'name' => $tmpFileName, 'type' => 'image/jpeg', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'jpg', 'error' => UPLOAD_ERR_OK, ); @@ -169,21 +180,17 @@ class UploadTest extends SapphireTest ); // create tmp file $tmpFileName = 'myfile.jpg'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i=0; $i<100; - $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(100); + file_put_contents($this->tmpFilePath, $tmpFileContent); // Build file $upload = new Upload(); $tmpFile = array( 'name' => $tmpFileName, 'type' => '', - 'tmp_name' => $tmpFilePath, - 'size' => filesize($tmpFilePath), + 'tmp_name' => $this->tmpFilePath, + 'size' => filesize($this->tmpFilePath), 'error' => UPLOAD_ERR_OK, ); @@ -250,10 +257,18 @@ class UploadTest extends SapphireTest $v = new UploadTest\Validator(); $retrievedSize = $v->getAllowedMaxFileSize('[image]'); - $this->assertEquals(1024, $retrievedSize, 'Max file size check on default values failed (config category set check)'); + $this->assertEquals( + 1024, + $retrievedSize, + 'Max file size check on default values failed (config category set check)' + ); $retrievedSize = $v->getAllowedMaxFileSize('txt'); - $this->assertEquals(1000, $retrievedSize, 'Max file size check on default values failed (config extension set check)'); + $this->assertEquals( + 1000, + $retrievedSize, + 'Max file size check on default values failed (config extension set check)' + ); // Check instance values for max file size $maxFileSizes = array( @@ -264,7 +279,11 @@ class UploadTest extends SapphireTest $v->setAllowedMaxFileSize($maxFileSizes); $retrievedSize = $v->getAllowedMaxFileSize('[document]'); - $this->assertEquals(2000, $retrievedSize, 'Max file size check on instance values failed (instance category set check)'); + $this->assertEquals( + 2000, + $retrievedSize, + 'Max file size check on instance values failed (instance category set check)' + ); // Check that the instance values overwrote the default values // ie. The max file size will not exist for [image] @@ -280,14 +299,22 @@ class UploadTest extends SapphireTest $this->assertFalse($retrievedSize, 'Max file size check on instance values failed (extension not set check)'); $retrievedSize = $v->getAllowedMaxFileSize('txt'); - $this->assertEquals(4096, $retrievedSize, 'Max file size check on instance values failed (instance extension set check)'); + $this->assertEquals( + 4096, + $retrievedSize, + 'Max file size check on instance values failed (instance extension set check)' + ); // Check a wildcard max file size against a file with an extension $v = new UploadTest\Validator(); $v->setAllowedMaxFileSize(2000); $retrievedSize = $v->getAllowedMaxFileSize('.jpg'); - $this->assertEquals(2000, $retrievedSize, 'Max file size check on instance values failed (wildcard max file size)'); + $this->assertEquals( + 2000, + $retrievedSize, + 'Max file size check on instance values failed (wildcard max file size)' + ); Config::unnest(); } @@ -296,20 +323,16 @@ class UploadTest extends SapphireTest { // create tmp file $tmpFileName = 'UploadTest-testUpload'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i=0; $i<10000; - $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => '', 'error' => UPLOAD_ERR_OK, ); @@ -329,20 +352,16 @@ class UploadTest extends SapphireTest { // create tmp file $tmpFileName = 'UploadTest-testUpload.php'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i=0; $i<10000; - $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'php', 'error' => UPLOAD_ERR_OK, ); @@ -362,20 +381,16 @@ class UploadTest extends SapphireTest { // create tmp file $tmpFileName = 'UploadTest-testUpload.txt'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i=0; $i<10000; - $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'txt', 'error' => UPLOAD_ERR_OK, ); @@ -398,20 +413,16 @@ class UploadTest extends SapphireTest { // create tmp file $tmpFileName = 'UploadTest-testUpload'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i=0; $i<10000; - $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => '', 'error' => UPLOAD_ERR_OK, ); @@ -431,20 +442,16 @@ class UploadTest extends SapphireTest { // create tmp file $tmpFileName = 'UploadTest-testUpload.tar.gz'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i=0; $i<10000; - $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'tar.gz', 'error' => UPLOAD_ERR_OK, ); @@ -504,20 +511,16 @@ class UploadTest extends SapphireTest { // create tmp file $tmpFileName = 'UploadTest-testUpload'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i=0; $i<10000; - $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'txt', 'error' => UPLOAD_ERR_OK, ); @@ -565,20 +568,16 @@ class UploadTest extends SapphireTest { // create tmp file $tmpFileName = 'UploadTest-testUpload'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i=0; $i<10000; - $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'txt', 'error' => UPLOAD_ERR_OK, ); @@ -627,19 +626,16 @@ class UploadTest extends SapphireTest { // create tmp file $tmpFileName = 'UploadTest-testUpload.txt'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i = 0; $i < 10000; $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'txt', 'error' => UPLOAD_ERR_OK, ); @@ -708,17 +704,17 @@ class UploadTest extends SapphireTest public function testDeleteResampledImagesOnUpload() { $tmpFileName = 'UploadTest-testUpload.jpg'; - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $uploadImage = function () use ($tmpFileName, $tmpFilePath) { - copy(__DIR__ . '/GDTest/images/test_jpg.jpg', $tmpFilePath); + $uploadImage = function () use ($tmpFileName) { + copy(__DIR__ . '/GDTest/images/test_jpg.jpg', $this->tmpFilePath); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'jpg', 'error' => UPLOAD_ERR_OK, ); @@ -749,19 +745,16 @@ class UploadTest extends SapphireTest { $upload = function ($tmpFileName) { // create tmp file - $tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; - $tmpFileContent = ''; - for ($i = 0; $i < 10000; $i++) { - $tmpFileContent .= '0'; - } - file_put_contents($tmpFilePath, $tmpFileContent); + $this->tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName; + $tmpFileContent = $this->getTemporaryFileContent(); + file_put_contents($this->tmpFilePath, $tmpFileContent); // emulates the $_FILES array $tmpFile = array( 'name' => $tmpFileName, 'type' => 'text/plaintext', - 'size' => filesize($tmpFilePath), - 'tmp_name' => $tmpFilePath, + 'size' => filesize($this->tmpFilePath), + 'tmp_name' => $this->tmpFilePath, 'extension' => 'jpg', 'error' => UPLOAD_ERR_OK, ); @@ -843,4 +836,15 @@ class UploadTest extends SapphireTest 'File does receive new name' ); } + + /** + * Generate some dummy file content + * + * @param int $reps How many zeros to return + * @return string + */ + protected function getTemporaryFileContent($reps = 10000) + { + return str_repeat('0', $reps); + } }