├── .github └── FUNDING.yml ├── .gitignore ├── .styleci.yml ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── mailbox-layer.php ├── docs └── images │ └── check-image.png ├── phpstan.neon ├── phpunit.xml ├── src ├── Classes │ ├── MailboxLayer.php │ └── ValidationResult.php ├── Exceptions │ └── MailboxLayerException.php ├── Facades │ └── MailboxLayer.php └── Providers │ └── MailboxLayerProvider.php └── tests └── Unit ├── Classes ├── MailboxLayer │ ├── CheckManyTest.php │ └── CheckTest.php └── ValidationResult │ └── MakeFromResponseTest.php └── TestCase.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ash-jc-allen 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | vendor/ 3 | composer.lock 4 | .phpunit.result.cache -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | cache: 4 | directories: 5 | - $HOME/.composer/cache 6 | 7 | matrix: 8 | fast_finish: true 9 | include: 10 | # Laravel 7.* 11 | - php: 7.3 12 | env: LARAVEL='7.*' 13 | - php: 7.4 14 | env: LARAVEL='7.*' 15 | - php: 8.0 16 | env: LARAVEL='7.*' 17 | # Laravel 8.* 18 | - php: 7.3 19 | env: LARAVEL='8.*' 20 | - php: 7.4 21 | env: LARAVEL='8.*' 22 | - php: 8.0 23 | env: LARAVEL='8.*' 24 | 25 | before_install: 26 | - travis_retry composer self-update 27 | - travis_retry composer require --no-update --no-interaction "illuminate/container:${LARAVEL}" "illuminate/cache:${LARAVEL}" 28 | 29 | install: 30 | - travis_retry composer update --prefer-dist --no-interaction --no-suggest 31 | - travis_retry composer du -o 32 | 33 | before_script: 34 | - phpenv config-rm xdebug.ini || return 0 35 | 36 | script: 37 | - vendor/bin/phpunit 38 | - vendor/bin/phpstan analyze src config --level max -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | **1.0.1 (released 2020-12-31):** 4 | - Added the package logo to the documentation. 5 | 6 | **1.0.0 (released 2020-12-16):** 7 | - Improved code quality. 8 | - Added documentation. 9 | - Added unit tests. 10 | - Configured tests and phpstan to run on Travis CI. 11 | 12 | **0.1.0 (pre-release):** 13 | - Initial work. -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ashley Allen 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Laravel Mailbox Layer 3 |

4 | 5 |

6 | Latest Version on Packagist 7 | Build Status 8 | Total Downloads 9 | PHP from Packagist 10 | GitHub license 11 |

