LocalPart.php 5.52 KB
Newer Older
jiangbowen's avatar
jiangbowen committed
1 2 3 4 5
<?php

namespace Egulias\EmailValidator\Parser;

use Egulias\EmailValidator\EmailLexer;
jiangbowen's avatar
jiangbowen committed
6 7 8
use Egulias\EmailValidator\Result\Result;
use Egulias\EmailValidator\Result\ValidEmail;
use Egulias\EmailValidator\Result\InvalidEmail;
jiangbowen's avatar
jiangbowen committed
9
use Egulias\EmailValidator\Warning\LocalTooLong;
jiangbowen's avatar
jiangbowen committed
10 11 12 13 14
use Egulias\EmailValidator\Result\Reason\DotAtEnd;
use Egulias\EmailValidator\Result\Reason\DotAtStart;
use Egulias\EmailValidator\Result\Reason\ConsecutiveDot;
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
use Egulias\EmailValidator\Parser\CommentStrategy\LocalComment;
jiangbowen's avatar
jiangbowen committed
15

jiangbowen's avatar
jiangbowen committed
16
class LocalPart extends PartParser
jiangbowen's avatar
jiangbowen committed
17
{
jiangbowen's avatar
jiangbowen committed
18 19 20 21 22 23 24
    /**
     * @var string
     */
    private $localPart = '';


    public function parse() : Result
jiangbowen's avatar
jiangbowen committed
25
    {
jiangbowen's avatar
jiangbowen committed
26
        $this->lexer->startRecording();
jiangbowen's avatar
jiangbowen committed
27 28

        while ($this->lexer->token['type'] !== EmailLexer::S_AT && null !== $this->lexer->token['type']) {
jiangbowen's avatar
jiangbowen committed
29 30
            if ($this->hasDotAtStart()) {
                return new InvalidEmail(new DotAtStart(), $this->lexer->token['value']);
jiangbowen's avatar
jiangbowen committed
31 32
            }

jiangbowen's avatar
jiangbowen committed
33 34
            if ($this->lexer->token['type'] === EmailLexer::S_DQUOTE) {
                $dquoteParsingResult = $this->parseDoubleQuote();
jiangbowen's avatar
jiangbowen committed
35

jiangbowen's avatar
jiangbowen committed
36 37 38 39
                //Invalid double quote parsing
                if($dquoteParsingResult->isInvalid()) {
                    return $dquoteParsingResult;
                }
jiangbowen's avatar
jiangbowen committed
40 41
            }

jiangbowen's avatar
jiangbowen committed
42 43 44
            if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS || 
                $this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS ) {
                $commentsResult = $this->parseComments();
jiangbowen's avatar
jiangbowen committed
45

jiangbowen's avatar
jiangbowen committed
46 47 48 49
                //Invalid comment parsing
                if($commentsResult->isInvalid()) {
                    return $commentsResult;
                }
jiangbowen's avatar
jiangbowen committed
50 51
            }

jiangbowen's avatar
jiangbowen committed
52 53 54
            if ($this->lexer->token['type'] === EmailLexer::S_DOT && $this->lexer->isNextToken(EmailLexer::S_DOT)) {
                return new InvalidEmail(new ConsecutiveDot(), $this->lexer->token['value']);
            }
jiangbowen's avatar
jiangbowen committed
55 56 57 58

            if ($this->lexer->token['type'] === EmailLexer::S_DOT &&
                $this->lexer->isNextToken(EmailLexer::S_AT)
            ) {
jiangbowen's avatar
jiangbowen committed
59
                return new InvalidEmail(new DotAtEnd(), $this->lexer->token['value']);
jiangbowen's avatar
jiangbowen committed
60 61
            }

jiangbowen's avatar
jiangbowen committed
62 63 64 65 66 67 68 69 70
            $resultEscaping = $this->validateEscaping();
            if ($resultEscaping->isInvalid()) {
                return $resultEscaping;
            }

            $resultToken = $this->validateTokens(false);
            if ($resultToken->isInvalid()) {
                return $resultToken;
            }
jiangbowen's avatar
jiangbowen committed
71

jiangbowen's avatar
jiangbowen committed
72 73 74
            $resultFWS = $this->parseLocalFWS();
            if($resultFWS->isInvalid()) {
                return $resultFWS;
jiangbowen's avatar
jiangbowen committed
75 76 77 78 79
            }

            $this->lexer->moveNext();
        }

jiangbowen's avatar
jiangbowen committed
80 81 82
        $this->lexer->stopRecording();
        $this->localPart = rtrim($this->lexer->getAccumulatedValues(), '@');
        if (strlen($this->localPart) > LocalTooLong::LOCAL_PART_LENGTH) {
jiangbowen's avatar
jiangbowen committed
83 84
            $this->warnings[LocalTooLong::CODE] = new LocalTooLong();
        }
jiangbowen's avatar
jiangbowen committed
85 86

        return new ValidEmail();
jiangbowen's avatar
jiangbowen committed
87 88
    }

