Skip to content

Commit

Permalink
Fix code style and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanLutokhin committed Apr 21, 2019
1 parent 124c00b commit f48751b
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 130 deletions.
100 changes: 100 additions & 0 deletions EmailValidator/Helper/SmtpSocketHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

namespace Egulias\EmailValidator\Helper;

class SmtpSocketHelper
{
/**
* @var int
*/
private $port;

/**
* @var int
*/
private $timeout;

/**
* @var resource
*/
private $handle;

public function __construct($port = 25, $timeout = 15)
{
$this->port = $port;
$this->timeout = $timeout;
}

/**
* Checks is resource
*
* @return bool
*/
public function isResource()
{
return is_resource($this->handle);
}

/**
* Opens resource
*
* @param string $hostname
* @param int $errno
* @param string $errstr
*/
public function open($hostname, &$errno, &$errstr)
{
$this->handle = @fsockopen($hostname, $this->port, $errno, $errstr, $this->timeout);
}

/**
* Writes message
*
* @param string $message
*
* @return bool|int
*/
public function write($message)
{
if (!$this->isResource()) {
return false;
}

return @fwrite($this->handle, $message);
}

/**
* Get last response code
*
* @return int
*/
public function getResponseCode()
{
if (!$this->isResource()) {
return -1;
}

$data = '';
while (substr($data, 3, 1) !== ' ') {
if (!($data = @fgets($this->handle, 256))) {
return -1;
}
}

return intval(substr($data, 0, 3));
}

/**
* Closes resource
*/
public function close()
{
if (!$this->isResource()) {
return;
}

@fclose($this->handle);

$this->handle = null;
}
}
8 changes: 8 additions & 0 deletions EmailValidator/Validation/Error/IllegalMailbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ public function __construct($responseCode)

$this->responseCode = $responseCode;
}

/**
* @return int
*/
public function getResponseCode()
{
return $this->responseCode;
}
}
177 changes: 59 additions & 118 deletions EmailValidator/Validation/MailboxCheckValidation.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Egulias\EmailValidator\EmailLexer;
use Egulias\EmailValidator\Exception\InvalidEmail;
use Egulias\EmailValidator\Helper\SmtpSocketHelper;
use Egulias\EmailValidator\Validation\Error\IllegalMailbox;
use Egulias\EmailValidator\Warning\NoDNSMXRecord;
use Egulias\EmailValidator\Warning\SocketWarning;
Expand All @@ -24,33 +25,34 @@ class MailboxCheckValidation implements EmailValidation
private $warnings = [];

/**
* @var int
* @var SmtpSocketHelper
*/
private $lastResponseCode;
private $socketHelper;

/**
* @var int
* @var string
*/
private $port = 25;
private $fromEmail;

/**
* @var int
*/
private $timeout = 10;

/**
* @var string
*/
private $fromEmail = '[email protected]';
private $lastResponseCode;

/**
* MailboxCheckValidation constructor.
*
* @param SmtpSocketHelper $socketHelper
* @param string $fromEmail
*/
public function __construct()
public function __construct(SmtpSocketHelper $socketHelper, $fromEmail)
{
if (!extension_loaded('intl')) {
throw new \LogicException(sprintf('The %s class requires the Intl extension.', __CLASS__));
}

$this->socketHelper = $socketHelper;
$this->fromEmail = $fromEmail;
}

/**
Expand All @@ -69,62 +71,6 @@ public function getWarnings()
return $this->warnings;
}

/**
* @return int
*/
public function getLastResponseCode()
{
return $this->lastResponseCode;
}

/**
* @return int
*/
public function getPort()
{
return $this->port;
}

/**
* @param int $port
*/
public function setPort($port)
{
$this->port = $port;
}

/**
* @return int
*/
public function getTimeout()
{
return $this->timeout;
}

/**
* @param int $timeout
*/
public function setTimeout($timeout)
{
$this->timeout = $timeout;
}

/**
* @return string
*/
public function getFromEmail()
{
return $this->fromEmail;
}

/**
* @param string $fromEmail
*/
public function setFromEmail($fromEmail)
{
$this->fromEmail = $fromEmail;
}

/**
* @inheritDoc
*/
Expand All @@ -134,107 +80,102 @@ public function isValid($email, EmailLexer $emailLexer)

