问题描述
上传ttf后。文件mimes验证没有通过
namespace App\Http\Controllers\Test;
use Illuminate\Http\Request;
class IndexController
{
public function index(Request $request){
return view('test.index');
}
public function store(Request $request){
$validator = \validator($request->all(),['file'=>['required','mimes:ttf']],['file.mimes'=>'文件类型不是ttf']);
if($validator->fails()){
dd($validator->errors());
}
}
}
看源码
vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
通过容器创建了一个validator工厂,通过工厂创建validator
工厂模式
if (! function_exists('validator')) {
/**
* Create a new Validator instance.
*
* @param array $data
* @param array $rules
* @param array $messages
* @param array $customAttributes
* @return \Illuminate\Contracts\Validation\Validator|\Illuminate\Contracts\Validation\Factory
*/
function validator(array $data = [], array $rules = [], array $messages = [], array $customAttributes = [])
{
//通过容器实例化工厂类
$factory = app(ValidationFactory::class);
if (func_num_args() === 0) {
return $factory;
}
//通过工厂创建验证器
return $factory->make($data, $rules, $messages, $customAttributes);
}
}
vendor/composer/autoload_classmap.php
这个文件存储了大部分的容器映射关系
通过这个文件我们知道了
validator工厂类 vendor/laravel/framework/src/Illuminate/Validation/Factory.php
validator类 vendor/laravel/framework/src/Illuminate/Validation/Validator.php
接下来直接看 validator类
namespace Illuminate\Validation;
use BadMethodCallException;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Translation\Translator;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\ImplicitRule;
use Illuminate\Contracts\Validation\Rule as RuleContract;
use Illuminate\Contracts\Validation\Validator as ValidatorContract;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Support\Arr;
use Illuminate\Support\Fluent;
use Illuminate\Support\MessageBag;
use Illuminate\Support\Str;
use Illuminate\Support\ValidatedInput;
use InvalidArgumentException;
use RuntimeException;
use stdClass;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class Validator implements ValidatorContract
{
use Concerns\FormatsMessages,
Concerns\ValidatesAttributes;
//省略......
/**
* Determine if the data passes the validation rules.
*
* @return bool
*/
public function passes()
{
$this->messages = new MessageBag;
[$this->distinctValues, $this->failedRules] = [[], []];
// We'll spin through each rule, validating the attributes attached to that
// rule. Any error messages will be added to the containers with each of
// the other error messages, returning true if we don't have messages.
foreach ($this->rules as $attribute => $rules) {
if ($this->shouldBeExcluded($attribute)) {
$this->removeAttribute($attribute);
continue;
}
if ($this->stopOnFirstFailure && $this->messages->isNotEmpty()) {
break;
}
foreach ($rules as $rule) {
//主要看这里的属性验证
$this->validateAttribute($attribute, $rule);
if ($this->shouldBeExcluded($attribute)) {
$this->removeAttribute($attribute);
break;
}
if ($this->shouldStopValidating($attribute)) {
break;
}
}
}
// Here we will spin through all of the "after" hooks on this validator and
// fire them off. This gives the callbacks a chance to perform all kinds
// of other validation that needs to get wrapped up in this operation.
foreach ($this->after as $after) {
$after();
}
return $this->messages->isEmpty();
}
/**
* Determine if the data fails the validation rules.
*
* @return bool
*/
public function fails()
{
return ! $this->passes();
}
/**
* Validate a given attribute against a rule.
*
* @param string $attribute
* @param string $rule
* @return void
*/
protected function validateAttribute($attribute, $rule)
{
$this->currentRule = $rule;
[$rule, $parameters] = ValidationRuleParser::parse($rule);
if ($rule === '') {
return;
}
// First we will get the correct keys for the given attribute in case the field is nested in
// an array. Then we determine if the given rule accepts other field names as parameters.
// If so, we will replace any asterisks found in the parameters with the correct keys.
if ($this->dependsOnOtherFields($rule)) {
$parameters = $this->replaceDotInParameters($parameters);
if ($keys = $this->getExplicitKeys($attribute)) {
$parameters = $this->replaceAsterisksInParameters($parameters, $keys);
}
}
$value = $this->getValue($attribute);
// If the attribute is a file, we will verify that the file upload was actually successful
// and if it wasn't we will add a failure for the attribute. Files may not successfully
// upload if they are too large based on PHP's settings so we will bail in this case.
if ($value instanceof UploadedFile && ! $value->isValid() &&
$this->hasRule($attribute, array_merge($this->fileRules, $this->implicitRules))
) {
return $this->addFailure($attribute, 'uploaded', []);
}
// If we have made it this far we will make sure the attribute is validatable and if it is
// we will call the validation method with the attribute. If a method returns false the
// attribute is invalid and we will add a failure message for this failing attribute.
$validatable = $this->isValidatable($rule, $attribute, $value);
if ($rule instanceof RuleContract) {
return $validatable
? $this->validateUsingCustomRule($attribute, $value, $rule)
: null;
}
//如果验证mimes,调用validateMimes方法,该方法在vendor/laravel/framework/src/Illuminate/Validation/Concerns/ValidatesAttributes.php文件中,是trait
$method = "validate{$rule}";
if ($validatable && ! $this->$method($attribute, $value, $parameters, $this)) {
$this->addFailure($attribute, $rule, $parameters);
}
}
//省略......
}
vendor/laravel/framework/src/Illuminate/Validation/Concerns/ValidatesAttributes.php
namespace Illuminate\Validation\Concerns;
use Countable;
use DateTime;
use DateTimeInterface;
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\DNSCheckValidation;
use Egulias\EmailValidator\Validation\MultipleValidationWithAnd;
use Egulias\EmailValidator\Validation\NoRFCWarningsValidation;
use Egulias\EmailValidator\Validation\RFCValidation;
use Egulias\EmailValidator\Validation\SpoofCheckValidation;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules\Exists;
use Illuminate\Validation\Rules\Unique;
use Illuminate\Validation\ValidationData;
use InvalidArgumentException;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
trait ValidatesAttributes
{
/**
* Validate the guessed extension of a file upload is in a set of file extensions.
*
* @param string $attribute
* @param mixed $value
* @param array $parameters
* @return bool
*/
public function validateMimes($attribute, $value, $parameters)
{
if (! $this->isValidFileInstance($value)) {
return false;
}
if ($this->shouldBlockPhpUpload($value, $parameters)) {
return false;
}
if (in_array('jpg', $parameters) || in_array('jpeg', $parameters)) {
$parameters = array_unique(array_merge($parameters, ['jpg', 'jpeg']));
}
//文件选择了,且mimes在指定mimes中
//$value 是 vendor/laravel/framework/src/Illuminate/Http/UploadedFile.php
return $value->getPath() !== '' && in_array($value->guessExtension(), $parameters);
}
}
vendor/laravel/framework/src/Illuminate/Http/UploadedFile.php
extend
Symfony\Component\HttpFoundation\File\UploadedFile
Symfony\Component\HttpFoundation\File\UploadedFile
extend
mponent\HttpFoundation\File
看Symfony\Component\HttpFoundation\File
public function guessExtension()
{
if (!class_exists(MimeTypes::class)) {
throw new \LogicException('You cannot guess the extension as the Mime component is not installed. Try running "composer require symfony/mime".');
}
//vendor/symfony/mime/MimeTypes.php
return MimeTypes::getDefault()->getExtensions($this->getMimeType())[0] ?? null;
}
看vendor/symfony/mime/MimeTypes.php
public function getExtensions(string $mimeType): array
{
if ($this->extensions) {
$extensions = $this->extensions[$mimeType] ?? $this->extensions[$lcMimeType = strtolower($mimeType)] ?? null;
}
//通过mimetype来判断mime类型
//self::MAP维护了mime与mimetype的映射关系
//其中维护类三种对应关系,认为是ttf
//'font/ttf' => ['ttf'],'pplication/x-font-ttf'>['ttf'],'application/x-font-truetype' => ['ttf']
//由于没有维护application/font-sfnt,导致认为mime是null,所以mime验证无法通过
return $extensions ?? self::MAP[$mimeType] ?? self::MAP[$lcMimeType ?? strtolower($mimeType)] ?? [];
}
解决
namespace App\Http\Controllers\Test;
use Illuminate\Http\Request;
class IndexController
{
public function index(Request $request){
return view('test.index');
}
public function store(Request $request){
//不验证mimes,验证mimetypes
$validator = \validator($request->all(),['file'=>['required','mimetypes:application/font-sfnt,application/x-font-truetype,application/x-font-ttf,font/ttf']],['file.mimetypes'=>'文件类型不是ttf']);
if($validator->fails()){
dd($validator->errors());
}
dd('ok');
}
}