Skip to content
Permalink
Browse files

Add support for PSR7 file objects in Validation methods.

Add support for PSR7 UploadedFile objects in the various file upload
validators. This will make integrating UploadedFile instances much
simpler into existing ORM workflows that deal with uploaded files.
  • Loading branch information...
markstory committed Oct 10, 2016
1 parent 032c154 commit e646773b8502a66a2864943fe510df10ddd9021d
Showing with 137 additions and 23 deletions.
  1. +56 −21 src/Validation/Validation.php
  2. +81 −2 tests/TestCase/Validation/ValidationTest.php
@@ -945,28 +945,41 @@ public static function luhn($check)
/**
* Checks the mime type of a file.
*
* @param string|array $check Value to check.
* Will check the mimetype of files/UploadedFileInterface instances
* by checking the using finfo on the file, not relying on the content-type
* sent by the client.
*
* @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check.
* @param array|string $mimeTypes Array of mime types or regex pattern to check.
* @return bool Success
* @throws \RuntimeException when mime type can not be determined.
* @throws \LogicException when ext/fileinfo is missing
*/
public static function mimeType($check, $mimeTypes = [])
{
if (is_array($check) && isset($check['tmp_name'])) {
$check = $check['tmp_name'];
if ($check instanceof UploadedFileInterface) {
try {
// Uploaded files throw exceptions on upload errors.
$file = $check->getStream()->getMetadata('uri');
} catch (RuntimeException $e) {
return false;
}
} elseif (is_array($check) && isset($check['tmp_name'])) {
$file = $check['tmp_name'];
} else {
$file = $check;
}
if (!function_exists('finfo_open')) {
throw new LogicException('ext/fileinfo is required for validating file mime types');
}
if (!is_file($check)) {
if (!is_file($file)) {
throw new RuntimeException('Cannot validate mimetype for a missing file');
}
$finfo = finfo_open(FILEINFO_MIME);
$finfo = finfo_file($finfo, $check);
$finfo = finfo_file($finfo, $file);
if (!$finfo) {
throw new RuntimeException('Can not determine the mimetype.');
@@ -988,43 +1001,55 @@ public static function mimeType($check, $mimeTypes = [])
/**
* Checks the filesize
*
* @param string|array $check Value to check.
* Will check the filesize of files/UploadedFileInterface instances
* by checking the filesize() on disk and not relying on the length
* reported by the client.
*
* @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check.
* @param string|null $operator See `Validation::comparison()`.
* @param int|string|null $size Size in bytes or human readable string like '5MB'.
* @return bool Success
*/
public static function fileSize($check, $operator = null, $size = null)
{
if (is_array($check) && isset($check['tmp_name'])) {
$check = $check['tmp_name'];
if ($check instanceof UploadedFileInterface) {
$file = $check->getStream()->getMetadata('uri');
} elseif (is_array($check) && isset($check['tmp_name'])) {
$file = $check['tmp_name'];
} else {
$file = $check;
}
if (is_string($size)) {
$size = Text::parseFileSize($size);
}
$filesize = filesize($check);
$filesize = filesize($file);
return static::comparison($filesize, $operator, $size);
}
/**
* Checking for upload errors
*
* @param string|array $check Value to check.
* @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check.
* @param bool $allowNoFile Set to true to allow UPLOAD_ERR_NO_FILE as a pass.
* @return bool
* @see http://www.php.net/manual/en/features.file-upload.errors.php
*/
public static function uploadError($check, $allowNoFile = false)
{
if (is_array($check) && isset($check['error'])) {
$check = $check['error'];
if ($check instanceof UploadedFileInterface) {
$code = $check->getError();
} elseif (is_array($check) && isset($check['error'])) {
$code = $check['error'];
} else {
$code = $check;
}
if ($allowNoFile) {
return in_array((int)$check, [UPLOAD_ERR_OK, UPLOAD_ERR_NO_FILE], true);
return in_array((int)$code, [UPLOAD_ERR_OK, UPLOAD_ERR_NO_FILE], true);
}
return (int)$check === UPLOAD_ERR_OK;
return (int)$code === UPLOAD_ERR_OK;
}
/**
@@ -1055,18 +1080,28 @@ public static function uploadedFile($file, array $options = [])
'types' => null,
'optional' => false,
];
if (!is_array($file)) {
if (!is_array($file) && !($file instanceof UploadedFileInterface)) {
return false;
}
$keys = ['error', 'name', 'size', 'tmp_name', 'type'];
ksort($file);
if (array_keys($file) != $keys) {
return false;
$error = $isUploaded = false;
if ($file instanceof UploadedFileInterface) {
$error = $file->getError();
$isUploaded = true;
}
if (is_array($file)) {
$keys = ['error', 'name', 'size', 'tmp_name', 'type'];
ksort($file);
if (array_keys($file) != $keys) {
return false;
}
$error = (int)$file['error'];
$isUploaded = is_uploaded_file($file['tmp_name']);
}
if (!static::uploadError($file, $options['optional'])) {
return false;
}
if ($options['optional'] && (int)$file['error'] === UPLOAD_ERR_NO_FILE) {
if ($options['optional'] && $error === UPLOAD_ERR_NO_FILE) {
return true;
}
if (isset($options['minSize']) && !static::fileSize($file, '>=', $options['minSize'])) {
@@ -1079,7 +1114,7 @@ public static function uploadedFile($file, array $options = [])
return false;
}
return is_uploaded_file($file['tmp_name']);
return $isUploaded;
}
/**
@@ -21,6 +21,7 @@
use Cake\TestSuite\TestCase;
use Cake\Validation\Validation;
use Cake\Validation\Validator;
use Zend\Diactoros\UploadedFile;
use Locale;
require_once __DIR__ . '/stubs.php';
@@ -2362,7 +2363,7 @@ public function testDatetime()
*/
public function testMimeType()
{
$image = CORE_TESTS . 'test_app/webroot/img/cake.power.gif';
$image = TEST_APP . 'webroot/img/cake.power.gif';
$File = new File($image, false);
$this->skipIf(!$File->mime(), 'Cannot determine mimeType');
@@ -2376,6 +2377,23 @@ public function testMimeType()
$this->assertFalse(Validation::mimeType(['tmp_name' => $image], ['image/png']));
}
/**
* Test mimetype with a PSR7 object
*
* @return void
*/
public function testMimeTypePsr7()
{
$image = TEST_APP . 'webroot/img/cake.power.gif';
$file = new UploadedFile($image, 1000, UPLOAD_ERR_OK, 'cake.power.gif', 'image/lies');
$this->assertTrue(Validation::mimeType($file, ['image/gif']));
$this->assertFalse(Validation::mimeType($file, ['image/png']));
$image = CORE_TESTS . 'test_app/webroot/img/cake.power.gif';
$file = new UploadedFile($image, 1000, UPLOAD_ERR_INI_SIZE, 'cake.power.gif', 'image/lies');
$this->assertFalse(Validation::mimeType($file, ['image/gif']), 'Fails on upload error');
}
/**
* testMimeTypeFalse method
*
@@ -2384,7 +2402,7 @@ public function testMimeType()
*/
public function testMimeTypeFalse()
{
$image = CORE_PATH . 'Cake/Test/TestApp/webroot/img/cake.power.gif';
$image = CORE_TESTS . 'invalid-file.png';
$File = new File($image, false);
$this->skipIf($File->mime(), 'mimeType can be determined, no Exception will be thrown');
Validation::mimeType($image, ['image/gif']);
@@ -2412,6 +2430,22 @@ public function testUploadError()
$this->assertTrue(Validation::uploadError(UPLOAD_ERR_NO_FILE, true));
}
/**
* testUploadError method with an UploadedFile
*
* @return void
*/
public function testUploadErrorPsr7()
{
$image = TEST_APP . 'webroot/img/cake.power.gif';
$file = new UploadedFile($image, 1000, UPLOAD_ERR_OK, 'cake.power.gif', 'image/gif');
$this->assertTrue(Validation::uploadError($file));
$file = new UploadedFile($image, 1000, UPLOAD_ERR_NO_FILE, 'cake.power.gif', 'image/gif');
$this->assertFalse(Validation::uploadError($file));
$this->assertTrue(Validation::uploadError($file, true));
}
/**
* testFileSize method
*
@@ -2431,6 +2465,22 @@ public function testFileSize()
$this->assertFalse(Validation::fileSize(['tmp_name' => $image], '>', '1KB'));
}
/**
* Test fileSize() with a PSR7 object.
*
* @return void
*/
public function testFileSizePsr7()
{
$image = TEST_APP . 'webroot/img/cake.power.gif';
$file = new UploadedFile($image, 1000, UPLOAD_ERR_OK, 'cake.power.gif', 'image/gif');
$this->assertTrue(Validation::fileSize($file, '==', 201));
$this->assertTrue(Validation::fileSize($file, '<', 1024));
$this->assertFalse(Validation::fileSize($file, '>', 202));
$this->assertFalse(Validation::fileSize($file, '>', 1000));
}
/**
* Test uploaded file validation.
*
@@ -2552,6 +2602,35 @@ public function testUploadedFileWithDifferentFileParametersOrder()
$this->assertTrue(Validation::uploadedFile($file, $options), 'Wrong order');
}
/**
* Test uploadedFile with a PSR7 object.
*
* @return void
*/
public function testUploadedFilePsr7()
{
$image = TEST_APP . 'webroot/img/cake.power.gif';
$file = new UploadedFile($image, 1000, UPLOAD_ERR_OK, 'cake.power.gif', 'image/gif');
$options = ['minSize' => 500];
$this->assertFalse(Validation::uploadedFile($file, $options));
$options = ['minSize' => 190];
$this->assertTrue(Validation::uploadedFile($file, $options));
$options = ['maxSize' => 10];
$this->assertFalse(Validation::uploadedFile($file, $options));
$options = ['maxSize' => 1000];
$this->assertTrue(Validation::uploadedFile($file, $options));
$options = ['types' => ['image/gif', 'image/png']];
$this->assertTrue(Validation::uploadedFile($file, $options));
$options = ['types' => ['text/plain']];
$this->assertFalse(Validation::uploadedFile($file, $options));
}
/**
* Test the compareWith method.
*

0 comments on commit e646773

Please sign in to comment.
You can’t perform that action at this time.