$isValid = false;
foreach ($mxHosts as $mxHost) {
if ( ($isValid = $this->checkMailbox($mxHost, $this->port, $this->timeout, $this->fromEmail, $email)) ) {
if ($this->checkMailboxExists($mxHost, $email)) {
$isValid = true;
break;
}
}

if ( ! $isValid ) {
if (!$isValid) {
$this->error = new IllegalMailbox($this->lastResponseCode);
}

return $this->error === null;
}

/**
* Gets MX Hosts from email
*
* @param string $email
*
* @return array
*/
protected function getMXHosts($email)
{
$variant = defined('INTL_IDNA_VARIANT_UTS46') ? INTL_IDNA_VARIANT_UTS46 : INTL_IDNA_VARIANT_2003;

$hostname = $email;
if ( false !== ($lastAtPos = strrpos($email, '@')) ) {
$hostname = substr($email, $lastAtPos + 1);
}
$hostname = rtrim(idn_to_ascii($hostname, IDNA_DEFAULT, $variant), '.') . '.';
$hostname = $this->extractHostname($email);

$mxHosts = [];
$result = getmxrr($hostname, $mxHosts);
if ( ! $result ) {
if (!$result) {
$this->warnings[NoDNSMXRecord::CODE] = new NoDNSMXRecord();
}

return $mxHosts;
}

/**
* Extracts hostname from email
*
* @param string $email
*
* @return string
*/
private function extractHostname($email)
{
$variant = defined('INTL_IDNA_VARIANT_UTS46') ? INTL_IDNA_VARIANT_UTS46 : INTL_IDNA_VARIANT_2003;

$lastAtPos = strrpos($email, '@');
if ((bool) $lastAtPos) {
$hostname = substr($email, $lastAtPos + 1);
return rtrim(idn_to_ascii($hostname, IDNA_DEFAULT, $variant), '.') . '.';
}

return rtrim(idn_to_ascii($email, IDNA_DEFAULT, $variant), '.') . '.';
}

/**
* Checks mailbox
*
* @param string $hostname
* @param int $port
* @param int $timeout
* @param string $fromEmail
* @param string $toEmail
* @param string $email
* @return bool
*/
protected function checkMailbox($hostname, $port, $timeout, $fromEmail, $toEmail)
private function checkMailboxExists($hostname, $email)
{
$socket = @fsockopen($hostname, $port, $errno, $errstr, $timeout);
$this->socketHelper->open($hostname, $errno, $errstr);

if ( ! $socket ) {
if (!$this->socketHelper->isResource()) {
$this->warnings[SocketWarning::CODE][] = new SocketWarning($hostname, $errno, $errstr);

return false;
}

if ( ! ($this->assertResponse($socket, 220) ) ) {
$this->lastResponseCode = $this->socketHelper->getResponseCode();
if ($this->lastResponseCode !== 220) {
return false;
}

fwrite($socket, "EHLO {$hostname}" . self::END_OF_LINE);
if ( ! ($this->assertResponse($socket, 250) ) ) {
$this->socketHelper->write("EHLO {$hostname}" . self::END_OF_LINE);
$this->lastResponseCode = $this->socketHelper->getResponseCode();
if ($this->lastResponseCode !== 250) {
return false;
}

fwrite($socket, "MAIL FROM: <{$fromEmail}>" . self::END_OF_LINE);
if ( ! ($this->assertResponse($socket, 250) ) ) {
$this->socketHelper->write("MAIL FROM: <{$this->fromEmail}>" . self::END_OF_LINE);
$this->lastResponseCode = $this->socketHelper->getResponseCode();
if ($this->lastResponseCode !== 250) {
return false;
}

fwrite($socket, "RCPT TO: <{$toEmail}>" . self::END_OF_LINE);
if ( ! ($this->assertResponse($socket, 250) ) ) {
$this->socketHelper->write("RCPT TO: <{$email}>" . self::END_OF_LINE);
$this->lastResponseCode = $this->socketHelper->getResponseCode();
if ($this->lastResponseCode !== 250) {
return false;
}

fwrite($socket, 'QUIT' . self::END_OF_LINE);
$this->socketHelper->write('QUIT' . self::END_OF_LINE);

fclose($socket);
$this->socketHelper->close();

return true;
}

/**
* @param resource $socket
* @param int $expectedCode
*
* @return bool
*/
private function assertResponse($socket, $expectedCode)
{
if ( ! is_resource($socket) ) {
return false;
}

$data = '';
while (substr($data, 3, 1) !== ' ') {
if ( ! ( $data = @fgets($socket, 256) ) ) {
$this->lastResponseCode = -1;

return false;
}
}

return ($this->lastResponseCode = intval( substr($data, 0, 3) )) === $expectedCode;
}
}
Loading

0 comments on commit f48751b

Please sign in to comment.