12 | 13 | ## Table of Contents 14 | 15 | - [Overview](#overview) 16 | - [Installation](#installation) 17 | - [Requirements](#requirements) 18 | - [Install the Package](#install-the-package) 19 | - [Publish the Config](#publish-the-config) 20 | - [Getting Your Mailbox Layer API Key](#getting-your-mailbox-layer-api-key) 21 | - [Usage](#usage) 22 | - [Methods](#methods) 23 | - [Validating One Email Address](#validating-one-email-address) 24 | - [Validating Multiple Email Addresses](#validating-multiple-email-addresses) 25 | - [Facade](#facade) 26 | - [Available Validation Result Properties](#available-validation-result-properties) 27 | - [Caching](#caching) 28 | - [Caching Validation Results](#caching-validation-results) 29 | - [Busting the Cached Validation Results](#busting-the-cached-validation-results) 30 | - [Options](#options) 31 | - [Using HTTPS](#using-https) 32 | - [Running an SMTP Check](#running-an-smtp-check) 33 | - [Testing](#testing) 34 | - [Security](#security) 35 | - [Contribution](#contribution) 36 | - [Credits](#credits) 37 | - [Changelog](#changelog) 38 | - [License](#license) 39 | 40 | ## Overview 41 | Laravel Mailbox Layer is a lightweight wrapper Laravel package that can be used for validating email addresses via the 42 | [Mailbox Layer API](https://mailboxlayer.com/). The package supports caching so that you can start validating email addresses instantly. 43 | 44 | ![Mailbox Layer Usage Image](/docs/images/check-image.png?v=1) 45 | 46 | ## Installation 47 | 48 | ### Requirements 49 | The package has been developed and tested to work with the following minimum requirements: 50 | 51 | - PHP 7.3 52 | - Laravel 7 53 | 54 | ### Install the Package 55 | You can install the package via Composer: 56 | 57 | ```bash 58 | composer require ashallendesign/laravel-mailboxlayer 59 | ``` 60 | 61 | ### Publish the Config 62 | You can then publish the package's config file (so that you can make changes to them) by using the following command: 63 | ```bash 64 | php artisan vendor:publish --provider="AshAllenDesign\MailboxLayer\Providers\MailboxLayerProvider" 65 | ``` 66 | 67 | ### Getting Your Mailbox Layer API Key 68 | To use this package and interact with the Mailbox Layer API, you'll need to register on the [Mailbox Layer API](https://mailboxlayer.com/) 69 | website and get your API key. Once you have the key, you can set it in your ` .env ` file as shown below: 70 | 71 | ``` 72 | MAILBOX_LAYER_API_KEY=your-api-key-here 73 | ``` 74 | 75 | ## Usage 76 | ### Methods 77 | #### Validating One Email Address 78 | 79 | To validate a single email address, you can use the ` check() ` method that is provided in the package. This method returns a ` ValidationResult ` object. 80 | 81 | The example below shows how to validate a single email address: 82 | 83 | ```php 84 | use AshAllenDesign\MailboxLayer\Classes\MailboxLayer; 85 | 86 | $mailboxLayer = new MailboxLayer('api-key-here'); 87 | $validationResult = $mailboxLayer->check('example@domain.com'); 88 | ``` 89 | 90 | #### Validating Multiple Email Addresses 91 | 92 | To validate multiple email addresses, you can use the ` checkMany() ` method that is provided in the package. This method returns a ` Collection ` of ` ValidationResult ` objects. 93 | 94 | The example below shows how to validate multiple email addresses: 95 | 96 | ```php 97 | use AshAllenDesign\MailboxLayer\Classes\MailboxLayer; 98 | 99 | $mailboxLayer = new MailboxLayer('api-key-here'); 100 | $validationResults = $mailboxLayer->checkMany(['example@domain.com', 'test@test.com']); 101 | ``` 102 | 103 | 104 | ### Facade 105 | If you prefer to use facades in Laravel, you can choose to use the provided ` Mailbox Layer ` facade instead of instantiating the ``` AshAllenDesign\MailboxLayer\Classes\MailboxLayer ``` 106 | class manually. 107 | 108 | The example below shows an example of how you could use the facade to validate an email address: 109 | 110 | ```php 111 | use MailboxLayer; 112 | 113 | return MailboxLayer::check('example@domain.com'); 114 | ``` 115 | 116 | ### Available Validation Result Properties 117 | 118 | 119 | | Field | Description | 120 | |-------------|-------------------------------------------------------------------------------------------------------| 121 | | email | The email address that the validation was carried out on. | 122 | | didYouMean | A suggested email address in case a typo was detected. | 123 | | user | The local part of the email address. Example: 'mail' in 'mail@ashallendesign.co.uk'. | 124 | | domain | The domain part of the email address. Example: 'ashallendesign.co.uk' in 'mail@ashallendesign.co.uk'. | 125 | | formatValid | Whether or not the syntax of the requested email is valid. | 126 | | mxFound | Whether or not the MX records for the requested domain could be found. | 127 | | smtpCheck | Whether or not the SMTP check of the requested email address succeeded. | 128 | | catchAll | Whether or not the requested email address is found to be part of a catch-all mailbox. | 129 | | role | Whether or not the requested email is a role email address. Example: 'support@ashallendesign.co.uk'. | 130 | | disposable | Whether or not the requested email is disposable. Example: 'hello@mailinator.com'. | 131 | | free | Whether or not the requested email is a free email address. | 132 | | score | A score between 0 and 1 reflecting the quality and deliverability of the requested email address. | 133 | | validatedAt | A ` Carbon ` object containing the date and time that the original validation API request was made. | 134 | 135 | ### Caching 136 | #### Caching Validation Results 137 | There might be times when you want to cache the validation results for an email. This can have significant performance benefits for if 138 | you try to validate the email again, due to the fact that the results will be fetched from the cache rather than from a new API request. 139 | 140 | As an example, if you were importing a CSV containing email addresses, you might want to validate each of the addresses. However, if the 141 | CSV contains some duplicated email addresses, it could lead to unnecessary API calls being made. So, by using the caching, each unique 142 | address would only be fetched once from the API. To do this, you can use the ` shouldCache() ` method. 143 | 144 | Using caching is recommended as it reduces the chances of you reaching the monthly request limits or rate limits that are 145 | used by Mailbox Layer. Read more about the [API limits here](https://mailboxlayer.com/documentation#rate_limits). 146 | 147 | The example below shows how to cache the validation results: 148 | 149 | ```php 150 | use AshAllenDesign\MailboxLayer\Classes\MailboxLayer; 151 | 152 | $mailboxLayer = new MailboxLayer('api-key-here'); 153 | 154 | // Result fetched from the API. 155 | $validationResults = $mailboxLayer->shouldCache()->check('example@domain.com'); 156 | 157 | // Result fetched from the cache. 158 | $validationResults = $mailboxLayer->shouldCache()->check('example@domain.com'); 159 | ``` 160 | 161 | #### Busting the Cached Validation Results 162 | By default, the package will always try to fetch the validation results from the cache before trying to fetch them via the API. 163 | As mentioned before, this can lead to multiple performance benefits. 164 | 165 | However, there may be times that you want to ignore the cached results and make a new request to the API. As an example, you 166 | might have a cached validation result that is over 6 months old and could possibly be outdated or inaccurate, so it's likely 167 | that you want to update the validation data and ensure it is correct. To do this, you can use the ` fresh() ` method. 168 | 169 | The example below shows how to fetch a new validation result: 170 | 171 | ```php 172 | use AshAllenDesign\MailboxLayer\Classes\MailboxLayer; 173 | 174 | $mailboxLayer = new MailboxLayer('api-key-here'); 175 | 176 | $validationResults = $mailboxLayer->fresh()->check('example@domain.com'); 177 | ``` 178 | 179 | ### Options 180 | #### Using HTTPS 181 | 182 | By default, all the API requests are made using HTTPS. However, the [Mailbox Layer API](https://mailboxlayer.com/) 183 | allows for requests to be made using HTTP if needed. This can be particularly useful when working in a local, development environment. 184 | To use HTTP when making the API requests, you can use the ` withHttps() ` method. 185 | 186 | Please note, it is not recommended making the requests over HTTP in a live, production environment! 187 | 188 | The example below shows how to make the requests using HTTP rather than HTTPS: 189 | 190 | ```php 191 | use AshAllenDesign\MailboxLayer\Classes\MailboxLayer; 192 | 193 | $mailboxLayer = new MailboxLayer('api-key-here'); 194 | 195 | $validationResults = $mailboxLayer->withHttps(false)->check('example@domain.com'); 196 | ``` 197 | 198 | #### Running an SMTP Check 199 | 200 | By default, all the API requests will run an SMTP check on the email address. Running this check can improve the accuracy 201 | of the results and give better results. However, according to Mailbox Layer, running this checks take up around 75% of the 202 | API's entire response time. 203 | 204 | So, you can reduce the overall runtime before preventing the SMTP check from running by using the ` withSmtpCheck() ` method. 205 | 206 | The example below shows how to validate an email address without running an SMTP check: 207 | 208 | ```php 209 | use AshAllenDesign\MailboxLayer\Classes\MailboxLayer; 210 | 211 | $mailboxLayer = new MailboxLayer('api-key-here'); 212 | 213 | $validationResults = $mailboxLayer->withSmtpCheck(false)->check('example@domain.com'); 214 | ``` 215 | 216 | 217 | Read more about the SMTP check in the [Mailbox Layer API docs](https://mailboxlayer.com/documentation#smtp_mx_check). 218 | 219 | ## Testing 220 | 221 | ```bash 222 | vendor/bin/phpunit 223 | ``` 224 | 225 | ## Security 226 | 227 | If you find any security related issues, please contact me directly at [mail@ashallendesign.co.uk](mailto:mail@ashallendesign.co.uk) to report it. 228 | 229 | ## Contribution 230 | 231 | If you wish to make any changes or improvements to the package, feel free to make a pull request. 232 | 233 | To contribute to this library, please use the following guidelines before submitting your pull request: 234 | 235 | - Write tests for any new functions that are added. If you are updating existing code, make sure that the existing tests 236 | pass and write more if needed. 237 | - Follow [PSR-2](https://www.php-fig.org/psr/psr-2/) coding standards. 238 | - Make all pull requests to the ``` master ``` branch. 239 | 240 | ## Credits 241 | 242 | - [Ash Allen](https://ashallendesign.co.uk) 243 | - [Jess Pickup](https://jesspickup.co.uk) (Logo) 244 | - [All Contributors](https://github.com/ash-jc-allen/laravel-mailboxlayer/graphs/contributors) 245 | 246 | ## Changelog 247 | 248 | Check the [CHANGELOG](CHANGELOG.md) to get more information about the latest changes. 249 | 250 | ## License 251 | 252 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 253 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ashallendesign/laravel-mailboxlayer", 3 | "description": "A lightweight Laravel package for validating emails using the Mailbox Layer API.", 4 | "type": "library", 5 | "homepage": "https://github.com/ash-jc-allen/laravel-mailboxlayer", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Ash Allen", 10 | "email": "mail@ashallendesign.co.uk" 11 | } 12 | ], 13 | "keywords": [ 14 | "ashallendesign", 15 | "laravel", 16 | "laravel-package", 17 | "mailboxlayer", 18 | "email", 19 | "validation" 20 | ], 21 | "require": { 22 | "php": "^7.3|^8.0", 23 | "illuminate/container": "^7.0|^8.0", 24 | "guzzlehttp/guzzle": "^6.3|^7.0" 25 | }, 26 | "require-dev": { 27 | "mockery/mockery": "^1.0", 28 | "orchestra/testbench": "^4.0|^5.0|^6.0", 29 | "phpunit/phpunit": "^8.2", 30 | "nunomaduro/larastan": "^0.6.11" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "AshAllenDesign\\MailboxLayer\\": "src/" 35 | } 36 | }, 37 | "autoload-dev": { 38 | "psr-4": { 39 | "AshAllenDesign\\MailboxLayer\\Tests\\": "tests/" 40 | } 41 | }, 42 | "extra": { 43 | "laravel": { 44 | "providers": [ 45 | "AshAllenDesign\\MailboxLayer\\Providers\\MailboxLayerProvider" 46 | ], 47 | "aliases": { 48 | "MailboxLayer": "AshAllenDesign\\MailboxLayer\\Facades\\MailboxLayer" 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /config/mailbox-layer.php: -------------------------------------------------------------------------------- 1 | env('MAILBOX_LAYER_API_KEY'), 14 | 15 | ]; 16 | -------------------------------------------------------------------------------- /docs/images/check-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ash-jc-allen/laravel-mailboxlayer/6977f4a52fce78cfd823317435102d58287ca158/docs/images/check-image.png -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - ./vendor/nunomaduro/larastan/extension.neon 3 | 4 | parameters: 5 | paths: 6 | - src 7 | - tests 8 | 9 | # The level 8 is the highest level 10 | level: 8 11 | 12 | ignoreErrors: 13 | - '#Unsafe usage of new static#' 14 | 15 | excludes_analyse: 16 | - ./*/*/FileToBeExcluded.php 17 | 18 | checkMissingIterableValueType: false -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Classes/MailboxLayer.php: -------------------------------------------------------------------------------- 1 | apiKey = $apiKey; 64 | } 65 | 66 | /** 67 | * Run a validation check against the email address. 68 | * Once this has been done, return the results in 69 | * a ValidationObject. 70 | * 71 | * @param string $emailAddress 72 | * @return ValidationResult 73 | * 74 | * @throws MailboxLayerException 75 | */ 76 | public function check(string $emailAddress): ValidationResult 77 | { 78 | $cacheKey = $this->buildCacheKey($emailAddress); 79 | 80 | if ($this->fresh) { 81 | Cache::forget($cacheKey); 82 | } else { 83 | $cached = Cache::get($cacheKey); 84 | 85 | if ($cached) { 86 | $result = ValidationResult::makeFromResponse($cached); 87 | } 88 | } 89 | 90 | if (! isset($result)) { 91 | $result = $this->fetchFromApi($emailAddress); 92 | } 93 | 94 | if ($this->shouldCache) { 95 | Cache::forever($cacheKey, (array) $result); 96 | } 97 | 98 | return $result; 99 | } 100 | 101 | /** 102 | * Run validation checks on more than one email address. 103 | * Add each of the results to a Collection and then 104 | * return it. 105 | * 106 | * @param array $emailAddresses 107 | * @return Collection 108 | * 109 | * @throws MailboxLayerException 110 | */ 111 | public function checkMany(array $emailAddresses): Collection 112 | { 113 | $results = collect(); 114 | 115 | foreach ($emailAddresses as $email) { 116 | $results->push($this->check($email)); 117 | } 118 | 119 | return $results; 120 | } 121 | 122 | /** 123 | * Whether or not the email validation result should 124 | * be cached after it's fetched from the API. 125 | * 126 | * @param bool $shouldCache 127 | * @return $this 128 | */ 129 | public function shouldCache(bool $shouldCache = true): self 130 | { 131 | $this->shouldCache = $shouldCache; 132 | 133 | return $this; 134 | } 135 | 136 | /** 137 | * Whether or not a fresh result should be fetched from 138 | * the API. Setting field this to true will ignore 139 | * any cached values. It will also delete the 140 | * previously cached result if one exists. 141 | * 142 | * @param bool $fresh 143 | * @return $this 144 | */ 145 | public function fresh(bool $fresh = true): self 146 | { 147 | $this->fresh = $fresh; 148 | 149 | return $this; 150 | } 151 | 152 | /** 153 | * Determine whether if HTTPS should be used when 154 | * making the API request. 155 | * 156 | * @param bool $https 157 | * @return $this 158 | */ 159 | public function withHttps(bool $https = true): self 160 | { 161 | $this->withHttps = $https; 162 | 163 | return $this; 164 | } 165 | 166 | /** 167 | * Determine whether if an SMTP check should be used 168 | * when validating the address. By not running the 169 | * SMTP check, the API response time will be 170 | * decreased. 171 | * 172 | * @param bool $smtpCheck 173 | * @return $this 174 | */ 175 | public function withSmtpCheck(bool $smtpCheck = true): self 176 | { 177 | $this->smtpCheck = $smtpCheck; 178 | 179 | return $this; 180 | } 181 | 182 | /** 183 | * Build the URL that the request will be made to. 184 | * 185 | * @param string $emailAddress 186 | * @return string 187 | */ 188 | private function buildUrl(string $emailAddress): string 189 | { 190 | $protocol = $this->withHttps ? 'https://' : 'http://'; 191 | 192 | $params = http_build_query([ 193 | 'access_key' => $this->apiKey, 194 | 'email' => $emailAddress, 195 | 'smtp' => $this->smtpCheck, 196 | ]); 197 | 198 | return $protocol.self::BASE_URL.'?'.$params; 199 | } 200 | 201 | /** 202 | * Make a request to the API and fetch a new result. 203 | * 204 | * @param string $emailAddress 205 | * @return ValidationResult 206 | * 207 | * @throws MailboxLayerException 208 | */ 209 | private function fetchFromApi(string $emailAddress): ValidationResult 210 | { 211 | $response = Http::get($this->buildUrl($emailAddress)); 212 | 213 | if (isset($response->json()['error'])) { 214 | $error = $response->json()['error']; 215 | 216 | throw new MailboxLayerException($error['info'], $error['code']); 217 | } 218 | 219 | return ValidationResult::makeFromResponse($response->json()); 220 | } 221 | 222 | /** 223 | * Build and return the key that will be used when 224 | * setting or getting the validation result from 225 | * the cache. 226 | * 227 | * @param string $emailAddress 228 | * @return string 229 | */ 230 | private function buildCacheKey(string $emailAddress): string 231 | { 232 | return 'mailboxlayer_result_'.$emailAddress; 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/Classes/ValidationResult.php: -------------------------------------------------------------------------------- 1 | $value) { 128 | $objectFieldName = Str::camel((string) $fieldName); 129 | $validationResult->{$objectFieldName} = $value; 130 | } 131 | 132 | if (empty($validationResult->validatedAt)) { 133 | $validationResult->validatedAt = now(); 134 | } 135 | 136 | return $validationResult; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/Exceptions/MailboxLayerException.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__.'/../../config/mailbox-layer.php', 'mailbox-layer'); 18 | 19 | $this->app->bind('mailbox-layer', function ($app) { 20 | return new MailboxLayer(config('mailbox-layer.api_key')); 21 | }); 22 | } 23 | 24 | /** 25 | * Bootstrap any application services. 26 | * 27 | * @return void 28 | */ 29 | public function boot(): void 30 | { 31 | // Config 32 | $this->publishes([ 33 | __DIR__.'/../../config/mailbox-layer.php' => config_path('mailbox-layer.php'), 34 | ], 'mailbox-layer-config'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Unit/Classes/MailboxLayer/CheckManyTest.php: -------------------------------------------------------------------------------- 1 | once() 21 | ->withArgs(['mailboxlayer_result_mail@ashallendesign.co.uk']) 22 | ->andReturnNull(); 23 | 24 | Cache::shouldReceive('get') 25 | ->once() 26 | ->withArgs(['mailboxlayer_result_support1@ashallendesign.co.uk']) 27 | ->andReturnNull(); 28 | 29 | Http::fake([ 30 | 'https://apilayer.net/api/check?access_key=123&email=mail%40ashallendesign.co.uk&smtp=1' => Http::response([ 31 | 'email' => 'mail@ashallendesign.co.uk', 32 | 'didYouMean' => '', 33 | 'user' => 'mail', 34 | 'domain' => 'ashallendesign.co.uk', 35 | 'formatValid' => true, 36 | 'mxFound' => true, 37 | 'smtpCheck' => true, 38 | 'catchAll' => false, 39 | 'role' => true, 40 | 'disposable' => false, 41 | 'free' => false, 42 | 'score' => 0.8, 43 | ]), 44 | 'https://apilayer.net/api/check?access_key=123&email=support1%40ashallendesign.co.uk&smtp=1' => Http::response([ 45 | 'email' => 'support1@ashallendesign.co.uk', 46 | 'didYouMean' => 'support@ashallendesign.co.uk', 47 | 'user' => 'support1', 48 | 'domain' => 'ashallendesign.co.uk', 49 | 'formatValid' => false, 50 | 'mxFound' => false, 51 | 'smtpCheck' => false, 52 | 'catchAll' => true, 53 | 'role' => false, 54 | 'disposable' => true, 55 | 'free' => true, 56 | 'score' => 0.7, 57 | ]), 58 | ]); 59 | 60 | $mailboxLayer = new MailboxLayer(123); 61 | 62 | $result = $mailboxLayer->checkMany(['mail@ashallendesign.co.uk', 'support1@ashallendesign.co.uk']); 63 | $this->assertInstanceOf(Collection::class, $result); 64 | 65 | $this->assertSame('mail@ashallendesign.co.uk', $result[0]->email); 66 | $this->assertSame('', $result[0]->didYouMean); 67 | $this->assertSame('mail', $result[0]->user); 68 | $this->assertSame('ashallendesign.co.uk', $result[0]->domain); 69 | $this->assertTrue($result[0]->formatValid); 70 | $this->assertTrue($result[0]->smtpCheck); 71 | $this->assertTrue($result[0]->role); 72 | $this->assertFalse($result[0]->disposable); 73 | $this->assertFalse($result[0]->free); 74 | $this->assertSame(0.8, $result[0]->score); 75 | $this->assertEquals(now(), $result[0]->validatedAt); 76 | 77 | $this->assertSame('support1@ashallendesign.co.uk', $result[1]->email); 78 | $this->assertSame('support@ashallendesign.co.uk', $result[1]->didYouMean); 79 | $this->assertSame('support1', $result[1]->user); 80 | $this->assertSame('ashallendesign.co.uk', $result[1]->domain); 81 | $this->assertFalse($result[1]->formatValid); 82 | $this->assertFalse($result[1]->smtpCheck); 83 | $this->assertFalse($result[1]->role); 84 | $this->assertTrue($result[1]->disposable); 85 | $this->assertTrue($result[1]->free); 86 | $this->assertSame(0.7, $result[1]->score); 87 | $this->assertEquals(now(), $result[1]->validatedAt); 88 | } 89 | 90 | /** @test */ 91 | public function many_emails_can_be_validated_via_the_cache() 92 | { 93 | // Set a cached value that we can get. 94 | Cache::shouldReceive('get') 95 | ->once() 96 | ->withArgs(['mailboxlayer_result_mail@ashallendesign.co.uk']) 97 | ->andReturn([ 98 | 'email' => 'mail@ashallendesign.co.uk', 99 | 'didYouMean' => '', 100 | 'user' => 'mail', 101 | 'domain' => 'ashallendesign.co.uk', 102 | 'formatValid' => true, 103 | 'mxFound' => true, 104 | 'smtpCheck' => true, 105 | 'catchAll' => false, 106 | 'role' => true, 107 | 'disposable' => false, 108 | 'free' => false, 109 | 'score' => 0.8, 110 | 'validatedAt' => now()->subDays(5)->startOfDay(), 111 | ]); 112 | 113 | // Set a cached value that we can get. 114 | Cache::shouldReceive('get') 115 | ->once() 116 | ->withArgs(['mailboxlayer_result_support1@ashallendesign.co.uk']) 117 | ->andReturn([ 118 | 'email' => 'support1@ashallendesign.co.uk', 119 | 'didYouMean' => 'support@ashallendesign.co.uk', 120 | 'user' => 'support1', 121 | 'domain' => 'ashallendesign.co.uk', 122 | 'formatValid' => false, 123 | 'mxFound' => false, 124 | 'smtpCheck' => false, 125 | 'catchAll' => true, 126 | 'role' => false, 127 | 'disposable' => true, 128 | 'free' => true, 129 | 'score' => 0.7, 130 | 'validatedAt' => now()->subYear()->startOfDay(), 131 | ]); 132 | 133 | // Assert that the HTTP client is never called. 134 | Http::shouldReceive('get')->never(); 135 | 136 | $mailboxLayer = new MailboxLayer(123); 137 | 138 | $result = $mailboxLayer->checkMany(['mail@ashallendesign.co.uk', 'support1@ashallendesign.co.uk']); 139 | $this->assertInstanceOf(Collection::class, $result); 140 | 141 | $this->assertSame('mail@ashallendesign.co.uk', $result[0]->email); 142 | $this->assertSame('', $result[0]->didYouMean); 143 | $this->assertSame('mail', $result[0]->user); 144 | $this->assertSame('ashallendesign.co.uk', $result[0]->domain); 145 | $this->assertTrue($result[0]->formatValid); 146 | $this->assertTrue($result[0]->smtpCheck); 147 | $this->assertTrue($result[0]->role); 148 | $this->assertFalse($result[0]->disposable); 149 | $this->assertFalse($result[0]->free); 150 | $this->assertSame(0.8, $result[0]->score); 151 | $this->assertEquals(now()->subDays(5)->startOfDay(), $result[0]->validatedAt); 152 | 153 | $this->assertSame('support1@ashallendesign.co.uk', $result[1]->email); 154 | $this->assertSame('support@ashallendesign.co.uk', $result[1]->didYouMean); 155 | $this->assertSame('support1', $result[1]->user); 156 | $this->assertSame('ashallendesign.co.uk', $result[1]->domain); 157 | $this->assertFalse($result[1]->formatValid); 158 | $this->assertFalse($result[1]->smtpCheck); 159 | $this->assertFalse($result[1]->role); 160 | $this->assertTrue($result[1]->disposable); 161 | $this->assertTrue($result[1]->free); 162 | $this->assertSame(0.7, $result[1]->score); 163 | $this->assertEquals(now()->subYear()->startOfDay(), $result[1]->validatedAt); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /tests/Unit/Classes/MailboxLayer/CheckTest.php: -------------------------------------------------------------------------------- 1 | once() 30 | ->withArgs(['mailboxlayer_result_mail@ashallendesign.co.uk']) 31 | ->andReturn($this->responseStructure()); 32 | 33 | // Assert that the HTTP client is never called. 34 | Http::shouldReceive('get')->never(); 35 | 36 | $mailboxLayer = new MailboxLayer(123); 37 | 38 | $result = $mailboxLayer->check('mail@ashallendesign.co.uk'); 39 | 40 | $this->assertValidationResultIsCorrect($result); 41 | } 42 | 43 | /** @test */ 44 | public function result_is_returned_from_the_api_if_fresh_is_set_to_false_but_it_does_not_exist_in_the_cache() 45 | { 46 | // Set a cached value that we can get. 47 | Cache::shouldReceive('get') 48 | ->once() 49 | ->withArgs(['mailboxlayer_result_mail@ashallendesign.co.uk']) 50 | ->andReturnNull(); 51 | 52 | // Mock the API response. 53 | Http::fake(function () { 54 | return Http::response($this->responseStructure()); 55 | }); 56 | 57 | $mailboxLayer = new MailboxLayer(123); 58 | 59 | $result = $mailboxLayer->check('mail@ashallendesign.co.uk'); 60 | 61 | $this->assertValidationResultIsCorrect($result); 62 | 63 | Http::assertSent(function (Request $request) { 64 | return $request->url() === 'https://apilayer.net/api/check?access_key=123&email=mail%40ashallendesign.co.uk&smtp=1'; 65 | }); 66 | } 67 | 68 | /** @test */ 69 | public function result_is_returned_from_the_api_if_fresh_is_set_to_true() 70 | { 71 | Cache::shouldReceive('forget') 72 | ->withArgs(['mailboxlayer_result_mail@ashallendesign.co.uk']) 73 | ->once() 74 | ->andReturnTrue(); 75 | 76 | Cache::shouldReceive('get')->never(); 77 | 78 | Cache::shouldReceive('forever')->never(); 79 | 80 | // Mock the API response. 81 | Http::fake(function () { 82 | return Http::response($this->responseStructure()); 83 | }); 84 | 85 | $mailboxLayer = new MailboxLayer(123); 86 | 87 | $result = $mailboxLayer->fresh()->check('mail@ashallendesign.co.uk'); 88 | 89 | $this->assertValidationResultIsCorrect($result); 90 | 91 | Http::assertSent(function (Request $request) { 92 | return $request->url() === 'https://apilayer.net/api/check?access_key=123&email=mail%40ashallendesign.co.uk&smtp=1'; 93 | }); 94 | } 95 | 96 | /** @test */ 97 | public function result_is_cached_if_should_bust_cache_is_set_to_true() 98 | { 99 | // Mock the API response. 100 | Http::fake(function () { 101 | return Http::response($this->responseStructure()); 102 | }); 103 | 104 | Cache::shouldReceive('get')->once()->andReturnNull(); 105 | 106 | Cache::shouldReceive('forever') 107 | ->withArgs([ 108 | 'mailboxlayer_result_mail@ashallendesign.co.uk', 109 | array_merge($this->responseStructure(), ['validatedAt' => now()]), 110 | ]) 111 | ->once() 112 | ->andReturnTrue(); 113 | 114 | $mailboxLayer = new MailboxLayer(123); 115 | 116 | $result = $mailboxLayer->shouldCache()->check('mail@ashallendesign.co.uk'); 117 | 118 | $this->assertValidationResultIsCorrect($result); 119 | 120 | Http::assertSent(function (Request $request) { 121 | return $request->url() === 'https://apilayer.net/api/check?access_key=123&email=mail%40ashallendesign.co.uk&smtp=1'; 122 | }); 123 | } 124 | 125 | /** @test */ 126 | public function email_can_be_validated_without_the_smtp_check() 127 | { 128 | // Mock the API response. 129 | Http::fake(function () { 130 | return Http::response($this->responseStructure()); 131 | }); 132 | 133 | $mailboxLayer = new MailboxLayer(123); 134 | 135 | $result = $mailboxLayer->withSmtpCheck(false)->check('mail@ashallendesign.co.uk'); 136 | 137 | $this->assertValidationResultIsCorrect($result); 138 | 139 | Http::assertSent(function (Request $request) { 140 | return $request->url() === 'https://apilayer.net/api/check?access_key=123&email=mail%40ashallendesign.co.uk&smtp=0'; 141 | }); 142 | } 143 | 144 | /** @test */ 145 | public function request_can_be_sent_without_using_https() 146 | { 147 | // Mock the API response. 148 | Http::fake(function () { 149 | return Http::response($this->responseStructure()); 150 | }); 151 | 152 | $mailboxLayer = new MailboxLayer(123); 153 | 154 | $result = $mailboxLayer->withHttps(false)->check('mail@ashallendesign.co.uk'); 155 | 156 | $this->assertValidationResultIsCorrect($result); 157 | 158 | Http::assertSent(function (Request $request) { 159 | return $request->url() === 'http://apilayer.net/api/check?access_key=123&email=mail%40ashallendesign.co.uk&smtp=1'; 160 | }); 161 | } 162 | 163 | /** @test */ 164 | public function exception_is_thrown_if_the_api_request_returns_an_error() 165 | { 166 | $this->expectException(MailboxLayerException::class); 167 | $this->expectExceptionCode(101); 168 | $this->expectExceptionMessage('You have not supplied a valid API Access Key. [Technical Support: support@apilayer.com]'); 169 | 170 | // Mock the API response. 171 | Http::fake(function () { 172 | return Http::response($this->errorResponseStructure()); 173 | }); 174 | 175 | $mailboxLayer = new MailboxLayer(123); 176 | 177 | $result = $mailboxLayer->withHttps(false)->check('mail@ashallendesign.co.uk'); 178 | 179 | $this->assertValidationResultIsCorrect($result); 180 | 181 | Http::assertSent(function (Request $request) { 182 | return $request->url() === 'http://apilayer.net/api/check?access_key=123&email=mail%40ashallendesign.co.uk&smtp=1'; 183 | }); 184 | } 185 | 186 | /** @test */ 187 | public function validation_can_be_carried_out_using_the_facade() 188 | { 189 | // Set the API key in the config so that it can be used when 190 | // creating the facade. 191 | config(['mailbox-layer.api_key' => 123]); 192 | 193 | // Mock the API response. 194 | Http::fake(function () { 195 | return Http::response($this->responseStructure()); 196 | }); 197 | 198 | $result = MailboxLayerFacade::withHttps(false)->check('mail@ashallendesign.co.uk'); 199 | 200 | $this->assertValidationResultIsCorrect($result); 201 | 202 | Http::assertSent(function (Request $request) { 203 | return $request->url() === 'http://apilayer.net/api/check?access_key=123&email=mail%40ashallendesign.co.uk&smtp=1'; 204 | }); 205 | } 206 | 207 | private function responseStructure(): array 208 | { 209 | return [ 210 | 'email' => 'mail@ashallendesign.co.uk', 211 | 'didYouMean' => '', 212 | 'user' => 'mail', 213 | 'domain' => 'ashallendesign.co.uk', 214 | 'formatValid' => true, 215 | 'mxFound' => true, 216 | 'smtpCheck' => true, 217 | 'catchAll' => false, 218 | 'role' => true, 219 | 'disposable' => false, 220 | 'free' => false, 221 | 'score' => 0.8, 222 | ]; 223 | } 224 | 225 | private function errorResponseStructure(): array 226 | { 227 | return [ 228 | 'success' => false, 229 | 'error' => [ 230 | 'code' => '101', 231 | 'type' => 'invalid_access_key', 232 | 'info' => 'You have not supplied a valid API Access Key. [Technical Support: support@apilayer.com]', 233 | ], 234 | ]; 235 | } 236 | 237 | private function assertValidationResultIsCorrect(ValidationResult $result): void 238 | { 239 | $this->assertSame('mail@ashallendesign.co.uk', $result->email); 240 | $this->assertSame('', $result->didYouMean); 241 | $this->assertSame('mail', $result->user); 242 | $this->assertSame('ashallendesign.co.uk', $result->domain); 243 | $this->assertTrue($result->formatValid); 244 | $this->assertTrue($result->smtpCheck); 245 | $this->assertTrue($result->role); 246 | $this->assertFalse($result->disposable); 247 | $this->assertFalse($result->free); 248 | $this->assertSame(0.8, $result->score); 249 | $this->assertEquals(now(), $result->validatedAt); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /tests/Unit/Classes/ValidationResult/MakeFromResponseTest.php: -------------------------------------------------------------------------------- 1 | 'mai1l@ashallendesign.co.uk', 18 | 'did_you_mean' => 'mail@ashallendesign.co.uk', 19 | 'user' => 'mai1l', 20 | 'domain' => 'ashallendesign.co.uk', 21 | 'format_valid' => true, 22 | 'smtp_check' => true, 23 | 'role' => true, 24 | 'disposable' => false, 25 | 'free' => false, 26 | 'score' => 0.8, 27 | 'validated_at' => now(), 28 | ]; 29 | 30 | $newObject = ValidationResult::makeFromResponse($responseData); 31 | 32 | $this->assertSame('mai1l@ashallendesign.co.uk', $newObject->email); 33 | $this->assertSame('mail@ashallendesign.co.uk', $newObject->didYouMean); 34 | $this->assertSame('mai1l', $newObject->user); 35 | $this->assertSame('ashallendesign.co.uk', $newObject->domain); 36 | $this->assertTrue($newObject->formatValid); 37 | $this->assertTrue($newObject->smtpCheck); 38 | $this->assertTrue($newObject->role); 39 | $this->assertFalse($newObject->disposable); 40 | $this->assertFalse($newObject->free); 41 | $this->assertSame(0.8, $newObject->score); 42 | $this->assertEquals(now(), $newObject->validatedAt); 43 | } 44 | 45 | /** @test */ 46 | public function new_object_is_returned_with_correct_fields_set_and_the_validatedAt_date_is_not_already_set() 47 | { 48 | Carbon::setTestNow(now()); 49 | 50 | $responseData = [ 51 | 'email' => 'mai1l@ashallendesign.co.uk', 52 | 'did_you_mean' => 'mail@ashallendesign.co.uk', 53 | 'user' => 'mai1l', 54 | 'domain' => 'ashallendesign.co.uk', 55 | 'format_valid' => true, 56 | 'smtp_check' => true, 57 | 'role' => true, 58 | 'disposable' => false, 59 | 'free' => false, 60 | 'score' => 0.8, 61 | ]; 62 | 63 | $newObject = ValidationResult::makeFromResponse($responseData); 64 | 65 | $this->assertSame('mai1l@ashallendesign.co.uk', $newObject->email); 66 | $this->assertSame('mail@ashallendesign.co.uk', $newObject->didYouMean); 67 | $this->assertSame('mai1l', $newObject->user); 68 | $this->assertSame('ashallendesign.co.uk', $newObject->domain); 69 | $this->assertTrue($newObject->formatValid); 70 | $this->assertTrue($newObject->smtpCheck); 71 | $this->assertTrue($newObject->role); 72 | $this->assertFalse($newObject->disposable); 73 | $this->assertFalse($newObject->free); 74 | $this->assertSame(0.8, $newObject->score); 75 | $this->assertEquals(now(), $newObject->validatedAt); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/Unit/TestCase.php: -------------------------------------------------------------------------------- 1 | MailboxLayer::class, 32 | ]; 33 | } 34 | } 35 | --------------------------------------------------------------------------------