LocalPart.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <?php
  2. namespace Egulias\EmailValidator\Parser;
  3. use Egulias\EmailValidator\Exception\DotAtEnd;
  4. use Egulias\EmailValidator\Exception\DotAtStart;
  5. use Egulias\EmailValidator\EmailLexer;
  6. use Egulias\EmailValidator\Exception\ExpectingAT;
  7. use Egulias\EmailValidator\Exception\ExpectingATEXT;
  8. use Egulias\EmailValidator\Exception\UnclosedQuotedString;
  9. use Egulias\EmailValidator\Exception\UnopenedComment;
  10. use Egulias\EmailValidator\Warning\CFWSWithFWS;
  11. use Egulias\EmailValidator\Warning\LocalTooLong;
  12. class LocalPart extends Parser
  13. {
  14. public function parse($localPart)
  15. {
  16. $parseDQuote = true;
  17. $closingQuote = false;
  18. $openedParenthesis = 0;
  19. $totalLength = 0;
  20. while ($this->lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) {
  21. if ($this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type']) {
  22. throw new DotAtStart();
  23. }
  24. $closingQuote = $this->checkDQUOTE($closingQuote);
  25. if ($closingQuote && $parseDQuote) {
  26. $parseDQuote = $this->parseDoubleQuote();
  27. }
  28. if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) {
  29. $this->parseComments();
  30. $openedParenthesis += $this->getOpenedParenthesis();
  31. }
  32. if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) {
  33. if ($openedParenthesis === 0) {
  34. throw new UnopenedComment();
  35. }
  36. $openedParenthesis--;
  37. }
  38. $this->checkConsecutiveDots();
  39. if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
  40. $this->lexer->isNextToken(EmailLexer::S_AT)
  41. ) {
  42. throw new DotAtEnd();
  43. }
  44. $this->warnEscaping();
  45. $this->isInvalidToken($this->lexer->token, $closingQuote);
  46. if ($this->isFWS()) {
  47. $this->parseFWS();
  48. }
  49. $totalLength += strlen($this->lexer->token['value']);
  50. $this->lexer->moveNext();
  51. }
  52. if ($totalLength > LocalTooLong::LOCAL_PART_LENGTH) {
  53. $this->warnings[LocalTooLong::CODE] = new LocalTooLong();
  54. }
  55. }
  56. /**
  57. * @return bool
  58. */
  59. protected function parseDoubleQuote()
  60. {
  61. $parseAgain = true;
  62. $special = array(
  63. EmailLexer::S_CR => true,
  64. EmailLexer::S_HTAB => true,
  65. EmailLexer::S_LF => true
  66. );
  67. $invalid = array(
  68. EmailLexer::C_NUL => true,
  69. EmailLexer::S_HTAB => true,
  70. EmailLexer::S_CR => true,
  71. EmailLexer::S_LF => true
  72. );
  73. $setSpecialsWarning = true;
  74. $this->lexer->moveNext();
  75. while ($this->lexer->token['type'] !== EmailLexer::S_DQUOTE && null !== $this->lexer->token['type']) {
  76. $parseAgain = false;
  77. if (isset($special[$this->lexer->token['type']]) && $setSpecialsWarning) {
  78. $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS();
  79. $setSpecialsWarning = false;
  80. }
  81. if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH && $this->lexer->isNextToken(EmailLexer::S_DQUOTE)) {
  82. $this->lexer->moveNext();
  83. }
  84. $this->lexer->moveNext();
  85. if (!$this->escaped() && isset($invalid[$this->lexer->token['type']])) {
  86. throw new ExpectingATEXT();
  87. }
  88. }
  89. $prev = $this->lexer->getPrevious();
  90. if ($prev['type'] === EmailLexer::S_BACKSLASH) {
  91. if (!$this->checkDQUOTE(false)) {
  92. throw new UnclosedQuotedString();
  93. }
  94. }
  95. if (!$this->lexer->isNextToken(EmailLexer::S_AT) && $prev['type'] !== EmailLexer::S_BACKSLASH) {
  96. throw new ExpectingAT();
  97. }
  98. return $parseAgain;
  99. }
  100. /**
  101. * @param bool $closingQuote
  102. */
  103. protected function isInvalidToken(array $token, $closingQuote)
  104. {
  105. $forbidden = array(
  106. EmailLexer::S_COMMA,
  107. EmailLexer::S_CLOSEBRACKET,
  108. EmailLexer::S_OPENBRACKET,
  109. EmailLexer::S_GREATERTHAN,
  110. EmailLexer::S_LOWERTHAN,
  111. EmailLexer::S_COLON,
  112. EmailLexer::S_SEMICOLON,
  113. EmailLexer::INVALID
  114. );
  115. if (in_array($token['type'], $forbidden) && !$closingQuote) {
  116. throw new ExpectingATEXT();
  117. }
  118. }
  119. }