PageRenderTime 26ms CodeModel.GetById 38ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/symfony/symfony/src/Symfony/Component/Validator/Constraints/FileValidator.php

https://gitlab.com/matijabelec/bigpandadev
PHP | 331 lines | 265 code | 41 blank | 25 comment | 53 complexity | b7d9c1ab978914422b5eb56b865a3554 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Validator\Constraints;
  11. use Symfony\Component\HttpFoundation\File\File as FileObject;
  12. use Symfony\Component\HttpFoundation\File\UploadedFile;
  13. use Symfony\Component\Validator\Context\ExecutionContextInterface;
  14. use Symfony\Component\Validator\Constraint;
  15. use Symfony\Component\Validator\ConstraintValidator;
  16. use Symfony\Component\Validator\Exception\UnexpectedTypeException;
  17. /**
  18. * @author Bernhard Schussek <bschussek@gmail.com>
  19. *
  20. * @api
  21. */
  22. class FileValidator extends ConstraintValidator
  23. {
  24. const KB_BYTES = 1000;
  25. const MB_BYTES = 1000000;
  26. const KIB_BYTES = 1024;
  27. const MIB_BYTES = 1048576;
  28. private static $suffices = array(
  29. 1 => 'bytes',
  30. self::KB_BYTES => 'kB',
  31. self::MB_BYTES => 'MB',
  32. self::KIB_BYTES => 'KiB',
  33. self::MIB_BYTES => 'MiB',
  34. );
  35. /**
  36. * {@inheritdoc}
  37. */
  38. public function validate($value, Constraint $constraint)
  39. {
  40. if (!$constraint instanceof File) {
  41. throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\File');
  42. }
  43. if (null === $value || '' === $value) {
  44. return;
  45. }
  46. if ($value instanceof UploadedFile && !$value->isValid()) {
  47. switch ($value->getError()) {
  48. case UPLOAD_ERR_INI_SIZE:
  49. $iniLimitSize = UploadedFile::getMaxFilesize();
  50. if ($constraint->maxSize && $constraint->maxSize < $iniLimitSize) {
  51. $limitInBytes = $constraint->maxSize;
  52. $binaryFormat = $constraint->binaryFormat;
  53. } else {
  54. $limitInBytes = $iniLimitSize;
  55. $binaryFormat = true;
  56. }
  57. list($sizeAsString, $limitAsString, $suffix) = $this->factorizeSizes(0, $limitInBytes, $binaryFormat);
  58. if ($this->context instanceof ExecutionContextInterface) {
  59. $this->context->buildViolation($constraint->uploadIniSizeErrorMessage)
  60. ->setParameter('{{ limit }}', $limitAsString)
  61. ->setParameter('{{ suffix }}', $suffix)
  62. ->setCode(UPLOAD_ERR_INI_SIZE)
  63. ->addViolation();
  64. } else {
  65. $this->buildViolation($constraint->uploadIniSizeErrorMessage)
  66. ->setParameter('{{ limit }}', $limitAsString)
  67. ->setParameter('{{ suffix }}', $suffix)
  68. ->setCode(UPLOAD_ERR_INI_SIZE)
  69. ->addViolation();
  70. }
  71. return;
  72. case UPLOAD_ERR_FORM_SIZE:
  73. if ($this->context instanceof ExecutionContextInterface) {
  74. $this->context->buildViolation($constraint->uploadFormSizeErrorMessage)
  75. ->setCode(UPLOAD_ERR_FORM_SIZE)
  76. ->addViolation();
  77. } else {
  78. $this->buildViolation($constraint->uploadFormSizeErrorMessage)
  79. ->setCode(UPLOAD_ERR_FORM_SIZE)
  80. ->addViolation();
  81. }
  82. return;
  83. case UPLOAD_ERR_PARTIAL:
  84. if ($this->context instanceof ExecutionContextInterface) {
  85. $this->context->buildViolation($constraint->uploadPartialErrorMessage)
  86. ->setCode(UPLOAD_ERR_PARTIAL)
  87. ->addViolation();
  88. } else {
  89. $this->buildViolation($constraint->uploadPartialErrorMessage)
  90. ->setCode(UPLOAD_ERR_PARTIAL)
  91. ->addViolation();
  92. }
  93. return;
  94. case UPLOAD_ERR_NO_FILE:
  95. if ($this->context instanceof ExecutionContextInterface) {
  96. $this->context->buildViolation($constraint->uploadNoFileErrorMessage)
  97. ->setCode(UPLOAD_ERR_NO_FILE)
  98. ->addViolation();
  99. } else {
  100. $this->buildViolation($constraint->uploadNoFileErrorMessage)
  101. ->setCode(UPLOAD_ERR_NO_FILE)
  102. ->addViolation();
  103. }
  104. return;
  105. case UPLOAD_ERR_NO_TMP_DIR:
  106. if ($this->context instanceof ExecutionContextInterface) {
  107. $this->context->buildViolation($constraint->uploadNoTmpDirErrorMessage)
  108. ->setCode(UPLOAD_ERR_NO_TMP_DIR)
  109. ->addViolation();
  110. } else {
  111. $this->buildViolation($constraint->uploadNoTmpDirErrorMessage)
  112. ->setCode(UPLOAD_ERR_NO_TMP_DIR)
  113. ->addViolation();
  114. }
  115. return;
  116. case UPLOAD_ERR_CANT_WRITE:
  117. if ($this->context instanceof ExecutionContextInterface) {
  118. $this->context->buildViolation($constraint->uploadCantWriteErrorMessage)
  119. ->setCode(UPLOAD_ERR_CANT_WRITE)
  120. ->addViolation();
  121. } else {
  122. $this->buildViolation($constraint->uploadCantWriteErrorMessage)
  123. ->setCode(UPLOAD_ERR_CANT_WRITE)
  124. ->addViolation();
  125. }
  126. return;
  127. case UPLOAD_ERR_EXTENSION:
  128. if ($this->context instanceof ExecutionContextInterface) {
  129. $this->context->buildViolation($constraint->uploadExtensionErrorMessage)
  130. ->setCode(UPLOAD_ERR_EXTENSION)
  131. ->addViolation();
  132. } else {
  133. $this->buildViolation($constraint->uploadExtensionErrorMessage)
  134. ->setCode(UPLOAD_ERR_EXTENSION)
  135. ->addViolation();
  136. }
  137. return;
  138. default:
  139. if ($this->context instanceof ExecutionContextInterface) {
  140. $this->context->buildViolation($constraint->uploadErrorMessage)
  141. ->setCode($value->getError())
  142. ->addViolation();
  143. } else {
  144. $this->buildViolation($constraint->uploadErrorMessage)
  145. ->setCode($value->getError())
  146. ->addViolation();
  147. }
  148. return;
  149. }
  150. }
  151. if (!is_scalar($value) && !$value instanceof FileObject && !(is_object($value) && method_exists($value, '__toString'))) {
  152. throw new UnexpectedTypeException($value, 'string');
  153. }
  154. $path = $value instanceof FileObject ? $value->getPathname() : (string) $value;
  155. if (!is_file($path)) {
  156. if ($this->context instanceof ExecutionContextInterface) {
  157. $this->context->buildViolation($constraint->notFoundMessage)
  158. ->setParameter('{{ file }}', $this->formatValue($path))
  159. ->setCode(File::NOT_FOUND_ERROR)
  160. ->addViolation();
  161. } else {
  162. $this->buildViolation($constraint->notFoundMessage)
  163. ->setParameter('{{ file }}', $this->formatValue($path))
  164. ->setCode(File::NOT_FOUND_ERROR)
  165. ->addViolation();
  166. }
  167. return;
  168. }
  169. if (!is_readable($path)) {
  170. if ($this->context instanceof ExecutionContextInterface) {
  171. $this->context->buildViolation($constraint->notReadableMessage)
  172. ->setParameter('{{ file }}', $this->formatValue($path))
  173. ->setCode(File::NOT_READABLE_ERROR)
  174. ->addViolation();
  175. } else {
  176. $this->buildViolation($constraint->notReadableMessage)
  177. ->setParameter('{{ file }}', $this->formatValue($path))
  178. ->setCode(File::NOT_READABLE_ERROR)
  179. ->addViolation();
  180. }
  181. return;
  182. }
  183. $sizeInBytes = filesize($path);
  184. if (0 === $sizeInBytes) {
  185. if ($this->context instanceof ExecutionContextInterface) {
  186. $this->context->buildViolation($constraint->disallowEmptyMessage)
  187. ->setParameter('{{ file }}', $this->formatValue($path))
  188. ->setCode(File::EMPTY_ERROR)
  189. ->addViolation();
  190. } else {
  191. $this->buildViolation($constraint->disallowEmptyMessage)
  192. ->setParameter('{{ file }}', $this->formatValue($path))
  193. ->setCode(File::EMPTY_ERROR)
  194. ->addViolation();
  195. }
  196. return;
  197. }
  198. if ($constraint->maxSize) {
  199. $limitInBytes = $constraint->maxSize;
  200. if ($sizeInBytes > $limitInBytes) {
  201. list($sizeAsString, $limitAsString, $suffix) = $this->factorizeSizes($sizeInBytes, $limitInBytes, $constraint->binaryFormat);
  202. if ($this->context instanceof ExecutionContextInterface) {
  203. $this->context->buildViolation($constraint->maxSizeMessage)
  204. ->setParameter('{{ file }}', $this->formatValue($path))
  205. ->setParameter('{{ size }}', $sizeAsString)
  206. ->setParameter('{{ limit }}', $limitAsString)
  207. ->setParameter('{{ suffix }}', $suffix)
  208. ->setCode(File::TOO_LARGE_ERROR)
  209. ->addViolation();
  210. } else {
  211. $this->buildViolation($constraint->maxSizeMessage)
  212. ->setParameter('{{ file }}', $this->formatValue($path))
  213. ->setParameter('{{ size }}', $sizeAsString)
  214. ->setParameter('{{ limit }}', $limitAsString)
  215. ->setParameter('{{ suffix }}', $suffix)
  216. ->setCode(File::TOO_LARGE_ERROR)
  217. ->addViolation();
  218. }
  219. return;
  220. }
  221. }
  222. if ($constraint->mimeTypes) {
  223. if (!$value instanceof FileObject) {
  224. $value = new FileObject($value);
  225. }
  226. $mimeTypes = (array) $constraint->mimeTypes;
  227. $mime = $value->getMimeType();
  228. foreach ($mimeTypes as $mimeType) {
  229. if ($mimeType === $mime) {
  230. return;
  231. }
  232. if ($discrete = strstr($mimeType, '/*', true)) {
  233. if (strstr($mime, '/', true) === $discrete) {
  234. return;
  235. }
  236. }
  237. }
  238. if ($this->context instanceof ExecutionContextInterface) {
  239. $this->context->buildViolation($constraint->mimeTypesMessage)
  240. ->setParameter('{{ file }}', $this->formatValue($path))
  241. ->setParameter('{{ type }}', $this->formatValue($mime))
  242. ->setParameter('{{ types }}', $this->formatValues($mimeTypes))
  243. ->setCode(File::INVALID_MIME_TYPE_ERROR)
  244. ->addViolation();
  245. } else {
  246. $this->buildViolation($constraint->mimeTypesMessage)
  247. ->setParameter('{{ file }}', $this->formatValue($path))
  248. ->setParameter('{{ type }}', $this->formatValue($mime))
  249. ->setParameter('{{ types }}', $this->formatValues($mimeTypes))
  250. ->setCode(File::INVALID_MIME_TYPE_ERROR)
  251. ->addViolation();
  252. }
  253. }
  254. }
  255. private static function moreDecimalsThan($double, $numberOfDecimals)
  256. {
  257. return strlen((string) $double) > strlen(round($double, $numberOfDecimals));
  258. }
  259. /**
  260. * Convert the limit to the smallest possible number
  261. * (i.e. try "MB", then "kB", then "bytes").
  262. */
  263. private function factorizeSizes($size, $limit, $binaryFormat)
  264. {
  265. if ($binaryFormat) {
  266. $coef = self::MIB_BYTES;
  267. $coefFactor = self::KIB_BYTES;
  268. } else {
  269. $coef = self::MB_BYTES;
  270. $coefFactor = self::KB_BYTES;
  271. }
  272. $limitAsString = (string) ($limit / $coef);
  273. // Restrict the limit to 2 decimals (without rounding! we
  274. // need the precise value)
  275. while (self::moreDecimalsThan($limitAsString, 2)) {
  276. $coef /= $coefFactor;
  277. $limitAsString = (string) ($limit / $coef);
  278. }
  279. // Convert size to the same measure, but round to 2 decimals
  280. $sizeAsString = (string) round($size / $coef, 2);
  281. // If the size and limit produce the same string output
  282. // (due to rounding), reduce the coefficient
  283. while ($sizeAsString === $limitAsString) {
  284. $coef /= $coefFactor;
  285. $limitAsString = (string) ($limit / $coef);
  286. $sizeAsString = (string) round($size / $coef, 2);
  287. }
  288. return array($sizeAsString, $limitAsString, self::$suffices[$coef]);
  289. }
  290. }