Как стать автором
Обновить

Неверная валидация файлов в Laravel 5. Скандалы, интриги, расследования

Хочу поделится небольшим решением проблемы, которая возникла у меня при использовании laravel 5, в частности, при загрузке файла он не всегда приходит валидацию по правилу mimes, несмотря на правильное расширение и совершенную валидность. Так же в конце статьи мы напишем свой собственный православный валидатор под 5-ю версию.

Введение


После вдумчивого гугления проблемы и просмотра сырков (добрался до Symfony библиотек), выяснился следующий момент:

Если доступна php-функция passthru (проверка что надо, все очевидно с первого раза же), то в класс MimeTypeGuesser, который используется в Symfony\Component\HttpFoundation\File, подмешается, можно сказать, «дополнительный валидатор» FileBinaryMimeTypeGuesser, который попытается для вашего загруженного файла выполнить:

file -b --mime {путь к файлу} 2>/dev/null

Бинго! Данная утилита очень зависима от системных таблиц mime типов, в которых может не оказаться такой экзотики, как docx, сохраненный из openoffice, txt файл (да-да!). В таком случае в результате после всех абстракций все преобразуется к расширению файла: bin

А вы добавили, это, можно сказать, «расширение» в таблицу разрешенных типов валидатора?

Решение проблемы


Два варианта:
  • Добавить расширение bin в список разрешенных типов (а что выдает txt? Домашнее задание!)
  • Написать свой валидатор

Если с первым вариантом, думаю, все думаю понятно, фактически образуется дыра, где любые бинарные файлы с неопределенным mime залетают за валидатор, то мы попробуем это решить хоть как-то (у меня еще есть более глубокое решение на уровне SH скриптов, но мы не будем углублять статью). Заодно покажу, как создать свой валидатор.

Поехали:

Как известно, мы можем создавать любые файлы в пределах namespace \App. Например, создаем файл — app/Classes/CustomValidator.php:

<?php namespace App\Classes;
class CustomValidator extends \Illuminate\Validation\Validator {
    protected function validateExt($attribute, $file, $allowExtension)
    {
        return in_array($file->getClientOriginalExtension(), $allowExtension);
    }

    protected function replaceExt($message, $attribute, $rule, $parameters)
    {
        return str_replace(':values', implode(', ', $parameters), $message);
    }
}

Подключаем в любом провайдере, для примера взял — app\Providers\AppServiceProvider.php. Заменяем/дополняем метод boot:

	public function boot()
	{
        Validator::resolver(function($translator, $data, $rules, $messages)
        {
            return new \App\Classes\CustomValidator($translator, $data, $rules, $messages);
        });
	}

Последним шагом дополняем ваш файл языка — app/resources/lang/en/validation.php.

 "ext"                  => "The :attribute must be a file of type: :values.",


Хотелось бы пояснить, что тут за магия. Оказывается, Laravel при валидации ищет метод validate{Имя правила} в своем же классе валидации и выполняет его. Если было возвращено логическое FALSE, то он из стандартного файла языка вернет текст ошибки, предварительно скормив его в магический метод replace{Имя правила} для пост обработки.

Edjoy!
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.