diff --git a/src/Symfony/Component/Validator/Constraints/FileValidator.php b/src/Symfony/Component/Validator/Constraints/FileValidator.php index 62a6139a6efb9..05f73b7497950 100644 --- a/src/Symfony/Component/Validator/Constraints/FileValidator.php +++ b/src/Symfony/Component/Validator/Constraints/FileValidator.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Constraints; +use Psr\Http\Message\UploadedFileInterface; use Symfony\Component\HttpFoundation\File\File as FileObject; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\Validator\Context\ExecutionContextInterface; @@ -51,10 +52,18 @@ public function validate($value, Constraint $constraint) return; } - if ($value instanceof UploadedFile && !$value->isValid()) { - switch ($value->getError()) { + $error = null; + if ( + ($value instanceof UploadedFile && !$value->isValid()) + || ($value instanceof UploadedFileInterface && UPLOAD_ERR_OK !== $value->getError()) + ) { + $error = $value->getError(); + } + + if (null !== $error) { + switch ($error) { case UPLOAD_ERR_INI_SIZE: - $iniLimitSize = UploadedFile::getMaxFilesize(); + $iniLimitSize = self::getMaxFilesize(); if ($constraint->maxSize && $constraint->maxSize < $iniLimitSize) { $limitInBytes = $constraint->maxSize; $binaryFormat = $constraint->binaryFormat; @@ -154,11 +163,11 @@ public function validate($value, Constraint $constraint) default: if ($this->context instanceof ExecutionContextInterface) { $this->context->buildViolation($constraint->uploadErrorMessage) - ->setCode($value->getError()) + ->setCode($error) ->addViolation(); } else { $this->buildViolation($constraint->uploadErrorMessage) - ->setCode($value->getError()) + ->setCode($error) ->addViolation(); } @@ -166,6 +175,12 @@ public function validate($value, Constraint $constraint) } } + if ($value instanceof UploadedFileInterface) { + // PSR-7 UploadedFileInterface does not represent a file + // in the filesystem. + return; + } + if (!is_scalar($value) && !$value instanceof FileObject && !(is_object($value) && method_exists($value, '__toString'))) { throw new UnexpectedTypeException($value, 'string'); } @@ -292,6 +307,33 @@ private static function moreDecimalsThan($double, $numberOfDecimals) return strlen((string) $double) > strlen(round($double, $numberOfDecimals)); } + private static function getMaxFilesize() + { + $iniMax = strtolower(ini_get('upload_max_filesize')); + + if ('' === $iniMax) { + return PHP_INT_MAX; + } + + $max = ltrim($iniMax, '+'); + if (0 === strpos($max, '0x')) { + $max = intval($max, 16); + } elseif (0 === strpos($max, '0')) { + $max = intval($max, 8); + } else { + $max = (int) $max; + } + + switch (substr($iniMax, -1)) { + case 't': $max *= 1024; + case 'g': $max *= 1024; + case 'm': $max *= 1024; + case 'k': $max *= 1024; + } + + return $max; + } + /** * Convert the limit to the smallest possible number * (i.e. try "MB", then "kB", then "bytes"). diff --git a/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorPsr7Test.php b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorPsr7Test.php new file mode 100644 index 0000000000000..003657658f50b --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorPsr7Test.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\File; +use Symfony\Component\Validator\Constraints\FileValidator; + +class FileValidatorPsr7Test extends AbstractConstraintValidatorTest +{ + protected function createValidator() + { + return new FileValidator(); + } + + public function testValidUploadedFile() + { + $file = $this->getMock('Psr\Http\Message\UploadedFileInterface'); + $file->expects($this->any())->method('getError')->willReturn(UPLOAD_ERR_OK); + + $this->validator->validate($file, new File()); + + $this->assertNoViolation(); + } + + public function testInvalidUploadedFile() + { + $file = $this->getMock('Psr\Http\Message\UploadedFileInterface'); + $file->expects($this->any())->method('getError')->willReturn(UPLOAD_ERR_FORM_SIZE); + + $constraint = new File(array( + 'uploadFormSizeErrorMessage' => 'myMessage', + )); + + $this->validator->validate($file, $constraint); + + $this->buildViolation('myMessage') + ->setCode(UPLOAD_ERR_FORM_SIZE) + ->assertRaised(); + } +} diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index 2208bc9a76844..b622343c630c0 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -29,7 +29,8 @@ "symfony/expression-language": "~2.4|~3.0.0", "doctrine/annotations": "~1.0", "doctrine/cache": "~1.0", - "egulias/email-validator": "~1.2,>=1.2.1" + "egulias/email-validator": "~1.2,>=1.2.1", + "psr/http-message": "~1.0" }, "suggest": { "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", @@ -40,7 +41,8 @@ "symfony/config": "", "egulias/email-validator": "Strict (RFC compliant) email validation", "symfony/property-access": "For using the 2.4 Validator API", - "symfony/expression-language": "For using the 2.4 Expression validator" + "symfony/expression-language": "For using the 2.4 Expression validator", + "psr/http-message": "For validating PSR-7 UploadedFileInterface implementations" }, "autoload": { "psr-4": { "Symfony\\Component\\Validator\\": "" }