├── .env.testing ├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── .gitignore ├── .styleci.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── laravel-spammail-checker.php ├── phpunit.xml.dist ├── resources └── config │ └── emails.txt ├── src ├── Abstracts │ └── Driver.php ├── Builders │ └── ConfigBuilder.php ├── Contracts │ ├── AuthenticateInterface.php │ └── DriverInterface.php ├── Drivers │ ├── AbstractApiDriver.php │ ├── LocalDriver.php │ ├── QuickEmailVerificationDriver.php │ ├── RemoteDriver.php │ ├── SendGridDriver.php │ └── VerifaliaDriver.php ├── Exceptions │ ├── SpamMailCheckerApiKeyNotSetException.php │ ├── SpamMailCheckerCredentialsNotSetException.php │ ├── SpamMailCheckerException.php │ └── SpamMailCheckerNotFoundException.php ├── Facades │ └── SpamMailCheckerFacade.php ├── Providers │ └── SpamMailCheckerServiceProvider.php └── SpamMailChecker.php └── tests ├── TestCase.php └── Unit ├── Builder └── ConfigBuilderTest.php ├── Drivers ├── AbstractApiDriverTest.php ├── LocalDriverTest.php ├── QuickEmailVerificationDriverTest.php ├── RemoteDriverTest.php ├── SendGridDriverTest.php └── VerifaliaDriverTest.php └── SpamMailCheckerTest.php /.env.testing: -------------------------------------------------------------------------------- 1 | APP_ENV=testing 2 | ABSTRACTAPI_API_KEY="" 3 | QUICKEMAILVERIFICATION_API_KEY="" 4 | VERIFALIA_USERNAME="" 5 | VERIFALIA_PASSWORD="" 6 | SENDGRID_API_KEY="" -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: [hendurhance] 3 | custom: ['https://www.buymeacoffee.com/hendurhance'] -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: run-tests 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: true 14 | matrix: 15 | os: [ubuntu-latest, windows-latest] 16 | php: [8.2, 8.3] 17 | laravel: [10.*, 11.*] 18 | stability: [prefer-stable] 19 | include: 20 | - laravel: 11.* 21 | testbench: ^9.0 22 | - laravel: 10.* 23 | testbench: ^8.0 24 | 25 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} 26 | 27 | steps: 28 | - name: Checkout code 29 | uses: actions/checkout@v4 30 | 31 | - name: Setup PHP 32 | uses: shivammathur/setup-php@v2 33 | with: 34 | php-version: ${{ matrix.php }} 35 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 36 | coverage: none 37 | 38 | - name: Setup problem matchers 39 | run: | 40 | echo "::add-matcher::${{ runner.tool_cache }}/php.json" 41 | echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 42 | 43 | - name: Install dependencies 44 | run: | 45 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 46 | composer update --${{ matrix.stability }} --prefer-dist --no-interaction 47 | 48 | - name: Execute tests 49 | run: vendor/bin/phpunit 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .php_cs 3 | .php_cs.cache 4 | .phpunit.result.cache 5 | build 6 | composer.lock 7 | coverage 8 | docs 9 | phpunit.xml 10 | phpstan.neon 11 | testbench.yaml 12 | vendor 13 | node_modules 14 | .php-cs-fixer.cache 15 | .DS_Store 16 | .env 17 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | 3 | disabled: 4 | - single_class_element_per_statement 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `spammailchecker` will be documented in this file 4 | 5 | ## 1.0.0 - 2022-01-25 (Deprecated) 6 | 7 | - initial release 8 | - [DEPRECATED] This package is deprecated and will no longer be maintained. Please use version 2.0.0 or higher. 9 | 10 | ## 2.0.0 - 2023-07-02 11 | - [ADDED] Added Laravel 10 support. 12 | - [IMPROVED] Improved compatibility with Laravel versions 5 and above. 13 | - [IMPROVED] Improved the SpamMailChecker class to support multiple API drivers. 14 | - [ADDED] Added driver support for the following email validation services: 15 | - [x] [Local](/resources/config/emails.txt) 16 | - [x] [Remote](https://www.php.net/manual/en/function.getmxrr.php) 17 | - [x] [QuickEmailVerification](https://quickemailverification.com/) 18 | - [x] [Verifalia](https://verifalia.com/) 19 | - [x] [AbstractApi](https://www.abstractapi.com/api/email-verification-validation-api) 20 | - [x] [SendGrid](https://sendgrid.com/solutions/email-validation-api/) 21 | - [ ] [ZeroBounce](https://www.zerobounce.net/) 22 | - [ ] [MailboxValidator](https://www.mailboxvalidator.com/) 23 | - [ ] [EmailListVerify](https://www.emaillistverify.com/) 24 | - [ ] [EmailChecker](https://www.emailchecker.com/) 25 | 26 | - [ADDED] Added the config file `config/laravel-spammail-checker.php` to handle package configuration. 27 | - [ADDED] Added Abstract Driver class `Driver.php` to handle API driver configuration and validation using the `DriverInterface.php` interface. 28 | - [ADDED] Added Config Builder class `ConfigBuilder.php` to handle package configuration building. 29 | - [ADDED] Added Exception classes to handle package exceptions. 30 | - [ADDED] Added SpamMailCheckerServiceProvider to handle package registration and validation rule extension. 31 | - [CHANGED] The SpamMailChecker class now implements the DriverInterface. 32 | - [DEPRECATED] Deprecated package versions less than 2.0.0. The package will no longer receive updates for versions 1.0.0 and below. 33 | - [ADDED] Add tests for all supported email validation services. 34 | 35 | ## 2.1.0 (Upcoming) 36 | - [ADDED] Added support for Laravel 10 and 11. 37 | - [ADDED] Added support for the rest of the email validation services. 38 | - [ ] [ZeroBounce](https://www.zerobounce.net/) 39 | - [ ] [MailboxValidator](https://www.mailboxvalidator.com/) 40 | - [ ] [EmailListVerify](https://www.emaillistverify.com/) 41 | - [ ] [Emailable](https://emailable.com/) 42 | - [IMPROVED] Enhanced error handling and exception messages for better debugging. 43 | - [IMPROVED] Improved package documentation with more examples and usage guidelines. 44 | - [FIXED] Addressed reported issues and bugs from previous versions. 45 | - [IMPROVED] Optimized package code for reduced memory usage and increased speed. 46 | - [REMOVED] Removed support for Laravel 8 and below. The package now supports Laravel 9 and above. 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | Please read and understand the contribution guide before creating an issue or pull request. 6 | 7 | ## Etiquette 8 | 9 | This project is open source, and as such, the maintainers give their free time to build and maintain the source code 10 | held within. They make the code freely available in the hope that it will be of use to other developers. It would be 11 | extremely unfair for them to suffer abuse or anger for their hard work. 12 | 13 | Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the 14 | world that developers are civilized and selfless people. 15 | 16 | It's the duty of the maintainer to ensure that all submissions to the project are of sufficient 17 | quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. 18 | 19 | ## Viability 20 | 21 | When requesting or submitting new features, first consider whether it might be useful to others. Open 22 | source projects are used by many developers, who may have entirely different needs to your own. Think about 23 | whether or not your feature is likely to be used by other users of the project. 24 | 25 | ## Procedure 26 | 27 | Before filing an issue: 28 | 29 | - Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. 30 | - Check to make sure your feature suggestion isn't already present within the project. 31 | - Check the pull requests tab to ensure that the bug doesn't have a fix in progress. 32 | - Check the pull requests tab to ensure that the feature isn't already in progress. 33 | 34 | Before submitting a pull request: 35 | 36 | - Check the codebase to ensure that your feature doesn't already exist. 37 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. 38 | 39 | ## Requirements 40 | 41 | If the project maintainer has any additional requirements, you will find them listed here. 42 | 43 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). 44 | 45 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 46 | 47 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 48 | 49 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. 50 | 51 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 52 | 53 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 54 | 55 | **Happy coding**! 56 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Josiah Endurance 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel SpamMailChecker 2 | 3 | [![Latest Stable Version](http://poser.pugx.org/martian/spammailchecker/v)](https://packagist.org/packages/martian/spammailchecker) [![Total Downloads](http://poser.pugx.org/martian/spammailchecker/downloads)](https://packagist.org/packages/martian/spammailchecker) [![Latest Unstable Version](http://poser.pugx.org/martian/spammailchecker/v/unstable)](https://packagist.org/packages/martian/spammailchecker) [![License](http://poser.pugx.org/martian/spammailchecker/license)](https://packagist.org/packages/martian/spammailchecker) [![PHP Version Require](http://poser.pugx.org/martian/spammailchecker/require/php)](https://packagist.org/packages/martian/spammailchecker) 4 | [![Made in Nigeria](https://img.shields.io/badge/made%20in-nigeria-008751.svg?style=flat-square)](https://github.com/acekyd/made-in-nigeria) 5 | 6 | 7 | A powerful Laravel package designed to effortlessly validate email addresses against various spam mail providers using a diverse range of drivers. Seamlessly integrated with Laravel's validation system, this package offers comprehensive support for validating email inputs in forms and RESTful APIs 8 | 9 | ## Supported Email Validation Services 10 | | Service | Descriptions | Driver | Documentation | Supported | 11 | |----------------------------|-------------------------------------------------------------------------------------------------------------|--------------------------|-------------------------------------------------------------------------------------|-----------| 12 | | **Local** | A local text list of spam email domains. | *local* | [Read More](/resources/config/emails.txt) | ✅ Yes | 13 | | **Remote** | Using PHP In-built functions `getmxrr()`, `checkdnsrr()`. `fsockopen()` to validate email domain | *remote* | [Read More](https://www.php.net/manual/en/function.getmxrr.php) | ✅ Yes | 14 | | **AbstractApi** | Using Abstract's suite of API to validate email domains | *abstractapi* | [Read More](https://www.abstractapi.com/email-verification-validation-api) | ✅ Yes | 15 | | **QuickEmailVerification** | A reliable, accurate, affordable, and advanced email verification service | *quickemailverification* | [Read More](https://quickemailverification.com/) | ✅ Yes | 16 | | **Verifalia** | A web-based email validation service which allows to upload and validate lists of email addresses with ease | *verifalia* | [Read More](https://verifalia.com/) | ✅ Yes | 17 | | **SendGrid** | A cloud-based SMTP provider that allows you to validate email addresses before you send. | *sendgrid* | [Read More](https://sendgrid.com/solutions/email-api/email-address-validation-api/) | ✅ Yes | 18 | 19 | > **NOTE:** More services will be added soon. (You can also contribute to this project by adding more services - ZeroBounce, Mailboxlayer, EmailListVerify, Emailable, etc) 20 | 21 | ## Installation 22 | 23 | You can install the package via composer: 24 | 25 | ```bash 26 | composer require martian/spammailchecker 27 | ``` 28 | 29 | ```php 30 | Martian\SpamMailChecker\SpamMailCheckerServiceProvider::class, 31 | ``` 32 | - If you are going to be using the facade, you'll need to register it as well. Open `config/app.php` and add the following line to the `aliases` array: 33 | 34 | ```php 35 | 'SpamMailChecker' => Martian\SpamMailChecker\Facades\SpamMailChecker::class, 36 | ``` 37 | 38 | ## Publish Configuration File 39 | Publish the configuration file using the following command: 40 | 41 | ```bash 42 | php artisan vendor:publish --provider="Martian\SpamMailChecker\Providers\SpamMailCheckerServiceProvider" 43 | ``` 44 | 45 | ## Configuration 46 | The configuration file is located at `config/laravel-spammail-checker.php`. You may configure the package to use any of the supported drivers. The default driver is `local` which uses a local text list of spam email domains. 47 | 48 | #### Local Driver Configuration 49 | - In order to use `local`as your driver of choice, you need to set the `default` key in the `config/laravel-spammail-checker.php` configuration file to `local`: 50 | 51 | ```php 52 | 'default' => 'local', 53 | ``` 54 | - Or you can set the `SPAM_MAIL_CHECKER_DEFAULT_DRIVER` environment variable to `local` in your `.env` file. 55 | 56 | ```env 57 | SPAM_MAIL_CHECKER_DEFAULT_DRIVER=local 58 | ``` 59 | - The local driver uses a local text list of spam email domains. The file is located at `resources/config/emails.txt`. You can include more domains by adding them to the `blacklist` array or exclude domains by adding them to the `whitelist` array. 60 | 61 | ```php 62 | 'drivers' => [ 63 | 'local' => [ 64 | ... 65 | 'whitelist' => [ 66 | // Add domains you want the local driver to ignore here 67 | 'gmail.com', 68 | 'yahoo.com', 69 | ], 70 | 'blacklist' => [ 71 | // Add domains you want the local driver to validate against here 72 | 'mailinator.com', 73 | 'spam.com', 74 | ], 75 | ] 76 | ] 77 | ``` 78 | - Clear the config and cache using the following commands after making changes to the configuration file: 79 | 80 | ```bash 81 | php artisan optimize:clear 82 | ``` 83 | > **NOTE:** The local driver is case-insensitive. So, you don't need to worry about the case of the email domain. 84 | 85 | #### Remote Driver Configuration 86 | - In order to use `remote` as your driver of choice, you need to set the `default` key in the `config/laravel-spammail-checker.php` configuration file to `remote`: 87 | 88 | ```php 89 | 'driver' => 'remote', 90 | ``` 91 | - Or you can set the `SPAM_MAIL_CHECKER_DEFAULT_DRIVER` environment variable to `remote` in your `.env` file. 92 | 93 | ```env 94 | SPAM_MAIL_CHECKER_DEFAULT_DRIVER=remote 95 | ``` 96 | - The remote driver uses PHP In-built functions `getmxrr()`, `checkdnsrr()`. `fsockopen()` to validate email domain. You can configure the remote driver on whether to check for MX - `getmxrr()`, DNS - `checkdnsrr()`, and SMTP -`fsockopen` or validate email domain. You can also configure the timeout value in seconds. 97 | 98 | ```php 99 | 'drivers' => [ 100 | ... 101 | 'remote' => [ 102 | ... 103 | 'check_dns' => true, // When set to true, it will check for DNS 104 | 'check_smtp' => false, // When set to true, it will check for SMTP 105 | 'check_mx' => false, // When set to true, it will check for MX record 106 | 'timeout' => 60 * 5, // 5 minutes 107 | ] 108 | ] 109 | ``` 110 | 111 | #### AbstractApi Driver Configuration 112 | - In order to use `abstractapi` as your driver of choice, you need to set the `default` key in the `config/laravel-spammail-checker.php` configuration file to `abstractapi`: 113 | 114 | ```env 115 | 'default' => 'abstractapi', 116 | ``` 117 | - Or you can set the `SPAM_MAIL_CHECKER_DEFAULT_DRIVER` environment variable to `abstractapi` in your `.env` file. 118 | 119 | ```env 120 | SPAM_MAIL_CHECKER_DEFAULT_DRIVER=abstractapi 121 | ``` 122 | - Add your `ABSTRACTAPI_API_KEY` AbstractAPI key you got from [here](https://app.abstractapi.com/dashboard) to your `env` file. 123 | 124 | ``` 125 | ABSTRACTAPI_API_KEY=abstractapi_api_key 126 | ``` 127 | - You can configure the `score` to determine the threshold for a valid email address. The score ranges from 0 to 1. The higher the score, the more likely the email address is valid. You can also accept disposable email addresses by setting `accept_disposable` to `true`. 128 | 129 | ```php 130 | 'drivers' => [ 131 | ... 132 | 'abstractapi' => [ 133 | ... 134 | 'score' => 0.5, // 0.5 is the default score 135 | 'accept_disposable_email' => true // When set to true, it will accept disposable email addresses 136 | ] 137 | ] 138 | ``` 139 | 140 | #### QuickEmailVerification Driver Configuration 141 | - In order to use `quickemailverification` as your driver of choice, you need to set the `default` key in the `config/laravel-spammail-checker.php` configuration file to `quickemailverification`: 142 | 143 | ```env 144 | 'default' => 'quickemailverification', 145 | ``` 146 | - Or you can set the `SPAM_MAIL_CHECKER_DEFAULT_DRIVER` environment variable to `quickemailverification` in your `.env` file. 147 | 148 | ```env 149 | SPAM_MAIL_CHECKER_DEFAULT_DRIVER=quickemailverification 150 | ``` 151 | - Add your `QUICKEMAILVERIFICATION_API_KEY` QuickEmailVerification key you got from [here](https://quickemailverification.com/dashboard) to your `env` file. 152 | ```env 153 | QUICKEMAILVERIFICATION_API_KEY=quickemailverification_api_key 154 | ``` 155 | - You can configure the driver to accept disposable email addresses by setting `accept_disposable` to `true`. 156 | 157 | ```php 158 | 'drivers' => [ 159 | ... 160 | 'quickemailverification' => [ 161 | ... 162 | 'accept_disposable' => true, // When set to true, it will accept disposable email addresses 163 | ] 164 | ] 165 | ``` 166 | 167 | #### Verifalia Driver Configuration 168 | - In order to use `verifalia` as your driver of choice, you need to set the `default` key in the `config/laravel-spammail-checker.php` configuration file to `verifalia`: 169 | 170 | ```env 171 | 'default' => 'verifalia', 172 | ``` 173 | - Or you can set the `SPAM_MAIL_CHECKER_DEFAULT_DRIVER` environment variable to `verifalia` in your `.env` file. 174 | 175 | ```env 176 | SPAM_MAIL_CHECKER_DEFAULT_DRIVER=verifalia 177 | ``` 178 | - In order to use verifalia service, you need to set login credentials in your `env` file. You can get your credentials from after you create a user [here](https://verifalia.com/client-area#/users). 179 | 180 | ```env 181 | VERIFALIA_USERNAME=verifalia_username 182 | VERIFALIA_PASSWORD=verifalia_password 183 | ``` 184 | 185 | - You can configure the driver to accept disposable email addresses by setting `accept_disposable` to `true`. 186 | 187 | ```php 188 | 'drivers' => [ 189 | ... 190 | 'verifalia' => [ 191 | ... 192 | 'accept_disposable' => true, // When set to true, it will accept disposable email addresses 193 | ] 194 | ] 195 | ``` 196 | > **NOTE:** A user on verifalia needs to be granted permission to use the API. You can do this by going [here](https://verifalia.com/client-area#/users) and clicking on the edit user you want to grant permission to. Then click on the `Permissions` tab and check the appropriate permissions on `Email validations` section. 197 | 198 | #### SendGrid Driver Configuration 199 | - In order to use `sendgrid` as your driver of choice, you need to set the `default` key in the `config/laravel-spammail-checker.php` configuration file to `sendgrid`: 200 | 201 | ```env 202 | 'default' => 'sendgrid', 203 | ``` 204 | - Or you can set the `SPAM_MAIL_CHECKER_DEFAULT_DRIVER` environment variable to `sendgrid` in your `.env` file. 205 | 206 | ```env 207 | SPAM_MAIL_CHECKER_DEFAULT_DRIVER=sendgrid 208 | ``` 209 | - Add your `SENDGRID_API_KEY` Sendgrid key you got from [here](https://app.sendgrid.com/settings/api_keys) to your `env` file. 210 | ```env 211 | SENDGRID_API_KEY=sendgrid_api_key 212 | ``` 213 | - You can configure the driver to accept disposable email addresses by setting `accept_disposable` to `true`. Score can also be configured to determine the threshold for a valid email address. The score ranges from 0 to 1. The higher the score, the more likely the email address is valid. Source can also be configured to determine the source of the email address. The source can be `signup` or `contact`. 214 | 215 | ```php 216 | 'drivers' => [ 217 | ... 218 | 'sendgrid' => [ 219 | ... 220 | 'score' => 0.5, // 0.5 is the default score 221 | 'accept_disposable' => true, // When set to true, it will accept disposable email addresses 222 | 'source' => 'signup' // The source is signup by default 223 | ] 224 | ] 225 | ``` 226 | 227 | ## Usage 228 | 229 | In order to use the package, you need to call the `spammail` validation rule in your validation rules. You can also change the rule name to whatever you want in the `config/laravel-spammail-checker.php` configuration file under the `rule` key, likewise the error message under the `error_message` key. 230 | 231 | ```php 232 | /** 233 | * Get a validator for an incoming registration request. 234 | * 235 | * @param array $data 236 | * @return \Illuminate\Contracts\Validation\Validator 237 | */ 238 | protected function validator(array $data) 239 | { 240 | return Validator::make($data, [ 241 | 'email' => 'required|spammail|max:255', 242 | ]); 243 | } 244 | ``` 245 | Or make use of `spammail` in your Requests file like this: 246 | ```php 247 | /** 248 | * Get the validation rules that apply to the request. 249 | * 250 | * @return array 251 | */ 252 | public function rules() 253 | { 254 | return [ 255 | 'email' => 'required|spammail|max:255', 256 | ]; 257 | } 258 | ``` 259 | 260 | #### Adding Custom Error Message 261 | A custom error message can be added to the `spammail` validation rule. 262 | 263 | ```php 264 | 'email.spammail' => 'This email address is invalid.', // Custom error message 265 | ``` 266 | Or you can change the error message in the `config/laravel-spammail-checker.php` configuration file under the `error_message` key. 267 | 268 | ```php 269 | 'error_message' => 'This email address is invalid.', // Custom error message 270 | ``` 271 | 272 | #### Using Classes Directly 273 | You can also use the classes directly without using the `spammail` validation rule. This is useful when you want to use the package in your own custom validation rule or your own custom class. 274 | 275 | ```php 276 | use Martian\SpamMailChecker\SpamMailChecker; 277 | 278 | public function checkEmail($email) 279 | { 280 | $spamMailChecker = new SpamMailChecker(); 281 | $spamMailChecker->validate($email); 282 | } 283 | ``` 284 | 285 | #### Using Facade 286 | 287 | You can also use the `SpamMailChecker` class directly without instantiating it. 288 | 289 | ```php 290 | use Martian\SpamMailChecker\Facades\SpamMailChecker; 291 | 292 | public function checkEmail($email) 293 | { 294 | SpamMailChecker::validate($email); 295 | } 296 | ``` 297 | 298 | #### Using Each Driver Individually 299 | You can also use each driver individually without using the `spammail` validation rule. This is useful when a certain driver is needed in a particular situation. 300 | ##### Using VerifaliaDriver 301 | ```php 302 | use Martian\SpamMailChecker\Drivers\VerifaliaDriver; 303 | 304 | public function checkEmail($email) 305 | { 306 | $verifaliaDriver = new VerifaliaDriver(); 307 | $verifaliaDriver->validate($email); 308 | } 309 | ``` 310 | ##### Using SendGridDriver 311 | ```php 312 | use Martian\SpamMailChecker\Drivers\SendGridDriver; 313 | 314 | public function checkEmail($email) 315 | { 316 | $sendGridDriver = new SendGridDriver(); 317 | $sendGridDriver->validate($email); 318 | } 319 | ``` 320 | ##### Using AbstractApiDriver 321 | ```php 322 | use Martian\SpamMailChecker\Drivers\AbstractApiDriver; 323 | 324 | public function checkEmail($email) 325 | { 326 | $abstractApiDriver = new AbstractApiDriver(); 327 | $abstractApiDriver->validate($email); 328 | } 329 | ``` 330 | ##### Using RemoteDriver 331 | ```php 332 | use Martian\SpamMailChecker\Drivers\RemoteDriver; 333 | 334 | public function checkEmail($email) 335 | { 336 | $remoteDriver = new RemoteDriver(); 337 | $remoteDriver->validate($email); 338 | } 339 | ``` 340 | ##### Using LocalDriver 341 | ```php 342 | use Martian\SpamMailChecker\Drivers\LocalDriver; 343 | 344 | public function checkEmail($email) 345 | { 346 | $localDriver = new LocalDriver(); 347 | $localDriver->validate($email); 348 | } 349 | ``` 350 | ##### Using QuickEmailVerificationDriver 351 | ```php 352 | use Martian\SpamMailChecker\Drivers\QuickEmailVerificationDriver; 353 | 354 | public function checkEmail($email) 355 | { 356 | $quickEmailVerificationDriver = new QuickEmailVerificationDriver(); 357 | $quickEmailVerificationDriver->validate($email); 358 | } 359 | ``` 360 | 361 | 362 | ### Testing 363 | 364 | ```bash 365 | composer test 366 | ``` 367 | 368 | ### Changelog 369 | 370 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. 371 | 372 | ## Contributing 373 | 374 | Please feel free to fork this project and make a pull request. For more information check [CONTRIBUTING](CONTRIBUTING.md) for details. 375 | 376 | ### Security 377 | 378 | If you discover any security related issues, please email hendurhance.dev@gmail.com instead of using the issue tracker. 379 | 380 | ## Credits 381 | 382 | - [Josiah Endurance](https://github.com/hendurhance) 383 | - [All Contributors](../../contributors) 384 | 385 | ## License 386 | 387 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 388 | 389 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "martian/spammailchecker", 3 | "description": "A laravel package that protect users from entering non-existing/spam email addresses.", 4 | "keywords": [ 5 | "php", 6 | "laravel", 7 | "github", 8 | "spamfree", 9 | "martian", 10 | "spammailchecker", 11 | "spam", 12 | "email", 13 | "laravel", 14 | "package", 15 | "checker", 16 | "validation", 17 | "security" 18 | ], 19 | "homepage": "https://github.com/hendurhance/laravel-spam-email", 20 | "license": "MIT", 21 | "type": "library", 22 | "authors": [ 23 | { 24 | "name": "Josiah Endurance", 25 | "email": "hendurhance.dev@gmail.com", 26 | "role": "Developer" 27 | } 28 | ], 29 | "require": { 30 | "php": "^8.0|^8.1|^8.2|^8.3", 31 | "guzzlehttp/guzzle": "^6.0|^7.0", 32 | "illuminate/support": "^9.0|^10.0|^11.0", 33 | "laravel/framework": "^9.0|^10.0|^11.0", 34 | "quickemailverification/quickemailverification": "^1.0" 35 | }, 36 | "require-dev": { 37 | "orchestra/testbench": "^8.0|^9.0", 38 | "phpunit/phpunit": "^9.0|^10.0|^11.0" 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "Martian\\SpamMailChecker\\": "src" 43 | } 44 | }, 45 | "autoload-dev": { 46 | "psr-4": { 47 | "Martian\\SpamMailChecker\\Tests\\": "tests" 48 | } 49 | }, 50 | "scripts": { 51 | "test": "vendor/bin/phpunit", 52 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 53 | 54 | }, 55 | "config": { 56 | "sort-packages": true 57 | }, 58 | "extra": { 59 | "laravel": { 60 | "providers": [ 61 | "Martian\\SpamMailChecker\\Providers\\SpamMailCheckerServiceProvider" 62 | ], 63 | "aliases": { 64 | "SpamMailChecker": "Martian\\SpamMailChecker\\Facades\\SpamMailCheckerFacade" 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /config/laravel-spammail-checker.php: -------------------------------------------------------------------------------- 1 | env('SPAM_MAIL_CHECKER_DEFAULT_DRIVER', 'local'), 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | Laravel Spam Email Drivers 48 | |-------------------------------------------------------------------------- 49 | | 50 | | Here you may configure the settings for each driver that is used to check 51 | | if an email address is a spam address. You can add as many drivers as 52 | | you want. 53 | | 54 | */ 55 | 56 | 'drivers' => [ 57 | 'local' => [ 58 | 'path' => 'resources/config/emails.txt', 59 | 'cache_key' => 'spammailchecker_' . base64_encode('resources/config/emails.txt'), 60 | 'cache_ttl' => 60 * 60 * 24 * 7, // 1 week 61 | 'whitelist' => [ 62 | // Email domains that should not be considered as spam, this excludes the domain from the spam check. 63 | // Example: 'gmail.com', 'yahoo.com' 64 | ], 65 | 'blacklist' => [ 66 | // Email domains that should be considered as spam, this includes the domain in the spam check. 67 | // Example: 'mail.ru', 'yandex.ru' 68 | ] 69 | ], 70 | 'remote' => [ 71 | 'timeout' => 5, 72 | 'check_dns' => true, 73 | 'check_smtp' => true, 74 | 'check_mx' => true, 75 | ], 76 | 'abstractapi' => [ 77 | 'api_url' => 'https://emailvalidation.abstractapi.com/v1', 78 | 'api_key' => env('ABSTRACTAPI_API_KEY'), 79 | 'score' => 0.5, // The score threshold to consider an email address as spam. 80 | 'accept_disposable_email' => true, // This option is to be used with caution, as it may block legitimate email addresses. 81 | ], 82 | 'quickemailverification' => [ 83 | 'api_url' => 'https://api.quickemailverification.com/v1', 84 | 'api_key' => env('QUICKEMAILVERIFICATION_API_KEY'), 85 | 'accept_disposable_email' => true, // This option is to be used with caution, as it may block legitimate email addresses. 86 | ], 87 | 'verifalia' => [ 88 | 'api_url' => 'https://api.verifalia.com/', // The base URL of the Verifalia API. 89 | 'base_uris' => [ 90 | 'https://api-1.verifalia.com/', 91 | 'https://api-2.verifalia.com/', 92 | 'https://api-3.verifalia.com/' 93 | ], 94 | 'version' => 'v2.4', // The version of the Verifalia API to use. 95 | 'username' => env('VERIFALIA_USERNAME'), 96 | 'password' => env('VERIFALIA_PASSWORD'), 97 | 'accept_disposable_email' => true, // This option is to be used with caution, as it may block legitimate email addresses. 98 | ], 99 | 'sendgrid' => [ 100 | 'api_url' => 'https://api.sendgrid.com/v3/validations/email', 101 | 'api_key' => env('SENDGRID_API_KEY'), 102 | 'score' => 0.5, // The score threshold to consider an email address as spam. 103 | 'accept_disposable_email' => true, // This option is to be used with caution, as it may block legitimate email addresses. 104 | 'source' => 'signup', // The source of the email address to be validated. 105 | ], 106 | ], 107 | 108 | /* 109 | |-------------------------------------------------------------------------- 110 | | Laravel Spam Email Rule 111 | |-------------------------------------------------------------------------- 112 | | 113 | | Here you may configure the settings for each rule that is used to check 114 | | if an email address is a spam address. You can change the rule to be 115 | | used to check if an email address is a spam address. 116 | | 117 | */ 118 | 119 | 'rule' => 'spammail', // The rule to be used to check if an email address is a spam address. 120 | 121 | /* 122 | |-------------------------------------------------------------------------- 123 | | Laravel Spam Email Error Message 124 | |-------------------------------------------------------------------------- 125 | | 126 | | Here you may configure the error message that is returned when an email 127 | | address is considered as a spam address. 128 | | 129 | */ 130 | 131 | 'error_message' => 'The :attribute is considered as a spam address.', 132 | ]; 133 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ./tests 10 | 11 | 12 | 13 | 14 | ./src 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Abstracts/Driver.php: -------------------------------------------------------------------------------- 1 | config = new ConfigBuilder(); 27 | $this->client = new Client(); 28 | } 29 | 30 | /** 31 | * Validate the email. 32 | * 33 | * @return bool 34 | */ 35 | abstract public function validate(string $email): bool; 36 | 37 | /** 38 | * Get the email domain. 39 | * 40 | * @param string $email 41 | * @return string 42 | */ 43 | protected function getDomain(string $email): string 44 | { 45 | return strtolower(substr(strrchr($email, "@"), 1)); 46 | } 47 | } -------------------------------------------------------------------------------- /src/Builders/ConfigBuilder.php: -------------------------------------------------------------------------------- 1 | defaultDriver = Config::get('laravel-spammail-checker.default'); 44 | $this->drivers = Config::get('laravel-spammail-checker.drivers'); 45 | $this->ruleName = Config::get('laravel-spammail-checker.rule'); 46 | $this->errorMessage = Config::get('laravel-spammail-checker.error_message'); 47 | } 48 | 49 | /** 50 | * Get the default driver. 51 | * 52 | * @return string 53 | */ 54 | public function getDefaultDriver(): string 55 | { 56 | return $this->defaultDriver; 57 | } 58 | 59 | /** 60 | * Set the default driver. 61 | * 62 | * @param string $defaultDriver 63 | * @return ConfigBuilder 64 | */ 65 | public function setDefaultDriver(string $defaultDriver): ConfigBuilder 66 | { 67 | $this->defaultDriver = $defaultDriver; 68 | return $this; 69 | } 70 | 71 | /** 72 | * Get the drivers supported by the package. 73 | * 74 | * @return array 75 | */ 76 | public function getDrivers(): array 77 | { 78 | return array_keys($this->drivers); 79 | } 80 | 81 | /** 82 | * Get the api key for the selected driver. 83 | * 84 | * @param string $driver 85 | * @return string 86 | */ 87 | public function getApiKeyForDriver(string $driver): string 88 | { 89 | if (!array_key_exists('api_key', $this->drivers[$driver])) { 90 | throw new SpamMailCheckerApiKeyNotSetException( 91 | 'The selected driver ' . $driver . ' does not use an API key.' 92 | ); 93 | } 94 | return $this->drivers[$driver]['api_key']; 95 | } 96 | 97 | /** 98 | * Get the api url for the selected driver. 99 | * 100 | * @param string $driver 101 | * @return string 102 | */ 103 | public function getApiUrlForDriver(string $driver): string 104 | { 105 | if (!array_key_exists('api_url', $this->drivers[$driver])) { 106 | throw new SpamMailCheckerApiKeyNotSetException( 107 | 'The selected driver ' . $driver . ' does not use an API url.' 108 | ); 109 | } 110 | return $this->drivers[$driver]['api_url']; 111 | } 112 | 113 | /** 114 | * Get the credentials for the selected driver. 115 | * 116 | * @param string $driver 117 | * @return array 118 | */ 119 | public function getCredentialsForDriver(string $driver): array 120 | { 121 | if (!array_key_exists('username', $this->drivers[$driver]) || !array_key_exists('password', $this->drivers[$driver])) { 122 | throw new SpamMailCheckerCredentialsNotSetException( 123 | 'The selected driver ' . $driver . ' does not use credentials.' 124 | ); 125 | } 126 | return [ 127 | 'username' => $this->drivers[$driver]['username'], 128 | 'password' => $this->drivers[$driver]['password'] 129 | ]; 130 | } 131 | 132 | /** 133 | * Get the score for the selected driver. 134 | * 135 | * @param string $driver 136 | * @return float 137 | */ 138 | public function getScoreForDriver(string $driver): float 139 | { 140 | if (!array_key_exists('score', $this->drivers[$driver])) { 141 | throw new SpamMailCheckerException( 142 | 'The selected driver ' . $driver . ' does not use a score.' 143 | ); 144 | } 145 | return $this->drivers[$driver]['score']; 146 | } 147 | 148 | /** 149 | * Get the accepts disposable email flag for the selected driver. 150 | * 151 | * @param string $driver 152 | * @return bool 153 | */ 154 | public function getAcceptsDisposableEmailForDriver(string $driver): bool 155 | { 156 | if (!array_key_exists('accept_disposable_email', $this->drivers[$driver])) { 157 | throw new SpamMailCheckerException( 158 | 'The selected driver ' . $driver . ' does not use a score.' 159 | ); 160 | } 161 | return $this->drivers[$driver]['accept_disposable_email']; 162 | } 163 | 164 | /** 165 | * Get the api version for the selected driver. 166 | * 167 | * @param string $driver 168 | * @return string 169 | */ 170 | public function getApiVersionForDriver(string $driver): string 171 | { 172 | if (!array_key_exists('version', $this->drivers[$driver])) { 173 | throw new SpamMailCheckerException( 174 | 'The selected driver ' . $driver . ' does not use an api version.' 175 | ); 176 | } 177 | return $this->drivers[$driver]['version']; 178 | } 179 | 180 | /** 181 | * Get the source for the selected driver. 182 | * 183 | * @return string 184 | */ 185 | public function getSourceForDriver(string $driver): string 186 | { 187 | if (!array_key_exists('source', $this->drivers[$driver])) { 188 | throw new SpamMailCheckerException( 189 | 'The selected driver ' . $driver . ' does not use a source.' 190 | ); 191 | } 192 | return $this->drivers[$driver]['source']; 193 | } 194 | 195 | /** 196 | * Get the base uris for the selected driver. 197 | * 198 | * @param string $driver 199 | * @return array 200 | */ 201 | public function getBaseUrisForDriver(string $driver): array 202 | { 203 | if (!array_key_exists('base_uris', $this->drivers[$driver])) { 204 | throw new SpamMailCheckerException( 205 | 'The selected driver ' . $driver . ' does not use base uris.' 206 | ); 207 | } 208 | return $this->drivers[$driver]['base_uris']; 209 | } 210 | 211 | /** 212 | * Get the API key for the selected driver. 213 | * 214 | * @return string 215 | * @throws \Martian\SpamMailChecker\Exceptions\SpamMailCheckerApiKeyNotSetException 216 | */ 217 | public function getApiKey(): string 218 | { 219 | if (!array_key_exists('api_key', $this->drivers[$this->defaultDriver])) { 220 | throw new SpamMailCheckerApiKeyNotSetException('The selected driver does not use an API key.'); 221 | } 222 | 223 | $apiKey = $this->drivers[$this->defaultDriver]['api_key']; 224 | 225 | if (empty($apiKey)) { 226 | throw new SpamMailCheckerApiKeyNotSetException('The API key for the selected driver' . $this->defaultDriver . ' is not set.'); 227 | } 228 | 229 | return $apiKey; 230 | } 231 | 232 | /** 233 | * Get the Credentials (Username & Password) for Verifalia driver. 234 | * 235 | * @return array 236 | * @throws \Martian\SpamMailChecker\Exceptions\SpamMailCheckerCredentialsNotSetException 237 | */ 238 | public function getVerifaliaCredentials(): array 239 | { 240 | if ($this->defaultDriver === 'verifalia') { 241 | $username = $this->drivers[$this->defaultDriver]['username']; 242 | $password = $this->drivers[$this->defaultDriver]['password']; 243 | 244 | if (empty($username) || empty($password)) { 245 | throw new SpamMailCheckerCredentialsNotSetException('The credentials for the selected driver' . $this->defaultDriver . ' is not set.'); 246 | } 247 | 248 | return [ 249 | 'username' => $username, 250 | 'password' => $password 251 | ]; 252 | } 253 | 254 | throw new SpamMailCheckerCredentialsNotSetException('The selected driver does not use credentials.'); 255 | } 256 | 257 | /** 258 | * Get the rule name. 259 | * 260 | * @return string 261 | */ 262 | public function getRuleName(): string 263 | { 264 | return $this->ruleName; 265 | } 266 | 267 | /** 268 | * Set the rule name. 269 | * 270 | * @param string $ruleName 271 | * @return ConfigBuilder 272 | */ 273 | public function setRuleName(string $ruleName): ConfigBuilder 274 | { 275 | $this->ruleName = $ruleName; 276 | return $this; 277 | } 278 | 279 | /** 280 | * Get the error message. 281 | * 282 | * @return string 283 | */ 284 | public function getErrorMessage(): string 285 | { 286 | return $this->errorMessage; 287 | } 288 | 289 | /** 290 | * Set the error message. 291 | * 292 | * @param string $errorMessage 293 | * @return ConfigBuilder 294 | */ 295 | public function setErrorMessage(string $errorMessage): ConfigBuilder 296 | { 297 | $this->errorMessage = $errorMessage; 298 | return $this; 299 | } 300 | 301 | /** 302 | * Get Local Cache Key. 303 | * 304 | * @return string 305 | */ 306 | public function getLocalCacheKey(): string 307 | { 308 | return $this->drivers['local']['cache_key']; 309 | } 310 | 311 | /** 312 | * Get Local Cache TTL. 313 | * 314 | * @return int 315 | */ 316 | public function getLocalCacheTTL(): int 317 | { 318 | return $this->drivers['local']['cache_ttl']; 319 | } 320 | 321 | /** 322 | * Get Local Whitelist. 323 | * 324 | * @return array 325 | */ 326 | public function getLocalWhitelist(): array 327 | { 328 | return array_map('strtolower', $this->drivers['local']['whitelist']); 329 | } 330 | 331 | /** 332 | * Get Local Blacklist. 333 | * 334 | * @return array 335 | */ 336 | public function getLocalBlacklist(): array 337 | { 338 | return array_map('strtolower', $this->drivers['local']['blacklist']); 339 | } 340 | 341 | /** 342 | * Get Local Resource Path 343 | * 344 | * @return string 345 | */ 346 | public function getLocalResourcePath(): string 347 | { 348 | return realpath(__DIR__ . '/../../' . $this->drivers['local']['path']); 349 | } 350 | 351 | /** 352 | * Get Local Resource File Content. 353 | * 354 | * @return string 355 | */ 356 | public function getLocalResourceFileContent(): string 357 | { 358 | if (!file_exists($this->getLocalResourcePath())) { 359 | throw new SpamMailCheckerNotFoundException( 360 | 'The local resource file ' . $this->getLocalResourcePath() . ' was not found.' 361 | ); 362 | } 363 | return file_get_contents($this->getLocalResourcePath()); 364 | } 365 | 366 | /** 367 | * Get Local Resource File Content as Array. 368 | * 369 | * @return array 370 | */ 371 | public function getLocalResourceFileContentAsArray(): array 372 | { 373 | return array_filter( 374 | array_map('trim', explode("\n", $this->getLocalResourceFileContent())) 375 | ); 376 | } 377 | 378 | /** 379 | * Get Local Domain List 380 | * 381 | * @return array 382 | */ 383 | public function getLocalDomainList(): array 384 | { 385 | return collect($this->getLocalResourceFileContentAsArray()) 386 | ->reject(function ($domain) { 387 | return in_array(strtolower($domain), $this->getLocalWhitelist()); 388 | }) 389 | ->merge($this->getLocalBlacklist()) 390 | ->toArray(); 391 | } 392 | 393 | /** 394 | * Get Remote Driver Timeout. 395 | * 396 | * @return int 397 | */ 398 | public function getRemoteDriverTimeout(): int 399 | { 400 | return $this->drivers['remote']['timeout']; 401 | } 402 | 403 | /** 404 | * Get Remote Driver Check DNS. 405 | * 406 | * @return bool 407 | */ 408 | public function getRemoteDriverCheckDNS(): bool 409 | { 410 | return $this->drivers['remote']['check_dns']; 411 | } 412 | 413 | /** 414 | * Get Remote Driver Check SMTP. 415 | * 416 | * @return bool 417 | */ 418 | public function getRemoteDriverCheckSMTP(): bool 419 | { 420 | return $this->drivers['remote']['check_smtp']; 421 | } 422 | 423 | /** 424 | * Get Remote Driver Check MX 425 | * 426 | * @return bool 427 | */ 428 | public function getRemoteDriverCheckMX(): bool 429 | { 430 | return $this->drivers['remote']['check_mx']; 431 | } 432 | 433 | } 434 | -------------------------------------------------------------------------------- /src/Contracts/AuthenticateInterface.php: -------------------------------------------------------------------------------- 1 | apiKey = $this->config->getApiKeyForDriver($this->diverName); 43 | $this->apiUrl = $this->config->getApiUrlForDriver($this->diverName); 44 | $this->score = $this->config->getScoreForDriver($this->diverName); 45 | $this->acceptDisposable = $this->config->getAcceptsDisposableEmailForDriver($this->diverName); 46 | } 47 | 48 | /** 49 | * Validate the email using the remote driver. 50 | * 51 | * @return bool 52 | * @throws SpamMailCheckerValidationException 53 | */ 54 | public function validate(string $email): bool 55 | { 56 | try { 57 | $response = $this->client->request('GET', $this->apiUrl, [ 58 | 'query' => [ 59 | 'email' => $email, 60 | 'api_key' => $this->apiKey, 61 | ], 62 | ]); 63 | 64 | $response = json_decode($response->getBody()->getContents(), true); 65 | 66 | if (is_numeric($response['quality_score']) && floatval($response['quality_score']) < $this->score) { 67 | return false; 68 | } 69 | 70 | if (!$this->acceptDisposable && $response['is_disposable_email']['value']) { 71 | return false; 72 | } 73 | 74 | return true; 75 | } catch (RequestException $e) { 76 | throw new SpamMailCheckerException($e->getMessage()); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Drivers/LocalDriver.php: -------------------------------------------------------------------------------- 1 | cacheKey = $this->config->getLocalCacheKey(); 28 | $this->cacheTTL = $this->config->getLocalCacheTTL(); 29 | } 30 | 31 | /** 32 | * Validate the email using the local driver. 33 | * 34 | * @return bool 35 | */ 36 | public function validate(string $email): bool 37 | { 38 | try { 39 | $emailDomain = $this->getDomain($email); 40 | 41 | $domainList = Cache::remember($this->cacheKey, $this->cacheTTL, function () { 42 | return $this->config->getLocalDomainList(); 43 | }); 44 | 45 | return !in_array($emailDomain, $domainList); 46 | } catch (\Exception $e) { 47 | throw new SpamMailCheckerException($e->getMessage()); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Drivers/QuickEmailVerificationDriver.php: -------------------------------------------------------------------------------- 1 | apiKey = $this->config->getApiKeyForDriver($this->diverName); 38 | $this->acceptDisposable = $this->config->getAcceptsDisposableEmailForDriver($this->diverName); 39 | $this->client = new QuickEmailVerificationClient($this->apiKey); 40 | } 41 | 42 | /** 43 | * Validate the email using the quickemailverification driver. 44 | * 45 | * @return bool 46 | * @throws SpamMailCheckerValidationException 47 | */ 48 | public function validate(string $email): bool 49 | { 50 | try { 51 | $response = $this->getVerificationResponse($email); 52 | 53 | if($response['result'] !== 'valid') { 54 | return false; 55 | } 56 | 57 | if(!$this->acceptDisposable && $response['disposable']) { 58 | return false; 59 | } 60 | 61 | return true; 62 | } catch (\Exception $e) { 63 | throw new SpamMailCheckerException($e->getMessage()); 64 | } 65 | } 66 | 67 | 68 | /** 69 | * Get the email verification response from the client. 70 | * 71 | * @param string $email 72 | * @return array|\stdClass 73 | */ 74 | protected function getVerificationResponse(string $email): array|\stdClass 75 | { 76 | if (function_exists('app') && app()->environment() !== 'production') { 77 | return $this->client->quickemailverification()->sandbox($email)->body; 78 | } else { 79 | return $this->client->quickemailverification()->verify($email)->body; 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/Drivers/RemoteDriver.php: -------------------------------------------------------------------------------- 1 | checkDNS = $this->config->getRemoteDriverCheckDNS(); 37 | $this->checkSMTP = $this->config->getRemoteDriverCheckSMTP(); 38 | $this->checkMX = $this->config->getRemoteDriverCheckMX(); 39 | $this->timeout = $this->config->getRemoteDriverTimeout(); 40 | } 41 | 42 | /** 43 | * Validate the email using the remote driver. 44 | * 45 | * @return bool 46 | * @throws SpamMailCheckerValidationException 47 | */ 48 | public function validate(string $email): bool 49 | { 50 | $emailDomain = $this->getDomain($email); 51 | 52 | try { 53 | // Check DNS 54 | if ($this->checkDNS && !$this->isDNSValid($emailDomain)) { 55 | return false; 56 | } 57 | 58 | // Check SMTP 59 | if ($this->checkSMTP && !$this->isSMTPValid($emailDomain)) { 60 | return false; 61 | } 62 | 63 | // Check MX 64 | if ($this->checkMX && !$this->isMXValid($emailDomain)) { 65 | return false; 66 | } 67 | 68 | return true; 69 | } catch (\Exception $e) { 70 | throw new SpamMailCheckerException('Error occurred during email validation.'); 71 | } 72 | } 73 | 74 | /** 75 | * Check if the DNS record is valid for the email domain. 76 | * 77 | * @param string $domain 78 | * @return bool 79 | */ 80 | protected function isDNSValid(string $domain): bool 81 | { 82 | return checkdnsrr($domain, 'A'); 83 | } 84 | 85 | /** 86 | * Check if the SMTP server is valid for the email domain. 87 | * 88 | * @param string $domain 89 | * @return bool 90 | */ 91 | protected function isSMTPValid(string $domain): bool 92 | { 93 | $smtp = fsockopen($domain, 25, $errno, $errstr, $this->timeout); 94 | if ($smtp) { 95 | fclose($smtp); 96 | return true; 97 | } 98 | return false; 99 | } 100 | 101 | /** 102 | * Check if the MX record is valid for the email domain. 103 | * 104 | * @param string $domain 105 | * @return bool 106 | */ 107 | protected function isMXValid(string $domain): bool 108 | { 109 | return getmxrr($domain, $mxhosts); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Drivers/SendGridDriver.php: -------------------------------------------------------------------------------- 1 | apiKey = $this->config->getApiKeyForDriver($this->diverName); 47 | $this->apiUrl = $this->config->getApiUrlForDriver($this->diverName); 48 | $this->score = $this->config->getScoreForDriver($this->diverName); 49 | $this->acceptDisposable = $this->config->getAcceptsDisposableEmailForDriver($this->diverName); 50 | $this->source = $this->config->getSourceForDriver($this->diverName); 51 | } 52 | 53 | /** 54 | * Validate the email using the sendgrid driver. 55 | * 56 | * @return bool 57 | */ 58 | public function validate(string $email): bool 59 | { 60 | try { 61 | $response = $this->getVerificationResponse($email); 62 | 63 | if ($response['result']['verdict'] !== 'Valid' || $response['result']['score'] > $this->score) { 64 | return false; 65 | } 66 | 67 | if ($response['result']['checks']['domain']['is_suspected_disposable_address'] && $this->acceptDisposable) { 68 | return false; 69 | } 70 | 71 | return true; 72 | } catch (\Exception $e) { 73 | throw new SpamMailCheckerException($e->getMessage()); 74 | } 75 | } 76 | 77 | /** 78 | * Get the response from the sendgrid API. 79 | * 80 | * @return array 81 | */ 82 | protected function getVerificationResponse(string $email): array 83 | { 84 | $response = $this->client->request('GET', $this->apiUrl, [ 85 | 'headers' => [ 86 | 'Authorization' => 'Bearer ' . $this->apiKey, 87 | 'Content-Type' => 'application/json' 88 | ], 89 | 'json' => [ 90 | 'email' => $email, 91 | 'source' => $this->source 92 | ] 93 | ]); 94 | 95 | return json_decode($response->getBody()->getContents(), true); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Drivers/VerifaliaDriver.php: -------------------------------------------------------------------------------- 1 | initializeConfig(); 51 | $this->bearerToken = $this->authenticate(); 52 | $this->acceptDisposable = $this->config->getAcceptsDisposableEmailForDriver($this->driverName); 53 | } 54 | 55 | protected function initializeConfig() 56 | { 57 | $this->credential = $this->config->getCredentialsForDriver($this->driverName); 58 | $this->apiBaseUris = $this->config->getBaseUrisForDriver($this->driverName); 59 | $this->apiUrl = $this->config->getApiUrlForDriver($this->driverName); 60 | $this->version = $this->config->getApiVersionForDriver($this->driverName); 61 | } 62 | 63 | public function authenticate(): string 64 | { 65 | try { 66 | $selectedApiUrl = $this->getRandomApiUrl(); 67 | $response = $this->client->request('POST', $selectedApiUrl . $this->version . '/auth/tokens', [ 68 | 'json' => [ 69 | 'username' => $this->credential['username'], 70 | 'password' => $this->credential['password'], 71 | ], 72 | ]); 73 | 74 | $responseData = json_decode($response->getBody()->getContents(), true); 75 | 76 | return $responseData['accessToken']; 77 | } catch (RequestException $e) { 78 | throw new SpamMailCheckerException($e->getMessage()); 79 | } 80 | } 81 | 82 | protected function getRandomApiUrl(): string 83 | { 84 | $allApiUrls = array_merge($this->apiBaseUris, [$this->apiUrl]); 85 | return $allApiUrls[array_rand($allApiUrls)]; 86 | } 87 | 88 | public function validate(string $email): bool 89 | { 90 | try { 91 | $response = $this->getVerificationResponse($email); 92 | 93 | if ($response['status'] !== 'Success' || $response['classification'] !== 'Deliverable') { 94 | return false; 95 | } 96 | 97 | if (!$this->acceptDisposable && $response['isDisposableEmailAddress']) { 98 | return false; 99 | } 100 | 101 | return true; 102 | } catch (\Exception $e) { 103 | throw new SpamMailCheckerException($e->getMessage()); 104 | } 105 | } 106 | 107 | protected function getVerificationResponse(string $email): array 108 | { 109 | $selectedApiUrl = $this->getRandomApiUrl(); 110 | 111 | $response = $this->client->request('POST', $selectedApiUrl . $this->version . '/email-validations', [ 112 | 'headers' => [ 113 | 'Authorization' => 'Bearer ' . $this->bearerToken, 114 | 'Accept' => 'application/json', 115 | 'Content-Type' => 'application/json', 116 | ], 117 | 'json' => [ 118 | 'entries' => [ 119 | ['inputData' => $email], 120 | ], 121 | ], 122 | ]); 123 | 124 | return json_decode($response->getBody()->getContents(), true)['entries']['data'][0]; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Exceptions/SpamMailCheckerApiKeyNotSetException.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 24 | $this->publishes([ 25 | __DIR__ . '/../../config/laravel-spammail-checker.php' => config_path('laravel-spammail-checker.php'), 26 | ], 'config'); 27 | } 28 | 29 | // Extend Validator 30 | $this->extendValidator(); 31 | } 32 | 33 | /** 34 | * Register the application services. 35 | */ 36 | public function register() 37 | { 38 | // Automatically apply the package configuration 39 | $this->mergeConfigFrom(__DIR__ . '/../../config/laravel-spammail-checker.php', 'laravel-spammail-checker'); 40 | 41 | // Register the main class to use with the facade 42 | $this->app->singleton('spammailchecker', function () { 43 | return new SpamMailChecker; 44 | }); 45 | } 46 | 47 | /** 48 | * Extend SpamMailChecker Validation Rule 49 | * 50 | * @return void 51 | */ 52 | protected function extendValidator() 53 | { 54 | $configBuilder = new ConfigBuilder; 55 | Validator::extend($configBuilder->getRuleName(), function ($attribute, $value, $parameters, $validator) { 56 | return app('spammailchecker')->validate($value); 57 | }, $configBuilder->getErrorMessage()); 58 | } 59 | 60 | /** 61 | * Get the services provided by the provider. 62 | */ 63 | public function provides() 64 | { 65 | return ['spammailchecker']; 66 | } 67 | } -------------------------------------------------------------------------------- /src/SpamMailChecker.php: -------------------------------------------------------------------------------- 1 | getDefaultDriver(); 31 | $this->driver = $this->initializeDriver($defaultDriver); 32 | } 33 | 34 | /** 35 | * Initialize the driver based on the default driver configuration. 36 | * 37 | * @param string $defaultDriver 38 | * @return DriverInterface 39 | * @throws SpamMailCheckerException 40 | */ 41 | protected function initializeDriver(string $defaultDriver): DriverInterface 42 | { 43 | switch ($defaultDriver) { 44 | case 'abstractapi': 45 | return new AbstractApiDriver(); 46 | case 'quickemailverification': 47 | return new QuickEmailVerificationDriver(); 48 | case 'verifalia': 49 | return new VerifaliaDriver(); 50 | case 'sendgrid': 51 | return new SendGridDriver(); 52 | case 'local': 53 | return new LocalDriver(); 54 | case 'remote': 55 | return new RemoteDriver(); 56 | default: 57 | throw new SpamMailCheckerException("Driver '{$defaultDriver}' not found."); 58 | } 59 | } 60 | 61 | /** 62 | * Validate the email using the default driver. 63 | * 64 | * @param string $email 65 | * @return bool 66 | */ 67 | public function validate(string $email): bool 68 | { 69 | return $this->driver->validate(filter_var(strtolower($email), FILTER_SANITIZE_EMAIL)); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | SpamMailCheckerFacade::class, 38 | ]; 39 | } 40 | 41 | public function setUp(): void 42 | { 43 | parent::setUp(); 44 | $this->configBuilder = new ConfigBuilder(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Unit/Builder/ConfigBuilderTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('local', $this->configBuilder->getDefaultDriver()); 15 | } 16 | 17 | /** 18 | * @test it can set default driver 19 | */ 20 | public function it_can_set_default_driver() 21 | { 22 | $this->configBuilder->setDefaultDriver('test'); 23 | $this->assertEquals('test', $this->configBuilder->getDefaultDriver()); 24 | } 25 | 26 | /** 27 | * @test it can get all available drivers 28 | */ 29 | public function it_can_get_all_available_drivers() 30 | { 31 | $this->assertEquals( 32 | [ 33 | "local", 34 | "remote", 35 | "abstractapi", 36 | "quickemailverification", 37 | "verifalia", 38 | "sendgrid", 39 | ], 40 | $this->configBuilder->getDrivers() 41 | ); 42 | } 43 | 44 | /** 45 | * @test it can get api key for a driver (abstractapi) 46 | */ 47 | public function it_can_get_api_key_for_a_driver() 48 | { 49 | $this->assertEquals( 50 | env('ABSTRACTAPI_API_KEY'), 51 | $this->configBuilder->getApiKeyForDriver('abstractapi') 52 | ); 53 | } 54 | 55 | /** 56 | * @test it can get api url for a driver (abstractapi) 57 | */ 58 | public function it_can_get_api_url_for_a_driver() 59 | { 60 | $this->assertEquals( 61 | 'https://emailvalidation.abstractapi.com/v1', 62 | $this->configBuilder->getApiUrlForDriver('abstractapi') 63 | ); 64 | } 65 | 66 | /** 67 | * @test it can get credentials for a driver (verifalia) 68 | */ 69 | public function it_can_get_credentials_for_a_driver() 70 | { 71 | $this->assertEquals( 72 | [ 73 | 'username' => env('VERIFALIA_USERNAME'), 74 | 'password' => env('VERIFALIA_PASSWORD'), 75 | ], 76 | $this->configBuilder->getCredentialsForDriver('verifalia') 77 | ); 78 | } 79 | 80 | /** 81 | * @test it can get score for a driver (sendgrid) 82 | */ 83 | public function it_can_get_score_for_a_driver() 84 | { 85 | $this->assertEquals( 86 | 0.5, 87 | $this->configBuilder->getScoreForDriver('sendgrid') 88 | ); 89 | } 90 | 91 | /** 92 | * @test it can get accept disposable email for a driver (sendgrid) 93 | */ 94 | public function it_can_get_accept_disposable_email_for_a_driver() 95 | { 96 | $this->assertTrue( 97 | $this->configBuilder->getAcceptsDisposableEmailForDriver('sendgrid') 98 | ); 99 | } 100 | 101 | /** 102 | * @test it can get api version for a driver (verifalia) 103 | */ 104 | public function it_can_get_api_version_for_a_driver() 105 | { 106 | $this->assertEquals( 107 | 'v2.4', 108 | $this->configBuilder->getApiVersionForDriver('verifalia') 109 | ); 110 | } 111 | 112 | /** 113 | * @test it can get source for a driver (sendgrid) 114 | */ 115 | public function it_can_get_source_for_a_driver() 116 | { 117 | $this->assertEquals( 118 | 'signup', 119 | $this->configBuilder->getSourceForDriver('sendgrid') 120 | ); 121 | } 122 | 123 | /** 124 | * @test it can get base uris for a driver (verifalia) 125 | */ 126 | public function it_can_get_base_uris_for_a_driver() 127 | { 128 | $this->assertEquals( 129 | [ 130 | 'https://api-1.verifalia.com/', 131 | 'https://api-2.verifalia.com/', 132 | 'https://api-3.verifalia.com/' 133 | ], 134 | $this->configBuilder->getBaseUrisForDriver('verifalia') 135 | ); 136 | } 137 | 138 | /** 139 | * @test it can get timeout for a driver (remote) 140 | */ 141 | public function it_can_get_timeout_for_a_driver() 142 | { 143 | $this->assertEquals( 144 | 5, 145 | $this->configBuilder->getRemoteDriverTimeout() 146 | ); 147 | } 148 | } -------------------------------------------------------------------------------- /tests/Unit/Drivers/AbstractApiDriverTest.php: -------------------------------------------------------------------------------- 1 | driver = new AbstractApiDriver(); 20 | } 21 | 22 | /** 23 | * @test it can get default driver as remote 24 | */ 25 | public function it_can_check_if_an_email_is_spam() 26 | { 27 | $this->expectException(SpamMailCheckerException::class); 28 | $this->assertFalse($this->driver->validate('hello@23456gg.com')); 29 | $this->assertTrue($this->driver->validate('jane@gmail.com')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Drivers/LocalDriverTest.php: -------------------------------------------------------------------------------- 1 | driver = new LocalDriver(); 22 | } 23 | 24 | /** 25 | * @test it can check if an email is spam 26 | */ 27 | public function it_can_check_if_an_email_is_spam() 28 | { 29 | $this->assertFalse($this->driver->validate('hello@example.com')); 30 | $this->assertTrue($this->driver->validate('hello@gmail.com')); 31 | } 32 | 33 | /** 34 | * @test it cannot validate an email in whitelist 35 | */ 36 | public function it_cannot_validate_an_email_in_whitelist() 37 | { 38 | $this->assertTrue($this->driver->validate('hello@12345gmail.com')); 39 | } 40 | 41 | /** 42 | * @test it can validate an email in blacklist 43 | */ 44 | public function it_can_validate_an_email_in_blacklist() 45 | { 46 | $this->assertFalse($this->driver->validate('hello@yahoo.com')); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/Unit/Drivers/QuickEmailVerificationDriverTest.php: -------------------------------------------------------------------------------- 1 | driver = new QuickEmailVerificationDriver(); 20 | } 21 | 22 | /** 23 | * @test it can get default driver as remote 24 | */ 25 | public function it_can_check_if_an_email_is_spam() 26 | { 27 | $this->expectException(SpamMailCheckerException::class); 28 | $this->assertFalse($this->driver->validate('hello@23456gg.com')); 29 | $this->assertTrue($this->driver->validate('jane@gmail.com')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Drivers/RemoteDriverTest.php: -------------------------------------------------------------------------------- 1 | driver = new RemoteDriver(); 20 | } 21 | 22 | /** 23 | * @test it can get default driver as remote 24 | */ 25 | public function it_can_check_if_an_email_is_spam() 26 | { 27 | $this->expectException(SpamMailCheckerException::class); 28 | $this->assertFalse($this->driver->validate('hello@23456gg.com')); 29 | $this->assertTrue($this->driver->validate('jane@gmail.com')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Drivers/SendGridDriverTest.php: -------------------------------------------------------------------------------- 1 | driver = new SendGridDriver(); 20 | } 21 | 22 | /** 23 | * @test it can get default driver as remote 24 | */ 25 | public function it_can_check_if_an_email_is_spam() 26 | { 27 | $this->expectException(SpamMailCheckerException::class); 28 | $this->assertFalse($this->driver->validate('hello@23456gg.com')); 29 | $this->assertTrue($this->driver->validate('jane@gmail.com')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/Drivers/VerifaliaDriverTest.php: -------------------------------------------------------------------------------- 1 | driver = new VerifaliaDriver(); 20 | } 21 | 22 | /** 23 | * @test it can get default driver as remote 24 | */ 25 | public function it_can_check_if_an_email_is_spam() 26 | { 27 | $this->expectException(SpamMailCheckerException::class); 28 | $this->assertFalse($this->driver->validate('hello@23456gg.com')); 29 | $this->assertTrue($this->driver->validate('jane@gmail.com')); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/Unit/SpamMailCheckerTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('local', $this->configBuilder->getDefaultDriver()); 23 | } 24 | 25 | /** 26 | * @test it can get default driver as remote 27 | */ 28 | public function it_can_get_default_driver_as_remote_when_it_is_set() 29 | { 30 | $this->configBuilder->setDefaultDriver('remote'); 31 | $this->assertEquals('remote', $this->configBuilder->getDefaultDriver()); 32 | } 33 | 34 | /** 35 | * @test it can get absractapi driver has api key 36 | */ 37 | public function it_can_get_absractapi_driver_has_api_key() 38 | { 39 | $this->configBuilder->setDefaultDriver('abstractapi'); 40 | $this->assertEquals('abstractapi', $this->configBuilder->getDefaultDriver()); 41 | $this->assertEquals(Config::get('laravel-spammail-checker.drivers.abstractapi.api_key'), $this->configBuilder->getApiKey()); 42 | } 43 | 44 | 45 | /** 46 | * @test it can get sendgrid driver has api key 47 | */ 48 | public function it_can_get_sendgrid_driver_has_api_key_when_it_is_set() 49 | { 50 | $this->configBuilder->setDefaultDriver('sendgrid'); 51 | $this->assertEquals('sendgrid', $this->configBuilder->getDefaultDriver()); 52 | $this->assertEquals(Config::get('laravel-spammail-checker.drivers.sendgrid.api_key'), $this->configBuilder->getApiKey()); 53 | } 54 | 55 | /** 56 | * @test it can get validate email using verifalia as default driver 57 | */ 58 | public function it_can_get_validate_email_using_verifalia_as_default_driver() 59 | { 60 | Config::set('laravel-spammail-checker.default', 'verifalia'); 61 | $spammailchecker = new SpamMailChecker(); 62 | $this->expectException(SpamMailCheckerException::class); 63 | $this->assertFalse($spammailchecker->validate('hello@random00address.com')); 64 | $this->assertTrue($spammailchecker->validate('jane@gmail.com')); 65 | } 66 | } --------------------------------------------------------------------------------