Almaz Mubinov

Ставим точку над темой валидацей email на PHP

11 Июл 2012 г. 

При разработке веб-сервисов одной из частых рутинных задач является валидация email на корректность. Многие разработчики не видят в этом проблемы и просто вставляют какое-то готовое решение. Встречаясь с нагруженными сервисами, с большим потоком пользователей, понимаешь, что не все готовые решения «одинаково полезны». Зачем нам вообще нужна валидация email? Она нужна прежде всего пользователю, чтобы он мог подтвердить свой email, чтобы ему на его адрес приходили важные сообщения, чтобы он мог восстановить доступ к сервису в случае утери пароля. К тому же на многих ресурсах email используется как главный идентификатор пользователя.

Много раз встречал, что функция валидации email либо некорректна, либо имеет существенные недостатки.
Email, используемый пользователем при регистрации должен быть корректным с точки зрения международного стандарта RFC 2822, но это не всегда возможно.

Соответствие стандартам

Есть несколько путей проверять email на соответствие стандартам:

1) Самый простой и доступный способ для PHP >= 5.2 это использование filter_var

  1. function filter_var_wrapper($email)
  2. {
  3. return (bool) (filter_var($email, FILTER_VALIDATE_EMAIL) === $email );
  4. }

Внимание! После filter_var() результат необходимо сравнивать эквивалентно (===) с исходным значением, так как после фильтрации может вернуться измененный email.

Главный минус этого способа — это отсечение некоторых реально существующих email. Например, bob.@marley.com. Точка на конце левой части email является нарушением RFC, однако популярный российский почтовый сервер mail.ru разрешал регистрировать такие email вплоть до 2010 года. Таким образом, существует процент пользователей, которые до сих пор пользуются такими почтовыми ящиками, только я честно не понимаю, как им удается регистрироваться на большинстве сайтов.

Еще один пример из наследия mail.ru — super..mario@example.com. Две точки подряд — это тоже нарушение, но с этими пользователями приходится считаться. К счастью, теперь нельзя регистрировать на mail.ru подобные аккаунты.

Еще один неприятный пример, это i.love@ice--cream.com. Домен c двумя дефисами признан не валидным, а значит и все пользователи, использующие email там, ограничены в правах… Стоп, Вы не верите в существование таких доменов? А попробуйте http://1--0.com/ . Не впечатляет? А как насчет http://online---casino.com ? Здесь целых три дефиса подряд. А ведь домены рабочие и соответствуют стандарту.

2) Для строго соответствия RFC 2822 Вы можете использовать код открытого проекта RFC 822 Email Address Parser in PHP. Ссылка на исходный код https://github.com/iamcal/rfc822/blob/master/rfc822.php.
Использование очень простое:

  1. include_once(«rfc822.php»);
  2. (bool) is_valid_email_address($some_email);

Этот способ решает проблемы доменов, а также многие другие недочеты в официальном варианте PHP filter_var(). Однако он не убережет от преставленного выше способа с двумя точками в имени пользователя, или точки на конце. (еще один луч в сторону mail.ru)

3) Третий способ. Мой любимый и используемый.

  1. function regexp_email_wrapper($email){
  2. return (bool) preg_match('/^(?!(?:(?:\x22?\x5C[\x00—\x7E]\
  3. x22?)|(?:\x22?[^\x5C\x22]\x22?)){255,})(?!(?:(?:\x22?\x5C[\x
  4. 00-\x7E]\x22?)|(?:\x22?[^\x5C\x22]\x22?)){65,}@)(?:(?:[\.\x
  5. 21\x23—\x27\x2A\x2B\x2D\x2F—\x39\x3D\x3F\x5E—\x7E]+)|(?:
  6. \x22(?:[\x01—\x08\x0B\x0C\x0E—\x1F\x21\x23—\x5B\x5D—\x7
  7. F]|(?:\x5C[\x00—\x7F]))*\x22))(?:\.(?:(?:[\.\x21\x23—\x27
  8. x2A\x2B\x2D\x2F—\x39\x3D\x3F\x5E—\x7E]+)|(?:\x22(?:[\x01
  9. —\x08\x0B\x0C\x0E—\x1F\x21\x23—\x5B\x5D—\x7F]|(?:\x5C[\
  10. x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]
  11. +(?:-+[a-z0-9]+)*\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[
  12. a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?:
  13. :[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}
  14. (?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})
  15. ?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?
  16. :.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-
  17. f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-
  18. 9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-
  19. 9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\]))$/iD',
  20. $email);
  21. }

Копия на pastebin: http://pastebin.com/AsuRG46e. Это регулярное выражение, из исходников PHP. Оно было переведено с С++ на PHP и дополнено правками, исправляющими те проблемы о которых я писал выше. Таким образом, эта регулярка нормально валидирует email по RFC 2822, разрешает использование нескольких дефисов, а также позволяет использовать в части имени пользователя email две точки и точку на конце. Не забывайте, что регулярки не содержат переносов строк. Будьте аккуратны при копировании.

Это регулярное выражение работает довольно быстро, несмотря на внешнюю монстроузность. А если Вы используете акселераторы, такие как APC, eAccelerator или XCache, то этот код работает даже быстрее, чем filter_var().


Разработка

Комментарии

  1. Станислав
    13 июля 2012 г. в 23:50

    эээ… не понял последний пункт. Это ты написал такое регулярное выражение??? Монстр.
    Кстати работает! Спасибо!

  2. Амир Ибрагимов
    26 сентября 2012 г. в 21:05

    Просто класс. Спасибо, Алмаз, всё работает.

  3. Имя (обязательно)
    27 февраля 2013 г. в 15:40

    Это кто сказал, что работает? Пробелы убраны, валидация всегда false!

  4. Almaz Mubinov
    27 февраля 2013 г. в 21:02

    Возможно некорректно скопировали код? Попробуйте скопировать код без форматирования http://pastebin.com/AsuRG46e

Almaz Mubinov © 2010-2023