jiangbowen's avatar
jiangbowen committed
89
    protected function validateTokens(bool $hasComments) : Result
jiangbowen's avatar
jiangbowen committed
90
    {
jiangbowen's avatar
jiangbowen committed
91 92 93 94 95 96 97 98 99
        $invalidTokens = array(
            EmailLexer::S_COMMA => EmailLexer::S_COMMA,
            EmailLexer::S_CLOSEBRACKET => EmailLexer::S_CLOSEBRACKET,
            EmailLexer::S_OPENBRACKET => EmailLexer::S_OPENBRACKET,
            EmailLexer::S_GREATERTHAN => EmailLexer::S_GREATERTHAN,
            EmailLexer::S_LOWERTHAN => EmailLexer::S_LOWERTHAN,
            EmailLexer::S_COLON => EmailLexer::S_COLON,
            EmailLexer::S_SEMICOLON => EmailLexer::S_SEMICOLON,
            EmailLexer::INVALID => EmailLexer::INVALID
jiangbowen's avatar
jiangbowen committed
100
        );
jiangbowen's avatar
jiangbowen committed
101 102
        if (isset($invalidTokens[$this->lexer->token['type']])) {
            return new InvalidEmail(new ExpectingATEXT('Invalid token found'), $this->lexer->token['value']);
jiangbowen's avatar
jiangbowen committed
103
        }
jiangbowen's avatar
jiangbowen committed
104 105
        return new ValidEmail();
    }
jiangbowen's avatar
jiangbowen committed
106

jiangbowen's avatar
jiangbowen committed
107 108 109 110
    public function localPart() : string
    {
        return $this->localPart;
    }
jiangbowen's avatar
jiangbowen committed
111

jiangbowen's avatar
jiangbowen committed
112 113 114 115 116 117
    private function parseLocalFWS() : Result 
    {
        $foldingWS = new FoldingWhiteSpace($this->lexer);
        $resultFWS = $foldingWS->parse();
        if ($resultFWS->isValid()) {
            $this->warnings = array_merge($this->warnings, $foldingWS->getWarnings());
jiangbowen's avatar
jiangbowen committed
118
        }
jiangbowen's avatar
jiangbowen committed
119 120
        return $resultFWS;
    }
jiangbowen's avatar
jiangbowen committed
121

jiangbowen's avatar
jiangbowen committed
122 123 124 125 126 127 128 129 130 131
    private function hasDotAtStart() : bool
    {
            return $this->lexer->token['type'] === EmailLexer::S_DOT && null === $this->lexer->getPrevious()['type'];
    }

    private function parseDoubleQuote() : Result
    {
        $dquoteParser = new DoubleQuote($this->lexer);
        $parseAgain = $dquoteParser->parse();
        $this->warnings = array_merge($this->warnings, $dquoteParser->getWarnings());
jiangbowen's avatar
jiangbowen committed
132 133 134 135

        return $parseAgain;
    }

jiangbowen's avatar
jiangbowen committed
136
    protected function parseComments(): Result
jiangbowen's avatar
jiangbowen committed
137
    {
jiangbowen's avatar
jiangbowen committed
138 139 140 141 142 143 144 145
        $commentParser = new Comment($this->lexer, new LocalComment());
        $result = $commentParser->parse();
        $this->warnings = array_merge($this->warnings, $commentParser->getWarnings());
        if($result->isInvalid()) {
            return $result;
        }
        return $result;
    }
jiangbowen's avatar
jiangbowen committed
146

jiangbowen's avatar
jiangbowen committed
147 148 149 150 151
    private function validateEscaping() : Result
    {
        //Backslash found
        if ($this->lexer->token['type'] !== EmailLexer::S_BACKSLASH) {
            return new ValidEmail();
jiangbowen's avatar
jiangbowen committed
152
        }
jiangbowen's avatar
jiangbowen committed
153 154 155 156 157 158 159 160 161 162

        if ($this->lexer->isNextToken(EmailLexer::GENERIC)) {
            return new InvalidEmail(new ExpectingATEXT('Found ATOM after escaping'), $this->lexer->token['value']);
        }

        if (!$this->lexer->isNextTokenAny(array(EmailLexer::S_SP, EmailLexer::S_HTAB, EmailLexer::C_DEL))) {
            return new ValidEmail();
        }

        return new ValidEmail();
jiangbowen's avatar
jiangbowen committed
163
    }
jiangbowen's avatar
jiangbowen committed
164
}