├── .gitignore
├── src
├── Validations
│ ├── ValidatorInterface.php
│ ├── ValidFormatValidator.php
│ ├── FreeEmailServiceValidator.php
│ ├── RoleBasedEmailValidator.php
│ ├── DisposableEmailValidator.php
│ ├── EmailHostValidator.php
│ ├── MxRecordsValidator.php
│ ├── Validator.php
│ └── MisspelledEmailValidator.php
├── EmailDataProviderInterface.php
├── ValidationResults.php
├── EmailDataProvider.php
├── EmailValidatorFactory.php
├── EmailAddress.php
├── EmailValidator.php
└── data
│ ├── role-based-email-prefixes.php
│ ├── top-level-domains.php
│ └── email-providers.php
├── index.php
├── docker-compose.yml
├── Dockerfile
├── Docker
└── nginx
│ └── default.conf
├── .github
└── workflows
│ └── php.yml
├── phpunit.xml
├── tests
├── Validations
│ ├── EmailHostValidatorTest.php
│ ├── DisposableEmailValidatorTest.php
│ ├── FreeEmailProviderValidatorTest.php
│ ├── RoleBasedEmailValidatorTest.php
│ ├── MxRecordsValidatorTest.php
│ ├── MisspelledEmailValidatorTest.php
│ └── ValidFormatValidatorTest.php
├── ValidationResultsTest.php
├── EmailDataProviderTest.php
├── EmailAddressTest.php
└── EmailValidationTest.php
├── composer.json
├── LICENSE
├── scripts
├── update-disposable-email-providers.php
└── update-top-level-domains.php
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 | coverage
4 | .idea
5 | coverage.xml
--------------------------------------------------------------------------------
/src/Validations/ValidatorInterface.php:
--------------------------------------------------------------------------------
1 | getValidationResults()->asJson();
11 |
--------------------------------------------------------------------------------
/src/EmailDataProviderInterface.php:
--------------------------------------------------------------------------------
1 | composer-setup.php \
4 | && php composer-setup.php \
5 | && mv composer.phar /usr/local/bin/composer \
6 | && rm composer-setup.php
7 |
8 | # Install git
9 | RUN apt-get update \
10 | && apt-get -y install git \
11 | && apt-get clean; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
12 |
13 | WORKDIR /web
14 | ADD . /web
15 |
16 | RUN /usr/local/bin/composer install --prefer-dist --no-dev
17 |
--------------------------------------------------------------------------------
/src/Validations/ValidFormatValidator.php:
--------------------------------------------------------------------------------
1 | getEmailAddress()->isValidEmailAddressFormat();
17 | }
18 | }
--------------------------------------------------------------------------------
/Docker/nginx/default.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | index index.php index.html;
4 | root /www;
5 |
6 | location / {
7 | try_files $uri /index.php?$args;
8 | }
9 |
10 | location ~ \.php$ {
11 | fastcgi_split_path_info ^(.+\.php)(/.+)$;
12 | fastcgi_pass php:9000;
13 | fastcgi_index index.php;
14 | include fastcgi_params;
15 | fastcgi_param SCRIPT_FILENAME /web/$fastcgi_script_name;
16 | fastcgi_param PATH_INFO $fastcgi_path_info;
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Validations/FreeEmailServiceValidator.php:
--------------------------------------------------------------------------------
1 | getEmailAddress()->getHostPart(),
18 | $this->getEmailDataProvider()->getEmailProviders(),
19 | true
20 | );
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Validations/RoleBasedEmailValidator.php:
--------------------------------------------------------------------------------
1 | getEmailAddress()->getNamePart(),
18 | $this->getEmailDataProvider()->getRoleEmailPrefixes(),
19 | true
20 | );
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Validations/DisposableEmailValidator.php:
--------------------------------------------------------------------------------
1 | getEmailAddress()->getHostPart(),
18 | $this->getEmailDataProvider()->getDisposableEmailProviders(),
19 | true
20 | );
21 | }
22 | }
--------------------------------------------------------------------------------
/src/ValidationResults.php:
--------------------------------------------------------------------------------
1 | results[$resultName] = $resultValue;
14 | }
15 |
16 | public function asArray(): array
17 | {
18 | return $this->results;
19 | }
20 |
21 | public function asJson(): string
22 | {
23 | return json_encode($this->results);
24 | }
25 |
26 | public function hasResults(): bool
27 | {
28 | return !empty($this->results);
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Validations/EmailHostValidator.php:
--------------------------------------------------------------------------------
1 | getEmailAddress()->getHostPart();
17 | if ($hostName) {
18 | return ($this->getHostByName($hostName) !== $hostName);
19 | }
20 |
21 | return false; // @codeCoverageIgnore
22 | }
23 |
24 | protected function getHostByName(string $hostName): string
25 | {
26 | return gethostbyname($hostName); // @codeCoverageIgnore
27 | }
28 | }
--------------------------------------------------------------------------------
/.github/workflows/php.yml:
--------------------------------------------------------------------------------
1 | name: PHP Composer
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 |
12 | strategy:
13 | matrix:
14 | php-version: ['7.4', '8.0']
15 |
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 |
21 | - name: "Install PHP with extensions"
22 | uses: shivammathur/setup-php@v2
23 | with:
24 | coverage: "none"
25 | php-version: ${{ matrix.php-version }}
26 |
27 | - name: Validate composer.json and composer.lock
28 | run: composer validate
29 |
30 | - name: Install dependencies
31 | run: composer install --prefer-dist --no-progress --no-suggest
32 |
33 | - name: Run test suite
34 | run: ./vendor/bin/phpunit
35 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 | tests
13 |
14 |
15 |
16 |
17 | src
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/Validations/EmailHostValidatorTest.php:
--------------------------------------------------------------------------------
1 | hostValidator->shouldReceive('getHostByName')->with('gmail.com');
21 | $this->hostValidator->getResultResponse();
22 | }
23 |
24 | protected function setUp(): void
25 | {
26 | $this->hostValidator = Mockery::mock(EmailHostValidator::class, [
27 | new EmailAddress('dave@gmail.com'),
28 | ])
29 | ->shouldAllowMockingProtectedMethods()
30 | ->makePartial();
31 | }
32 | }
--------------------------------------------------------------------------------
/src/EmailDataProvider.php:
--------------------------------------------------------------------------------
1 | =7.4",
14 | "ext-json": "*"
15 | },
16 | "require-dev": {
17 | "mockery/mockery": "^1.3",
18 | "phpunit/phpunit": "^9.3"
19 | },
20 | "license": "MIT",
21 | "autoload": {
22 | "psr-4": {
23 | "EmailValidation\\": "src/",
24 | "EmailValidation\\Tests\\": "tests"
25 | }
26 | },
27 | "authors": [
28 | {
29 | "name": "Dave Earley",
30 | "email": "dave@earley.email"
31 | }
32 | ],
33 | "scripts": {
34 | "update-data-files": [
35 | "(cd scripts && ./update-disposable-email-providers.php)",
36 | "(cd scripts && ./update-top-level-domains.php)"
37 | ],
38 | "post-install-cmd": "@update-data-files",
39 | "post-update-cmd": "@update-data-files"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Dave Earley
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/Validations/MxRecordsValidator.php:
--------------------------------------------------------------------------------
1 | getEmailAddress()->isValidEmailAddressFormat()) {
19 | return false; // @codeCoverageIgnore
20 | }
21 |
22 | $results = [];
23 | foreach (self::VALID_DNS_RECORDS as $dns) {
24 | $results[] = $this->checkDns($this->getEmailAddress()->getHostPart(true), $dns);
25 | }
26 |
27 | // To be considered valid we needs an NS record and at least one MX, A or AAA record
28 | return count(array_filter($results)) >= 2;
29 | }
30 |
31 | protected function checkDns(string $host, string $type = null): bool
32 | {
33 | return checkdnsrr($host, $type);
34 | }
35 | }
--------------------------------------------------------------------------------
/tests/Validations/DisposableEmailValidatorTest.php:
--------------------------------------------------------------------------------
1 | assertSame($expectedResult, $disposableEmailValidation->getResultResponse());
23 | }
24 |
25 | public function disposableEmailsDataProvider(): array
26 | {
27 | return [
28 | ['dave@gmail.com', false],
29 | ['dave@yahoo.com', false],
30 | ['dave@something.com', false],
31 | ['dave@bestvpn.top', true],
32 | ['dave@bel.kr', true],
33 | ['dave@10minutemail.de', true]
34 | ];
35 | }
36 | }
--------------------------------------------------------------------------------
/tests/Validations/FreeEmailProviderValidatorTest.php:
--------------------------------------------------------------------------------
1 | assertSame($expectedResult, $freeEmailServiceValidator->getResultResponse());
23 | }
24 |
25 | public function freeEmailsDataProvider(): array
26 | {
27 | return [
28 | ['dave@gmail.com', true],
29 | ['dave@yahoo.com', true],
30 | ['dave@hotmail.com', true],
31 | ['dave@something.com', false],
32 | ['dave@anonfreeemailservice.com', false],
33 | ['dave@reddit.com', false],
34 | ];
35 | }
36 | }
--------------------------------------------------------------------------------
/scripts/update-disposable-email-providers.php:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
3 | assertSame($expectedResult, $roleBasedEmailValidator->getResultResponse());
23 | }
24 |
25 | public function rolesDataProvider(): array
26 | {
27 | return [
28 | ['info@email.com', true],
29 | ['support@yahoo.com', true],
30 | ['contact@hotmail.com', true],
31 | ['accounts@apple.com', true],
32 | ['brian.mcgee@something.com', false],
33 | ['john.johnson@anonfreeemailservice.com', false],
34 | ['somerandom.name@reddit.com', false],
35 | ];
36 | }
37 | }
--------------------------------------------------------------------------------
/src/Validations/Validator.php:
--------------------------------------------------------------------------------
1 | emailAddress = $emailAddress;
19 | $this->emailDataProvider = $emailDataProvider;
20 | }
21 |
22 | public function getEmailAddress(): EmailAddress
23 | {
24 | return $this->emailAddress;
25 | }
26 |
27 | public function setEmailAddress(EmailAddress $emailAddress): Validator
28 | {
29 | $this->emailAddress = $emailAddress;
30 | return $this;
31 | }
32 |
33 | public function getEmailDataProvider(): EmailDataProviderInterface
34 | {
35 | return $this->emailDataProvider;
36 | }
37 |
38 | public function setEmailDataProvider(EmailDataProviderInterface $emailDataProvider): Validator
39 | {
40 | $this->emailDataProvider = $emailDataProvider;
41 | return $this;
42 | }
43 | }
--------------------------------------------------------------------------------
/tests/Validations/MxRecordsValidatorTest.php:
--------------------------------------------------------------------------------
1 | mxValidator
21 | ->shouldReceive('checkDns')
22 | ->with('gmail.com.', $dns);
23 | }
24 |
25 | $this->mxValidator->getResultResponse();
26 | }
27 |
28 | protected function setUp(): void
29 | {
30 | $this->mxValidator = Mockery::mock(MxRecordsValidator::class, [
31 | new EmailAddress('dave@gmail.com'),
32 | ])
33 | ->shouldAllowMockingProtectedMethods()
34 | ->shouldReceive('getResultResponse')
35 | ->passthru()
36 | ->getMock()
37 | ->shouldReceive('getEmailAddress')
38 | ->passthru()
39 | ->getMock();
40 | }
41 | }
--------------------------------------------------------------------------------
/scripts/update-top-level-domains.php:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
3 | assertSame($expectedResult, $misspelledEmailValidator->getResultResponse());
23 | }
24 |
25 | public function emailsDataProvider(): array
26 | {
27 | return [
28 | ['dave@gmail.con', 'dave@gmail.com'],
29 | ['dave@gmaal.com', 'dave@gmail.com'],
30 | ['dave@gmail.xom', 'dave@gmail.com'],
31 | ['dave@yahoo.oe', 'dave@yahoo.de'],
32 | ['dave@a-made-up-domain.infi', 'dave@a-made-up-domain.info'],
33 | ['info@iroland.cim', 'info@ireland.com'],
34 | ['info@gmail.com', '']
35 | ];
36 | }
37 | }
--------------------------------------------------------------------------------
/tests/ValidationResultsTest.php:
--------------------------------------------------------------------------------
1 | validationResults->addResult('a-key', 'a-value');
15 | $actual = $this->validationResults->asArray();
16 | $expected = [
17 | 'a-key' => 'a-value'
18 | ];
19 |
20 | $this->assertSame($expected, $actual);
21 | }
22 |
23 | public function testReturnsJson()
24 | {
25 | $this->validationResults->addResult('a-key', 'a-value');
26 | $actual = $this->validationResults->asJson();
27 | $expected = json_encode([
28 | 'a-key' => 'a-value'
29 | ]);
30 |
31 | $this->assertSame($expected, $actual);
32 | }
33 |
34 | public function testHasResults(): void
35 | {
36 | $validationResults = new ValidationResults();
37 | $this->assertFalse($validationResults->hasResults());
38 |
39 | $validationResults->addResult('a-key', 'a-value');
40 | $this->assertTrue($validationResults->hasResults());
41 | }
42 |
43 | protected function setUp(): void
44 | {
45 | $this->validationResults = new ValidationResults();
46 | }
47 | }
--------------------------------------------------------------------------------
/tests/EmailDataProviderTest.php:
--------------------------------------------------------------------------------
1 | emailDataProvider->getEmailProviders();
15 | $this->assertIsArray($emailProviders);
16 | $this->assertContains('gmail.com', $emailProviders);
17 | }
18 |
19 | public function testGetDisposableEmailProviders(): void
20 | {
21 | $emailProviders = $this->emailDataProvider->getDisposableEmailProviders();
22 | $this->assertIsArray($emailProviders);
23 | $this->assertContains('banit.club', $emailProviders);
24 | }
25 |
26 | public function testGetRoleBasesPrefixes(): void
27 | {
28 | $prefixes = $this->emailDataProvider->getRoleEmailPrefixes();
29 | $this->assertIsArray($prefixes);
30 | $this->assertContains('ceo', $prefixes);
31 | }
32 |
33 | public function testGetTopLevelDomains(): void
34 | {
35 | $tlds = $this->emailDataProvider->getTopLevelDomains();
36 | $this->assertIsArray($tlds);
37 | $this->assertContains('aero', $tlds);
38 | }
39 |
40 | protected function setUp(): void
41 | {
42 | $this->emailDataProvider = new EmailDataProvider();
43 | }
44 | }
--------------------------------------------------------------------------------
/tests/Validations/ValidFormatValidatorTest.php:
--------------------------------------------------------------------------------
1 | assertSame($expectedResult, $disposableEmailValidation->getResultResponse());
23 | }
24 |
25 | public function emailsDataProvider(): array
26 | {
27 | return [
28 | ['dave@gmail.com', true],
29 | ['dave.earley@yahoo.com', true],
30 | ['dave@something.ie', true],
31 | ['john.doe@buy.tickets', true],
32 | ['firstname+lastname@example.com', true],
33 | ['firstname-lastname@[127.0.0.1]', true],
34 | ['_______@example.com', true],
35 | ['someone@example.com.com.jp', true],
36 | ['dave', false],
37 | ['172.123.2.4', false],
38 | ['hello', false],
39 | [true, false],
40 | [12, false],
41 | [-99, false],
42 | ];
43 | }
44 | }
--------------------------------------------------------------------------------
/src/EmailValidatorFactory.php:
--------------------------------------------------------------------------------
1 | registerValidator(new $validator);
38 | }
39 |
40 | return $emailValidator;
41 | }
42 | }
--------------------------------------------------------------------------------
/src/EmailAddress.php:
--------------------------------------------------------------------------------
1 | emailAddress = $emailAddress;
17 | }
18 |
19 | public function getNamePart(): ?string
20 | {
21 | if ($this->isValidEmailAddressFormat()) {
22 | return $this->getEmailPart(self::EMAIL_NAME_PART);
23 | }
24 |
25 | return null;
26 | }
27 |
28 | public function isValidEmailAddressFormat(): bool
29 | {
30 | return filter_var($this->emailAddress, FILTER_VALIDATE_EMAIL) !== false;
31 | }
32 |
33 | private function getEmailPart(int $partNumber): string
34 | {
35 | return (explode('@', $this->emailAddress))[$partNumber];
36 | }
37 |
38 | public function getHostPart(bool $returnFqdn = false): ?string
39 | {
40 | if ($this->isValidEmailAddressFormat()) {
41 | return $this->getEmailPart(self::EMAIL_HOST_PART) . ($returnFqdn ? '.' : '');
42 | }
43 |
44 | return null;
45 | }
46 |
47 | public function getTopLevelDomainPart(): ?string
48 | {
49 | if ($this->isValidEmailAddressFormat()) {
50 | return explode('.', $this->getEmailPart(self::EMAIL_HOST_PART))[1];
51 | }
52 |
53 | return null;
54 | }
55 |
56 | public function asString(): string
57 | {
58 | return $this->emailAddress;
59 | }
60 | }
--------------------------------------------------------------------------------
/tests/EmailAddressTest.php:
--------------------------------------------------------------------------------
1 | assertSame(self::VALID_TEST_EMAIL, $this->validEmail->asString());
20 | }
21 |
22 | public function testGetHostPart(): void
23 | {
24 | $this->assertSame('gmail.com', $this->validEmail->getHostPart());
25 | }
26 |
27 | public function testGetTldPart(): void
28 | {
29 | $this->assertSame('com', $this->validEmail->getTopLevelDomainPart());
30 | }
31 |
32 | public function testGetNamePart(): void
33 | {
34 | $this->assertSame('dave', $this->validEmail->getNamePart());
35 | }
36 |
37 | public function testGetHostPartForInvalidEmail(): void
38 | {
39 | $this->assertNull($this->invalidEmail->getHostPart());
40 | }
41 |
42 | public function testGetTldPartForInvalidEmail(): void
43 | {
44 | $this->assertNull($this->invalidEmail->getTopLevelDomainPart());
45 | }
46 |
47 | public function testGetNamePartForInvalidEmail(): void
48 | {
49 | $this->assertNull($this->invalidEmail->getNamePart());
50 | }
51 |
52 | public function testIsValidFormat(): void
53 | {
54 | $this->assertTrue($this->validEmail->isValidEmailAddressFormat());
55 | }
56 |
57 | public function testIsValidFormatForInvalidEmail(): void
58 | {
59 | $this->assertFalse($this->invalidEmail->isValidEmailAddressFormat());
60 | }
61 |
62 | protected function setUp(): void
63 | {
64 | $this->validEmail = new EmailAddress(self::VALID_TEST_EMAIL);
65 | $this->invalidEmail = new EmailAddress(self::INVALID_TEST_EMAIL);
66 | }
67 | }
--------------------------------------------------------------------------------
/src/EmailValidator.php:
--------------------------------------------------------------------------------
1 | emailAddress = $emailAddress;
27 | $this->validationResults = $validationResults;
28 | $this->emailDataProvider = $emailDataProvider;
29 | }
30 |
31 | /**
32 | * @param Validator[] $validators
33 | * @return self
34 | */
35 | public function registerValidators(array $validators): EmailValidator
36 | {
37 | foreach ($validators as $validator) {
38 | $this->registerValidator($validator);
39 | }
40 | return $this;
41 | }
42 |
43 | public function registerValidator(Validator $validator): EmailValidator
44 | {
45 | $this->registeredValidators[] = $validator
46 | ->setEmailAddress($this->getEmailAddress())
47 | ->setEmailDataProvider($this->getEmailDataProvider());
48 | return $this;
49 | }
50 |
51 | private function getEmailAddress(): EmailAddress
52 | {
53 | return $this->emailAddress;
54 | }
55 |
56 | private function getEmailDataProvider(): EmailDataProviderInterface
57 | {
58 | return $this->emailDataProvider;
59 | }
60 |
61 | public function getValidationResults(): ValidationResults
62 | {
63 | if (!$this->validationResults->hasResults()) {
64 | $this->runValidation();
65 | }
66 | return $this->validationResults;
67 | }
68 |
69 | public function runValidation(): void
70 | {
71 | foreach ($this->registeredValidators as $validator) {
72 | $this->validationResults->addResult($validator->getValidatorName(), $validator->getResultResponse());
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/tests/EmailValidationTest.php:
--------------------------------------------------------------------------------
1 | validationResultsMock
27 | ->shouldReceive('addResult')
28 | ->times(1);
29 |
30 | /** @var MisspelledEmailValidator|MockInterface $mockValidation */
31 | $mockValidation = Mockery::mock(MisspelledEmailValidator::class);
32 |
33 | $mockValidation->shouldReceive('getValidatorName')->andReturn('hello');
34 | $mockValidation->shouldReceive('getResultResponse')->andReturn('hello');
35 | $mockValidation->shouldReceive('setEmailAddress')->andReturnSelf();
36 | $mockValidation->shouldReceive('setEmailDataProvider')->andReturnSelf();
37 |
38 | $this->emailValidation->registerValidators([$mockValidation]);
39 | $this->emailValidation->runValidation();
40 | }
41 |
42 | public function testGetValidationResults(): void
43 | {
44 | $this->validationResultsMock->shouldReceive('addResult')->times(1);
45 | $this->validationResultsMock->shouldReceive('hasResults')->andReturn(false);
46 | $this->validationResultsMock->shouldReceive('getValidationResults')->andReturnSelf();
47 | $this->validationResultsMock->shouldReceive('asArray')->andReturn([
48 | 'valid_email' => true
49 | ]
50 | );
51 |
52 | /** @var ValidFormatValidator|MockInterface $mockValidation */
53 | $mockValidation = Mockery::mock(ValidFormatValidator::class);
54 |
55 | $mockValidation->shouldReceive('getValidatorName')->andReturn('valid_format');
56 | $mockValidation->shouldReceive('getResultResponse')->andReturn(true);
57 | $mockValidation->shouldReceive('setEmailAddress')->andReturnSelf();
58 | $mockValidation->shouldReceive('setEmailDataProvider')->andReturnSelf();
59 |
60 | $this->emailValidation->registerValidator($mockValidation);
61 |
62 | $actual = $this->emailValidation->getValidationResults();
63 | $this->assertInstanceOf(ValidationResults::class, $actual);
64 | }
65 |
66 | protected function setUp(): void
67 | {
68 | $emailMock = Mockery::mock(EmailAddress::class);
69 | $this->validationResultsMock = Mockery::mock(ValidationResults::class);
70 | $emailDataProviderMock = Mockery::mock(EmailDataProvider::class);
71 | $this->emailValidation = new EmailValidator(
72 | $emailMock,
73 | $this->validationResultsMock,
74 | $emailDataProviderMock
75 | );
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Validations/MisspelledEmailValidator.php:
--------------------------------------------------------------------------------
1 | getEmailAddress()->isValidEmailAddressFormat()) {
23 | return ''; // @codeCoverageIgnore
24 | }
25 |
26 | $suggestion = $this->findEmailAddressSuggestion();
27 | if ($suggestion === $this->getEmailAddress()->asString()) {
28 | return ''; // @codeCoverageIgnore
29 | }
30 |
31 | return $suggestion;
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | private function findEmailAddressSuggestion(): string
38 | {
39 | if ($domainSuggestion = $this->findDomainSuggestion()) {
40 | return str_replace(
41 | $this->getEmailAddress()->getHostPart(),
42 | $domainSuggestion,
43 | $this->getEmailAddress()->asString()
44 | );
45 | }
46 |
47 | if ($topLevelDomainSuggestion = $this->findTopLevelDomainSuggestion()) {
48 | return str_replace(
49 | $this->getEmailAddress()->getTopLevelDomainPart(),
50 | $topLevelDomainSuggestion,
51 | $this->getEmailAddress()->asString()
52 | );
53 | }
54 |
55 | return '';
56 | }
57 |
58 | /**
59 | * @return bool|null|string
60 | */
61 | private function findDomainSuggestion()
62 | {
63 | $domain = $this->getEmailAddress()->getHostPart();
64 | $possibleMatch = $this->findClosestWord(
65 | $domain,
66 | $this->getEmailDataProvider()->getEmailProviders(),
67 | self::MINIMUM_WORD_DISTANCE_DOMAIN
68 | );
69 |
70 | return $domain === $possibleMatch ? null : $possibleMatch;
71 | }
72 |
73 | private function findClosestWord(string $stringToCheck, array $wordsToCheck, int $minimumDistance): string
74 | {
75 | if (in_array($stringToCheck, $wordsToCheck)) {
76 | return $stringToCheck;
77 | }
78 |
79 | $closestMatch = '';
80 | foreach ($wordsToCheck as $testedWord) {
81 | $distance = levenshtein($stringToCheck, $testedWord);
82 | if ($distance <= $minimumDistance) {
83 | $minimumDistance = $distance - 1;
84 | $closestMatch = $testedWord;
85 | }
86 | }
87 |
88 | return $closestMatch;
89 | }
90 |
91 | /**
92 | * @return bool|null|string
93 | */
94 | private function findTopLevelDomainSuggestion()
95 | {
96 | $topLevelDomain = $this->getEmailAddress()->getTopLevelDomainPart();
97 | $possibleTopLevelMatch = $this->findClosestWord(
98 | $topLevelDomain,
99 | $this->getEmailDataProvider()->getTopLevelDomains(),
100 | self::MINIMUM_WORD_DISTANCE_TLD
101 | );
102 |
103 | return $topLevelDomain === $possibleTopLevelMatch ? null : $possibleTopLevelMatch;
104 | }
105 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://codecov.io/gh/daveearley/Email-Validation-Tool/) [](https://travis-ci.org/daveearley/Email-Validation-Tool) [](https://codeclimate.com/github/daveearley/Email-Validation-Tool/)
6 |
7 | **An extensible email validation library for PHP 7+**
8 |
9 | The aim of this library is to offer a more detailed email validation report than simply checking if an email is the valid format, and also to make it possible to easily add custom validations.
10 |
11 | Currently this tool checks the following:
12 |
13 |
14 | | Validation | Description |
15 | | ------------- | ------------- |
16 | | MX records | Checks if the email's domain has valid MX records |
17 | | Valid format | Validates e-mail addresses against the syntax in RFC 822, with the exceptions that comments and whitespace folding and dotless domain names are not supported (as it uses PHP's [filter_var()](http://php.net/manual/en/function.filter-var.php)). |
18 | | Email Host | Checks if the email's host (e.g gmail.com) is reachable |
19 | | Role/Business Email^ | Checks if the email is a role/business based email (e.g info@reddit.com). |
20 | | Disposable email provider^ | Checks if the email is a [disposable email](https://en.wikipedia.org/wiki/Disposable_email_address) (e.g person@10minutemail.com). |
21 | | Free email provider^ | Checks if the email is a free email (e.g person@yahoo.com). |
22 | | Misspelled Email ^ | Checks the email for possible typos and returns a suggested correction (e.g hi@gmaol.con -> hi@gmail.com). |
23 |
24 | ^ **Data used for these checks can be found [here](https://github.com/daveearley/Email-Validation-Tool/tree/master/src/data)**
25 |
26 | # Installation
27 |
28 | ```bash
29 | composer require daveearley/daves-email-validation-tool
30 | ```
31 |
32 | # Usage
33 | ## Quick Start
34 |
35 | ```php
36 | // Include the composer autoloader
37 | require __DIR__ . '/vendor/autoload.php';
38 |
39 | $validator = EmailValidation\EmailValidatorFactory::create('dave@gmoil.con');
40 |
41 | $jsonResult = $validator->getValidationResults()->asJson();
42 | $arrayResult = $validator->getValidationResults()->asArray();
43 |
44 | echo $jsonResult;
45 |
46 | ```
47 |
48 | Expected output:
49 |
50 | ```json
51 | {
52 | "valid_format": true,
53 | "valid_mx_records": false,
54 | "possible_email_correction": "dave@gmail.com",
55 | "free_email_provider": false,
56 | "disposable_email_provider": false,
57 | "role_or_business_email": false,
58 | "valid_host": false
59 | }
60 | ```
61 |
62 | ## Adding Custom Validations
63 |
64 | To add a custom validation simply extend the [EmailValidation\Validations\Validator](https://github.com/daveearley/Email-Validation-Tool/blob/master/src/Validations/Validator.php) class and implement the **getResultResponse()** and **getValidatorName()** methods. You then register the validation using the **EmailValidation\EmailValidator->registerValidator()** method.
65 |
66 |
67 | ### Example code
68 |
69 | // Validations/GmailValidator.php
70 | ```php
71 | getEmailAddress()->getHostPart();
85 | return strpos($hostName, 'gmail.com') !== false;
86 | }
87 | }
88 | ```
89 |
90 | // file-where-you-are-doing-your-validation.php
91 | ```php
92 | registerValidator(new GmailValidator());
101 |
102 | echo $validator->getValidationResults()->asJson();
103 | ```
104 |
105 | The expected output will be:
106 |
107 | ```json
108 | {
109 | "is_gmail": true,
110 | "valid_format": true,
111 | "valid_mx_records": false,
112 | "possible_email_correction": "",
113 | "free_email_provider": true,
114 | "disposable_email_provider": false,
115 | "role_or_business_email": false,
116 | "valid_host": false
117 | }
118 | ```
119 |
120 | ## Running in Docker
121 | ```bash
122 | docker-compose up -d
123 | ```
124 | You can then validate an email by navigating to http://localhost:8880?email=email.to.validate@example.com. The result will be JSON string as per above.
125 |
126 | ## Adding a custom data source
127 |
128 | You can create your own data provider by creating a data provider class which implements the [EmailValidation\EmailDataProviderInterface](https://github.com/daveearley/Email-Validation-Tool/blob/master/src/EmailDataProviderInterface.php).
129 |
130 | Example Code:
131 |
132 | ```php
133 |