Email.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <?php
  2. namespace Illuminate\Validation\Rules;
  3. use Illuminate\Contracts\Validation\DataAwareRule;
  4. use Illuminate\Contracts\Validation\Rule;
  5. use Illuminate\Contracts\Validation\ValidatorAwareRule;
  6. use Illuminate\Support\Arr;
  7. use Illuminate\Support\Facades\Validator;
  8. use Illuminate\Support\Traits\Conditionable;
  9. use Illuminate\Support\Traits\Macroable;
  10. use InvalidArgumentException;
  11. class Email implements Rule, DataAwareRule, ValidatorAwareRule
  12. {
  13. use Conditionable, Macroable;
  14. public bool $validateMxRecord = false;
  15. public bool $preventSpoofing = false;
  16. public bool $nativeValidation = false;
  17. public bool $nativeValidationWithUnicodeAllowed = false;
  18. public bool $rfcCompliant = false;
  19. public bool $strictRfcCompliant = false;
  20. /**
  21. * The validator performing the validation.
  22. *
  23. * @var \Illuminate\Validation\Validator
  24. */
  25. protected $validator;
  26. /**
  27. * The data under validation.
  28. *
  29. * @var array
  30. */
  31. protected $data;
  32. /**
  33. * An array of custom rules that will be merged into the validation rules.
  34. *
  35. * @var array
  36. */
  37. protected $customRules = [];
  38. /**
  39. * The error message after validation, if any.
  40. *
  41. * @var array
  42. */
  43. protected $messages = [];
  44. /**
  45. * The callback that will generate the "default" version of the email rule.
  46. *
  47. * @var string|array|callable|null
  48. */
  49. public static $defaultCallback;
  50. /**
  51. * Set the default callback to be used for determining the email default rules.
  52. *
  53. * If no arguments are passed, the default email rule configuration will be returned.
  54. *
  55. * @param static|callable|null $callback
  56. * @return static|void
  57. *
  58. * @phpstan-return ($callback is null ? static : ($callback is callable ? void : ($callback is static ? void : never)))
  59. *
  60. * @throws \InvalidArgumentException
  61. */
  62. public static function defaults($callback = null)
  63. {
  64. if (is_null($callback)) {
  65. return static::default();
  66. }
  67. if (! is_callable($callback) && ! $callback instanceof static) {
  68. throw new InvalidArgumentException('The given callback should be callable or an instance of '.static::class);
  69. }
  70. static::$defaultCallback = $callback;
  71. }
  72. /**
  73. * Get the default configuration of the email rule.
  74. *
  75. * @return static
  76. */
  77. public static function default()
  78. {
  79. $email = is_callable(static::$defaultCallback)
  80. ? call_user_func(static::$defaultCallback)
  81. : static::$defaultCallback;
  82. return $email instanceof static ? $email : new static;
  83. }
  84. /**
  85. * Ensure that the email is an RFC compliant email address.
  86. *
  87. * @param bool $strict
  88. * @return $this
  89. */
  90. public function rfcCompliant(bool $strict = false)
  91. {
  92. if ($strict) {
  93. $this->strictRfcCompliant = true;
  94. } else {
  95. $this->rfcCompliant = true;
  96. }
  97. return $this;
  98. }
  99. /**
  100. * Ensure that the email is a strictly enforced RFC compliant email address.
  101. *
  102. * @return $this
  103. */
  104. public function strict()
  105. {
  106. return $this->rfcCompliant(true);
  107. }
  108. /**
  109. * Ensure that the email address has a valid MX record.
  110. *
  111. * Requires the PHP intl extension.
  112. *
  113. * @return $this
  114. */
  115. public function validateMxRecord()
  116. {
  117. $this->validateMxRecord = true;
  118. return $this;
  119. }
  120. /**
  121. * Ensure that the email address is not attempting to spoof another email address using invalid unicode characters.
  122. *
  123. * @return $this
  124. */
  125. public function preventSpoofing()
  126. {
  127. $this->preventSpoofing = true;
  128. return $this;
  129. }
  130. /**
  131. * Ensure the email address is valid using PHP's native email validation functions.
  132. *
  133. * @param bool $allowUnicode
  134. * @return $this
  135. */
  136. public function withNativeValidation(bool $allowUnicode = false)
  137. {
  138. if ($allowUnicode) {
  139. $this->nativeValidationWithUnicodeAllowed = true;
  140. } else {
  141. $this->nativeValidation = true;
  142. }
  143. return $this;
  144. }
  145. /**
  146. * Specify additional validation rules that should be merged with the default rules during validation.
  147. *
  148. * @param string|array $rules
  149. * @return $this
  150. */
  151. public function rules($rules)
  152. {
  153. $this->customRules = array_merge($this->customRules, Arr::wrap($rules));
  154. return $this;
  155. }
  156. /**
  157. * Determine if the validation rule passes.
  158. *
  159. * @param string $attribute
  160. * @param mixed $value
  161. * @return bool
  162. */
  163. public function passes($attribute, $value)
  164. {
  165. $this->messages = [];
  166. $validator = Validator::make(
  167. $this->data,
  168. [$attribute => $this->buildValidationRules()],
  169. $this->validator->customMessages,
  170. $this->validator->customAttributes
  171. );
  172. if ($validator->fails()) {
  173. $this->messages = array_merge($this->messages, $validator->messages()->all());
  174. return false;
  175. }
  176. return true;
  177. }
  178. /**
  179. * Build the array of underlying validation rules based on the current state.
  180. *
  181. * @return array
  182. */
  183. protected function buildValidationRules()
  184. {
  185. $rules = [];
  186. if ($this->rfcCompliant) {
  187. $rules[] = 'rfc';
  188. }
  189. if ($this->strictRfcCompliant) {
  190. $rules[] = 'strict';
  191. }
  192. if ($this->validateMxRecord) {
  193. $rules[] = 'dns';
  194. }
  195. if ($this->preventSpoofing) {
  196. $rules[] = 'spoof';
  197. }
  198. if ($this->nativeValidation) {
  199. $rules[] = 'filter';
  200. }
  201. if ($this->nativeValidationWithUnicodeAllowed) {
  202. $rules[] = 'filter_unicode';
  203. }
  204. if ($rules) {
  205. $rules = ['email:'.implode(',', $rules)];
  206. } else {
  207. $rules = ['email'];
  208. }
  209. return array_merge(array_filter($rules), $this->customRules);
  210. }
  211. /**
  212. * Get the validation error message.
  213. *
  214. * @return array
  215. */
  216. public function message()
  217. {
  218. return $this->messages;
  219. }
  220. /**
  221. * Set the current validator.
  222. *
  223. * @param \Illuminate\Contracts\Validation\Validator $validator
  224. * @return $this
  225. */
  226. public function setValidator($validator)
  227. {
  228. $this->validator = $validator;
  229. return $this;
  230. }
  231. /**
  232. * Set the current data under validation.
  233. *
  234. * @param array $data
  235. * @return $this
  236. */
  237. public function setData($data)
  238. {
  239. $this->data = $data;
  240. return $this;
  241. }
  242. }