diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b28549..fa73feb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to this package will be documented in this file. +## v0.2.0 + +* switch to manager driver pattern + * `\Elbgoods\TrashmailRule\TrashmailManager` + * `\Elbgoods\TrashmailRule\Providers` +* add service class `\Elbgoods\TrashmailRule\Trashmail` +* add facade `\Elbgoods\TrashmailRule\Facades\Trashmail` +* try-catch all errors in providers and skip this provider + ## v0.1.0 * initial release \ No newline at end of file diff --git a/README.md b/README.md index fcb3661..87389e1 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ If you want to pass some domains always you can add them to the whitelist. These ## Usage +## Validation Rule + This package provides a basic `TrashmailRule` which you can use. All more specific rules only extend this rule with a predefined `format`. ```php @@ -60,6 +62,45 @@ $rule = new TrashmailRule(false); $rule->nullable(); ``` +## Facade + +You can also use the facade if you want to check any email address outside validation. +This will run the same logic as the validation rule and runs all providers set in the config. + +```php +use Elbgoods\TrashmailRule\Facades\Trashmail; + +Trashmail::isDisposable('example@elbgoods.de'); +``` + +## single Provider + +You can also check using a single provider only. +Keep in mind that all providers only accept the domain to check and not a full email address. +The facade provides a method that returns the domain used in an email address. + +```php +use Elbgoods\TrashmailRule\Facades\Trashmail; + +Trashmail::provider('config')->isDisposable( + Trashmail::getDomain('example@elbgoods.de') +); +``` + +## custom Provider + +If you want to add your own provider you can do so. + +```php +use Elbgoods\TrashmailRule\Facades\Trashmail; +use Illuminate\Contracts\Container\Container; +use Elbgoods\TrashmailRule\Contracts\ProviderContract; + +Trashmail::extend('custom_provider', static function (Container $app): ProviderContract { + return new CustomProvider(); +}); +``` + ## Changelog Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. diff --git a/config/trashmail.php b/config/trashmail.php index 5cb0b0c..70f7a0e 100644 --- a/config/trashmail.php +++ b/config/trashmail.php @@ -3,6 +3,11 @@ use GuzzleHttp\RequestOptions; return [ + 'providers' => [ + 'config', + 'dead_letter', + ], + /* * This package can load a remote blacklist from https://www.dead-letter.email */ diff --git a/src/Contracts/ProviderContract.php b/src/Contracts/ProviderContract.php new file mode 100644 index 0000000..c50c1cb --- /dev/null +++ b/src/Contracts/ProviderContract.php @@ -0,0 +1,8 @@ +whitelist = $whitelist; + $this->blacklist = $blacklist; + } + + public function isDisposable(string $domain): ?bool + { + if (in_array($domain, $this->whitelist)) { + return false; + } + + if (in_array($domain, $this->blacklist)) { + return true; + } + + return null; + } +} diff --git a/src/Providers/DeadLetterProvider.php b/src/Providers/DeadLetterProvider.php new file mode 100644 index 0000000..44665ad --- /dev/null +++ b/src/Providers/DeadLetterProvider.php @@ -0,0 +1,81 @@ +config = $config; + $this->cache = $cache; + } + + public function isDisposable(string $domain): ?bool + { + if (! $this->config['enabled']) { + return null; + } + + if (in_array($this->hashDomain($domain), $this->getBlacklist())) { + return true; + } + + return null; + } + + protected function getBlacklist(): array + { + if (! $this->config['cache']['enabled']) { + return $this->loadDeadLetter(); + } + + $cache = $this->getCache(); + + $key = $this->config['cache']['key']; + + if ($cache->has($key)) { + return json_decode($cache->get($key), true); + } + + $blacklist = $this->loadDeadLetter(); + + $cache->put( + $key, + json_encode($blacklist), + $this->config['cache']['ttl'] + ); + + return $blacklist; + } + + protected function loadDeadLetter(): array + { + $response = guzzle( + self::BLACKLIST_URL, + $this->config['guzzle'] + )->request('GET', ''); + + $body = $response->getBody()->getContents(); + + return json_decode($body, true); + } + + protected function hashDomain(string $domain): string + { + return hash('sha1', hash('sha1', $domain)); + } + + protected function getCache(): CacheRepository + { + return $this->cache->store($this->config['cache']['store']); + } +} diff --git a/src/Rules/TrashmailRule.php b/src/Rules/TrashmailRule.php index 8bbe7e8..23ccfc7 100644 --- a/src/Rules/TrashmailRule.php +++ b/src/Rules/TrashmailRule.php @@ -2,15 +2,13 @@ namespace Elbgoods\TrashmailRule\Rules; -use Illuminate\Cache\Repository as CacheRepository; +use Elbgoods\TrashmailRule\Facades\Trashmail; use Illuminate\Contracts\Validation\Rule; use Illuminate\Support\Facades\Lang; use Illuminate\Support\Str; class TrashmailRule implements Rule { - protected const BLACKLIST_URL = 'https://www.dead-letter.email/blacklist_flat.json'; - protected bool $required; public function __construct(bool $required = true) @@ -45,16 +43,7 @@ public function passes($attribute, $value): bool return false; } - $domain = trim(mb_strtolower(Str::after($value, '@'))); - - if (in_array($domain, config('trashmail.whitelist'))) { - return true; - } - - return ! in_array( - $this->hashDomain($domain), - $this->getBlacklist() - ); + return ! Trashmail::isDisposable($value); } public function message(): string @@ -71,60 +60,4 @@ public function isNullable(): bool { return ! $this->required; } - - protected function getBlacklist(): array - { - $deadLetter = $this->getDeadLetter(); - - $blacklist = array_map([$this, 'hashDomain'], config('trashmail.blacklist')); - - return array_merge($deadLetter, $blacklist); - } - - protected function getDeadLetter(): array - { - if (! config('trashmail.dead_letter.enabled')) { - return []; - } - - if (! config('trashmail.dead_letter.cache.enabled')) { - return $this->loadDeadLetter(); - } - - /** @var CacheRepository $cache */ - $cache = app('cache')->store(config('trashmail.dead_letter.cache.store')); - - $key = config('trashmail.dead_letter.cache.key'); - - if ($cache->has($key)) { - return json_decode($cache->get($key), true); - } - - $blacklist = $this->loadDeadLetter(); - - $cache->put( - $key, - json_encode($blacklist), - config('trashmail.dead_letter.cache.ttl') - ); - - return $blacklist; - } - - protected function loadDeadLetter(): array - { - $response = guzzle( - self::BLACKLIST_URL, - config('trashmail.dead_letter.guzzle') - )->request('GET', ''); - - $body = $response->getBody()->getContents(); - - return json_decode($body, true); - } - - protected function hashDomain(string $domain): string - { - return hash('sha1', hash('sha1', $domain)); - } } diff --git a/src/Trashmail.php b/src/Trashmail.php new file mode 100644 index 0000000..f59ebfe --- /dev/null +++ b/src/Trashmail.php @@ -0,0 +1,67 @@ +config = $config; + $this->manager = $manager; + $this->log = $log; + } + + public function isDisposable(string $email): bool + { + $domain = $this->getDomain($email); + + $providers = $this->config->get('trashmail.providers'); + + foreach ($providers as $provider) { + try { + $isDisposable = $this->manager->driver($provider)->isDisposable($domain); + } catch (Exception $exception) { + $this->log->error($exception); + + continue; + } + + if ($isDisposable === null) { + continue; + } + + return $isDisposable; + } + + return false; + } + + public function provider(string $provider): ProviderContract + { + return $this->manager->driver($provider); + } + + public function extend(string $provider, Closure $creator): TrashmailManager + { + return $this->manager->extend($provider, $creator); + } + + public function getDomain(string $email): string + { + return trim(mb_strtolower(Str::after($email, '@'))); + } +} diff --git a/src/TrashmailManager.php b/src/TrashmailManager.php new file mode 100644 index 0000000..7bd74fa --- /dev/null +++ b/src/TrashmailManager.php @@ -0,0 +1,51 @@ +container->make(ConfigProvider::class, [ + 'whitelist' => $this->config->get('trashmail.whitelist'), + 'blacklist' => $this->config->get('trashmail.blacklist'), + ]); + } + + protected function createDeadLetterDriver(): DeadLetterProvider + { + return $this->container->make(DeadLetterProvider::class, [ + 'config' => $this->config->get('trashmail.dead_letter'), + ]); + } +} diff --git a/src/TrashmailRuleServiceProvider.php b/src/TrashmailRuleServiceProvider.php index 17b2539..3a27b70 100644 --- a/src/TrashmailRuleServiceProvider.php +++ b/src/TrashmailRuleServiceProvider.php @@ -7,13 +7,31 @@ class TrashmailRuleServiceProvider extends ServiceProvider { public function boot(): void + { + if ($this->app->runningInConsole()) { + $this->bootConfig(); + $this->bootLang(); + } + } + + public function register(): void + { + $this->app->singleton(TrashmailManager::class); + + $this->app->singleton(Trashmail::class); + } + + protected function bootConfig(): void { $this->publishes([ __DIR__.'/../config/trashmail.php' => config_path('trashmail.php'), ], 'config'); $this->mergeConfigFrom(__DIR__.'/../config/trashmail.php', 'trashmail'); + } + protected function bootLang(): void + { $this->publishes([ __DIR__.'/../resources/lang' => resource_path('lang/vendor/trashmailRule'), ], 'lang');