[ Index ] |
MailPress 7.2 |
[ Index ] [ Classes ] [ Functions ] [ Variables ] [ Constants ] [ Statistics ] |
[Summary view] [Print] [Text view]
1 <?php 2 3 namespace Egulias\EmailValidator\Parser; 4 5 use Egulias\EmailValidator\EmailLexer; 6 use Egulias\EmailValidator\Exception\CharNotAllowed; 7 use Egulias\EmailValidator\Exception\CommaInDomain; 8 use Egulias\EmailValidator\Exception\ConsecutiveAt; 9 use Egulias\EmailValidator\Exception\CRLFAtTheEnd; 10 use Egulias\EmailValidator\Exception\CRNoLF; 11 use Egulias\EmailValidator\Exception\DomainHyphened; 12 use Egulias\EmailValidator\Exception\DotAtEnd; 13 use Egulias\EmailValidator\Exception\DotAtStart; 14 use Egulias\EmailValidator\Exception\ExpectingATEXT; 15 use Egulias\EmailValidator\Exception\ExpectingDomainLiteralClose; 16 use Egulias\EmailValidator\Exception\ExpectingDTEXT; 17 use Egulias\EmailValidator\Exception\NoDomainPart; 18 use Egulias\EmailValidator\Exception\UnopenedComment; 19 use Egulias\EmailValidator\Warning\AddressLiteral; 20 use Egulias\EmailValidator\Warning\CFWSWithFWS; 21 use Egulias\EmailValidator\Warning\DeprecatedComment; 22 use Egulias\EmailValidator\Warning\DomainLiteral; 23 use Egulias\EmailValidator\Warning\DomainTooLong; 24 use Egulias\EmailValidator\Warning\IPV6BadChar; 25 use Egulias\EmailValidator\Warning\IPV6ColonEnd; 26 use Egulias\EmailValidator\Warning\IPV6ColonStart; 27 use Egulias\EmailValidator\Warning\IPV6Deprecated; 28 use Egulias\EmailValidator\Warning\IPV6DoubleColon; 29 use Egulias\EmailValidator\Warning\IPV6GroupCount; 30 use Egulias\EmailValidator\Warning\IPV6MaxGroups; 31 use Egulias\EmailValidator\Warning\LabelTooLong; 32 use Egulias\EmailValidator\Warning\ObsoleteDTEXT; 33 use Egulias\EmailValidator\Warning\TLD; 34 35 class DomainPart extends Parser 36 { 37 const DOMAIN_MAX_LENGTH = 254; 38 protected $domainPart = ''; 39 40 public function parse($domainPart) 41 { 42 $this->lexer->moveNext(); 43 44 if ($this->lexer->token['type'] === EmailLexer::S_DOT) { 45 throw new DotAtStart(); 46 } 47 48 if ($this->lexer->token['type'] === EmailLexer::S_EMPTY) { 49 throw new NoDomainPart(); 50 } 51 if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN) { 52 throw new DomainHyphened(); 53 } 54 55 if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { 56 $this->warnings[DeprecatedComment::CODE] = new DeprecatedComment(); 57 $this->parseDomainComments(); 58 } 59 60 $domain = $this->doParseDomainPart(); 61 62 $prev = $this->lexer->getPrevious(); 63 $length = strlen($domain); 64 65 if ($prev['type'] === EmailLexer::S_DOT) { 66 throw new DotAtEnd(); 67 } 68 if ($prev['type'] === EmailLexer::S_HYPHEN) { 69 throw new DomainHyphened(); 70 } 71 if ($length > self::DOMAIN_MAX_LENGTH) { 72 $this->warnings[DomainTooLong::CODE] = new DomainTooLong(); 73 } 74 if ($prev['type'] === EmailLexer::S_CR) { 75 throw new CRLFAtTheEnd(); 76 } 77 $this->domainPart = $domain; 78 } 79 80 public function getDomainPart() 81 { 82 return $this->domainPart; 83 } 84 85 public function checkIPV6Tag($addressLiteral, $maxGroups = 8) 86 { 87 $prev = $this->lexer->getPrevious(); 88 if ($prev['type'] === EmailLexer::S_COLON) { 89 $this->warnings[IPV6ColonEnd::CODE] = new IPV6ColonEnd(); 90 } 91 92 $IPv6 = substr($addressLiteral, 5); 93 //Daniel Marschall's new IPv6 testing strategy 94 $matchesIP = explode(':', $IPv6); 95 $groupCount = count($matchesIP); 96 $colons = strpos($IPv6, '::'); 97 98 if (count(preg_grep('/^[0-9A-Fa-f]{0,4}$/', $matchesIP, PREG_GREP_INVERT)) !== 0) { 99 $this->warnings[IPV6BadChar::CODE] = new IPV6BadChar(); 100 } 101 102 if ($colons === false) { 103 // We need exactly the right number of groups 104 if ($groupCount !== $maxGroups) { 105 $this->warnings[IPV6GroupCount::CODE] = new IPV6GroupCount(); 106 } 107 return; 108 } 109 110 if ($colons !== strrpos($IPv6, '::')) { 111 $this->warnings[IPV6DoubleColon::CODE] = new IPV6DoubleColon(); 112 return; 113 } 114 115 if ($colons === 0 || $colons === (strlen($IPv6) - 2)) { 116 // RFC 4291 allows :: at the start or end of an address 117 //with 7 other groups in addition 118 ++$maxGroups; 119 } 120 121 if ($groupCount > $maxGroups) { 122 $this->warnings[IPV6MaxGroups::CODE] = new IPV6MaxGroups(); 123 } elseif ($groupCount === $maxGroups) { 124 $this->warnings[IPV6Deprecated::CODE] = new IPV6Deprecated(); 125 } 126 } 127 128 protected function doParseDomainPart() 129 { 130 $domain = ''; 131 $openedParenthesis = 0; 132 do { 133 $prev = $this->lexer->getPrevious(); 134 135 $this->checkNotAllowedChars($this->lexer->token); 136 137 if ($this->lexer->token['type'] === EmailLexer::S_OPENPARENTHESIS) { 138 $this->parseComments(); 139 $openedParenthesis += $this->getOpenedParenthesis(); 140 $this->lexer->moveNext(); 141 $tmpPrev = $this->lexer->getPrevious(); 142 if ($tmpPrev['type'] === EmailLexer::S_CLOSEPARENTHESIS) { 143 $openedParenthesis--; 144 } 145 } 146 if ($this->lexer->token['type'] === EmailLexer::S_CLOSEPARENTHESIS) { 147 if ($openedParenthesis === 0) { 148 throw new UnopenedComment(); 149 } else { 150 $openedParenthesis--; 151 } 152 } 153 154 $this->checkConsecutiveDots(); 155 $this->checkDomainPartExceptions($prev); 156 157 if ($this->hasBrackets()) { 158 $this->parseDomainLiteral(); 159 } 160 161 $this->checkLabelLength($prev); 162 163 if ($this->isFWS()) { 164 $this->parseFWS(); 165 } 166 167 $domain .= $this->lexer->token['value']; 168 $this->lexer->moveNext(); 169 } while ($this->lexer->token); 170 171 return $domain; 172 } 173 174 private function checkNotAllowedChars($token) 175 { 176 $notAllowed = [EmailLexer::S_BACKSLASH => true, EmailLexer::S_SLASH=> true]; 177 if (isset($notAllowed[$token['type']])) { 178 throw new CharNotAllowed(); 179 } 180 } 181 182 protected function parseDomainLiteral() 183 { 184 if ($this->lexer->isNextToken(EmailLexer::S_COLON)) { 185 $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart(); 186 } 187 if ($this->lexer->isNextToken(EmailLexer::S_IPV6TAG)) { 188 $lexer = clone $this->lexer; 189 $lexer->moveNext(); 190 if ($lexer->isNextToken(EmailLexer::S_DOUBLECOLON)) { 191 $this->warnings[IPV6ColonStart::CODE] = new IPV6ColonStart(); 192 } 193 } 194 195 return $this->doParseDomainLiteral(); 196 } 197 198 protected function doParseDomainLiteral() 199 { 200 $IPv6TAG = false; 201 $addressLiteral = ''; 202 do { 203 if ($this->lexer->token['type'] === EmailLexer::C_NUL) { 204 throw new ExpectingDTEXT(); 205 } 206 207 if ($this->lexer->token['type'] === EmailLexer::INVALID || 208 $this->lexer->token['type'] === EmailLexer::C_DEL || 209 $this->lexer->token['type'] === EmailLexer::S_LF 210 ) { 211 $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT(); 212 } 213 214 if ($this->lexer->isNextTokenAny(array(EmailLexer::S_OPENQBRACKET, EmailLexer::S_OPENBRACKET))) { 215 throw new ExpectingDTEXT(); 216 } 217 218 if ($this->lexer->isNextTokenAny( 219 array(EmailLexer::S_HTAB, EmailLexer::S_SP, $this->lexer->token['type'] === EmailLexer::CRLF) 220 )) { 221 $this->warnings[CFWSWithFWS::CODE] = new CFWSWithFWS(); 222 $this->parseFWS(); 223 } 224 225 if ($this->lexer->isNextToken(EmailLexer::S_CR)) { 226 throw new CRNoLF(); 227 } 228 229 if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH) { 230 $this->warnings[ObsoleteDTEXT::CODE] = new ObsoleteDTEXT(); 231 $addressLiteral .= $this->lexer->token['value']; 232 $this->lexer->moveNext(); 233 $this->validateQuotedPair(); 234 } 235 if ($this->lexer->token['type'] === EmailLexer::S_IPV6TAG) { 236 $IPv6TAG = true; 237 } 238 if ($this->lexer->token['type'] === EmailLexer::S_CLOSEQBRACKET) { 239 break; 240 } 241 242 $addressLiteral .= $this->lexer->token['value']; 243 244 } while ($this->lexer->moveNext()); 245 246 $addressLiteral = str_replace('[', '', $addressLiteral); 247 $addressLiteral = $this->checkIPV4Tag($addressLiteral); 248 249 if (false === $addressLiteral) { 250 return $addressLiteral; 251 } 252 253 if (!$IPv6TAG) { 254 $this->warnings[DomainLiteral::CODE] = new DomainLiteral(); 255 return $addressLiteral; 256 } 257 258 $this->warnings[AddressLiteral::CODE] = new AddressLiteral(); 259 260 $this->checkIPV6Tag($addressLiteral); 261 262 return $addressLiteral; 263 } 264 265 protected function checkIPV4Tag($addressLiteral) 266 { 267 $matchesIP = array(); 268 269 // Extract IPv4 part from the end of the address-literal (if there is one) 270 if (preg_match( 271 '/\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/', 272 $addressLiteral, 273 $matchesIP 274 ) > 0 275 ) { 276 $index = strrpos($addressLiteral, $matchesIP[0]); 277 if ($index === 0) { 278 $this->warnings[AddressLiteral::CODE] = new AddressLiteral(); 279 return false; 280 } 281 // Convert IPv4 part to IPv6 format for further testing 282 $addressLiteral = substr($addressLiteral, 0, $index) . '0:0'; 283 } 284 285 return $addressLiteral; 286 } 287 288 protected function checkDomainPartExceptions($prev) 289 { 290 $invalidDomainTokens = array( 291 EmailLexer::S_DQUOTE => true, 292 EmailLexer::S_SEMICOLON => true, 293 EmailLexer::S_GREATERTHAN => true, 294 EmailLexer::S_LOWERTHAN => true, 295 ); 296 297 if (isset($invalidDomainTokens[$this->lexer->token['type']])) { 298 throw new ExpectingATEXT(); 299 } 300 301 if ($this->lexer->token['type'] === EmailLexer::S_COMMA) { 302 throw new CommaInDomain(); 303 } 304 305 if ($this->lexer->token['type'] === EmailLexer::S_AT) { 306 throw new ConsecutiveAt(); 307 } 308 309 if ($this->lexer->token['type'] === EmailLexer::S_OPENQBRACKET && $prev['type'] !== EmailLexer::S_AT) { 310 throw new ExpectingATEXT(); 311 } 312 313 if ($this->lexer->token['type'] === EmailLexer::S_HYPHEN && $this->lexer->isNextToken(EmailLexer::S_DOT)) { 314 throw new DomainHyphened(); 315 } 316 317 if ($this->lexer->token['type'] === EmailLexer::S_BACKSLASH 318 && $this->lexer->isNextToken(EmailLexer::GENERIC)) { 319 throw new ExpectingATEXT(); 320 } 321 } 322 323 protected function hasBrackets() 324 { 325 if ($this->lexer->token['type'] !== EmailLexer::S_OPENBRACKET) { 326 return false; 327 } 328 329 try { 330 $this->lexer->find(EmailLexer::S_CLOSEBRACKET); 331 } catch (\RuntimeException $e) { 332 throw new ExpectingDomainLiteralClose(); 333 } 334 335 return true; 336 } 337 338 protected function checkLabelLength($prev) 339 { 340 if ($this->lexer->token['type'] === EmailLexer::S_DOT && 341 $prev['type'] === EmailLexer::GENERIC && 342 strlen($prev['value']) > 63 343 ) { 344 $this->warnings[LabelTooLong::CODE] = new LabelTooLong(); 345 } 346 } 347 348 protected function parseDomainComments() 349 { 350 $this->isUnclosedComment(); 351 while (!$this->lexer->isNextToken(EmailLexer::S_CLOSEPARENTHESIS)) { 352 $this->warnEscaping(); 353 $this->lexer->moveNext(); 354 } 355 356 $this->lexer->moveNext(); 357 if ($this->lexer->isNextToken(EmailLexer::S_DOT)) { 358 throw new ExpectingATEXT(); 359 } 360 } 361 362 protected function addTLDWarnings() 363 { 364 if ($this->warnings[DomainLiteral::CODE]) { 365 $this->warnings[TLD::CODE] = new TLD(); 366 } 367 } 368 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue May 19 15:55:14 2020 | Cross-referenced by PHPXref 0.7.1 |