├── .editorconfig
├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
├── composer.json
├── phpcs.xml
└── src
├── Attribute.php
├── ErrorBag.php
├── Helper.php
├── MimeTypeGuesser.php
├── MissingRequiredParameterException.php
├── Rule.php
├── RuleNotFoundException.php
├── RuleQuashException.php
├── Rules
├── Accepted.php
├── After.php
├── Alpha.php
├── AlphaDash.php
├── AlphaNum.php
├── AlphaSpaces.php
├── Before.php
├── Between.php
├── Boolean.php
├── Callback.php
├── Date.php
├── Defaults.php
├── Different.php
├── Digits.php
├── DigitsBetween.php
├── Email.php
├── Extension.php
├── In.php
├── Integer.php
├── Interfaces
│ ├── BeforeValidate.php
│ └── ModifyValue.php
├── Ip.php
├── Ipv4.php
├── Ipv6.php
├── Json.php
├── Lowercase.php
├── Max.php
├── Mimes.php
├── Min.php
├── NotIn.php
├── Nullable.php
├── Numeric.php
├── Present.php
├── Regex.php
├── Required.php
├── RequiredIf.php
├── RequiredUnless.php
├── RequiredWith.php
├── RequiredWithAll.php
├── RequiredWithout.php
├── RequiredWithoutAll.php
├── Same.php
├── Traits
│ ├── DateUtilsTrait.php
│ ├── FileTrait.php
│ └── SizeTrait.php
├── TypeArray.php
├── UploadedFile.php
├── Uppercase.php
└── Url.php
├── Traits
├── MessagesTrait.php
└── TranslationsTrait.php
├── Validation.php
└── Validator.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.php]
4 | indent_size = 4
5 | indent_style = space
6 | end_of_line = lf
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Exclude unused files
2 | /tests export-ignore
3 | /phpunit.xml.dist export-ignore
4 | /.travis.yml export-ignore
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2019 Muhammad Syifa
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Rakit Validation - PHP Standalone Validation Library
2 | ======================================================
3 |
4 | [](https://travis-ci.org/rakit/validation)
5 | [](https://coveralls.io/github/rakit/validation)
6 | [](http://doge.mit-license.org)
7 |
8 |
9 | PHP Standalone library for validating data. Inspired by `Illuminate\Validation` Laravel.
10 |
11 | ## Features
12 |
13 | * API like Laravel validation.
14 | * Array validation.
15 | * `$_FILES` validation with multiple file support.
16 | * Custom attribute aliases.
17 | * Custom validation messages.
18 | * Custom rule.
19 |
20 | ## Requirements
21 |
22 | * PHP 7.0 or higher
23 | * Composer for installation
24 |
25 | ## Quick Start
26 |
27 | #### Installation
28 |
29 | ```
30 | composer require "rakit/validation"
31 | ```
32 |
33 | #### Usage
34 |
35 | There are two ways to validating data with this library. Using `make` to make validation object,
36 | then validate it using `validate`. Or just use `validate`.
37 | Examples:
38 |
39 | Using `make`:
40 |
41 | ```php
42 | make($_POST + $_FILES, [
52 | 'name' => 'required',
53 | 'email' => 'required|email',
54 | 'password' => 'required|min:6',
55 | 'confirm_password' => 'required|same:password',
56 | 'avatar' => 'required|uploaded_file:0,500K,png,jpeg',
57 | 'skills' => 'array',
58 | 'skills.*.id' => 'required|numeric',
59 | 'skills.*.percentage' => 'required|numeric'
60 | ]);
61 |
62 | // then validate
63 | $validation->validate();
64 |
65 | if ($validation->fails()) {
66 | // handling errors
67 | $errors = $validation->errors();
68 | echo "
";
69 | print_r($errors->firstOfAll());
70 | echo " ";
71 | exit;
72 | } else {
73 | // validation passes
74 | echo "Success!";
75 | }
76 |
77 | ```
78 |
79 | or just `validate` it:
80 |
81 | ```php
82 | validate($_POST + $_FILES, [
91 | 'name' => 'required',
92 | 'email' => 'required|email',
93 | 'password' => 'required|min:6',
94 | 'confirm_password' => 'required|same:password',
95 | 'avatar' => 'required|uploaded_file:0,500K,png,jpeg',
96 | 'skills' => 'array',
97 | 'skills.*.id' => 'required|numeric',
98 | 'skills.*.percentage' => 'required|numeric'
99 | ]);
100 |
101 | if ($validation->fails()) {
102 | // handling errors
103 | $errors = $validation->errors();
104 | echo "";
105 | print_r($errors->firstOfAll());
106 | echo " ";
107 | exit;
108 | } else {
109 | // validation passes
110 | echo "Success!";
111 | }
112 |
113 | ```
114 |
115 | In this case, 2 examples above will output the same results.
116 |
117 | But with `make` you can setup something like custom invalid message, custom attribute alias, etc before validation running.
118 |
119 | ### Attribute Alias
120 |
121 | By default we will transform your attribute into more readable text. For example `confirm_password` will be displayed as `Confirm password`.
122 | But you can set it anything you want with `setAlias` or `setAliases` method.
123 |
124 | Example:
125 |
126 | ```php
127 | $validator = new Validator;
128 |
129 | // To set attribute alias, you should use `make` instead `validate`.
130 | $validation->make([
131 | 'province_id' => $_POST['province_id'],
132 | 'district_id' => $_POST['district_id']
133 | ], [
134 | 'province_id' => 'required|numeric',
135 | 'district_id' => 'required|numeric'
136 | ]);
137 |
138 | // now you can set aliases using this way:
139 | $validation->setAlias('province_id', 'Province');
140 | $validation->setAlias('district_id', 'District');
141 |
142 | // or this way:
143 | $validation->setAliases([
144 | 'province_id' => 'Province',
145 | 'district_id' => 'District'
146 | ]);
147 |
148 | // then validate it
149 | $validation->validate();
150 |
151 | ```
152 |
153 | Now if `province_id` value is empty, error message would be 'Province is required'.
154 |
155 | ## Custom Validation Message
156 |
157 | Before register/set custom messages, here are some variables you can use in your custom messages:
158 |
159 | * `:attribute`: will replaced into attribute alias.
160 | * `:value`: will replaced into stringify value of attribute. For array and object will replaced to json.
161 |
162 | And also there are several message variables depends on their rules.
163 |
164 | Here are some ways to register/set your custom message(s):
165 |
166 | #### Custom Messages for Validator
167 |
168 | With this way, anytime you make validation using `make` or `validate` it will set your custom messages for it.
169 | It is useful for localization.
170 |
171 | To do this, you can set custom messages as first argument constructor like this:
172 |
173 | ```php
174 | $validator = new Validator([
175 | 'required' => ':attribute harus diisi',
176 | 'email' => ':email tidak valid',
177 | // etc
178 | ]);
179 |
180 | // then validation belows will use those custom messages
181 | $validation_a = $validator->validate($dataset_a, $rules_for_a);
182 | $validation_b = $validator->validate($dataset_b, $rules_for_b);
183 |
184 | ```
185 |
186 | Or using `setMessages` method like this:
187 |
188 | ```php
189 | $validator = new Validator;
190 | $validator->setMessages([
191 | 'required' => ':attribute harus diisi',
192 | 'email' => ':email tidak valid',
193 | // etc
194 | ]);
195 |
196 | // now validation belows will use those custom messages
197 | $validation_a = $validator->validate($dataset_a, $rules_for_dataset_a);
198 | $validation_b = $validator->validate($dataset_b, $rules_for_dataset_b);
199 |
200 | ```
201 |
202 | #### Custom Messages for Validation
203 |
204 | Sometimes you may want to set custom messages for specific validation.
205 | To do this you can set your custom messages as 3rd argument of `$validator->make` or `$validator->validate` like this:
206 |
207 | ```php
208 | $validator = new Validator;
209 |
210 | $validation_a = $validator->validate($dataset_a, $rules_for_dataset_a, [
211 | 'required' => ':attribute harus diisi',
212 | 'email' => ':email tidak valid',
213 | // etc
214 | ]);
215 |
216 | ```
217 |
218 | Or you can use `$validation->setMessages` like this:
219 |
220 | ```php
221 | $validator = new Validator;
222 |
223 | $validation_a = $validator->make($dataset_a, $rules_for_dataset_a);
224 | $validation_a->setMessages([
225 | 'required' => ':attribute harus diisi',
226 | 'email' => ':email tidak valid',
227 | // etc
228 | ]);
229 |
230 | ...
231 |
232 | $validation_a->validate();
233 | ```
234 |
235 | #### Custom Message for Specific Attribute Rule
236 |
237 | Sometimes you may want to set custom message for specific rule attribute.
238 | To do this you can use `:` as message separator or using chaining methods.
239 |
240 | Examples:
241 |
242 | ```php
243 | $validator = new Validator;
244 |
245 | $validation_a = $validator->make($dataset_a, [
246 | 'age' => 'required|min:18'
247 | ]);
248 |
249 | $validation_a->setMessages([
250 | 'age:min' => '18+ only',
251 | ]);
252 |
253 | $validation_a->validate();
254 | ```
255 |
256 | Or using chaining methods:
257 |
258 | ```php
259 | $validator = new Validator;
260 |
261 | $validation_a = $validator->make($dataset_a, [
262 | 'photo' => [
263 | 'required',
264 | $validator('uploaded_file')->fileTypes('jpeg|png')->message('Photo must be jpeg/png image')
265 | ]
266 | ]);
267 |
268 | $validation_a->validate();
269 | ```
270 |
271 | ## Translation
272 |
273 | Translation is different with custom messages.
274 | Translation may needed when you use custom message for rule `in`, `not_in`, `mimes`, and `uploaded_file`.
275 |
276 | For example if you use rule `in:1,2,3` we will set invalid message like "The Attribute only allows '1', '2', or '3'"
277 | where part "'1', '2', or '3'" is comes from ":allowed_values" tag.
278 | So if you have custom Indonesian message ":attribute hanya memperbolehkan :allowed_values",
279 | we will set invalid message like "Attribute hanya memperbolehkan '1', '2', or '3'" which is the "or" word is not part of Indonesian language.
280 |
281 | So, to solve this problem, we can use translation like this:
282 |
283 | ```php
284 | // Set translation for words 'and' and 'or'.
285 | $validator->setTranslations([
286 | 'and' => 'dan',
287 | 'or' => 'atau'
288 | ]);
289 |
290 | // Set custom message for 'in' rule
291 | $validator->setMessage('in', ":attribute hanya memperbolehkan :allowed_values");
292 |
293 | // Validate
294 | $validation = $validator->validate($inputs, [
295 | 'nomor' => 'in:1,2,3'
296 | ]);
297 |
298 | $message = $validation->errors()->first('nomor'); // "Nomor hanya memperbolehkan '1', '2', atau '3'"
299 | ```
300 |
301 | > Actually, our built-in rules only use words 'and' and 'or' that you may need to translates.
302 |
303 | ## Working with Error Message
304 |
305 | Errors messages are collected in `Rakit\Validation\ErrorBag` object that you can get it using `errors()` method.
306 |
307 | ```php
308 | $validation = $validator->validate($inputs, $rules);
309 |
310 | $errors = $validation->errors(); // << ErrorBag
311 | ```
312 |
313 | Now you can use methods below to retrieves errors messages:
314 |
315 | #### `all(string $format = ':message')`
316 |
317 | Get all messages as flatten array.
318 |
319 | Examples:
320 |
321 | ```php
322 | $messages = $errors->all();
323 | // [
324 | // 'Email is not valid email',
325 | // 'Password minimum 6 character',
326 | // 'Password must contains capital letters'
327 | // ]
328 |
329 | $messages = $errors->all(':message ');
330 | // [
331 | // 'Email is not valid email ',
332 | // 'Password minimum 6 character ',
333 | // 'Password must contains capital letters '
334 | // ]
335 | ```
336 |
337 | #### `firstOfAll(string $format = ':message', bool $dotNotation = false)`
338 |
339 | Get only first message from all existing keys.
340 |
341 | Examples:
342 |
343 | ```php
344 | $messages = $errors->firstOfAll();
345 | // [
346 | // 'email' => Email is not valid email',
347 | // 'password' => 'Password minimum 6 character',
348 | // ]
349 |
350 | $messages = $errors->firstOfAll(':message ');
351 | // [
352 | // 'email' => 'Email is not valid email ',
353 | // 'password' => 'Password minimum 6 character ',
354 | // ]
355 | ```
356 |
357 | Argument `$dotNotation` is for array validation.
358 | If it is `false` it will return original array structure, if it `true` it will return flatten array with dot notation keys.
359 |
360 | For example:
361 |
362 | ```php
363 | $messages = $errors->firstOfAll(':message', false);
364 | // [
365 | // 'contacts' => [
366 | // 1 => [
367 | // 'email' => 'Email is not valid email',
368 | // 'phone' => 'Phone is not valid phone number'
369 | // ],
370 | // ],
371 | // ]
372 |
373 | $messages = $errors->firstOfAll(':message', true);
374 | // [
375 | // 'contacts.1.email' => 'Email is not valid email',
376 | // 'contacts.1.phone' => 'Email is not valid phone number',
377 | // ]
378 | ```
379 |
380 | #### `first(string $key)`
381 |
382 | Get first message from given key. It will return `string` if key has any error message, or `null` if key has no errors.
383 |
384 | For example:
385 |
386 | ```php
387 | if ($emailError = $errors->first('email')) {
388 | echo $emailError;
389 | }
390 | ```
391 |
392 | #### `toArray()`
393 |
394 | Get all messages grouped by it's keys.
395 |
396 | For example:
397 |
398 | ```php
399 | $messages = $errors->toArray();
400 | // [
401 | // 'email' => [
402 | // 'Email is not valid email'
403 | // ],
404 | // 'password' => [
405 | // 'Password minimum 6 character',
406 | // 'Password must contains capital letters'
407 | // ]
408 | // ]
409 | ```
410 |
411 | #### `count()`
412 |
413 | Get count messages.
414 |
415 | #### `has(string $key)`
416 |
417 | Check if given key has an error. It returns `bool` if a key has an error, and otherwise.
418 |
419 |
420 | ## Getting Validated, Valid, and Invalid Data
421 |
422 | For example you have validation like this:
423 |
424 | ```php
425 | $validation = $validator->validate([
426 | 'title' => 'Lorem Ipsum',
427 | 'body' => 'Lorem ipsum dolor sit amet ...',
428 | 'published' => null,
429 | 'something' => '-invalid-'
430 | ], [
431 | 'title' => 'required',
432 | 'body' => 'required',
433 | 'published' => 'default:1|required|in:0,1',
434 | 'something' => 'required|numeric'
435 | ]);
436 | ```
437 |
438 | You can get validated data, valid data, or invalid data using methods in example below:
439 |
440 | ```php
441 | $validatedData = $validation->getValidatedData();
442 | // [
443 | // 'title' => 'Lorem Ipsum',
444 | // 'body' => 'Lorem ipsum dolor sit amet ...',
445 | // 'published' => '1' // notice this
446 | // 'something' => '-invalid-'
447 | // ]
448 |
449 | $validData = $validation->getValidData();
450 | // [
451 | // 'title' => 'Lorem Ipsum',
452 | // 'body' => 'Lorem ipsum dolor sit amet ...',
453 | // 'published' => '1'
454 | // ]
455 |
456 | $invalidData = $validation->getInvalidData();
457 | // [
458 | // 'something' => '-invalid-'
459 | // ]
460 | ```
461 |
462 | ## Available Rules
463 |
464 | > Click to show details.
465 |
466 | required
467 |
468 | The field under this validation must be present and not 'empty'.
469 |
470 | Here are some examples:
471 |
472 | | Value | Valid |
473 | | ------------- | ----- |
474 | | `'something'` | true |
475 | | `'0'` | true |
476 | | `0` | true |
477 | | `[0]` | true |
478 | | `[null]` | true |
479 | | null | false |
480 | | [] | false |
481 | | '' | false |
482 |
483 | For uploaded file, `$_FILES['key']['error']` must not `UPLOAD_ERR_NO_FILE`.
484 |
485 |
486 |
487 | required_if :another_field,value_1,value_2,...
488 |
489 | The field under this rule must be present and not empty if the anotherfield field is equal to any value.
490 |
491 | For example `required_if:something,1,yes,on` will be required if `something` value is one of `1`, `'1'`, `'yes'`, or `'on'`.
492 |
493 |
494 |
495 | required_unless :another_field,value_1,value_2,...
496 |
497 | The field under validation must be present and not empty unless the anotherfield field is equal to any value.
498 |
499 |
500 |
501 | required_with :field_1,field_2,...
502 |
503 | The field under validation must be present and not empty only if any of the other specified fields are present.
504 |
505 |
506 |
507 | required_without :field_1,field_2,...
508 |
509 | The field under validation must be present and not empty only when any of the other specified fields are not present.
510 |
511 |
512 |
513 | required_with_all :field_1,field_2,...
514 |
515 | The field under validation must be present and not empty only if all of the other specified fields are present.
516 |
517 |
518 |
519 | required_without_all :field_1,field_2,...
520 |
521 | The field under validation must be present and not empty only when all of the other specified fields are not present.
522 |
523 |
524 |
525 | uploaded_file :min_size,max_size,extension_a,extension_b,...
526 |
527 | This rule will validate data from `$_FILES`.
528 | Field under this rule must be follows rules below to be valid:
529 |
530 | * `$_FILES['key']['error']` must be `UPLOAD_ERR_OK` or `UPLOAD_ERR_NO_FILE`. For `UPLOAD_ERR_NO_FILE` you can validate it with `required` rule.
531 | * If min size is given, uploaded file size **MUST NOT** be lower than min size.
532 | * If max size is given, uploaded file size **MUST NOT** be higher than max size.
533 | * If file types is given, mime type must be one of those given types.
534 |
535 | Here are some example definitions and explanations:
536 |
537 | * `uploaded_file`: uploaded file is optional. When it is not empty, it must be `ERR_UPLOAD_OK`.
538 | * `required|uploaded_file`: uploaded file is required, and it must be `ERR_UPLOAD_OK`.
539 | * `uploaded_file:0,1M`: uploaded file size must be between 0 - 1 MB, but uploaded file is optional.
540 | * `required|uploaded_file:0,1M,png,jpeg`: uploaded file size must be between 0 - 1MB and mime types must be `image/jpeg` or `image/png`.
541 |
542 | Optionally, if you want to have separate error message between size and type validation.
543 | You can use `mimes` rule to validate file types, and `min`, `max`, or `between` to validate it's size.
544 |
545 | For multiple file upload, PHP will give you undesirable array `$_FILES` structure ([here](http://php.net/manual/en/features.file-upload.multiple.php#53240) is the topic). So we make `uploaded_file` rule to automatically resolve your `$_FILES` value to be well-organized array structure. That means, you cannot only use `min`, `max`, `between`, or `mimes` rules to validate multiple file upload. You should put `uploaded_file` just to resolve it's value and make sure that value is correct uploaded file value.
546 |
547 | For example if you have input files like this:
548 |
549 | ```html
550 |
551 |
552 |
553 | ```
554 |
555 | You can simply validate it like this:
556 |
557 | ```php
558 | $validation = $validator->validate($_FILES, [
559 | 'photos.*' => 'uploaded_file:0,2M,jpeg,png'
560 | ]);
561 |
562 | // or
563 |
564 | $validation = $validator->validate($_FILES, [
565 | 'photos.*' => 'uploaded_file|max:2M|mimes:jpeg,png'
566 | ]);
567 | ```
568 |
569 | Or if you have input files like this:
570 |
571 | ```html
572 |
573 |
574 | ```
575 |
576 | You can validate it like this:
577 |
578 | ```php
579 | $validation = $validator->validate($_FILES, [
580 | 'images.*' => 'uploaded_file|max:2M|mimes:jpeg,png',
581 | ]);
582 |
583 | // or
584 |
585 | $validation = $validator->validate($_FILES, [
586 | 'images.profile' => 'uploaded_file|max:2M|mimes:jpeg,png',
587 | 'images.cover' => 'uploaded_file|max:5M|mimes:jpeg,png',
588 | ]);
589 | ```
590 |
591 | Now when you use `getValidData()` or `getInvalidData()` you will get well array structure just like single file upload.
592 |
593 |
594 |
595 | mimes :extension_a,extension_b,...
596 |
597 | The `$_FILES` item under validation must have a MIME type corresponding to one of the listed extensions.
598 |
599 |
600 |
601 | default/defaults
602 |
603 | This is special rule that doesn't validate anything.
604 | It just set default value to your attribute if that attribute is empty or not present.
605 |
606 | For example if you have validation like this
607 |
608 | ```php
609 | $validation = $validator->validate([
610 | 'enabled' => null
611 | ], [
612 | 'enabled' => 'default:1|required|in:0,1'
613 | 'published' => 'default:0|required|in:0,1'
614 | ]);
615 |
616 | $validation->passes(); // true
617 |
618 | // Get the valid/default data
619 | $valid_data = $validation->getValidData();
620 |
621 | $enabled = $valid_data['enabled'];
622 | $published = $valid_data['published'];
623 | ```
624 |
625 | Validation passes because we sets default value for `enabled` and `published` to `1` and `0` which is valid. Then we can get the valid/default data.
626 |
627 |
628 |
629 | email
630 |
631 | The field under this validation must be valid email address.
632 |
633 |
634 |
635 | uppercase
636 |
637 | The field under this validation must be valid uppercase.
638 |
639 |
640 |
641 | lowercase
642 |
643 | The field under this validation must be valid lowercase.
644 |
645 |
646 |
647 | json
648 |
649 | The field under this validation must be valid JSON string.
650 |
651 |
652 |
653 | alpha
654 |
655 | The field under this rule must be entirely alphabetic characters.
656 |
657 |
658 |
659 | numeric
660 |
661 | The field under this rule must be numeric.
662 |
663 |
664 |
665 | alpha_num
666 |
667 | The field under this rule must be entirely alpha-numeric characters.
668 |
669 |
670 |
671 | alpha_dash
672 |
673 | The field under this rule may have alpha-numeric characters, as well as dashes and underscores.
674 |
675 |
676 |
677 | alpha_spaces
678 |
679 | The field under this rule may have alpha characters, as well as spaces.
680 |
681 |
682 |
683 | in :value_1,value_2,...
684 |
685 | The field under this rule must be included in the given list of values.
686 |
687 | This rule is using `in_array` to check the value.
688 | By default `in_array` disable strict checking.
689 | So it doesn't check data type.
690 | If you want enable strict checking, you can invoke validator like this:
691 |
692 | ```php
693 | $validation = $validator->validate($data, [
694 | 'enabled' => [
695 | 'required',
696 | $validator('in', [true, 1])->strict()
697 | ]
698 | ]);
699 | ```
700 |
701 | Then 'enabled' value should be boolean `true`, or int `1`.
702 |
703 |
704 |
705 | not_in :value_1,value_2,...
706 |
707 | The field under this rule must not be included in the given list of values.
708 |
709 | This rule also using `in_array`. You can enable strict checking by invoking validator and call `strict()` like example in rule `in` above.
710 |
711 |
712 |
713 | min :number
714 |
715 | The field under this rule must have a size greater or equal than the given number.
716 |
717 | For string value, size corresponds to the number of characters. For integer or float value, size corresponds to its numerical value. For an array, size corresponds to the count of the array. If your value is numeric string, you can put `numeric` rule to treat its size by numeric value instead of number of characters.
718 |
719 | You can also validate uploaded file using this rule to validate minimum size of uploaded file.
720 | For example:
721 |
722 | ```php
723 | $validation = $validator->validate([
724 | 'photo' => $_FILES['photo']
725 | ], [
726 | 'photo' => 'required|min:1M'
727 | ]);
728 | ```
729 |
730 |
731 |
732 | max :number
733 |
734 | The field under this rule must have a size lower or equal than the given number.
735 | Value size calculated in same way like `min` rule.
736 |
737 | You can also validate uploaded file using this rule to validate maximum size of uploaded file.
738 | For example:
739 |
740 | ```php
741 | $validation = $validator->validate([
742 | 'photo' => $_FILES['photo']
743 | ], [
744 | 'photo' => 'required|max:2M'
745 | ]);
746 | ```
747 |
748 |
749 |
750 | between :min,max
751 |
752 | The field under this rule must have a size between min and max params.
753 | Value size calculated in same way like `min` and `max` rule.
754 |
755 | You can also validate uploaded file using this rule to validate size of uploaded file.
756 | For example:
757 |
758 | ```php
759 | $validation = $validator->validate([
760 | 'photo' => $_FILES['photo']
761 | ], [
762 | 'photo' => 'required|between:1M,2M'
763 | ]);
764 | ```
765 |
766 |
767 |
768 | digits :value
769 |
770 | The field under validation must be numeric and must have an exact length of `value`.
771 |
772 |
773 |
774 | digits_between :min,max
775 |
776 | The field under validation must have a length between the given `min` and `max`.
777 |
778 |
779 |
780 | url
781 |
782 | The field under this rule must be valid url format.
783 | By default it check common URL scheme format like `any_scheme://...`.
784 | But you can specify URL schemes if you want.
785 |
786 | For example:
787 |
788 | ```php
789 | $validation = $validator->validate($inputs, [
790 | 'random_url' => 'url', // value can be `any_scheme://...`
791 | 'https_url' => 'url:http', // value must be started with `https://`
792 | 'http_url' => 'url:http,https', // value must be started with `http://` or `https://`
793 | 'ftp_url' => 'url:ftp', // value must be started with `ftp://`
794 | 'custom_url' => 'url:custom', // value must be started with `custom://`
795 | 'mailto_url' => 'url:mailto', // value must conatin valid mailto URL scheme like `mailto:a@mail.com,b@mail.com`
796 | 'jdbc_url' => 'url:jdbc', // value must contain valid jdbc URL scheme like `jdbc:mysql://localhost/dbname`
797 | ]);
798 | ```
799 |
800 | > For common URL scheme and mailto, we combine `FILTER_VALIDATE_URL` to validate URL format and `preg_match` to validate it's scheme.
801 | Except for JDBC URL, currently it just check a valid JDBC scheme.
802 |
803 |
804 |
805 | integer
806 | The field under t rule must be integer.
807 |
808 |
809 |
810 | boolean
811 |
812 | The field under this rule must be boolean. Accepted input are `true`, `false`, `1`, `0`, `"1"`, and `"0"`.
813 |
814 |
815 |
816 | ip
817 |
818 | The field under this rule must be valid ipv4 or ipv6.
819 |
820 |
821 |
822 | ipv4
823 |
824 | The field under this rule must be valid ipv4.
825 |
826 |
827 |
828 | ipv6
829 |
830 | The field under this rule must be valid ipv6.
831 |
832 |
833 |
834 | extension :extension_a,extension_b,...
835 |
836 | The field under this rule must end with an extension corresponding to one of those listed.
837 |
838 | This is useful for validating a file type for a given a path or url. The `mimes` rule should be used for validating uploads.
839 |
840 |
841 |
842 | array
843 |
844 | The field under this rule must be array.
845 |
846 |
847 |
848 | same :another_field
849 |
850 | The field value under this rule must be same with `another_field` value.
851 |
852 |
853 |
854 | regex :/your-regex/
855 |
856 | The field under this rule must be match with given regex.
857 |
858 |
859 |
860 | date :format
861 |
862 | The field under this rule must be valid date format. Parameter `format` is optional, default format is `Y-m-d`.
863 |
864 |
865 |
866 | accepted
867 |
868 | The field under this rule must be one of `'on'`, `'yes'`, `'1'`, `'true'`, or `true`.
869 |
870 |
871 |
872 | present
873 |
874 | The field under this rule must be exists, whatever the value is.
875 |
876 |
877 |
878 | different :another_field
879 |
880 | Opposite of `same`. The field value under this rule must be different with `another_field` value.
881 |
882 |
883 |
884 | after :tomorrow
885 |
886 | Anything that can be parsed by `strtotime` can be passed as a parameter to this rule. Valid examples include :
887 | - after:next week
888 | - after:2016-12-31
889 | - after:2016
890 | - after:2016-12-31 09:56:02
891 |
892 |
893 |
894 | before :yesterday
895 |
896 | This also works the same way as the [after rule](#after). Pass anything that can be parsed by `strtotime`
897 |
898 |
899 |
900 | callback
901 |
902 | You can use this rule to define your own validation rule.
903 | This rule can't be registered using string pipe.
904 | To use this rule, you should put Closure inside array of rules.
905 |
906 | For example:
907 |
908 | ```php
909 | $validation = $validator->validate($_POST, [
910 | 'even_number' => [
911 | 'required',
912 | function ($value) {
913 | // false = invalid
914 | return (is_numeric($value) AND $value % 2 === 0);
915 | }
916 | ]
917 | ]);
918 | ```
919 |
920 | You can set invalid message by returning a string.
921 | For example, example above would be:
922 |
923 | ```php
924 | $validation = $validator->validate($_POST, [
925 | 'even_number' => [
926 | 'required',
927 | function ($value) {
928 | if (!is_numeric($value)) {
929 | return ":attribute must be numeric.";
930 | }
931 | if ($value % 2 !== 0) {
932 | return ":attribute is not even number.";
933 | }
934 | // you can return true or don't return anything if value is valid
935 | }
936 | ]
937 | ]);
938 | ```
939 |
940 | > Note: `Rakit\Validation\Rules\Callback` instance is binded into your Closure.
941 | So you can access rule properties and methods using `$this`.
942 |
943 |
944 |
945 | nullable
946 |
947 | Field under this rule may be empty.
948 |
949 |
950 |
951 | ## Register/Override Rule
952 |
953 | Another way to use custom validation rule is to create a class extending `Rakit\Validation\Rule`.
954 | Then register it using `setValidator` or `addValidator`.
955 |
956 | For example, you want to create `unique` validator that check field availability from database.
957 |
958 | First, lets create `UniqueRule` class:
959 |
960 | ```php
961 | pdo = $pdo;
976 | }
977 |
978 | public function check($value): bool
979 | {
980 | // make sure required parameters exists
981 | $this->requireParameters(['table', 'column']);
982 |
983 | // getting parameters
984 | $column = $this->parameter('column');
985 | $table = $this->parameter('table');
986 | $except = $this->parameter('except');
987 |
988 | if ($except AND $except == $value) {
989 | return true;
990 | }
991 |
992 | // do query
993 | $stmt = $this->pdo->prepare("select count(*) as count from `{$table}` where `{$column}` = :value");
994 | $stmt->bindParam(':value', $value);
995 | $stmt->execute();
996 | $data = $stmt->fetch(PDO::FETCH_ASSOC);
997 |
998 | // true for valid, false for invalid
999 | return intval($data['count']) === 0;
1000 | }
1001 | }
1002 |
1003 | ```
1004 |
1005 | Then you need to register `UniqueRule` instance into validator like this:
1006 |
1007 | ```php
1008 | use Rakit\Validation\Validator;
1009 |
1010 | $validator = new Validator;
1011 |
1012 | $validator->addValidator('unique', new UniqueRule($pdo));
1013 | ```
1014 |
1015 | Now you can use it like this:
1016 |
1017 | ```php
1018 | $validation = $validator->validate($_POST, [
1019 | 'email' => 'email|unique:users,email,exception@mail.com'
1020 | ]);
1021 | ```
1022 |
1023 | In `UniqueRule` above, property `$message` is used for default invalid message. And property `$fillable_params` is used for `fillParameters` method (defined in `Rakit\Validation\Rule` class). By default `fillParameters` will fill parameters listed in `$fillable_params`. For example `unique:users,email,exception@mail.com` in example above, will set:
1024 |
1025 | ```php
1026 | $params['table'] = 'users';
1027 | $params['column'] = 'email';
1028 | $params['except'] = 'exception@mail.com';
1029 | ```
1030 |
1031 | > If you want your custom rule accept parameter list like `in`,`not_in`, or `uploaded_file` rules,
1032 | you just need to override `fillParameters(array $params)` method in your custom rule class.
1033 |
1034 | Note that `unique` rule that we created above also can be used like this:
1035 |
1036 | ```php
1037 | $validation = $validator->validate($_POST, [
1038 | 'email' => [
1039 | 'required', 'email',
1040 | $validator('unique', 'users', 'email')->message('Custom message')
1041 | ]
1042 | ]);
1043 | ```
1044 |
1045 | So you can improve `UniqueRule` class above by adding some methods that returning its own instance like this:
1046 |
1047 | ```php
1048 | params['table'] = $table;
1059 | return $this;
1060 | }
1061 |
1062 | public function column($column)
1063 | {
1064 | $this->params['column'] = $column;
1065 | return $this;
1066 | }
1067 |
1068 | public function except($value)
1069 | {
1070 | $this->params['except'] = $value;
1071 | return $this;
1072 | }
1073 |
1074 | ...
1075 | }
1076 |
1077 | ```
1078 |
1079 | Then you can use it in more funky way like this:
1080 |
1081 | ```php
1082 | $validation = $validator->validate($_POST, [
1083 | 'email' => [
1084 | 'required', 'email',
1085 | $validator('unique')->table('users')->column('email')->except('exception@mail.com')->message('Custom message')
1086 | ]
1087 | ]);
1088 | ```
1089 |
1090 | #### Implicit Rule
1091 |
1092 | Implicit rule is a rule that if it's invalid, then next rules will be ignored. For example if attribute didn't pass `required*` rules, mostly it's next rules will also be invalids. So to prevent our next rules messages to get collected, we make `required*` rules to be implicit.
1093 |
1094 | To make your custom rule implicit, you can make `$implicit` property value to be `true`. For example:
1095 |
1096 | ```php
1097 | getAttribute(); // Rakit\Validation\Attribute instance
1159 | $validation = $this->validation; // Rakit\Validation\Validation instance
1160 |
1161 | // Do something with $attribute and $validation
1162 | // For example change attribute value
1163 | $validation->setValue($attribute->getKey(), "your custom value");
1164 | }
1165 |
1166 | ...
1167 | }
1168 | ```
1169 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rakit/validation",
3 | "description": "PHP Laravel like standalone validation library",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "email": "emsifa@gmail.com",
8 | "name": "Muhammad Syifa"
9 | }
10 | ],
11 | "autoload": {
12 | "psr-4": {
13 | "Rakit\\Validation\\": "src"
14 | }
15 | },
16 | "autoload-dev": {
17 | "psr-4": {
18 | "Rakit\\Validation\\Tests\\": ["tests", "tests/Fixtures"]
19 | }
20 | },
21 | "require": {
22 | "php": ">=7.0",
23 | "ext-mbstring": "*"
24 | },
25 | "require-dev": {
26 | "phpunit/phpunit": "^6.5",
27 | "squizlabs/php_codesniffer": "^3",
28 | "php-coveralls/php-coveralls": "^2.2"
29 | },
30 | "scripts": {
31 | "test": [
32 | "phpunit",
33 | "phpcs"
34 | ]
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Rakit validation coding standard
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | src
16 | tests
17 |
--------------------------------------------------------------------------------
/src/Attribute.php:
--------------------------------------------------------------------------------
1 | validation = $validation;
48 | $this->alias = $alias;
49 | $this->key = $key;
50 | foreach ($rules as $rule) {
51 | $this->addRule($rule);
52 | }
53 | }
54 |
55 | /**
56 | * Set the primary attribute
57 | *
58 | * @param \Rakit\Validation\Attribute $primaryAttribute
59 | * @return void
60 | */
61 | public function setPrimaryAttribute(Attribute $primaryAttribute)
62 | {
63 | $this->primaryAttribute = $primaryAttribute;
64 | }
65 |
66 | /**
67 | * Set key indexes
68 | *
69 | * @param array $keyIndexes
70 | * @return void
71 | */
72 | public function setKeyIndexes(array $keyIndexes)
73 | {
74 | $this->keyIndexes = $keyIndexes;
75 | }
76 |
77 | /**
78 | * Get primary attributes
79 | *
80 | * @return \Rakit\Validation\Attribute|null
81 | */
82 | public function getPrimaryAttribute()
83 | {
84 | return $this->primaryAttribute;
85 | }
86 |
87 | /**
88 | * Set other attributes
89 | *
90 | * @param array $otherAttributes
91 | * @return void
92 | */
93 | public function setOtherAttributes(array $otherAttributes)
94 | {
95 | $this->otherAttributes = [];
96 | foreach ($otherAttributes as $otherAttribute) {
97 | $this->addOtherAttribute($otherAttribute);
98 | }
99 | }
100 |
101 | /**
102 | * Add other attributes
103 | *
104 | * @param \Rakit\Validation\Attribute $otherAttribute
105 | * @return void
106 | */
107 | public function addOtherAttribute(Attribute $otherAttribute)
108 | {
109 | $this->otherAttributes[] = $otherAttribute;
110 | }
111 |
112 | /**
113 | * Get other attributes
114 | *
115 | * @return array
116 | */
117 | public function getOtherAttributes(): array
118 | {
119 | return $this->otherAttributes;
120 | }
121 |
122 | /**
123 | * Add rule
124 | *
125 | * @param \Rakit\Validation\Rule $rule
126 | * @return void
127 | */
128 | public function addRule(Rule $rule)
129 | {
130 | $rule->setAttribute($this);
131 | $rule->setValidation($this->validation);
132 | $this->rules[$rule->getKey()] = $rule;
133 | }
134 |
135 | /**
136 | * Get rule
137 | *
138 | * @param string $ruleKey
139 | * @return void
140 | */
141 | public function getRule(string $ruleKey)
142 | {
143 | return $this->hasRule($ruleKey)? $this->rules[$ruleKey] : null;
144 | }
145 |
146 | /**
147 | * Get rules
148 | *
149 | * @return array
150 | */
151 | public function getRules(): array
152 | {
153 | return $this->rules;
154 | }
155 |
156 | /**
157 | * Check the $ruleKey has in the rule
158 | *
159 | * @param string $ruleKey
160 | * @return bool
161 | */
162 | public function hasRule(string $ruleKey): bool
163 | {
164 | return isset($this->rules[$ruleKey]);
165 | }
166 |
167 | /**
168 | * Set required
169 | *
170 | * @param boolean $required
171 | * @return void
172 | */
173 | public function setRequired(bool $required)
174 | {
175 | $this->required = $required;
176 | }
177 |
178 | /**
179 | * Set rule is required
180 | *
181 | * @return boolean
182 | */
183 | public function isRequired(): bool
184 | {
185 | return $this->required;
186 | }
187 |
188 | /**
189 | * Get key
190 | *
191 | * @return string
192 | */
193 | public function getKey(): string
194 | {
195 | return $this->key;
196 | }
197 |
198 | /**
199 | * Get key indexes
200 | *
201 | * @return array
202 | */
203 | public function getKeyIndexes(): array
204 | {
205 | return $this->keyIndexes;
206 | }
207 |
208 | /**
209 | * Get value
210 | *
211 | * @param string|null $key
212 | * @return mixed
213 | */
214 | public function getValue(string $key = null)
215 | {
216 | if ($key && $this->isArrayAttribute()) {
217 | $key = $this->resolveSiblingKey($key);
218 | }
219 |
220 | if (!$key) {
221 | $key = $this->getKey();
222 | }
223 |
224 | return $this->validation->getValue($key);
225 | }
226 |
227 | /**
228 | * Get that is array attribute
229 | *
230 | * @return boolean
231 | */
232 | public function isArrayAttribute(): bool
233 | {
234 | return count($this->getKeyIndexes()) > 0;
235 | }
236 |
237 | /**
238 | * Check this attribute is using dot notation
239 | *
240 | * @return boolean
241 | */
242 | public function isUsingDotNotation(): bool
243 | {
244 | return strpos($this->getKey(), '.') !== false;
245 | }
246 |
247 | /**
248 | * Resolve sibling key
249 | *
250 | * @param string $key
251 | * @return string
252 | */
253 | public function resolveSiblingKey(string $key): string
254 | {
255 | $indexes = $this->getKeyIndexes();
256 | $keys = explode("*", $key);
257 | $countAsterisks = count($keys) - 1;
258 | if (count($indexes) < $countAsterisks) {
259 | $indexes = array_merge($indexes, array_fill(0, $countAsterisks - count($indexes), "*"));
260 | }
261 | $args = array_merge([str_replace("*", "%s", $key)], $indexes);
262 | return call_user_func_array('sprintf', $args);
263 | }
264 |
265 | /**
266 | * Get humanize key
267 | *
268 | * @return string
269 | */
270 | public function getHumanizedKey()
271 | {
272 | $primaryAttribute = $this->getPrimaryAttribute();
273 | $key = str_replace('_', ' ', $this->key);
274 |
275 | // Resolve key from array validation
276 | if ($primaryAttribute) {
277 | $split = explode('.', $key);
278 | $key = implode(' ', array_map(function ($word) {
279 | if (is_numeric($word)) {
280 | $word = $word + 1;
281 | }
282 | return Helper::snakeCase($word, ' ');
283 | }, $split));
284 | }
285 |
286 | return ucfirst($key);
287 | }
288 |
289 | /**
290 | * Set alias
291 | *
292 | * @param string $alias
293 | * @return void
294 | */
295 | public function setAlias(string $alias)
296 | {
297 | $this->alias = $alias;
298 | }
299 |
300 | /**
301 | * Get alias
302 | *
303 | * @return string|null
304 | */
305 | public function getAlias()
306 | {
307 | return $this->alias;
308 | }
309 | }
310 |
--------------------------------------------------------------------------------
/src/ErrorBag.php:
--------------------------------------------------------------------------------
1 | messages = $messages;
20 | }
21 |
22 | /**
23 | * Add message for given key and rule
24 | *
25 | * @param string $key
26 | * @param string $rule
27 | * @param string $message
28 | * @return void
29 | */
30 | public function add(string $key, string $rule, string $message)
31 | {
32 | if (!isset($this->messages[$key])) {
33 | $this->messages[$key] = [];
34 | }
35 |
36 | $this->messages[$key][$rule] = $message;
37 | }
38 |
39 | /**
40 | * Get messages count
41 | *
42 | * @return int
43 | */
44 | public function count(): int
45 | {
46 | return count($this->all());
47 | }
48 |
49 | /**
50 | * Check given key is existed
51 | *
52 | * @param string $key
53 | * @return bool
54 | */
55 | public function has(string $key): bool
56 | {
57 | list($key, $ruleName) = $this->parsekey($key);
58 | if ($this->isWildcardKey($key)) {
59 | $messages = $this->filterMessagesForWildcardKey($key, $ruleName);
60 | return count(Helper::arrayDot($messages)) > 0;
61 | } else {
62 | $messages = isset($this->messages[$key])? $this->messages[$key] : null;
63 |
64 | if (!$ruleName) {
65 | return !empty($messages);
66 | } else {
67 | return !empty($messages) and isset($messages[$ruleName]);
68 | }
69 | }
70 | }
71 |
72 | /**
73 | * Get the first value of array
74 | *
75 | * @param string $key
76 | * @return mixed
77 | */
78 | public function first(string $key)
79 | {
80 | list($key, $ruleName) = $this->parsekey($key);
81 | if ($this->isWildcardKey($key)) {
82 | $messages = $this->filterMessagesForWildcardKey($key, $ruleName);
83 | $flattenMessages = Helper::arrayDot($messages);
84 | return array_shift($flattenMessages);
85 | } else {
86 | $keyMessages = isset($this->messages[$key])? $this->messages[$key] : [];
87 |
88 | if (empty($keyMessages)) {
89 | return null;
90 | }
91 |
92 | if ($ruleName) {
93 | return isset($keyMessages[$ruleName])? $keyMessages[$ruleName] : null;
94 | } else {
95 | return array_shift($keyMessages);
96 | }
97 | }
98 | }
99 |
100 | /**
101 | * Get messages from given key, can be use custom format
102 | *
103 | * @param string $key
104 | * @param string $format
105 | * @return array
106 | */
107 | public function get(string $key, string $format = ':message'): array
108 | {
109 | list($key, $ruleName) = $this->parsekey($key);
110 | $results = [];
111 | if ($this->isWildcardKey($key)) {
112 | $messages = $this->filterMessagesForWildcardKey($key, $ruleName);
113 | foreach ($messages as $explicitKey => $keyMessages) {
114 | foreach ($keyMessages as $rule => $message) {
115 | $results[$explicitKey][$rule] = $this->formatMessage($message, $format);
116 | }
117 | }
118 | } else {
119 | $keyMessages = isset($this->messages[$key])? $this->messages[$key] : [];
120 | foreach ($keyMessages as $rule => $message) {
121 | if ($ruleName and $ruleName != $rule) {
122 | continue;
123 | }
124 | $results[$rule] = $this->formatMessage($message, $format);
125 | }
126 | }
127 |
128 | return $results;
129 | }
130 |
131 | /**
132 | * Get all messages
133 | *
134 | * @param string $format
135 | * @return array
136 | */
137 | public function all(string $format = ':message'): array
138 | {
139 | $messages = $this->messages;
140 | $results = [];
141 | foreach ($messages as $key => $keyMessages) {
142 | foreach ($keyMessages as $message) {
143 | $results[] = $this->formatMessage($message, $format);
144 | }
145 | }
146 | return $results;
147 | }
148 |
149 | /**
150 | * Get the first message from existing keys
151 | *
152 | * @param string $format
153 | * @param boolean $dotNotation
154 | * @return array
155 | */
156 | public function firstOfAll(string $format = ':message', bool $dotNotation = false): array
157 | {
158 | $messages = $this->messages;
159 | $results = [];
160 | foreach ($messages as $key => $keyMessages) {
161 | if ($dotNotation) {
162 | $results[$key] = $this->formatMessage(array_shift($messages[$key]), $format);
163 | } else {
164 | Helper::arraySet($results, $key, $this->formatMessage(array_shift($messages[$key]), $format));
165 | }
166 | }
167 | return $results;
168 | }
169 |
170 | /**
171 | * Get plain array messages
172 | *
173 | * @return array
174 | */
175 | public function toArray(): array
176 | {
177 | return $this->messages;
178 | }
179 |
180 | /**
181 | * Parse $key to get the array of $key and $ruleName
182 | *
183 | * @param string $key
184 | * @return array
185 | */
186 | protected function parseKey(string $key): array
187 | {
188 | $expl = explode(':', $key, 2);
189 | $key = $expl[0];
190 | $ruleName = isset($expl[1])? $expl[1] : null;
191 | return [$key, $ruleName];
192 | }
193 |
194 | /**
195 | * Check the $key is wildcard
196 | *
197 | * @param mixed $key
198 | * @return bool
199 | */
200 | protected function isWildcardKey(string $key): bool
201 | {
202 | return false !== strpos($key, '*');
203 | }
204 |
205 | /**
206 | * Filter messages with wildcard key
207 | *
208 | * @param string $key
209 | * @param mixed $ruleName
210 | * @return array
211 | */
212 | protected function filterMessagesForWildcardKey(string $key, $ruleName = null): array
213 | {
214 | $messages = $this->messages;
215 | $pattern = preg_quote($key, '#');
216 | $pattern = str_replace('\*', '.*', $pattern);
217 |
218 | $filteredMessages = [];
219 |
220 | foreach ($messages as $k => $keyMessages) {
221 | if ((bool) preg_match('#^'.$pattern.'\z#u', $k) === false) {
222 | continue;
223 | }
224 |
225 | foreach ($keyMessages as $rule => $message) {
226 | if ($ruleName and $rule != $ruleName) {
227 | continue;
228 | }
229 | $filteredMessages[$k][$rule] = $message;
230 | }
231 | }
232 |
233 | return $filteredMessages;
234 | }
235 |
236 | /**
237 | * Get formatted message
238 | *
239 | * @param string $message
240 | * @param string $format
241 | * @return string
242 | */
243 | protected function formatMessage(string $message, string $format): string
244 | {
245 | return str_replace(':message', $message, $format);
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/src/Helper.php:
--------------------------------------------------------------------------------
1 | $value) {
100 | if (is_array($value) && ! empty($value)) {
101 | $results = array_merge($results, static::arrayDot($value, $prepend.$key.'.'));
102 | } else {
103 | $results[$prepend.$key] = $value;
104 | }
105 | }
106 |
107 | return $results;
108 | }
109 |
110 | /**
111 | * Set an item on an array or object using dot notation.
112 | * Adapted from: https://github.com/illuminate/support/blob/v5.3.23/helpers.php#L437
113 | *
114 | * @param mixed $target
115 | * @param string|array|null $key
116 | * @param mixed $value
117 | * @param bool $overwrite
118 | * @return mixed
119 | */
120 | public static function arraySet(&$target, $key, $value, $overwrite = true): array
121 | {
122 | if (is_null($key)) {
123 | if ($overwrite) {
124 | return $target = array_merge($target, $value);
125 | }
126 | return $target = array_merge($value, $target);
127 | }
128 |
129 | $segments = is_array($key) ? $key : explode('.', $key);
130 |
131 | if (($segment = array_shift($segments)) === '*') {
132 | if (! is_array($target)) {
133 | $target = [];
134 | }
135 |
136 | if ($segments) {
137 | foreach ($target as &$inner) {
138 | static::arraySet($inner, $segments, $value, $overwrite);
139 | }
140 | } elseif ($overwrite) {
141 | foreach ($target as &$inner) {
142 | $inner = $value;
143 | }
144 | }
145 | } elseif (is_array($target)) {
146 | if ($segments) {
147 | if (! array_key_exists($segment, $target)) {
148 | $target[$segment] = [];
149 | }
150 |
151 | static::arraySet($target[$segment], $segments, $value, $overwrite);
152 | } elseif ($overwrite || ! array_key_exists($segment, $target)) {
153 | $target[$segment] = $value;
154 | }
155 | } else {
156 | $target = [];
157 |
158 | if ($segments) {
159 | static::arraySet($target[$segment], $segments, $value, $overwrite);
160 | } elseif ($overwrite) {
161 | $target[$segment] = $value;
162 | }
163 | }
164 |
165 | return $target;
166 | }
167 |
168 | /**
169 | * Unset an item on an array or object using dot notation.
170 | *
171 | * @param mixed $target
172 | * @param string|array $key
173 | * @return mixed
174 | */
175 | public static function arrayUnset(&$target, $key)
176 | {
177 | if (!is_array($target)) {
178 | return $target;
179 | }
180 |
181 | $segments = is_array($key) ? $key : explode('.', $key);
182 | $segment = array_shift($segments);
183 |
184 | if ($segment == '*') {
185 | $target = [];
186 | } elseif ($segments) {
187 | if (array_key_exists($segment, $target)) {
188 | static::arrayUnset($target[$segment], $segments);
189 | }
190 | } elseif (array_key_exists($segment, $target)) {
191 | unset($target[$segment]);
192 | }
193 |
194 | return $target;
195 | }
196 |
197 | /**
198 | * Get snake_case format from given string
199 | *
200 | * @param string $value
201 | * @param string $delimiter
202 | * @return string
203 | */
204 | public static function snakeCase(string $value, string $delimiter = '_'): string
205 | {
206 | if (! ctype_lower($value)) {
207 | $value = preg_replace('/\s+/u', '', ucwords($value));
208 | $value = strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1'.$delimiter, $value));
209 | }
210 |
211 | return $value;
212 | }
213 |
214 | /**
215 | * Join string[] to string with given $separator and $lastSeparator.
216 | *
217 | * @param array $pieces
218 | * @param string $separator
219 | * @param string|null $lastSeparator
220 | * @return string
221 | */
222 | public static function join(array $pieces, string $separator, string $lastSeparator = null): string
223 | {
224 | if (is_null($lastSeparator)) {
225 | $lastSeparator = $separator;
226 | }
227 |
228 | $last = array_pop($pieces);
229 |
230 | switch (count($pieces)) {
231 | case 0:
232 | return $last ?: '';
233 | case 1:
234 | return $pieces[0] . $lastSeparator . $last;
235 | default:
236 | return implode($separator, $pieces) . $lastSeparator . $last;
237 | }
238 | }
239 |
240 | /**
241 | * Wrap string[] by given $prefix and $suffix
242 | *
243 | * @param array $strings
244 | * @param string $prefix
245 | * @param string|null $suffix
246 | * @return array
247 | */
248 | public static function wraps(array $strings, string $prefix, string $suffix = null): array
249 | {
250 | if (is_null($suffix)) {
251 | $suffix = $prefix;
252 | }
253 |
254 | return array_map(function ($str) use ($prefix, $suffix) {
255 | return $prefix . $str . $suffix;
256 | }, $strings);
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/src/MimeTypeGuesser.php:
--------------------------------------------------------------------------------
1 | 'ez',
11 | 'application/applixware' => 'aw',
12 | 'application/atom+xml' => 'atom',
13 | 'application/atomcat+xml' => 'atomcat',
14 | 'application/atomsvc+xml' => 'atomsvc',
15 | 'application/ccxml+xml' => 'ccxml',
16 | 'application/cdmi-capability' => 'cdmia',
17 | 'application/cdmi-container' => 'cdmic',
18 | 'application/cdmi-domain' => 'cdmid',
19 | 'application/cdmi-object' => 'cdmio',
20 | 'application/cdmi-queue' => 'cdmiq',
21 | 'application/cu-seeme' => 'cu',
22 | 'application/davmount+xml' => 'davmount',
23 | 'application/docbook+xml' => 'dbk',
24 | 'application/dssc+der' => 'dssc',
25 | 'application/dssc+xml' => 'xdssc',
26 | 'application/ecmascript' => 'ecma',
27 | 'application/emma+xml' => 'emma',
28 | 'application/epub+zip' => 'epub',
29 | 'application/exi' => 'exi',
30 | 'application/font-tdpfr' => 'pfr',
31 | 'application/gml+xml' => 'gml',
32 | 'application/gpx+xml' => 'gpx',
33 | 'application/gxf' => 'gxf',
34 | 'application/hyperstudio' => 'stk',
35 | 'application/inkml+xml' => 'ink',
36 | 'application/ipfix' => 'ipfix',
37 | 'application/java-archive' => 'jar',
38 | 'application/java-serialized-object' => 'ser',
39 | 'application/java-vm' => 'class',
40 | 'application/javascript' => 'js',
41 | 'application/json' => 'json',
42 | 'application/jsonml+json' => 'jsonml',
43 | 'application/lost+xml' => 'lostxml',
44 | 'application/mac-binhex40' => 'hqx',
45 | 'application/mac-compactpro' => 'cpt',
46 | 'application/mads+xml' => 'mads',
47 | 'application/marc' => 'mrc',
48 | 'application/marcxml+xml' => 'mrcx',
49 | 'application/mathematica' => 'ma',
50 | 'application/mathml+xml' => 'mathml',
51 | 'application/mbox' => 'mbox',
52 | 'application/mediaservercontrol+xml' => 'mscml',
53 | 'application/metalink+xml' => 'metalink',
54 | 'application/metalink4+xml' => 'meta4',
55 | 'application/mets+xml' => 'mets',
56 | 'application/mods+xml' => 'mods',
57 | 'application/mp21' => 'm21',
58 | 'application/mp4' => 'mp4s',
59 | 'application/msword' => 'doc',
60 | 'application/mxf' => 'mxf',
61 | 'application/octet-stream' => 'bin',
62 | 'application/oda' => 'oda',
63 | 'application/oebps-package+xml' => 'opf',
64 | 'application/ogg' => 'ogx',
65 | 'application/omdoc+xml' => 'omdoc',
66 | 'application/onenote' => 'onetoc',
67 | 'application/oxps' => 'oxps',
68 | 'application/patch-ops-error+xml' => 'xer',
69 | 'application/pdf' => 'pdf',
70 | 'application/pgp-encrypted' => 'pgp',
71 | 'application/pgp-signature' => 'asc',
72 | 'application/pics-rules' => 'prf',
73 | 'application/pkcs10' => 'p10',
74 | 'application/pkcs7-mime' => 'p7m',
75 | 'application/pkcs7-signature' => 'p7s',
76 | 'application/pkcs8' => 'p8',
77 | 'application/pkix-attr-cert' => 'ac',
78 | 'application/pkix-cert' => 'cer',
79 | 'application/pkix-crl' => 'crl',
80 | 'application/pkix-pkipath' => 'pkipath',
81 | 'application/pkixcmp' => 'pki',
82 | 'application/pls+xml' => 'pls',
83 | 'application/postscript' => 'ai',
84 | 'application/prs.cww' => 'cww',
85 | 'application/pskc+xml' => 'pskcxml',
86 | 'application/rdf+xml' => 'rdf',
87 | 'application/reginfo+xml' => 'rif',
88 | 'application/relax-ng-compact-syntax' => 'rnc',
89 | 'application/resource-lists+xml' => 'rl',
90 | 'application/resource-lists-diff+xml' => 'rld',
91 | 'application/rls-services+xml' => 'rs',
92 | 'application/rpki-ghostbusters' => 'gbr',
93 | 'application/rpki-manifest' => 'mft',
94 | 'application/rpki-roa' => 'roa',
95 | 'application/rsd+xml' => 'rsd',
96 | 'application/rss+xml' => 'rss',
97 | 'application/rtf' => 'rtf',
98 | 'application/sbml+xml' => 'sbml',
99 | 'application/scvp-cv-request' => 'scq',
100 | 'application/scvp-cv-response' => 'scs',
101 | 'application/scvp-vp-request' => 'spq',
102 | 'application/scvp-vp-response' => 'spp',
103 | 'application/sdp' => 'sdp',
104 | 'application/set-payment-initiation' => 'setpay',
105 | 'application/set-registration-initiation' => 'setreg',
106 | 'application/shf+xml' => 'shf',
107 | 'application/smil+xml' => 'smi',
108 | 'application/sparql-query' => 'rq',
109 | 'application/sparql-results+xml' => 'srx',
110 | 'application/srgs' => 'gram',
111 | 'application/srgs+xml' => 'grxml',
112 | 'application/sru+xml' => 'sru',
113 | 'application/ssdl+xml' => 'ssdl',
114 | 'application/ssml+xml' => 'ssml',
115 | 'application/tei+xml' => 'tei',
116 | 'application/thraud+xml' => 'tfi',
117 | 'application/timestamped-data' => 'tsd',
118 | 'application/vnd.3gpp.pic-bw-large' => 'plb',
119 | 'application/vnd.3gpp.pic-bw-small' => 'psb',
120 | 'application/vnd.3gpp.pic-bw-var' => 'pvb',
121 | 'application/vnd.3gpp2.tcap' => 'tcap',
122 | 'application/vnd.3m.post-it-notes' => 'pwn',
123 | 'application/vnd.accpac.simply.aso' => 'aso',
124 | 'application/vnd.accpac.simply.imp' => 'imp',
125 | 'application/vnd.acucobol' => 'acu',
126 | 'application/vnd.acucorp' => 'atc',
127 | 'application/vnd.adobe.air-application-installer-package+zip' => 'air',
128 | 'application/vnd.adobe.formscentral.fcdt' => 'fcdt',
129 | 'application/vnd.adobe.fxp' => 'fxp',
130 | 'application/vnd.adobe.xdp+xml' => 'xdp',
131 | 'application/vnd.adobe.xfdf' => 'xfdf',
132 | 'application/vnd.ahead.space' => 'ahead',
133 | 'application/vnd.airzip.filesecure.azf' => 'azf',
134 | 'application/vnd.airzip.filesecure.azs' => 'azs',
135 | 'application/vnd.amazon.ebook' => 'azw',
136 | 'application/vnd.americandynamics.acc' => 'acc',
137 | 'application/vnd.amiga.ami' => 'ami',
138 | 'application/vnd.android.package-archive' => 'apk',
139 | 'application/vnd.anser-web-certificate-issue-initiation' => 'cii',
140 | 'application/vnd.anser-web-funds-transfer-initiation' => 'fti',
141 | 'application/vnd.antix.game-component' => 'atx',
142 | 'application/vnd.apple.installer+xml' => 'mpkg',
143 | 'application/vnd.apple.mpegurl' => 'm3u8',
144 | 'application/vnd.aristanetworks.swi' => 'swi',
145 | 'application/vnd.astraea-software.iota' => 'iota',
146 | 'application/vnd.audiograph' => 'aep',
147 | 'application/vnd.blueice.multipass' => 'mpm',
148 | 'application/vnd.bmi' => 'bmi',
149 | 'application/vnd.businessobjects' => 'rep',
150 | 'application/vnd.chemdraw+xml' => 'cdxml',
151 | 'application/vnd.chipnuts.karaoke-mmd' => 'mmd',
152 | 'application/vnd.cinderella' => 'cdy',
153 | 'application/vnd.claymore' => 'cla',
154 | 'application/vnd.cloanto.rp9' => 'rp9',
155 | 'application/vnd.clonk.c4group' => 'c4g',
156 | 'application/vnd.cluetrust.cartomobile-config' => 'c11amc',
157 | 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz',
158 | 'application/vnd.commonspace' => 'csp',
159 | 'application/vnd.contact.cmsg' => 'cdbcmsg',
160 | 'application/vnd.cosmocaller' => 'cmc',
161 | 'application/vnd.crick.clicker' => 'clkx',
162 | 'application/vnd.crick.clicker.keyboard' => 'clkk',
163 | 'application/vnd.crick.clicker.palette' => 'clkp',
164 | 'application/vnd.crick.clicker.template' => 'clkt',
165 | 'application/vnd.crick.clicker.wordbank' => 'clkw',
166 | 'application/vnd.criticaltools.wbs+xml' => 'wbs',
167 | 'application/vnd.ctc-posml' => 'pml',
168 | 'application/vnd.cups-ppd' => 'ppd',
169 | 'application/vnd.curl.car' => 'car',
170 | 'application/vnd.curl.pcurl' => 'pcurl',
171 | 'application/vnd.dart' => 'dart',
172 | 'application/vnd.data-vision.rdz' => 'rdz',
173 | 'application/vnd.dece.data' => 'uvf',
174 | 'application/vnd.dece.ttml+xml' => 'uvt',
175 | 'application/vnd.dece.unspecified' => 'uvx',
176 | 'application/vnd.dece.zip' => 'uvz',
177 | 'application/vnd.denovo.fcselayout-link' => 'fe_launch',
178 | 'application/vnd.dna' => 'dna',
179 | 'application/vnd.dolby.mlp' => 'mlp',
180 | 'application/vnd.dpgraph' => 'dpg',
181 | 'application/vnd.dreamfactory' => 'dfac',
182 | 'application/vnd.ds-keypoint' => 'kpxx',
183 | 'application/vnd.dvb.ait' => 'ait',
184 | 'application/vnd.dvb.service' => 'svc',
185 | 'application/vnd.dynageo' => 'geo',
186 | 'application/vnd.ecowin.chart' => 'mag',
187 | 'application/vnd.enliven' => 'nml',
188 | 'application/vnd.epson.esf' => 'esf',
189 | 'application/vnd.epson.msf' => 'msf',
190 | 'application/vnd.epson.quickanime' => 'qam',
191 | 'application/vnd.epson.salt' => 'slt',
192 | 'application/vnd.epson.ssf' => 'ssf',
193 | 'application/vnd.eszigno3+xml' => 'es3',
194 | 'application/vnd.ezpix-album' => 'ez2',
195 | 'application/vnd.ezpix-package' => 'ez3',
196 | 'application/vnd.fdf' => 'fdf',
197 | 'application/vnd.fdsn.mseed' => 'mseed',
198 | 'application/vnd.fdsn.seed' => 'seed',
199 | 'application/vnd.flographit' => 'gph',
200 | 'application/vnd.fluxtime.clip' => 'ftc',
201 | 'application/vnd.framemaker' => 'fm',
202 | 'application/vnd.frogans.fnc' => 'fnc',
203 | 'application/vnd.frogans.ltf' => 'ltf',
204 | 'application/vnd.fsc.weblaunch' => 'fsc',
205 | 'application/vnd.fujitsu.oasys' => 'oas',
206 | 'application/vnd.fujitsu.oasys2' => 'oa2',
207 | 'application/vnd.fujitsu.oasys3' => 'oa3',
208 | 'application/vnd.fujitsu.oasysgp' => 'fg5',
209 | 'application/vnd.fujitsu.oasysprs' => 'bh2',
210 | 'application/vnd.fujixerox.ddd' => 'ddd',
211 | 'application/vnd.fujixerox.docuworks' => 'xdw',
212 | 'application/vnd.fujixerox.docuworks.binder' => 'xbd',
213 | 'application/vnd.fuzzysheet' => 'fzs',
214 | 'application/vnd.genomatix.tuxedo' => 'txd',
215 | 'application/vnd.geogebra.file' => 'ggb',
216 | 'application/vnd.geogebra.tool' => 'ggt',
217 | 'application/vnd.geometry-explorer' => 'gex',
218 | 'application/vnd.geonext' => 'gxt',
219 | 'application/vnd.geoplan' => 'g2w',
220 | 'application/vnd.geospace' => 'g3w',
221 | 'application/vnd.gmx' => 'gmx',
222 | 'application/vnd.google-earth.kml+xml' => 'kml',
223 | 'application/vnd.google-earth.kmz' => 'kmz',
224 | 'application/vnd.grafeq' => 'gqf',
225 | 'application/vnd.groove-account' => 'gac',
226 | 'application/vnd.groove-help' => 'ghf',
227 | 'application/vnd.groove-identity-message' => 'gim',
228 | 'application/vnd.groove-injector' => 'grv',
229 | 'application/vnd.groove-tool-message' => 'gtm',
230 | 'application/vnd.groove-tool-template' => 'tpl',
231 | 'application/vnd.groove-vcard' => 'vcg',
232 | 'application/vnd.hal+xml' => 'hal',
233 | 'application/vnd.handheld-entertainment+xml' => 'zmm',
234 | 'application/vnd.hbci' => 'hbci',
235 | 'application/vnd.hhe.lesson-player' => 'les',
236 | 'application/vnd.hp-hpgl' => 'hpgl',
237 | 'application/vnd.hp-hpid' => 'hpid',
238 | 'application/vnd.hp-hps' => 'hps',
239 | 'application/vnd.hp-jlyt' => 'jlt',
240 | 'application/vnd.hp-pcl' => 'pcl',
241 | 'application/vnd.hp-pclxl' => 'pclxl',
242 | 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx',
243 | 'application/vnd.ibm.minipay' => 'mpy',
244 | 'application/vnd.ibm.modcap' => 'afp',
245 | 'application/vnd.ibm.rights-management' => 'irm',
246 | 'application/vnd.ibm.secure-container' => 'sc',
247 | 'application/vnd.iccprofile' => 'icc',
248 | 'application/vnd.igloader' => 'igl',
249 | 'application/vnd.immervision-ivp' => 'ivp',
250 | 'application/vnd.immervision-ivu' => 'ivu',
251 | 'application/vnd.insors.igm' => 'igm',
252 | 'application/vnd.intercon.formnet' => 'xpw',
253 | 'application/vnd.intergeo' => 'i2g',
254 | 'application/vnd.intu.qbo' => 'qbo',
255 | 'application/vnd.intu.qfx' => 'qfx',
256 | 'application/vnd.ipunplugged.rcprofile' => 'rcprofile',
257 | 'application/vnd.irepository.package+xml' => 'irp',
258 | 'application/vnd.is-xpr' => 'xpr',
259 | 'application/vnd.isac.fcs' => 'fcs',
260 | 'application/vnd.jam' => 'jam',
261 | 'application/vnd.jcp.javame.midlet-rms' => 'rms',
262 | 'application/vnd.jisp' => 'jisp',
263 | 'application/vnd.joost.joda-archive' => 'joda',
264 | 'application/vnd.kahootz' => 'ktz',
265 | 'application/vnd.kde.karbon' => 'karbon',
266 | 'application/vnd.kde.kchart' => 'chrt',
267 | 'application/vnd.kde.kformula' => 'kfo',
268 | 'application/vnd.kde.kivio' => 'flw',
269 | 'application/vnd.kde.kontour' => 'kon',
270 | 'application/vnd.kde.kpresenter' => 'kpr',
271 | 'application/vnd.kde.kspread' => 'ksp',
272 | 'application/vnd.kde.kword' => 'kwd',
273 | 'application/vnd.kenameaapp' => 'htke',
274 | 'application/vnd.kidspiration' => 'kia',
275 | 'application/vnd.kinar' => 'kne',
276 | 'application/vnd.koan' => 'skp',
277 | 'application/vnd.kodak-descriptor' => 'sse',
278 | 'application/vnd.las.las+xml' => 'lasxml',
279 | 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd',
280 | 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe',
281 | 'application/vnd.lotus-1-2-3' => '123',
282 | 'application/vnd.lotus-approach' => 'apr',
283 | 'application/vnd.lotus-freelance' => 'pre',
284 | 'application/vnd.lotus-notes' => 'nsf',
285 | 'application/vnd.lotus-organizer' => 'org',
286 | 'application/vnd.lotus-screencam' => 'scm',
287 | 'application/vnd.lotus-wordpro' => 'lwp',
288 | 'application/vnd.macports.portpkg' => 'portpkg',
289 | 'application/vnd.mcd' => 'mcd',
290 | 'application/vnd.medcalcdata' => 'mc1',
291 | 'application/vnd.mediastation.cdkey' => 'cdkey',
292 | 'application/vnd.mfer' => 'mwf',
293 | 'application/vnd.mfmp' => 'mfm',
294 | 'application/vnd.micrografx.flo' => 'flo',
295 | 'application/vnd.micrografx.igx' => 'igx',
296 | 'application/vnd.mif' => 'mif',
297 | 'application/vnd.mobius.daf' => 'daf',
298 | 'application/vnd.mobius.dis' => 'dis',
299 | 'application/vnd.mobius.mbk' => 'mbk',
300 | 'application/vnd.mobius.mqy' => 'mqy',
301 | 'application/vnd.mobius.msl' => 'msl',
302 | 'application/vnd.mobius.plc' => 'plc',
303 | 'application/vnd.mobius.txf' => 'txf',
304 | 'application/vnd.mophun.application' => 'mpn',
305 | 'application/vnd.mophun.certificate' => 'mpc',
306 | 'application/vnd.mozilla.xul+xml' => 'xul',
307 | 'application/vnd.ms-artgalry' => 'cil',
308 | 'application/vnd.ms-cab-compressed' => 'cab',
309 | 'application/vnd.ms-excel' => 'xls',
310 | 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam',
311 | 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb',
312 | 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm',
313 | 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm',
314 | 'application/vnd.ms-fontobject' => 'eot',
315 | 'application/vnd.ms-htmlhelp' => 'chm',
316 | 'application/vnd.ms-ims' => 'ims',
317 | 'application/vnd.ms-lrm' => 'lrm',
318 | 'application/vnd.ms-officetheme' => 'thmx',
319 | 'application/vnd.ms-pki.seccat' => 'cat',
320 | 'application/vnd.ms-pki.stl' => 'stl',
321 | 'application/vnd.ms-powerpoint' => 'ppt',
322 | 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam',
323 | 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm',
324 | 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm',
325 | 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm',
326 | 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm',
327 | 'application/vnd.ms-project' => 'mpp',
328 | 'application/vnd.ms-word.document.macroenabled.12' => 'docm',
329 | 'application/vnd.ms-word.template.macroenabled.12' => 'dotm',
330 | 'application/vnd.ms-works' => 'wps',
331 | 'application/vnd.ms-wpl' => 'wpl',
332 | 'application/vnd.ms-xpsdocument' => 'xps',
333 | 'application/vnd.mseq' => 'mseq',
334 | 'application/vnd.musician' => 'mus',
335 | 'application/vnd.muvee.style' => 'msty',
336 | 'application/vnd.mynfc' => 'taglet',
337 | 'application/vnd.neurolanguage.nlu' => 'nlu',
338 | 'application/vnd.nitf' => 'ntf',
339 | 'application/vnd.noblenet-directory' => 'nnd',
340 | 'application/vnd.noblenet-sealer' => 'nns',
341 | 'application/vnd.noblenet-web' => 'nnw',
342 | 'application/vnd.nokia.n-gage.data' => 'ngdat',
343 | 'application/vnd.nokia.n-gage.symbian.install' => 'n-gage',
344 | 'application/vnd.nokia.radio-preset' => 'rpst',
345 | 'application/vnd.nokia.radio-presets' => 'rpss',
346 | 'application/vnd.novadigm.edm' => 'edm',
347 | 'application/vnd.novadigm.edx' => 'edx',
348 | 'application/vnd.novadigm.ext' => 'ext',
349 | 'application/vnd.oasis.opendocument.chart' => 'odc',
350 | 'application/vnd.oasis.opendocument.chart-template' => 'otc',
351 | 'application/vnd.oasis.opendocument.database' => 'odb',
352 | 'application/vnd.oasis.opendocument.formula' => 'odf',
353 | 'application/vnd.oasis.opendocument.formula-template' => 'odft',
354 | 'application/vnd.oasis.opendocument.graphics' => 'odg',
355 | 'application/vnd.oasis.opendocument.graphics-template' => 'otg',
356 | 'application/vnd.oasis.opendocument.image' => 'odi',
357 | 'application/vnd.oasis.opendocument.image-template' => 'oti',
358 | 'application/vnd.oasis.opendocument.presentation' => 'odp',
359 | 'application/vnd.oasis.opendocument.presentation-template' => 'otp',
360 | 'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
361 | 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots',
362 | 'application/vnd.oasis.opendocument.text' => 'odt',
363 | 'application/vnd.oasis.opendocument.text-master' => 'odm',
364 | 'application/vnd.oasis.opendocument.text-template' => 'ott',
365 | 'application/vnd.oasis.opendocument.text-web' => 'oth',
366 | 'application/vnd.olpc-sugar' => 'xo',
367 | 'application/vnd.oma.dd2+xml' => 'dd2',
368 | 'application/vnd.openofficeorg.extension' => 'oxt',
369 | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
370 | 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx',
371 | 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx',
372 | 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx',
373 | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
374 | 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx',
375 | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
376 | 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx',
377 | 'application/vnd.osgeo.mapguide.package' => 'mgp',
378 | 'application/vnd.osgi.dp' => 'dp',
379 | 'application/vnd.osgi.subsystem' => 'esa',
380 | 'application/vnd.palm' => 'pdb',
381 | 'application/vnd.pawaafile' => 'paw',
382 | 'application/vnd.pg.format' => 'str',
383 | 'application/vnd.pg.osasli' => 'ei6',
384 | 'application/vnd.picsel' => 'efif',
385 | 'application/vnd.pmi.widget' => 'wg',
386 | 'application/vnd.pocketlearn' => 'plf',
387 | 'application/vnd.powerbuilder6' => 'pbd',
388 | 'application/vnd.previewsystems.box' => 'box',
389 | 'application/vnd.proteus.magazine' => 'mgz',
390 | 'application/vnd.publishare-delta-tree' => 'qps',
391 | 'application/vnd.pvi.ptid1' => 'ptid',
392 | 'application/vnd.quark.quarkxpress' => 'qxd',
393 | 'application/vnd.realvnc.bed' => 'bed',
394 | 'application/vnd.recordare.musicxml' => 'mxl',
395 | 'application/vnd.recordare.musicxml+xml' => 'musicxml',
396 | 'application/vnd.rig.cryptonote' => 'cryptonote',
397 | 'application/vnd.rim.cod' => 'cod',
398 | 'application/vnd.rn-realmedia' => 'rm',
399 | 'application/vnd.rn-realmedia-vbr' => 'rmvb',
400 | 'application/vnd.route66.link66+xml' => 'link66',
401 | 'application/vnd.sailingtracker.track' => 'st',
402 | 'application/vnd.seemail' => 'see',
403 | 'application/vnd.sema' => 'sema',
404 | 'application/vnd.semd' => 'semd',
405 | 'application/vnd.semf' => 'semf',
406 | 'application/vnd.shana.informed.formdata' => 'ifm',
407 | 'application/vnd.shana.informed.formtemplate' => 'itp',
408 | 'application/vnd.shana.informed.interchange' => 'iif',
409 | 'application/vnd.shana.informed.package' => 'ipk',
410 | 'application/vnd.simtech-mindmapper' => 'twd',
411 | 'application/vnd.smaf' => 'mmf',
412 | 'application/vnd.smart.teacher' => 'teacher',
413 | 'application/vnd.solent.sdkm+xml' => 'sdkm',
414 | 'application/vnd.spotfire.dxp' => 'dxp',
415 | 'application/vnd.spotfire.sfs' => 'sfs',
416 | 'application/vnd.stardivision.calc' => 'sdc',
417 | 'application/vnd.stardivision.draw' => 'sda',
418 | 'application/vnd.stardivision.impress' => 'sdd',
419 | 'application/vnd.stardivision.math' => 'smf',
420 | 'application/vnd.stardivision.writer' => 'sdw',
421 | 'application/vnd.stardivision.writer-global' => 'sgl',
422 | 'application/vnd.stepmania.package' => 'smzip',
423 | 'application/vnd.stepmania.stepchart' => 'sm',
424 | 'application/vnd.sun.xml.calc' => 'sxc',
425 | 'application/vnd.sun.xml.calc.template' => 'stc',
426 | 'application/vnd.sun.xml.draw' => 'sxd',
427 | 'application/vnd.sun.xml.draw.template' => 'std',
428 | 'application/vnd.sun.xml.impress' => 'sxi',
429 | 'application/vnd.sun.xml.impress.template' => 'sti',
430 | 'application/vnd.sun.xml.math' => 'sxm',
431 | 'application/vnd.sun.xml.writer' => 'sxw',
432 | 'application/vnd.sun.xml.writer.global' => 'sxg',
433 | 'application/vnd.sun.xml.writer.template' => 'stw',
434 | 'application/vnd.sus-calendar' => 'sus',
435 | 'application/vnd.svd' => 'svd',
436 | 'application/vnd.symbian.install' => 'sis',
437 | 'application/vnd.syncml+xml' => 'xsm',
438 | 'application/vnd.syncml.dm+wbxml' => 'bdm',
439 | 'application/vnd.syncml.dm+xml' => 'xdm',
440 | 'application/vnd.tao.intent-module-archive' => 'tao',
441 | 'application/vnd.tcpdump.pcap' => 'pcap',
442 | 'application/vnd.tmobile-livetv' => 'tmo',
443 | 'application/vnd.trid.tpt' => 'tpt',
444 | 'application/vnd.triscape.mxs' => 'mxs',
445 | 'application/vnd.trueapp' => 'tra',
446 | 'application/vnd.ufdl' => 'ufd',
447 | 'application/vnd.uiq.theme' => 'utz',
448 | 'application/vnd.umajin' => 'umj',
449 | 'application/vnd.unity' => 'unityweb',
450 | 'application/vnd.uoml+xml' => 'uoml',
451 | 'application/vnd.vcx' => 'vcx',
452 | 'application/vnd.visio' => 'vsd',
453 | 'application/vnd.visionary' => 'vis',
454 | 'application/vnd.vsf' => 'vsf',
455 | 'application/vnd.wap.wbxml' => 'wbxml',
456 | 'application/vnd.wap.wmlc' => 'wmlc',
457 | 'application/vnd.wap.wmlscriptc' => 'wmlsc',
458 | 'application/vnd.webturbo' => 'wtb',
459 | 'application/vnd.wolfram.player' => 'nbp',
460 | 'application/vnd.wordperfect' => 'wpd',
461 | 'application/vnd.wqd' => 'wqd',
462 | 'application/vnd.wt.stf' => 'stf',
463 | 'application/vnd.xara' => 'xar',
464 | 'application/vnd.xfdl' => 'xfdl',
465 | 'application/vnd.yamaha.hv-dic' => 'hvd',
466 | 'application/vnd.yamaha.hv-script' => 'hvs',
467 | 'application/vnd.yamaha.hv-voice' => 'hvp',
468 | 'application/vnd.yamaha.openscoreformat' => 'osf',
469 | 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg',
470 | 'application/vnd.yamaha.smaf-audio' => 'saf',
471 | 'application/vnd.yamaha.smaf-phrase' => 'spf',
472 | 'application/vnd.yellowriver-custom-menu' => 'cmp',
473 | 'application/vnd.zul' => 'zir',
474 | 'application/vnd.zzazz.deck+xml' => 'zaz',
475 | 'application/voicexml+xml' => 'vxml',
476 | 'application/widget' => 'wgt',
477 | 'application/winhlp' => 'hlp',
478 | 'application/wsdl+xml' => 'wsdl',
479 | 'application/wspolicy+xml' => 'wspolicy',
480 | 'application/x-7z-compressed' => '7z',
481 | 'application/x-abiword' => 'abw',
482 | 'application/x-ace-compressed' => 'ace',
483 | 'application/x-apple-diskimage' => 'dmg',
484 | 'application/x-authorware-bin' => 'aab',
485 | 'application/x-authorware-map' => 'aam',
486 | 'application/x-authorware-seg' => 'aas',
487 | 'application/x-bcpio' => 'bcpio',
488 | 'application/x-bittorrent' => 'torrent',
489 | 'application/x-blorb' => 'blb',
490 | 'application/x-bzip' => 'bz',
491 | 'application/x-bzip2' => 'bz2',
492 | 'application/x-cbr' => 'cbr',
493 | 'application/x-cdlink' => 'vcd',
494 | 'application/x-cfs-compressed' => 'cfs',
495 | 'application/x-chat' => 'chat',
496 | 'application/x-chess-pgn' => 'pgn',
497 | 'application/x-conference' => 'nsc',
498 | 'application/x-cpio' => 'cpio',
499 | 'application/x-csh' => 'csh',
500 | 'application/x-debian-package' => 'deb',
501 | 'application/x-dgc-compressed' => 'dgc',
502 | 'application/x-director' => 'dir',
503 | 'application/x-doom' => 'wad',
504 | 'application/x-dtbncx+xml' => 'ncx',
505 | 'application/x-dtbook+xml' => 'dtb',
506 | 'application/x-dtbresource+xml' => 'res',
507 | 'application/x-dvi' => 'dvi',
508 | 'application/x-envoy' => 'evy',
509 | 'application/x-eva' => 'eva',
510 | 'application/x-font-bdf' => 'bdf',
511 | 'application/x-font-ghostscript' => 'gsf',
512 | 'application/x-font-linux-psf' => 'psf',
513 | 'application/x-font-otf' => 'otf',
514 | 'application/x-font-pcf' => 'pcf',
515 | 'application/x-font-snf' => 'snf',
516 | 'application/x-font-ttf' => 'ttf',
517 | 'application/x-font-type1' => 'pfa',
518 | 'application/x-font-woff' => 'woff',
519 | 'application/x-freearc' => 'arc',
520 | 'application/x-futuresplash' => 'spl',
521 | 'application/x-gca-compressed' => 'gca',
522 | 'application/x-glulx' => 'ulx',
523 | 'application/x-gnumeric' => 'gnumeric',
524 | 'application/x-gramps-xml' => 'gramps',
525 | 'application/x-gtar' => 'gtar',
526 | 'application/x-hdf' => 'hdf',
527 | 'application/x-install-instructions' => 'install',
528 | 'application/x-iso9660-image' => 'iso',
529 | 'application/x-java-jnlp-file' => 'jnlp',
530 | 'application/x-latex' => 'latex',
531 | 'application/x-lzh-compressed' => 'lzh',
532 | 'application/x-mie' => 'mie',
533 | 'application/x-mobipocket-ebook' => 'prc',
534 | 'application/x-ms-application' => 'application',
535 | 'application/x-ms-shortcut' => 'lnk',
536 | 'application/x-ms-wmd' => 'wmd',
537 | 'application/x-ms-wmz' => 'wmz',
538 | 'application/x-ms-xbap' => 'xbap',
539 | 'application/x-msaccess' => 'mdb',
540 | 'application/x-msbinder' => 'obd',
541 | 'application/x-mscardfile' => 'crd',
542 | 'application/x-msclip' => 'clp',
543 | 'application/x-msdownload' => 'exe',
544 | 'application/x-msmediaview' => 'mvb',
545 | 'application/x-msmetafile' => 'wmf',
546 | 'application/x-msmoney' => 'mny',
547 | 'application/x-mspublisher' => 'pub',
548 | 'application/x-msschedule' => 'scd',
549 | 'application/x-msterminal' => 'trm',
550 | 'application/x-mswrite' => 'wri',
551 | 'application/x-netcdf' => 'nc',
552 | 'application/x-nzb' => 'nzb',
553 | 'application/x-pkcs12' => 'p12',
554 | 'application/x-pkcs7-certificates' => 'p7b',
555 | 'application/x-pkcs7-certreqresp' => 'p7r',
556 | 'application/x-rar-compressed' => 'rar',
557 | 'application/x-rar' => 'rar',
558 | 'application/x-research-info-systems' => 'ris',
559 | 'application/x-sh' => 'sh',
560 | 'application/x-shar' => 'shar',
561 | 'application/x-shockwave-flash' => 'swf',
562 | 'application/x-silverlight-app' => 'xap',
563 | 'application/x-sql' => 'sql',
564 | 'application/x-stuffit' => 'sit',
565 | 'application/x-stuffitx' => 'sitx',
566 | 'application/x-subrip' => 'srt',
567 | 'application/x-sv4cpio' => 'sv4cpio',
568 | 'application/x-sv4crc' => 'sv4crc',
569 | 'application/x-t3vm-image' => 't3',
570 | 'application/x-tads' => 'gam',
571 | 'application/x-tar' => 'tar',
572 | 'application/x-tcl' => 'tcl',
573 | 'application/x-tex' => 'tex',
574 | 'application/x-tex-tfm' => 'tfm',
575 | 'application/x-texinfo' => 'texinfo',
576 | 'application/x-tgif' => 'obj',
577 | 'application/x-ustar' => 'ustar',
578 | 'application/x-wais-source' => 'src',
579 | 'application/x-x509-ca-cert' => 'der',
580 | 'application/x-xfig' => 'fig',
581 | 'application/x-xliff+xml' => 'xlf',
582 | 'application/x-xpinstall' => 'xpi',
583 | 'application/x-xz' => 'xz',
584 | 'application/x-zmachine' => 'z1',
585 | 'application/xaml+xml' => 'xaml',
586 | 'application/xcap-diff+xml' => 'xdf',
587 | 'application/xenc+xml' => 'xenc',
588 | 'application/xhtml+xml' => 'xhtml',
589 | 'application/xml' => 'xml',
590 | 'application/xml-dtd' => 'dtd',
591 | 'application/xop+xml' => 'xop',
592 | 'application/xproc+xml' => 'xpl',
593 | 'application/xslt+xml' => 'xslt',
594 | 'application/xspf+xml' => 'xspf',
595 | 'application/xv+xml' => 'mxml',
596 | 'application/yang' => 'yang',
597 | 'application/yin+xml' => 'yin',
598 | 'application/zip' => 'zip',
599 | 'audio/adpcm' => 'adp',
600 | 'audio/basic' => 'au',
601 | 'audio/midi' => 'mid',
602 | 'audio/mp3' => 'mp3',
603 | 'audio/mp4' => 'mp4a',
604 | 'audio/mpeg' => 'mpga',
605 | 'audio/ogg' => 'oga',
606 | 'audio/s3m' => 's3m',
607 | 'audio/silk' => 'sil',
608 | 'audio/vnd.dece.audio' => 'uva',
609 | 'audio/vnd.digital-winds' => 'eol',
610 | 'audio/vnd.dra' => 'dra',
611 | 'audio/vnd.dts' => 'dts',
612 | 'audio/vnd.dts.hd' => 'dtshd',
613 | 'audio/vnd.lucent.voice' => 'lvp',
614 | 'audio/vnd.ms-playready.media.pya' => 'pya',
615 | 'audio/vnd.nuera.ecelp4800' => 'ecelp4800',
616 | 'audio/vnd.nuera.ecelp7470' => 'ecelp7470',
617 | 'audio/vnd.nuera.ecelp9600' => 'ecelp9600',
618 | 'audio/vnd.rip' => 'rip',
619 | 'audio/webm' => 'weba',
620 | 'audio/x-aac' => 'aac',
621 | 'audio/x-aiff' => 'aif',
622 | 'audio/x-caf' => 'caf',
623 | 'audio/x-flac' => 'flac',
624 | 'audio/x-matroska' => 'mka',
625 | 'audio/x-mpegurl' => 'm3u',
626 | 'audio/x-ms-wax' => 'wax',
627 | 'audio/x-ms-wma' => 'wma',
628 | 'audio/x-pn-realaudio' => 'ram',
629 | 'audio/x-pn-realaudio-plugin' => 'rmp',
630 | 'audio/x-wav' => 'wav',
631 | 'audio/xm' => 'xm',
632 | 'chemical/x-cdx' => 'cdx',
633 | 'chemical/x-cif' => 'cif',
634 | 'chemical/x-cmdf' => 'cmdf',
635 | 'chemical/x-cml' => 'cml',
636 | 'chemical/x-csml' => 'csml',
637 | 'chemical/x-xyz' => 'xyz',
638 | 'image/bmp' => 'bmp',
639 | 'image/x-ms-bmp' => 'bmp',
640 | 'image/cgm' => 'cgm',
641 | 'image/g3fax' => 'g3',
642 | 'image/gif' => 'gif',
643 | 'image/ief' => 'ief',
644 | 'image/jpeg' => 'jpeg',
645 | 'image/pjpeg' => 'jpeg',
646 | 'image/ktx' => 'ktx',
647 | 'image/png' => 'png',
648 | 'image/prs.btif' => 'btif',
649 | 'image/sgi' => 'sgi',
650 | 'image/svg+xml' => 'svg',
651 | 'image/tiff' => 'tiff',
652 | 'image/vnd.adobe.photoshop' => 'psd',
653 | 'image/vnd.dece.graphic' => 'uvi',
654 | 'image/vnd.dvb.subtitle' => 'sub',
655 | 'image/vnd.djvu' => 'djvu',
656 | 'image/vnd.dwg' => 'dwg',
657 | 'image/vnd.dxf' => 'dxf',
658 | 'image/vnd.fastbidsheet' => 'fbs',
659 | 'image/vnd.fpx' => 'fpx',
660 | 'image/vnd.fst' => 'fst',
661 | 'image/vnd.fujixerox.edmics-mmr' => 'mmr',
662 | 'image/vnd.fujixerox.edmics-rlc' => 'rlc',
663 | 'image/vnd.ms-modi' => 'mdi',
664 | 'image/vnd.ms-photo' => 'wdp',
665 | 'image/vnd.net-fpx' => 'npx',
666 | 'image/vnd.wap.wbmp' => 'wbmp',
667 | 'image/vnd.xiff' => 'xif',
668 | 'image/webp' => 'webp',
669 | 'image/x-3ds' => '3ds',
670 | 'image/x-cmu-raster' => 'ras',
671 | 'image/x-cmx' => 'cmx',
672 | 'image/x-freehand' => 'fh',
673 | 'image/x-icon' => 'ico',
674 | 'image/x-mrsid-image' => 'sid',
675 | 'image/x-pcx' => 'pcx',
676 | 'image/x-pict' => 'pic',
677 | 'image/x-portable-anymap' => 'pnm',
678 | 'image/x-portable-bitmap' => 'pbm',
679 | 'image/x-portable-graymap' => 'pgm',
680 | 'image/x-portable-pixmap' => 'ppm',
681 | 'image/x-rgb' => 'rgb',
682 | 'image/x-tga' => 'tga',
683 | 'image/x-xbitmap' => 'xbm',
684 | 'image/x-xpixmap' => 'xpm',
685 | 'image/x-xwindowdump' => 'xwd',
686 | 'message/rfc822' => 'eml',
687 | 'model/iges' => 'igs',
688 | 'model/mesh' => 'msh',
689 | 'model/vnd.collada+xml' => 'dae',
690 | 'model/vnd.dwf' => 'dwf',
691 | 'model/vnd.gdl' => 'gdl',
692 | 'model/vnd.gtw' => 'gtw',
693 | 'model/vnd.mts' => 'mts',
694 | 'model/vnd.vtu' => 'vtu',
695 | 'model/vrml' => 'wrl',
696 | 'model/x3d+binary' => 'x3db',
697 | 'model/x3d+vrml' => 'x3dv',
698 | 'model/x3d+xml' => 'x3d',
699 | 'text/cache-manifest' => 'appcache',
700 | 'text/calendar' => 'ics',
701 | 'text/css' => 'css',
702 | 'text/csv' => 'csv',
703 | 'text/html' => 'html',
704 | 'text/n3' => 'n3',
705 | 'text/plain' => 'txt',
706 | 'text/prs.lines.tag' => 'dsc',
707 | 'text/richtext' => 'rtx',
708 | 'text/rtf' => 'rtf',
709 | 'text/sgml' => 'sgml',
710 | 'text/tab-separated-values' => 'tsv',
711 | 'text/troff' => 't',
712 | 'text/turtle' => 'ttl',
713 | 'text/uri-list' => 'uri',
714 | 'text/vcard' => 'vcard',
715 | 'text/vnd.curl' => 'curl',
716 | 'text/vnd.curl.dcurl' => 'dcurl',
717 | 'text/vnd.curl.scurl' => 'scurl',
718 | 'text/vnd.curl.mcurl' => 'mcurl',
719 | 'text/vnd.dvb.subtitle' => 'sub',
720 | 'text/vnd.fly' => 'fly',
721 | 'text/vnd.fmi.flexstor' => 'flx',
722 | 'text/vnd.graphviz' => 'gv',
723 | 'text/vnd.in3d.3dml' => '3dml',
724 | 'text/vnd.in3d.spot' => 'spot',
725 | 'text/vnd.sun.j2me.app-descriptor' => 'jad',
726 | 'text/vnd.wap.wml' => 'wml',
727 | 'text/vnd.wap.wmlscript' => 'wmls',
728 | 'text/x-asm' => 's',
729 | 'text/x-c' => 'c',
730 | 'text/x-fortran' => 'f',
731 | 'text/x-pascal' => 'p',
732 | 'text/x-java-source' => 'java',
733 | 'text/x-opml' => 'opml',
734 | 'text/x-nfo' => 'nfo',
735 | 'text/x-setext' => 'etx',
736 | 'text/x-sfv' => 'sfv',
737 | 'text/x-uuencode' => 'uu',
738 | 'text/x-vcalendar' => 'vcs',
739 | 'text/x-vcard' => 'vcf',
740 | 'video/3gpp' => '3gp',
741 | 'video/3gpp2' => '3g2',
742 | 'video/h261' => 'h261',
743 | 'video/h263' => 'h263',
744 | 'video/h264' => 'h264',
745 | 'video/jpeg' => 'jpgv',
746 | 'video/jpm' => 'jpm',
747 | 'video/mj2' => 'mj2',
748 | 'video/mp4' => 'mp4',
749 | 'video/mpeg' => 'mpeg',
750 | 'video/ogg' => 'ogv',
751 | 'video/quicktime' => 'qt',
752 | 'video/vnd.dece.hd' => 'uvh',
753 | 'video/vnd.dece.mobile' => 'uvm',
754 | 'video/vnd.dece.pd' => 'uvp',
755 | 'video/vnd.dece.sd' => 'uvs',
756 | 'video/vnd.dece.video' => 'uvv',
757 | 'video/vnd.dvb.file' => 'dvb',
758 | 'video/vnd.fvt' => 'fvt',
759 | 'video/vnd.mpegurl' => 'mxu',
760 | 'video/vnd.ms-playready.media.pyv' => 'pyv',
761 | 'video/vnd.uvvu.mp4' => 'uvu',
762 | 'video/vnd.vivo' => 'viv',
763 | 'video/webm' => 'webm',
764 | 'video/x-f4v' => 'f4v',
765 | 'video/x-fli' => 'fli',
766 | 'video/x-flv' => 'flv',
767 | 'video/x-m4v' => 'm4v',
768 | 'video/x-matroska' => 'mkv',
769 | 'video/x-mng' => 'mng',
770 | 'video/x-ms-asf' => 'asf',
771 | 'video/x-ms-vob' => 'vob',
772 | 'video/x-ms-wm' => 'wm',
773 | 'video/x-ms-wmv' => 'wmv',
774 | 'video/x-ms-wmx' => 'wmx',
775 | 'video/x-ms-wvx' => 'wvx',
776 | 'video/x-msvideo' => 'avi',
777 | 'video/x-sgi-movie' => 'movie',
778 | 'video/x-smv' => 'smv',
779 | 'x-conference/x-cooltalk' => 'ice'
780 | ];
781 |
782 | /**
783 | * Get extension by mime type
784 | *
785 | * @param string $mimeType
786 | * @return string|null
787 | */
788 | public function getExtension(string $mimeType)
789 | {
790 | return isset($this->mimeTypes[$mimeType])? $this->mimeTypes[$mimeType] : null;
791 | }
792 |
793 | /**
794 | * Get mime type by extension
795 | *
796 | * @param string $extension
797 | * @return string|null
798 | */
799 | public function getMimeType(string $extension)
800 | {
801 | $key = array_search($extension, $this->mimeTypes);
802 | return $key ?: null;
803 | }
804 | }
805 |
--------------------------------------------------------------------------------
/src/MissingRequiredParameterException.php:
--------------------------------------------------------------------------------
1 | validation = $validation;
44 | }
45 |
46 | /**
47 | * Set key
48 | *
49 | * @param string $key
50 | * @return void
51 | */
52 | public function setKey(string $key)
53 | {
54 | $this->key = $key;
55 | }
56 |
57 | /**
58 | * Get key
59 | *
60 | * @return string
61 | */
62 | public function getKey()
63 | {
64 | return $this->key ?: get_class($this);
65 | }
66 |
67 | /**
68 | * Set attribute
69 | *
70 | * @param \Rakit\Validation\Attribute $attribute
71 | * @return void
72 | */
73 | public function setAttribute(Attribute $attribute)
74 | {
75 | $this->attribute = $attribute;
76 | }
77 |
78 | /**
79 | * Get attribute
80 | *
81 | * @return \Rakit\Validation\Attribute|null
82 | */
83 | public function getAttribute()
84 | {
85 | return $this->attribute;
86 | }
87 |
88 | /**
89 | * Get parameters
90 | *
91 | * @return array
92 | */
93 | public function getParameters(): array
94 | {
95 | return $this->params;
96 | }
97 |
98 | /**
99 | * Set params
100 | *
101 | * @param array $params
102 | * @return \Rakit\Validation\Rule
103 | */
104 | public function setParameters(array $params): Rule
105 | {
106 | $this->params = array_merge($this->params, $params);
107 | return $this;
108 | }
109 |
110 | /**
111 | * Set parameters
112 | *
113 | * @param string $key
114 | * @param mixed $value
115 | * @return \Rakit\Validation\Rule
116 | */
117 | public function setParameter(string $key, $value): Rule
118 | {
119 | $this->params[$key] = $value;
120 | return $this;
121 | }
122 |
123 | /**
124 | * Fill $params to $this->params
125 | *
126 | * @param array $params
127 | * @return \Rakit\Validation\Rule
128 | */
129 | public function fillParameters(array $params): Rule
130 | {
131 | foreach ($this->fillableParams as $key) {
132 | if (empty($params)) {
133 | break;
134 | }
135 | $this->params[$key] = array_shift($params);
136 | }
137 | return $this;
138 | }
139 |
140 | /**
141 | * Get parameter from given $key, return null if it not exists
142 | *
143 | * @param string $key
144 | * @return mixed
145 | */
146 | public function parameter(string $key)
147 | {
148 | return isset($this->params[$key])? $this->params[$key] : null;
149 | }
150 |
151 | /**
152 | * Set parameter text that can be displayed in error message using ':param_key'
153 | *
154 | * @param string $key
155 | * @param string $text
156 | * @return void
157 | */
158 | public function setParameterText(string $key, string $text)
159 | {
160 | $this->paramsTexts[$key] = $text;
161 | }
162 |
163 | /**
164 | * Get $paramsTexts
165 | *
166 | * @return array
167 | */
168 | public function getParametersTexts(): array
169 | {
170 | return $this->paramsTexts;
171 | }
172 |
173 | /**
174 | * Check whether this rule is implicit
175 | *
176 | * @return boolean
177 | */
178 | public function isImplicit(): bool
179 | {
180 | return $this->implicit;
181 | }
182 |
183 | /**
184 | * Just alias of setMessage
185 | *
186 | * @param string $message
187 | * @return \Rakit\Validation\Rule
188 | */
189 | public function message(string $message): Rule
190 | {
191 | return $this->setMessage($message);
192 | }
193 |
194 | /**
195 | * Set message
196 | *
197 | * @param string $message
198 | * @return \Rakit\Validation\Rule
199 | */
200 | public function setMessage(string $message): Rule
201 | {
202 | $this->message = $message;
203 | return $this;
204 | }
205 |
206 | /**
207 | * Get message
208 | *
209 | * @return string
210 | */
211 | public function getMessage(): string
212 | {
213 | return $this->message;
214 | }
215 |
216 | /**
217 | * Check given $params must be exists
218 | *
219 | * @param array $params
220 | * @return void
221 | * @throws \Rakit\Validation\MissingRequiredParameterException
222 | */
223 | protected function requireParameters(array $params)
224 | {
225 | foreach ($params as $param) {
226 | if (!isset($this->params[$param])) {
227 | $rule = $this->getKey();
228 | throw new MissingRequiredParameterException("Missing required parameter '{$param}' on rule '{$rule}'");
229 | }
230 | }
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/src/RuleNotFoundException.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
28 | $time = $this->parameter('time');
29 |
30 | if (!$this->isValidDate($value)) {
31 | throw $this->throwException($value);
32 | }
33 |
34 | if (!$this->isValidDate($time)) {
35 | throw $this->throwException($time);
36 | }
37 |
38 | return $this->getTimeStamp($time) < $this->getTimeStamp($value);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Rules/Alpha.php:
--------------------------------------------------------------------------------
1 | 0;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Rules/AlphaNum.php:
--------------------------------------------------------------------------------
1 | 0;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Rules/AlphaSpaces.php:
--------------------------------------------------------------------------------
1 | 0;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Rules/Before.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
27 | $time = $this->parameter('time');
28 |
29 | if (!$this->isValidDate($value)) {
30 | throw $this->throwException($value);
31 | }
32 |
33 | if (!$this->isValidDate($time)) {
34 | throw $this->throwException($time);
35 | }
36 |
37 | return $this->getTimeStamp($time) > $this->getTimeStamp($value);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Rules/Between.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
26 |
27 | $min = $this->getBytesSize($this->parameter('min'));
28 | $max = $this->getBytesSize($this->parameter('max'));
29 |
30 | $valueSize = $this->getValueSize($value);
31 |
32 | if (!is_numeric($valueSize)) {
33 | return false;
34 | }
35 |
36 | return ($valueSize >= $min && $valueSize <= $max);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Rules/Boolean.php:
--------------------------------------------------------------------------------
1 | setParameter('callback', $callback);
27 | }
28 |
29 | /**
30 | * Check the $value is valid
31 | *
32 | * @param mixed $value
33 | * @return bool
34 | * @throws \Exception
35 | */
36 | public function check($value): bool
37 | {
38 | $this->requireParameters($this->fillableParams);
39 |
40 | $callback = $this->parameter('callback');
41 | if (false === $callback instanceof Closure) {
42 | $key = $this->attribute->getKey();
43 | throw new InvalidArgumentException("Callback rule for '{$key}' is not callable.");
44 | }
45 |
46 | $callback = $callback->bindTo($this);
47 | $invalidMessage = $callback($value);
48 |
49 | if (is_string($invalidMessage)) {
50 | $this->setMessage($invalidMessage);
51 | return false;
52 | } elseif (false === $invalidMessage) {
53 | return false;
54 | }
55 |
56 | return true;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Rules/Date.php:
--------------------------------------------------------------------------------
1 | 'Y-m-d'
19 | ];
20 |
21 | /**
22 | * Check the $value is valid
23 | *
24 | * @param mixed $value
25 | * @return bool
26 | */
27 | public function check($value): bool
28 | {
29 | $this->requireParameters($this->fillableParams);
30 |
31 | $format = $this->parameter('format');
32 | return date_create_from_format($format, $value) !== false;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Rules/Defaults.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
26 |
27 | $default = $this->parameter('default');
28 | return true;
29 | }
30 |
31 | /**
32 | * {@inheritDoc}
33 | */
34 | public function modifyValue($value)
35 | {
36 | return $this->isEmptyValue($value) ? $this->parameter('default') : $value;
37 | }
38 |
39 | /**
40 | * Check $value is empty value
41 | *
42 | * @param mixed $value
43 | * @return boolean
44 | */
45 | protected function isEmptyValue($value): bool
46 | {
47 | $requiredValidator = new Required;
48 | return false === $requiredValidator->check($value, []);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Rules/Different.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
25 |
26 | $field = $this->parameter('field');
27 | $anotherValue = $this->validation->getValue($field);
28 |
29 | return $value != $anotherValue;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Rules/Digits.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
25 |
26 | $length = (int) $this->parameter('length');
27 |
28 | return ! preg_match('/[^0-9]/', $value)
29 | && strlen((string) $value) == $length;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Rules/DigitsBetween.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
25 |
26 | $min = (int) $this->parameter('min');
27 | $max = (int) $this->parameter('max');
28 |
29 | $length = strlen((string) $value);
30 |
31 | return ! preg_match('/[^0-9]/', $value)
32 | && $length >= $min && $length <= $max;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Rules/Email.php:
--------------------------------------------------------------------------------
1 | params
16 | *
17 | * @param array $params
18 | * @return self
19 | */
20 | public function fillParameters(array $params): Rule
21 | {
22 | if (count($params) == 1 && is_array($params[0])) {
23 | $params = $params[0];
24 | }
25 | $this->params['allowed_extensions'] = $params;
26 | return $this;
27 | }
28 |
29 | /**
30 | * Check the $value is valid
31 | *
32 | * @param mixed $value
33 | * @return bool
34 | */
35 | public function check($value): bool
36 | {
37 | $this->requireParameters(['allowed_extensions']);
38 | $allowedExtensions = $this->parameter('allowed_extensions');
39 | foreach ($allowedExtensions as $key => $ext) {
40 | $allowedExtensions[$key] = ltrim($ext, '.');
41 | }
42 |
43 | $or = $this->validation ? $this->validation->getTranslation('or') : 'or';
44 | $allowedExtensionsText = Helper::join(Helper::wraps($allowedExtensions, ".", ""), ', ', ", {$or} ");
45 | $this->setParameterText('allowed_extensions', $allowedExtensionsText);
46 |
47 | $ext = strtolower(pathinfo($value, PATHINFO_EXTENSION));
48 | return ($ext && in_array($ext, $allowedExtensions)) ? true : false;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Rules/In.php:
--------------------------------------------------------------------------------
1 | params
19 | *
20 | * @param array $params
21 | * @return self
22 | */
23 | public function fillParameters(array $params): Rule
24 | {
25 | if (count($params) == 1 && is_array($params[0])) {
26 | $params = $params[0];
27 | }
28 | $this->params['allowed_values'] = $params;
29 | return $this;
30 | }
31 |
32 | /**
33 | * Set strict value
34 | *
35 | * @param bool $strict
36 | * @return void
37 | */
38 | public function strict(bool $strict = true)
39 | {
40 | $this->strict = $strict;
41 | }
42 |
43 | /**
44 | * Check $value is existed
45 | *
46 | * @param mixed $value
47 | * @return bool
48 | */
49 | public function check($value): bool
50 | {
51 | $this->requireParameters(['allowed_values']);
52 |
53 | $allowedValues = $this->parameter('allowed_values');
54 |
55 | $or = $this->validation ? $this->validation->getTranslation('or') : 'or';
56 | $allowedValuesText = Helper::join(Helper::wraps($allowedValues, "'"), ', ', ", {$or} ");
57 | $this->setParameterText('allowed_values', $allowedValuesText);
58 |
59 | return in_array($value, $allowedValues, $this->strict);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Rules/Integer.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
26 |
27 | $max = $this->getBytesSize($this->parameter('max'));
28 | $valueSize = $this->getValueSize($value);
29 |
30 | if (!is_numeric($valueSize)) {
31 | return false;
32 | }
33 |
34 | return $valueSize <= $max;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Rules/Mimes.php:
--------------------------------------------------------------------------------
1 | params
27 | *
28 | * @param array $params
29 | * @return self
30 | */
31 | public function fillParameters(array $params): Rule
32 | {
33 | $this->allowTypes($params);
34 | return $this;
35 | }
36 |
37 | /**
38 | * Given $types and assign $this->params
39 | *
40 | * @param mixed $types
41 | * @return self
42 | */
43 | public function allowTypes($types): Rule
44 | {
45 | if (is_string($types)) {
46 | $types = explode('|', $types);
47 | }
48 |
49 | $this->params['allowed_types'] = $types;
50 |
51 | return $this;
52 | }
53 |
54 | /**
55 | * Check the $value is valid
56 | *
57 | * @param mixed $value
58 | * @return bool
59 | */
60 | public function check($value): bool
61 | {
62 | $allowedTypes = $this->parameter('allowed_types');
63 |
64 | if ($allowedTypes) {
65 | $or = $this->validation ? $this->validation->getTranslation('or') : 'or';
66 | $this->setParameterText('allowed_types', Helper::join(Helper::wraps($allowedTypes, "'"), ', ', ", {$or} "));
67 | }
68 |
69 | // below is Required rule job
70 | if (!$this->isValueFromUploadedFiles($value) or $value['error'] == UPLOAD_ERR_NO_FILE) {
71 | return true;
72 | }
73 |
74 | if (!$this->isUploadedFile($value)) {
75 | return false;
76 | }
77 |
78 | // just make sure there is no error
79 | if ($value['error']) {
80 | return false;
81 | }
82 |
83 | if (!empty($allowedTypes)) {
84 | $guesser = new MimeTypeGuesser;
85 | $ext = $guesser->getExtension($value['type']);
86 | unset($guesser);
87 |
88 | if (!in_array($ext, $allowedTypes)) {
89 | return false;
90 | }
91 | }
92 |
93 | return true;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/Rules/Min.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
26 |
27 | $min = $this->getBytesSize($this->parameter('min'));
28 | $valueSize = $this->getValueSize($value);
29 |
30 | if (!is_numeric($valueSize)) {
31 | return false;
32 | }
33 |
34 | return $valueSize >= $min;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Rules/NotIn.php:
--------------------------------------------------------------------------------
1 | params
19 | *
20 | * @param array $params
21 | * @return self
22 | */
23 | public function fillParameters(array $params): Rule
24 | {
25 | if (count($params) == 1 and is_array($params[0])) {
26 | $params = $params[0];
27 | }
28 | $this->params['disallowed_values'] = $params;
29 | return $this;
30 | }
31 |
32 | /**
33 | * Set strict value
34 | *
35 | * @param bool $strict
36 | * @return void
37 | */
38 | public function strict($strict = true)
39 | {
40 | $this->strict = $strict;
41 | }
42 |
43 | /**
44 | * Check the $value is valid
45 | *
46 | * @param mixed $value
47 | * @return bool
48 | */
49 | public function check($value): bool
50 | {
51 | $this->requireParameters(['disallowed_values']);
52 |
53 | $disallowedValues = (array) $this->parameter('disallowed_values');
54 |
55 | $and = $this->validation ? $this->validation->getTranslation('and') : 'and';
56 | $disallowedValuesText = Helper::join(Helper::wraps($disallowedValues, "'"), ', ', ", {$and} ");
57 | $this->setParameterText('disallowed_values', $disallowedValuesText);
58 |
59 | return !in_array($value, $disallowedValues, $this->strict);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Rules/Nullable.php:
--------------------------------------------------------------------------------
1 | setAttributeAsRequired();
24 |
25 | return $this->validation->hasValue($this->attribute->getKey());
26 | }
27 |
28 | /**
29 | * Set attribute is required if $this->attribute is set
30 | *
31 | * @return void
32 | */
33 | protected function setAttributeAsRequired()
34 | {
35 | if ($this->attribute) {
36 | $this->attribute->setRequired(true);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Rules/Regex.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
25 | $regex = $this->parameter('regex');
26 | return preg_match($regex, $value) > 0;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Rules/Required.php:
--------------------------------------------------------------------------------
1 | setAttributeAsRequired();
26 |
27 | if ($this->attribute and $this->attribute->hasRule('uploaded_file')) {
28 | return $this->isValueFromUploadedFiles($value) and $value['error'] != UPLOAD_ERR_NO_FILE;
29 | }
30 |
31 | if (is_string($value)) {
32 | return mb_strlen(trim($value), 'UTF-8') > 0;
33 | }
34 | if (is_array($value)) {
35 | return count($value) > 0;
36 | }
37 | return !is_null($value);
38 | }
39 |
40 | /**
41 | * Set attribute is required if $this->attribute is set
42 | *
43 | * @return void
44 | */
45 | protected function setAttributeAsRequired()
46 | {
47 | if ($this->attribute) {
48 | $this->attribute->setRequired(true);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Rules/RequiredIf.php:
--------------------------------------------------------------------------------
1 | params
17 | *
18 | * @param array $params
19 | * @return self
20 | */
21 | public function fillParameters(array $params): Rule
22 | {
23 | $this->params['field'] = array_shift($params);
24 | $this->params['values'] = $params;
25 | return $this;
26 | }
27 |
28 | /**
29 | * Check the $value is valid
30 | *
31 | * @param mixed $value
32 | * @return bool
33 | */
34 | public function check($value): bool
35 | {
36 | $this->requireParameters(['field', 'values']);
37 |
38 | $anotherAttribute = $this->parameter('field');
39 | $definedValues = $this->parameter('values');
40 | $anotherValue = $this->getAttribute()->getValue($anotherAttribute);
41 |
42 | $validator = $this->validation->getValidator();
43 | $requiredValidator = $validator('required');
44 |
45 | if (in_array($anotherValue, $definedValues)) {
46 | $this->setAttributeAsRequired();
47 | return $requiredValidator->check($value, []);
48 | }
49 |
50 | return true;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Rules/RequiredUnless.php:
--------------------------------------------------------------------------------
1 | params
17 | *
18 | * @param array $params
19 | * @return self
20 | */
21 | public function fillParameters(array $params): Rule
22 | {
23 | $this->params['field'] = array_shift($params);
24 | $this->params['values'] = $params;
25 | return $this;
26 | }
27 |
28 | /**
29 | * Check the $value is valid
30 | *
31 | * @param mixed $value
32 | * @return bool
33 | */
34 | public function check($value): bool
35 | {
36 | $this->requireParameters(['field', 'values']);
37 |
38 | $anotherAttribute = $this->parameter('field');
39 | $definedValues = $this->parameter('values');
40 | $anotherValue = $this->getAttribute()->getValue($anotherAttribute);
41 |
42 | $validator = $this->validation->getValidator();
43 | $requiredValidator = $validator('required');
44 |
45 | if (!in_array($anotherValue, $definedValues)) {
46 | $this->setAttributeAsRequired();
47 | return $requiredValidator->check($value, []);
48 | }
49 |
50 | return true;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Rules/RequiredWith.php:
--------------------------------------------------------------------------------
1 | params
17 | *
18 | * @param array $params
19 | * @return self
20 | */
21 | public function fillParameters(array $params): Rule
22 | {
23 | $this->params['fields'] = $params;
24 | return $this;
25 | }
26 |
27 | /**
28 | * Check the $value is valid
29 | *
30 | * @param mixed $value
31 | * @return bool
32 | */
33 | public function check($value): bool
34 | {
35 | $this->requireParameters(['fields']);
36 | $fields = $this->parameter('fields');
37 | $validator = $this->validation->getValidator();
38 | $requiredValidator = $validator('required');
39 |
40 | foreach ($fields as $field) {
41 | if ($this->validation->hasValue($field)) {
42 | $this->setAttributeAsRequired();
43 | return $requiredValidator->check($value, []);
44 | }
45 | }
46 |
47 | return true;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Rules/RequiredWithAll.php:
--------------------------------------------------------------------------------
1 | params
17 | *
18 | * @param array $params
19 | * @return self
20 | */
21 | public function fillParameters(array $params): Rule
22 | {
23 | $this->params['fields'] = $params;
24 | return $this;
25 | }
26 |
27 | /**
28 | * Check the $value is valid
29 | *
30 | * @param mixed $value
31 | * @return bool
32 | */
33 | public function check($value): bool
34 | {
35 | $this->requireParameters(['fields']);
36 | $fields = $this->parameter('fields');
37 | $validator = $this->validation->getValidator();
38 | $requiredValidator = $validator('required');
39 |
40 | foreach ($fields as $field) {
41 | if (!$this->validation->hasValue($field)) {
42 | return true;
43 | }
44 | }
45 |
46 | $this->setAttributeAsRequired();
47 | return $requiredValidator->check($value, []);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Rules/RequiredWithout.php:
--------------------------------------------------------------------------------
1 | params
17 | *
18 | * @param array $params
19 | * @return self
20 | */
21 | public function fillParameters(array $params): Rule
22 | {
23 | $this->params['fields'] = $params;
24 | return $this;
25 | }
26 |
27 | /**
28 | * Check the $value is valid
29 | *
30 | * @param mixed $value
31 | * @return bool
32 | */
33 | public function check($value): bool
34 | {
35 | $this->requireParameters(['fields']);
36 | $fields = $this->parameter('fields');
37 | $validator = $this->validation->getValidator();
38 | $requiredValidator = $validator('required');
39 |
40 | foreach ($fields as $field) {
41 | if (!$this->validation->hasValue($field)) {
42 | $this->setAttributeAsRequired();
43 | return $requiredValidator->check($value, []);
44 | }
45 | }
46 |
47 | return true;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Rules/RequiredWithoutAll.php:
--------------------------------------------------------------------------------
1 | params
17 | *
18 | * @param array $params
19 | * @return self
20 | */
21 | public function fillParameters(array $params): Rule
22 | {
23 | $this->params['fields'] = $params;
24 | return $this;
25 | }
26 |
27 | /**
28 | * Check the $value is valid
29 | *
30 | * @param mixed $value
31 | * @return bool
32 | */
33 | public function check($value): bool
34 | {
35 | $this->requireParameters(['fields']);
36 | $fields = $this->parameter('fields');
37 | $validator = $this->validation->getValidator();
38 | $requiredValidator = $validator('required');
39 |
40 | foreach ($fields as $field) {
41 | if ($this->validation->hasValue($field)) {
42 | return true;
43 | }
44 | }
45 |
46 | $this->setAttributeAsRequired();
47 | return $requiredValidator->check($value, []);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Rules/Same.php:
--------------------------------------------------------------------------------
1 | requireParameters($this->fillableParams);
25 |
26 | $field = $this->parameter('field');
27 | $anotherValue = $this->getAttribute()->getValue($field);
28 |
29 | return $value == $anotherValue;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Rules/Traits/DateUtilsTrait.php:
--------------------------------------------------------------------------------
1 | isValueFromUploadedFiles($value) && is_uploaded_file($value['tmp_name']);
42 | }
43 |
44 | /**
45 | * Resolve uploaded file value
46 | *
47 | * @param mixed $value
48 | * @return array|null
49 | */
50 | public function resolveUploadedFileValue($value)
51 | {
52 | if (!$this->isValueFromUploadedFiles($value)) {
53 | return null;
54 | }
55 |
56 | // Here $value should be an array:
57 | // [
58 | // 'name' => string|array,
59 | // 'type' => string|array,
60 | // 'size' => int|array,
61 | // 'tmp_name' => string|array,
62 | // 'error' => string|array,
63 | // ]
64 |
65 | // Flatten $value to it's array dot format,
66 | // so our array must be something like:
67 | // ['name' => string, 'type' => string, 'size' => int, ...]
68 | // or for multiple values:
69 | // ['name.0' => string, 'name.1' => string, 'type.0' => string, 'type.1' => string, ...]
70 | // or for nested array:
71 | // ['name.foo.bar' => string, 'name.foo.baz' => string, 'type.foo.bar' => string, 'type.foo.baz' => string, ...]
72 | $arrayDots = Helper::arrayDot($value);
73 |
74 | $results = [];
75 | foreach ($arrayDots as $key => $val) {
76 | // Move first key to last key
77 | // name.foo.bar -> foo.bar.name
78 | $splits = explode(".", $key);
79 | $firstKey = array_shift($splits);
80 | $key = count($splits) ? implode(".", $splits) . ".{$firstKey}" : $firstKey;
81 |
82 | Helper::arraySet($results, $key, $val);
83 | }
84 | return $results;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Rules/Traits/SizeTrait.php:
--------------------------------------------------------------------------------
1 | getAttribute()
19 | && ($this->getAttribute()->hasRule('numeric') || $this->getAttribute()->hasRule('integer'))
20 | && is_numeric($value)
21 | ) {
22 | $value = (float) $value;
23 | }
24 |
25 | if (is_int($value) || is_float($value)) {
26 | return (float) $value;
27 | } elseif (is_string($value)) {
28 | return (float) mb_strlen($value, 'UTF-8');
29 | } elseif ($this->isUploadedFileValue($value)) {
30 | return (float) $value['size'];
31 | } elseif (is_array($value)) {
32 | return (float) count($value);
33 | } else {
34 | return false;
35 | }
36 | }
37 |
38 | /**
39 | * Given $size and get the bytes
40 | *
41 | * @param string|int $size
42 | * @return float
43 | * @throws InvalidArgumentException
44 | */
45 | protected function getBytesSize($size)
46 | {
47 | if (is_numeric($size)) {
48 | return (float) $size;
49 | }
50 |
51 | if (!is_string($size)) {
52 | throw new InvalidArgumentException("Size must be string or numeric Bytes", 1);
53 | }
54 |
55 | if (!preg_match("/^(?((\d+)?\.)?\d+)(?(B|K|M|G|T|P)B?)?$/i", $size, $match)) {
56 | throw new InvalidArgumentException("Size is not valid format", 1);
57 | }
58 |
59 | $number = (float) $match['number'];
60 | $format = isset($match['format']) ? $match['format'] : '';
61 |
62 | switch (strtoupper($format)) {
63 | case "KB":
64 | case "K":
65 | return $number * 1024;
66 |
67 | case "MB":
68 | case "M":
69 | return $number * pow(1024, 2);
70 |
71 | case "GB":
72 | case "G":
73 | return $number * pow(1024, 3);
74 |
75 | case "TB":
76 | case "T":
77 | return $number * pow(1024, 4);
78 |
79 | case "PB":
80 | case "P":
81 | return $number * pow(1024, 5);
82 |
83 | default:
84 | return $number;
85 | }
86 | }
87 |
88 | /**
89 | * Check whether value is from $_FILES
90 | *
91 | * @param mixed $value
92 | * @return bool
93 | */
94 | public function isUploadedFileValue($value): bool
95 | {
96 | if (!is_array($value)) {
97 | return false;
98 | }
99 |
100 | $keys = ['name', 'type', 'tmp_name', 'size', 'error'];
101 | foreach ($keys as $key) {
102 | if (!array_key_exists($key, $value)) {
103 | return false;
104 | }
105 | }
106 |
107 | return true;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/Rules/TypeArray.php:
--------------------------------------------------------------------------------
1 | params
28 | *
29 | * @param array $params
30 | * @return self
31 | */
32 | public function fillParameters(array $params): Rule
33 | {
34 | $this->minSize(array_shift($params));
35 | $this->maxSize(array_shift($params));
36 | $this->fileTypes($params);
37 |
38 | return $this;
39 | }
40 |
41 | /**
42 | * Given $size and set the max size
43 | *
44 | * @param string|int $size
45 | * @return self
46 | */
47 | public function maxSize($size): Rule
48 | {
49 | $this->params['max_size'] = $size;
50 | return $this;
51 | }
52 |
53 | /**
54 | * Given $size and set the min size
55 | *
56 | * @param string|int $size
57 | * @return self
58 | */
59 | public function minSize($size): Rule
60 | {
61 | $this->params['min_size'] = $size;
62 | return $this;
63 | }
64 |
65 | /**
66 | * Given $min and $max then set the range size
67 | *
68 | * @param string|int $min
69 | * @param string|int $max
70 | * @return self
71 | */
72 | public function sizeBetween($min, $max): Rule
73 | {
74 | $this->minSize($min);
75 | $this->maxSize($max);
76 |
77 | return $this;
78 | }
79 |
80 | /**
81 | * Given $types and assign $this->params
82 | *
83 | * @param mixed $types
84 | * @return self
85 | */
86 | public function fileTypes($types): Rule
87 | {
88 | if (is_string($types)) {
89 | $types = explode('|', $types);
90 | }
91 |
92 | $this->params['allowed_types'] = $types;
93 |
94 | return $this;
95 | }
96 |
97 | /**
98 | * {@inheritDoc}
99 | */
100 | public function beforeValidate()
101 | {
102 | $attribute = $this->getAttribute();
103 |
104 | // We only resolve uploaded file value
105 | // from complex attribute such as 'files.photo', 'images.*', 'images.foo.bar', etc.
106 | if (!$attribute->isUsingDotNotation()) {
107 | return;
108 | }
109 |
110 | $keys = explode(".", $attribute->getKey());
111 | $firstKey = array_shift($keys);
112 | $firstKeyValue = $this->validation->getValue($firstKey);
113 |
114 | $resolvedValue = $this->resolveUploadedFileValue($firstKeyValue);
115 |
116 | // Return original value if $value can't be resolved as uploaded file value
117 | if (!$resolvedValue) {
118 | return;
119 | }
120 |
121 | $this->validation->setValue($firstKey, $resolvedValue);
122 | }
123 |
124 | /**
125 | * Check the $value is valid
126 | *
127 | * @param mixed $value
128 | * @return bool
129 | */
130 | public function check($value): bool
131 | {
132 | $minSize = $this->parameter('min_size');
133 | $maxSize = $this->parameter('max_size');
134 | $allowedTypes = $this->parameter('allowed_types');
135 |
136 | if ($allowedTypes) {
137 | $or = $this->validation ? $this->validation->getTranslation('or') : 'or';
138 | $this->setParameterText('allowed_types', Helper::join(Helper::wraps($allowedTypes, "'"), ', ', ", {$or} "));
139 | }
140 |
141 | // below is Required rule job
142 | if (!$this->isValueFromUploadedFiles($value) or $value['error'] == UPLOAD_ERR_NO_FILE) {
143 | return true;
144 | }
145 |
146 | if (!$this->isUploadedFile($value)) {
147 | return false;
148 | }
149 |
150 | // just make sure there is no error
151 | if ($value['error']) {
152 | return false;
153 | }
154 |
155 | if ($minSize) {
156 | $bytesMinSize = $this->getBytesSize($minSize);
157 | if ($value['size'] < $bytesMinSize) {
158 | $this->setMessage('The :attribute file is too small, minimum size is :min_size');
159 | return false;
160 | }
161 | }
162 |
163 | if ($maxSize) {
164 | $bytesMaxSize = $this->getBytesSize($maxSize);
165 | if ($value['size'] > $bytesMaxSize) {
166 | $this->setMessage('The :attribute file is too large, maximum size is :max_size');
167 | return false;
168 | }
169 | }
170 |
171 | if (!empty($allowedTypes)) {
172 | $guesser = new MimeTypeGuesser;
173 | $ext = $guesser->getExtension($value['type']);
174 | unset($guesser);
175 |
176 | if (!in_array($ext, $allowedTypes)) {
177 | $this->setMessage('The :attribute file type must be :allowed_types');
178 | return false;
179 | }
180 | }
181 |
182 | return true;
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/Rules/Uppercase.php:
--------------------------------------------------------------------------------
1 | params
15 | *
16 | * @param array $params
17 | * @return self
18 | */
19 | public function fillParameters(array $params): Rule
20 | {
21 | if (count($params) == 1 and is_array($params[0])) {
22 | $params = $params[0];
23 | }
24 | return $this->forScheme($params);
25 | }
26 |
27 | /**
28 | * Given $schemes and assign $this->params
29 | *
30 | * @param array $schemes
31 | * @return self
32 | */
33 | public function forScheme($schemes): Rule
34 | {
35 | $this->params['schemes'] = (array) $schemes;
36 | return $this;
37 | }
38 |
39 | /**
40 | * Check the $value is valid
41 | *
42 | * @param mixed $value
43 | * @return bool
44 | */
45 | public function check($value): bool
46 | {
47 | $schemes = $this->parameter('schemes');
48 |
49 | if (!$schemes) {
50 | return $this->validateCommonScheme($value);
51 | } else {
52 | foreach ($schemes as $scheme) {
53 | $method = 'validate' . ucfirst($scheme) .'Scheme';
54 | if (method_exists($this, $method)) {
55 | if ($this->{$method}($value)) {
56 | return true;
57 | }
58 | } elseif ($this->validateCommonScheme($value, $scheme)) {
59 | return true;
60 | }
61 | }
62 |
63 | return false;
64 | }
65 | }
66 |
67 | /**
68 | * Validate $value is valid URL format
69 | *
70 | * @param mixed $value
71 | * @return bool
72 | */
73 | public function validateBasic($value): bool
74 | {
75 | return filter_var($value, FILTER_VALIDATE_URL) !== false;
76 | }
77 |
78 | /**
79 | * Validate $value is correct $scheme format
80 | *
81 | * @param mixed $value
82 | * @param null $scheme
83 | * @return bool
84 | */
85 | public function validateCommonScheme($value, $scheme = null): bool
86 | {
87 | if (!$scheme) {
88 | return $this->validateBasic($value) && (bool) preg_match("/^\w+:\/\//i", $value);
89 | } else {
90 | return $this->validateBasic($value) && (bool) preg_match("/^{$scheme}:\/\//", $value);
91 | }
92 | }
93 |
94 | /**
95 | * Validate the $value is mailto scheme format
96 | *
97 | * @param mixed $value
98 | * @return bool
99 | */
100 | public function validateMailtoScheme($value): bool
101 | {
102 | return $this->validateBasic($value) && preg_match("/^mailto:/", $value);
103 | }
104 |
105 | /**
106 | * Validate the $value is jdbc scheme format
107 | *
108 | * @param mixed $value
109 | * @return bool
110 | */
111 | public function validateJdbcScheme($value): bool
112 | {
113 | return (bool) preg_match("/^jdbc:\w+:\/\//", $value);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/Traits/MessagesTrait.php:
--------------------------------------------------------------------------------
1 | messages[$key] = $message;
21 | }
22 |
23 | /**
24 | * Given $messages and set multiple messages
25 | *
26 | * @param array $messages
27 | * @return void
28 | */
29 | public function setMessages(array $messages)
30 | {
31 | $this->messages = array_merge($this->messages, $messages);
32 | }
33 |
34 | /**
35 | * Given message from given $key
36 | *
37 | * @param string $key
38 | * @return string
39 | */
40 | public function getMessage(string $key): string
41 | {
42 | return array_key_exists($key, $this->messages) ? $this->messages[$key] : $key;
43 | }
44 |
45 | /**
46 | * Get all $messages
47 | *
48 | * @return array
49 | */
50 | public function getMessages(): array
51 | {
52 | return $this->messages;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Traits/TranslationsTrait.php:
--------------------------------------------------------------------------------
1 | translations[$key] = $translation;
21 | }
22 |
23 | /**
24 | * Given $translations and set multiple translations
25 | *
26 | * @param array $translations
27 | * @return void
28 | */
29 | public function setTranslations(array $translations)
30 | {
31 | $this->translations = array_merge($this->translations, $translations);
32 | }
33 |
34 | /**
35 | * Given translation from given $key
36 | *
37 | * @param string $key
38 | * @return string
39 | */
40 | public function getTranslation(string $key): string
41 | {
42 | return array_key_exists($key, $this->translations) ? $this->translations[$key] : $key;
43 | }
44 |
45 | /**
46 | * Get all $translations
47 | *
48 | * @return array
49 | */
50 | public function getTranslations(): array
51 | {
52 | return $this->translations;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Validation.php:
--------------------------------------------------------------------------------
1 | validator = $validator;
54 | $this->inputs = $this->resolveInputAttributes($inputs);
55 | $this->messages = $messages;
56 | $this->errors = new ErrorBag;
57 | foreach ($rules as $attributeKey => $rules) {
58 | $this->addAttribute($attributeKey, $rules);
59 | }
60 | }
61 |
62 | /**
63 | * Add attribute rules
64 | *
65 | * @param string $attributeKey
66 | * @param string|array $rules
67 | * @return void
68 | */
69 | public function addAttribute(string $attributeKey, $rules)
70 | {
71 | $resolvedRules = $this->resolveRules($rules);
72 | $attribute = new Attribute($this, $attributeKey, $this->getAlias($attributeKey), $resolvedRules);
73 | $this->attributes[$attributeKey] = $attribute;
74 | }
75 |
76 | /**
77 | * Get attribute by key
78 | *
79 | * @param string $attributeKey
80 | * @return null|\Rakit\Validation\Attribute
81 | */
82 | public function getAttribute(string $attributeKey)
83 | {
84 | return isset($this->attributes[$attributeKey])? $this->attributes[$attributeKey] : null;
85 | }
86 |
87 | /**
88 | * Run validation
89 | *
90 | * @param array $inputs
91 | * @return void
92 | */
93 | public function validate(array $inputs = [])
94 | {
95 | $this->errors = new ErrorBag; // reset error bag
96 | $this->inputs = array_merge($this->inputs, $this->resolveInputAttributes($inputs));
97 |
98 | // Before validation hooks
99 | foreach ($this->attributes as $attributeKey => $attribute) {
100 | foreach ($attribute->getRules() as $rule) {
101 | if ($rule instanceof BeforeValidate) {
102 | $rule->beforeValidate();
103 | }
104 | }
105 | }
106 |
107 | foreach ($this->attributes as $attributeKey => $attribute) {
108 | $this->validateAttribute($attribute);
109 | }
110 | }
111 |
112 | /**
113 | * Get ErrorBag instance
114 | *
115 | * @return \Rakit\Validation\ErrorBag
116 | */
117 | public function errors(): ErrorBag
118 | {
119 | return $this->errors;
120 | }
121 |
122 | /**
123 | * Validate attribute
124 | *
125 | * @param \Rakit\Validation\Attribute $attribute
126 | * @return void
127 | */
128 | protected function validateAttribute(Attribute $attribute)
129 | {
130 | if ($this->isArrayAttribute($attribute)) {
131 | $attributes = $this->parseArrayAttribute($attribute);
132 | foreach ($attributes as $i => $attr) {
133 | $this->validateAttribute($attr);
134 | }
135 | return;
136 | }
137 |
138 | $attributeKey = $attribute->getKey();
139 | $rules = $attribute->getRules();
140 |
141 | $value = $this->getValue($attributeKey);
142 | $isEmptyValue = $this->isEmptyValue($value);
143 |
144 | if ($attribute->hasRule('nullable') && $isEmptyValue) {
145 | $rules = [];
146 | }
147 |
148 | $isValid = true;
149 | foreach ($rules as $ruleValidator) {
150 | $ruleValidator->setAttribute($attribute);
151 |
152 | if ($ruleValidator instanceof ModifyValue) {
153 | $value = $ruleValidator->modifyValue($value);
154 | $isEmptyValue = $this->isEmptyValue($value);
155 | }
156 |
157 | $valid = $ruleValidator->check($value);
158 |
159 | if ($isEmptyValue and $this->ruleIsOptional($attribute, $ruleValidator)) {
160 | continue;
161 | }
162 |
163 | if (!$valid) {
164 | $isValid = false;
165 | $this->addError($attribute, $value, $ruleValidator);
166 | if ($ruleValidator->isImplicit()) {
167 | break;
168 | }
169 | }
170 | }
171 |
172 | if ($isValid) {
173 | $this->setValidData($attribute, $value);
174 | } else {
175 | $this->setInvalidData($attribute, $value);
176 | }
177 | }
178 |
179 | /**
180 | * Check whether given $attribute is array attribute
181 | *
182 | * @param \Rakit\Validation\Attribute $attribute
183 | * @return bool
184 | */
185 | protected function isArrayAttribute(Attribute $attribute): bool
186 | {
187 | $key = $attribute->getKey();
188 | return strpos($key, '*') !== false;
189 | }
190 |
191 | /**
192 | * Parse array attribute into it's child attributes
193 | *
194 | * @param \Rakit\Validation\Attribute $attribute
195 | * @return array
196 | */
197 | protected function parseArrayAttribute(Attribute $attribute): array
198 | {
199 | $attributeKey = $attribute->getKey();
200 | $data = Helper::arrayDot($this->initializeAttributeOnData($attributeKey));
201 |
202 | $pattern = str_replace('\*', '([^\.]+)', preg_quote($attributeKey));
203 |
204 | $data = array_merge($data, $this->extractValuesForWildcards(
205 | $data,
206 | $attributeKey
207 | ));
208 |
209 | $attributes = [];
210 |
211 | foreach ($data as $key => $value) {
212 | if ((bool) preg_match('/^'.$pattern.'\z/', $key, $match)) {
213 | $attr = new Attribute($this, $key, null, $attribute->getRules());
214 | $attr->setPrimaryAttribute($attribute);
215 | $attr->setKeyIndexes(array_slice($match, 1));
216 | $attributes[] = $attr;
217 | }
218 | }
219 |
220 | // set other attributes to each attributes
221 | foreach ($attributes as $i => $attr) {
222 | $otherAttributes = $attributes;
223 | unset($otherAttributes[$i]);
224 | $attr->setOtherAttributes($otherAttributes);
225 | }
226 |
227 | return $attributes;
228 | }
229 |
230 | /**
231 | * Gather a copy of the attribute data filled with any missing attributes.
232 | * Adapted from: https://github.com/illuminate/validation/blob/v5.3.23/Validator.php#L334
233 | *
234 | * @param string $attribute
235 | * @return array
236 | */
237 | protected function initializeAttributeOnData(string $attributeKey): array
238 | {
239 | $explicitPath = $this->getLeadingExplicitAttributePath($attributeKey);
240 |
241 | $data = $this->extractDataFromPath($explicitPath);
242 |
243 | $asteriskPos = strpos($attributeKey, '*');
244 |
245 | if (false === $asteriskPos || $asteriskPos === (mb_strlen($attributeKey, 'UTF-8') - 1)) {
246 | return $data;
247 | }
248 |
249 | return Helper::arraySet($data, $attributeKey, null, true);
250 | }
251 |
252 | /**
253 | * Get all of the exact attribute values for a given wildcard attribute.
254 | * Adapted from: https://github.com/illuminate/validation/blob/v5.3.23/Validator.php#L354
255 | *
256 | * @param array $data
257 | * @param string $attributeKey
258 | * @return array
259 | */
260 | public function extractValuesForWildcards(array $data, string $attributeKey): array
261 | {
262 | $keys = [];
263 |
264 | $pattern = str_replace('\*', '[^\.]+', preg_quote($attributeKey));
265 |
266 | foreach ($data as $key => $value) {
267 | if ((bool) preg_match('/^'.$pattern.'/', $key, $matches)) {
268 | $keys[] = $matches[0];
269 | }
270 | }
271 |
272 | $keys = array_unique($keys);
273 |
274 | $data = [];
275 |
276 | foreach ($keys as $key) {
277 | $data[$key] = Helper::arrayGet($this->inputs, $key);
278 | }
279 |
280 | return $data;
281 | }
282 |
283 | /**
284 | * Get the explicit part of the attribute name.
285 | * Adapted from: https://github.com/illuminate/validation/blob/v5.3.23/Validator.php#L2817
286 | *
287 | * E.g. 'foo.bar.*.baz' -> 'foo.bar'
288 | *
289 | * Allows us to not spin through all of the flattened data for some operations.
290 | *
291 | * @param string $attributeKey
292 | * @return string|null null when root wildcard
293 | */
294 | protected function getLeadingExplicitAttributePath(string $attributeKey)
295 | {
296 | return rtrim(explode('*', $attributeKey)[0], '.') ?: null;
297 | }
298 |
299 | /**
300 | * Extract data based on the given dot-notated path.
301 | * Adapted from: https://github.com/illuminate/validation/blob/v5.3.23/Validator.php#L2830
302 | *
303 | * Used to extract a sub-section of the data for faster iteration.
304 | *
305 | * @param string|null $attributeKey
306 | * @return array
307 | */
308 | protected function extractDataFromPath($attributeKey): array
309 | {
310 | $results = [];
311 |
312 | $value = Helper::arrayGet($this->inputs, $attributeKey, '__missing__');
313 |
314 | if ($value != '__missing__') {
315 | Helper::arraySet($results, $attributeKey, $value);
316 | }
317 |
318 | return $results;
319 | }
320 |
321 | /**
322 | * Add error to the $this->errors
323 | *
324 | * @param \Rakit\Validation\Attribute $attribute
325 | * @param mixed $value
326 | * @param \Rakit\Validation\Rule $ruleValidator
327 | * @return void
328 | */
329 | protected function addError(Attribute $attribute, $value, Rule $ruleValidator)
330 | {
331 | $ruleName = $ruleValidator->getKey();
332 | $message = $this->resolveMessage($attribute, $value, $ruleValidator);
333 |
334 | $this->errors->add($attribute->getKey(), $ruleName, $message);
335 | }
336 |
337 | /**
338 | * Check $value is empty value
339 | *
340 | * @param mixed $value
341 | * @return boolean
342 | */
343 | protected function isEmptyValue($value): bool
344 | {
345 | $requiredValidator = new Required;
346 | return false === $requiredValidator->check($value, []);
347 | }
348 |
349 | /**
350 | * Check the rule is optional
351 | *
352 | * @param \Rakit\Validation\Attribute $attribute
353 | * @param \Rakit\Validation\Rule $rule
354 | * @return bool
355 | */
356 | protected function ruleIsOptional(Attribute $attribute, Rule $rule): bool
357 | {
358 | return false === $attribute->isRequired() and
359 | false === $rule->isImplicit() and
360 | false === $rule instanceof Required;
361 | }
362 |
363 | /**
364 | * Resolve attribute name
365 | *
366 | * @param \Rakit\Validation\Attribute $attribute
367 | * @return string
368 | */
369 | protected function resolveAttributeName(Attribute $attribute): string
370 | {
371 | $primaryAttribute = $attribute->getPrimaryAttribute();
372 | if (isset($this->aliases[$attribute->getKey()])) {
373 | return $this->aliases[$attribute->getKey()];
374 | } elseif ($primaryAttribute and isset($this->aliases[$primaryAttribute->getKey()])) {
375 | return $this->aliases[$primaryAttribute->getKey()];
376 | } elseif ($this->validator->isUsingHumanizedKey()) {
377 | return $attribute->getHumanizedKey();
378 | } else {
379 | return $attribute->getKey();
380 | }
381 | }
382 |
383 | /**
384 | * Resolve message
385 | *
386 | * @param \Rakit\Validation\Attribute $attribute
387 | * @param mixed $value
388 | * @param \Rakit\Validation\Rule $validator
389 | * @return mixed
390 | */
391 | protected function resolveMessage(Attribute $attribute, $value, Rule $validator): string
392 | {
393 | $primaryAttribute = $attribute->getPrimaryAttribute();
394 | $params = array_merge($validator->getParameters(), $validator->getParametersTexts());
395 | $attributeKey = $attribute->getKey();
396 | $ruleKey = $validator->getKey();
397 | $alias = $attribute->getAlias() ?: $this->resolveAttributeName($attribute);
398 | $message = $validator->getMessage(); // default rule message
399 | $messageKeys = [
400 | $attributeKey.$this->messageSeparator.$ruleKey,
401 | $attributeKey,
402 | $ruleKey
403 | ];
404 |
405 | if ($primaryAttribute) {
406 | // insert primaryAttribute keys
407 | // $messageKeys = [
408 | // $attributeKey.$this->messageSeparator.$ruleKey,
409 | // >> here [1] <<
410 | // $attributeKey,
411 | // >> and here [3] <<
412 | // $ruleKey
413 | // ];
414 | $primaryAttributeKey = $primaryAttribute->getKey();
415 | array_splice($messageKeys, 1, 0, $primaryAttributeKey.$this->messageSeparator.$ruleKey);
416 | array_splice($messageKeys, 3, 0, $primaryAttributeKey);
417 | }
418 |
419 | foreach ($messageKeys as $key) {
420 | if (isset($this->messages[$key])) {
421 | $message = $this->messages[$key];
422 | break;
423 | }
424 | }
425 |
426 | // Replace message params
427 | $vars = array_merge($params, [
428 | 'attribute' => $alias,
429 | 'value' => $value,
430 | ]);
431 |
432 | foreach ($vars as $key => $value) {
433 | $value = $this->stringify($value);
434 | $message = str_replace(':'.$key, $value, $message);
435 | }
436 |
437 | // Replace key indexes
438 | $keyIndexes = $attribute->getKeyIndexes();
439 | foreach ($keyIndexes as $pathIndex => $index) {
440 | $replacers = [
441 | "[{$pathIndex}]" => $index,
442 | ];
443 |
444 | if (is_numeric($index)) {
445 | $replacers["{{$pathIndex}}"] = $index + 1;
446 | }
447 |
448 | $message = str_replace(array_keys($replacers), array_values($replacers), $message);
449 | }
450 |
451 | return $message;
452 | }
453 |
454 | /**
455 | * Stringify $value
456 | *
457 | * @param mixed $value
458 | * @return string
459 | */
460 | protected function stringify($value): string
461 | {
462 | if (is_string($value) || is_numeric($value)) {
463 | return $value;
464 | } elseif (is_array($value) || is_object($value)) {
465 | return json_encode($value);
466 | } else {
467 | return '';
468 | }
469 | }
470 |
471 | /**
472 | * Resolve $rules
473 | *
474 | * @param mixed $rules
475 | * @return array
476 | */
477 | protected function resolveRules($rules): array
478 | {
479 | if (is_string($rules)) {
480 | $rules = explode('|', $rules);
481 | }
482 |
483 | $resolvedRules = [];
484 | $validatorFactory = $this->getValidator();
485 |
486 | foreach ($rules as $i => $rule) {
487 | if (empty($rule)) {
488 | continue;
489 | }
490 | $params = [];
491 |
492 | if (is_string($rule)) {
493 | list($rulename, $params) = $this->parseRule($rule);
494 | $validator = call_user_func_array($validatorFactory, array_merge([$rulename], $params));
495 | } elseif ($rule instanceof Rule) {
496 | $validator = $rule;
497 | } elseif ($rule instanceof Closure) {
498 | $validator = call_user_func_array($validatorFactory, ['callback', $rule]);
499 | } else {
500 | $ruleName = is_object($rule) ? get_class($rule) : gettype($rule);
501 | $message = "Rule must be a string, Closure or '".Rule::class."' instance. ".$ruleName." given";
502 | throw new \Exception();
503 | }
504 |
505 | $resolvedRules[] = $validator;
506 | }
507 |
508 | return $resolvedRules;
509 | }
510 |
511 | /**
512 | * Parse $rule
513 | *
514 | * @param string $rule
515 | * @return array
516 | */
517 | protected function parseRule(string $rule): array
518 | {
519 | $exp = explode(':', $rule, 2);
520 | $rulename = $exp[0];
521 | if ($rulename !== 'regex') {
522 | $params = isset($exp[1])? explode(',', $exp[1]) : [];
523 | } else {
524 | $params = [$exp[1]];
525 | }
526 |
527 | return [$rulename, $params];
528 | }
529 |
530 | /**
531 | * Given $attributeKey and $alias then assign alias
532 | *
533 | * @param mixed $attributeKey
534 | * @param mixed $alias
535 | * @return void
536 | */
537 | public function setAlias(string $attributeKey, string $alias)
538 | {
539 | $this->aliases[$attributeKey] = $alias;
540 | }
541 |
542 | /**
543 | * Get attribute alias from given key
544 | *
545 | * @param mixed $attributeKey
546 | * @return string|null
547 | */
548 | public function getAlias(string $attributeKey)
549 | {
550 | return isset($this->aliases[$attributeKey])? $this->aliases[$attributeKey] : null;
551 | }
552 |
553 | /**
554 | * Set attributes aliases
555 | *
556 | * @param array $aliases
557 | * @return void
558 | */
559 | public function setAliases(array $aliases)
560 | {
561 | $this->aliases = array_merge($this->aliases, $aliases);
562 | }
563 |
564 | /**
565 | * Check validations are passed
566 | *
567 | * @return bool
568 | */
569 | public function passes(): bool
570 | {
571 | return $this->errors->count() == 0;
572 | }
573 |
574 | /**
575 | * Check validations are failed
576 | *
577 | * @return bool
578 | */
579 | public function fails(): bool
580 | {
581 | return !$this->passes();
582 | }
583 |
584 | /**
585 | * Given $key and get value
586 | *
587 | * @param string $key
588 | * @return mixed
589 | */
590 | public function getValue(string $key)
591 | {
592 | return Helper::arrayGet($this->inputs, $key);
593 | }
594 |
595 | /**
596 | * Set input value
597 | *
598 | * @param string $key
599 | * @param mixed $value
600 | * @return void
601 | */
602 | public function setValue(string $key, $value)
603 | {
604 | Helper::arraySet($this->inputs, $key, $value);
605 | }
606 |
607 | /**
608 | * Given $key and check value is exsited
609 | *
610 | * @param string $key
611 | * @return boolean
612 | */
613 | public function hasValue(string $key): bool
614 | {
615 | return Helper::arrayHas($this->inputs, $key);
616 | }
617 |
618 | /**
619 | * Get Validator class instance
620 | *
621 | * @return \Rakit\Validation\Validator
622 | */
623 | public function getValidator(): Validator
624 | {
625 | return $this->validator;
626 | }
627 |
628 | /**
629 | * Given $inputs and resolve input attributes
630 | *
631 | * @param array $inputs
632 | * @return array
633 | */
634 | protected function resolveInputAttributes(array $inputs): array
635 | {
636 | $resolvedInputs = [];
637 | foreach ($inputs as $key => $rules) {
638 | $exp = explode(':', $key);
639 |
640 | if (count($exp) > 1) {
641 | // set attribute alias
642 | $this->aliases[$exp[0]] = $exp[1];
643 | }
644 |
645 | $resolvedInputs[$exp[0]] = $rules;
646 | }
647 |
648 | return $resolvedInputs;
649 | }
650 |
651 | /**
652 | * Get validated data
653 | *
654 | * @return array
655 | */
656 | public function getValidatedData(): array
657 | {
658 | return array_merge($this->validData, $this->invalidData);
659 | }
660 |
661 | /**
662 | * Set valid data
663 | *
664 | * @param \Rakit\Validation\Attribute $attribute
665 | * @param mixed $value
666 | * @return void
667 | */
668 | protected function setValidData(Attribute $attribute, $value)
669 | {
670 | $key = $attribute->getKey();
671 | if ($attribute->isArrayAttribute() || $attribute->isUsingDotNotation()) {
672 | Helper::arraySet($this->validData, $key, $value);
673 | Helper::arrayUnset($this->invalidData, $key);
674 | } else {
675 | $this->validData[$key] = $value;
676 | }
677 | }
678 |
679 | /**
680 | * Get valid data
681 | *
682 | * @return array
683 | */
684 | public function getValidData(): array
685 | {
686 | return $this->validData;
687 | }
688 |
689 | /**
690 | * Set invalid data
691 | *
692 | * @param \Rakit\Validation\Attribute $attribute
693 | * @param mixed $value
694 | * @return void
695 | */
696 | protected function setInvalidData(Attribute $attribute, $value)
697 | {
698 | $key = $attribute->getKey();
699 | if ($attribute->isArrayAttribute() || $attribute->isUsingDotNotation()) {
700 | Helper::arraySet($this->invalidData, $key, $value);
701 | Helper::arrayUnset($this->validData, $key);
702 | } else {
703 | $this->invalidData[$key] = $value;
704 | }
705 | }
706 |
707 | /**
708 | * Get invalid data
709 | *
710 | * @return void
711 | */
712 | public function getInvalidData(): array
713 | {
714 | return $this->invalidData;
715 | }
716 | }
717 |
--------------------------------------------------------------------------------
/src/Validator.php:
--------------------------------------------------------------------------------
1 | messages = $messages;
30 | $this->registerBaseValidators();
31 | }
32 |
33 | /**
34 | * Register or override existing validator
35 | *
36 | * @param mixed $key
37 | * @param \Rakit\Validation\Rule $rule
38 | * @return void
39 | */
40 | public function setValidator(string $key, Rule $rule)
41 | {
42 | $this->validators[$key] = $rule;
43 | $rule->setKey($key);
44 | }
45 |
46 | /**
47 | * Get validator object from given $key
48 | *
49 | * @param mixed $key
50 | * @return mixed
51 | */
52 | public function getValidator($key)
53 | {
54 | return isset($this->validators[$key]) ? $this->validators[$key] : null;
55 | }
56 |
57 | /**
58 | * Validate $inputs
59 | *
60 | * @param array $inputs
61 | * @param array $rules
62 | * @param array $messages
63 | * @return Validation
64 | */
65 | public function validate(array $inputs, array $rules, array $messages = []): Validation
66 | {
67 | $validation = $this->make($inputs, $rules, $messages);
68 | $validation->validate();
69 | return $validation;
70 | }
71 |
72 | /**
73 | * Given $inputs, $rules and $messages to make the Validation class instance
74 | *
75 | * @param array $inputs
76 | * @param array $rules
77 | * @param array $messages
78 | * @return Validation
79 | */
80 | public function make(array $inputs, array $rules, array $messages = []): Validation
81 | {
82 | $messages = array_merge($this->messages, $messages);
83 | $validation = new Validation($this, $inputs, $rules, $messages);
84 | $validation->setTranslations($this->getTranslations());
85 |
86 | return $validation;
87 | }
88 |
89 | /**
90 | * Magic invoke method to make Rule instance
91 | *
92 | * @param string $rule
93 | * @return Rule
94 | * @throws RuleNotFoundException
95 | */
96 | public function __invoke(string $rule): Rule
97 | {
98 | $args = func_get_args();
99 | $rule = array_shift($args);
100 | $params = $args;
101 | $validator = $this->getValidator($rule);
102 | if (!$validator) {
103 | throw new RuleNotFoundException("Validator '{$rule}' is not registered", 1);
104 | }
105 |
106 | $clonedValidator = clone $validator;
107 | $clonedValidator->fillParameters($params);
108 |
109 | return $clonedValidator;
110 | }
111 |
112 | /**
113 | * Initialize base validators array
114 | *
115 | * @return void
116 | */
117 | protected function registerBaseValidators()
118 | {
119 | $baseValidator = [
120 | 'required' => new Rules\Required,
121 | 'required_if' => new Rules\RequiredIf,
122 | 'required_unless' => new Rules\RequiredUnless,
123 | 'required_with' => new Rules\RequiredWith,
124 | 'required_without' => new Rules\RequiredWithout,
125 | 'required_with_all' => new Rules\RequiredWithAll,
126 | 'required_without_all' => new Rules\RequiredWithoutAll,
127 | 'email' => new Rules\Email,
128 | 'alpha' => new Rules\Alpha,
129 | 'numeric' => new Rules\Numeric,
130 | 'alpha_num' => new Rules\AlphaNum,
131 | 'alpha_dash' => new Rules\AlphaDash,
132 | 'alpha_spaces' => new Rules\AlphaSpaces,
133 | 'in' => new Rules\In,
134 | 'not_in' => new Rules\NotIn,
135 | 'min' => new Rules\Min,
136 | 'max' => new Rules\Max,
137 | 'between' => new Rules\Between,
138 | 'url' => new Rules\Url,
139 | 'integer' => new Rules\Integer,
140 | 'boolean' => new Rules\Boolean,
141 | 'ip' => new Rules\Ip,
142 | 'ipv4' => new Rules\Ipv4,
143 | 'ipv6' => new Rules\Ipv6,
144 | 'extension' => new Rules\Extension,
145 | 'array' => new Rules\TypeArray,
146 | 'same' => new Rules\Same,
147 | 'regex' => new Rules\Regex,
148 | 'date' => new Rules\Date,
149 | 'accepted' => new Rules\Accepted,
150 | 'present' => new Rules\Present,
151 | 'different' => new Rules\Different,
152 | 'uploaded_file' => new Rules\UploadedFile,
153 | 'mimes' => new Rules\Mimes,
154 | 'callback' => new Rules\Callback,
155 | 'before' => new Rules\Before,
156 | 'after' => new Rules\After,
157 | 'lowercase' => new Rules\Lowercase,
158 | 'uppercase' => new Rules\Uppercase,
159 | 'json' => new Rules\Json,
160 | 'digits' => new Rules\Digits,
161 | 'digits_between' => new Rules\DigitsBetween,
162 | 'defaults' => new Rules\Defaults,
163 | 'default' => new Rules\Defaults, // alias of defaults
164 | 'nullable' => new Rules\Nullable,
165 | ];
166 |
167 | foreach ($baseValidator as $key => $validator) {
168 | $this->setValidator($key, $validator);
169 | }
170 | }
171 |
172 | /**
173 | * Given $ruleName and $rule to add new validator
174 | *
175 | * @param string $ruleName
176 | * @param \Rakit\Validation\Rule $rule
177 | * @return void
178 | */
179 | public function addValidator(string $ruleName, Rule $rule)
180 | {
181 | if (!$this->allowRuleOverride && array_key_exists($ruleName, $this->validators)) {
182 | throw new RuleQuashException(
183 | "You cannot override a built in rule. You have to rename your rule"
184 | );
185 | }
186 |
187 | $this->setValidator($ruleName, $rule);
188 | }
189 |
190 | /**
191 | * Set rule can allow to be overrided
192 | *
193 | * @param boolean $status
194 | * @return void
195 | */
196 | public function allowRuleOverride(bool $status = false)
197 | {
198 | $this->allowRuleOverride = $status;
199 | }
200 |
201 | /**
202 | * Set this can use humanize keys
203 | *
204 | * @param boolean $useHumanizedKeys
205 | * @return void
206 | */
207 | public function setUseHumanizedKeys(bool $useHumanizedKeys = true)
208 | {
209 | $this->useHumanizedKeys = $useHumanizedKeys;
210 | }
211 |
212 | /**
213 | * Get $this->useHumanizedKeys value
214 | *
215 | * @return void
216 | */
217 | public function isUsingHumanizedKey(): bool
218 | {
219 | return $this->useHumanizedKeys;
220 | }
221 | }
222 |
--------------------------------------------------------------------------------