From f06aee9a97704abeb9aa9cf1ec5285671e43d2ec Mon Sep 17 00:00:00 2001 From: Tom Witkowski Date: Tue, 18 Feb 2020 11:23:00 +0100 Subject: [PATCH 1/6] switch to manager driver pattern --- config/trashmail.php | 5 ++ src/Contracts/ProviderContract.php | 8 +++ src/Facades/Trashmail.php | 16 ++++++ src/Providers/ConfigProvider.php | 30 +++++++++++ src/Providers/DeadLetterProvider.php | 81 ++++++++++++++++++++++++++++ src/Rules/TrashmailRule.php | 71 +----------------------- src/Trashmail.php | 59 ++++++++++++++++++++ src/TrashmailManager.php | 36 +++++++++++++ src/TrashmailRuleServiceProvider.php | 8 +++ 9 files changed, 245 insertions(+), 69 deletions(-) create mode 100644 src/Contracts/ProviderContract.php create mode 100644 src/Facades/Trashmail.php create mode 100644 src/Providers/ConfigProvider.php create mode 100644 src/Providers/DeadLetterProvider.php create mode 100644 src/Trashmail.php create mode 100644 src/TrashmailManager.php 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..dd3439e --- /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; + } +} \ No newline at end of file diff --git a/src/Providers/DeadLetterProvider.php b/src/Providers/DeadLetterProvider.php new file mode 100644 index 0000000..116d4de --- /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']); + } +} \ No newline at end of file diff --git a/src/Rules/TrashmailRule.php b/src/Rules/TrashmailRule.php index 8bbe7e8..22599f1 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..2e6bd95 --- /dev/null +++ b/src/Trashmail.php @@ -0,0 +1,59 @@ +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; + } + + protected function getDomain(string $email): string + { + return trim(mb_strtolower(Str::after($email, '@'))); + } +} \ No newline at end of file diff --git a/src/TrashmailManager.php b/src/TrashmailManager.php new file mode 100644 index 0000000..d4d9895 --- /dev/null +++ b/src/TrashmailManager.php @@ -0,0 +1,36 @@ +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') + ]); + } +} \ No newline at end of file diff --git a/src/TrashmailRuleServiceProvider.php b/src/TrashmailRuleServiceProvider.php index 17b2539..92edb01 100644 --- a/src/TrashmailRuleServiceProvider.php +++ b/src/TrashmailRuleServiceProvider.php @@ -2,6 +2,7 @@ namespace Elbgoods\TrashmailRule; +use Elbgoods\TrashmailRule\Providers\ConfigProvider; use Illuminate\Support\ServiceProvider; class TrashmailRuleServiceProvider extends ServiceProvider @@ -20,4 +21,11 @@ public function boot(): void $this->loadTranslationsFrom(__DIR__.'/../resources/lang/', 'trashmailRule'); } + + public function register() + { + $this->app->singleton(TrashmailManager::class); + + $this->app->singleton(Trashmail::class); + } } From 0bdca98f2afeab5b3e2be1c23ee8d1b48c990048 Mon Sep 17 00:00:00 2001 From: Tom Witkowski Date: Tue, 18 Feb 2020 11:23:17 +0100 Subject: [PATCH 2/6] fix php cs --- src/Contracts/ProviderContract.php | 2 +- src/Facades/Trashmail.php | 2 +- src/Providers/ConfigProvider.php | 2 +- src/Providers/DeadLetterProvider.php | 4 ++-- src/Rules/TrashmailRule.php | 2 +- src/Trashmail.php | 7 +++---- src/TrashmailManager.php | 6 +++--- src/TrashmailRuleServiceProvider.php | 1 - 8 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/Contracts/ProviderContract.php b/src/Contracts/ProviderContract.php index dd3439e..c50c1cb 100644 --- a/src/Contracts/ProviderContract.php +++ b/src/Contracts/ProviderContract.php @@ -5,4 +5,4 @@ interface ProviderContract { public function isDisposable(string $domain): ?bool; -} \ No newline at end of file +} diff --git a/src/Facades/Trashmail.php b/src/Facades/Trashmail.php index 53e8c1c..d567c83 100644 --- a/src/Facades/Trashmail.php +++ b/src/Facades/Trashmail.php @@ -13,4 +13,4 @@ protected static function getFacadeAccessor(): string { return \Elbgoods\TrashmailRule\Trashmail::class; } -} \ No newline at end of file +} diff --git a/src/Providers/ConfigProvider.php b/src/Providers/ConfigProvider.php index bffd221..15bd053 100644 --- a/src/Providers/ConfigProvider.php +++ b/src/Providers/ConfigProvider.php @@ -27,4 +27,4 @@ public function isDisposable(string $domain): ?bool return null; } -} \ No newline at end of file +} diff --git a/src/Providers/DeadLetterProvider.php b/src/Providers/DeadLetterProvider.php index 116d4de..44665ad 100644 --- a/src/Providers/DeadLetterProvider.php +++ b/src/Providers/DeadLetterProvider.php @@ -3,8 +3,8 @@ namespace Elbgoods\TrashmailRule\Providers; use Elbgoods\TrashmailRule\Contracts\ProviderContract; -use Illuminate\Contracts\Cache\Repository as CacheRepository; use Illuminate\Contracts\Cache\Factory as CacheFactory; +use Illuminate\Contracts\Cache\Repository as CacheRepository; class DeadLetterProvider implements ProviderContract { @@ -78,4 +78,4 @@ protected function getCache(): CacheRepository { return $this->cache->store($this->config['cache']['store']); } -} \ No newline at end of file +} diff --git a/src/Rules/TrashmailRule.php b/src/Rules/TrashmailRule.php index 22599f1..23ccfc7 100644 --- a/src/Rules/TrashmailRule.php +++ b/src/Rules/TrashmailRule.php @@ -43,7 +43,7 @@ public function passes($attribute, $value): bool return false; } - return !Trashmail::isDisposable($value); + return ! Trashmail::isDisposable($value); } public function message(): string diff --git a/src/Trashmail.php b/src/Trashmail.php index 2e6bd95..cf1f6af 100644 --- a/src/Trashmail.php +++ b/src/Trashmail.php @@ -20,8 +20,7 @@ public function __construct( ConfigRepository $config, TrashmailManager $manager, LoggerInterface $log - ) - { + ) { $this->config = $config; $this->manager = $manager; $this->log = $log; @@ -36,7 +35,7 @@ public function isDisposable(string $email): bool foreach ($providers as $provider) { try { $isDisposable = $this->manager->driver($provider)->isDisposable($domain); - } catch(Exception $exception) { + } catch (Exception $exception) { $this->log->error($exception); continue; @@ -56,4 +55,4 @@ protected function getDomain(string $email): string { return trim(mb_strtolower(Str::after($email, '@'))); } -} \ No newline at end of file +} diff --git a/src/TrashmailManager.php b/src/TrashmailManager.php index d4d9895..6d15be2 100644 --- a/src/TrashmailManager.php +++ b/src/TrashmailManager.php @@ -23,14 +23,14 @@ protected function createConfigDriver(): ConfigProvider { return $this->container->make(ConfigProvider::class, [ 'whitelist' => $this->config->get('trashmail.whitelist'), - 'blacklist' => $this->config->get('trashmail.blacklist') + 'blacklist' => $this->config->get('trashmail.blacklist'), ]); } protected function createDeadLetterDriver(): DeadLetterProvider { return $this->container->make(DeadLetterProvider::class, [ - 'config' => $this->config->get('trashmail.dead_letter') + 'config' => $this->config->get('trashmail.dead_letter'), ]); } -} \ No newline at end of file +} diff --git a/src/TrashmailRuleServiceProvider.php b/src/TrashmailRuleServiceProvider.php index 92edb01..80e6c4e 100644 --- a/src/TrashmailRuleServiceProvider.php +++ b/src/TrashmailRuleServiceProvider.php @@ -2,7 +2,6 @@ namespace Elbgoods\TrashmailRule; -use Elbgoods\TrashmailRule\Providers\ConfigProvider; use Illuminate\Support\ServiceProvider; class TrashmailRuleServiceProvider extends ServiceProvider From 515803be0449cb47016169f4ebf8b08c961776ef Mon Sep 17 00:00:00 2001 From: Tom Witkowski Date: Tue, 18 Feb 2020 11:26:33 +0100 Subject: [PATCH 3/6] improve service provider --- src/TrashmailRuleServiceProvider.php | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/TrashmailRuleServiceProvider.php b/src/TrashmailRuleServiceProvider.php index 80e6c4e..3a27b70 100644 --- a/src/TrashmailRuleServiceProvider.php +++ b/src/TrashmailRuleServiceProvider.php @@ -7,24 +7,35 @@ 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'); $this->loadTranslationsFrom(__DIR__.'/../resources/lang/', 'trashmailRule'); } - - public function register() - { - $this->app->singleton(TrashmailManager::class); - - $this->app->singleton(Trashmail::class); - } } From c61dd5317b61540d33fc3efdd4dab2fcbfd6be19 Mon Sep 17 00:00:00 2001 From: Tom Witkowski Date: Tue, 18 Feb 2020 11:33:01 +0100 Subject: [PATCH 4/6] small fixes --- src/Facades/Trashmail.php | 3 ++- src/Trashmail.php | 5 +---- src/TrashmailManager.php | 17 ++++++++++++++++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Facades/Trashmail.php b/src/Facades/Trashmail.php index d567c83..2df1b86 100644 --- a/src/Facades/Trashmail.php +++ b/src/Facades/Trashmail.php @@ -2,6 +2,7 @@ namespace Elbgoods\TrashmailRule\Facades; +use Elbgoods\TrashmailRule\Trashmail as TrashmailService; use Illuminate\Support\Facades\Facade; /** @@ -11,6 +12,6 @@ class Trashmail extends Facade { protected static function getFacadeAccessor(): string { - return \Elbgoods\TrashmailRule\Trashmail::class; + return TrashmailService::class; } } diff --git a/src/Trashmail.php b/src/Trashmail.php index cf1f6af..80d148c 100644 --- a/src/Trashmail.php +++ b/src/Trashmail.php @@ -11,10 +11,7 @@ class Trashmail { protected ConfigRepository $config; protected TrashmailManager $manager; - /** - * @var LoggerInterface - */ - private LoggerInterface $log; + protected LoggerInterface $log; public function __construct( ConfigRepository $config, diff --git a/src/TrashmailManager.php b/src/TrashmailManager.php index 6d15be2..7bd74fa 100644 --- a/src/TrashmailManager.php +++ b/src/TrashmailManager.php @@ -6,6 +6,7 @@ use Elbgoods\TrashmailRule\Providers\ConfigProvider; use Elbgoods\TrashmailRule\Providers\DeadLetterProvider; use Illuminate\Support\Manager; +use RuntimeException; class TrashmailManager extends Manager { @@ -14,9 +15,23 @@ public function getDefaultDriver(): string return 'config'; } + /** + * @param string|null $driver + * + * @return ProviderContract + */ public function driver($driver = null): ProviderContract { - return parent::driver($driver); + $driver = parent::driver($driver); + + if (! is_a($driver, ProviderContract::class)) { + throw new RuntimeException(sprintf( + 'All drivers should implement [%s].', + ProviderContract::class + )); + } + + return $driver; } protected function createConfigDriver(): ConfigProvider From 3485b27d343f3ff47ad49256d8810b15009ba5f3 Mon Sep 17 00:00:00 2001 From: Tom Witkowski Date: Tue, 18 Feb 2020 12:01:57 +0100 Subject: [PATCH 5/6] improve trashmail facade --- CHANGELOG.md | 9 ++++++++ README.md | 43 +++++++++++++++++++++++++++++++++++++++ src/Facades/Trashmail.php | 6 ++++++ src/Trashmail.php | 14 ++++++++++++- 4 files changed, 71 insertions(+), 1 deletion(-) 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..a767d71 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,47 @@ $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/src/Facades/Trashmail.php b/src/Facades/Trashmail.php index 2df1b86..db9b55d 100644 --- a/src/Facades/Trashmail.php +++ b/src/Facades/Trashmail.php @@ -2,11 +2,17 @@ namespace Elbgoods\TrashmailRule\Facades; +use Closure; +use Elbgoods\TrashmailRule\Contracts\ProviderContract; use Elbgoods\TrashmailRule\Trashmail as TrashmailService; +use Elbgoods\TrashmailRule\TrashmailManager; use Illuminate\Support\Facades\Facade; /** * @method static bool isDisposable(string $email) + * @method static ProviderContract provider(string $provider) + * @method static string getDomain(string $email) + * @method static TrashmailManager extend(string $provider, Closure $creator) */ class Trashmail extends Facade { diff --git a/src/Trashmail.php b/src/Trashmail.php index 80d148c..f59ebfe 100644 --- a/src/Trashmail.php +++ b/src/Trashmail.php @@ -2,6 +2,8 @@ namespace Elbgoods\TrashmailRule; +use Closure; +use Elbgoods\TrashmailRule\Contracts\ProviderContract; use Exception; use Illuminate\Contracts\Config\Repository as ConfigRepository; use Illuminate\Support\Str; @@ -48,7 +50,17 @@ public function isDisposable(string $email): bool return false; } - protected function getDomain(string $email): string + 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, '@'))); } From 2685531adb98bce2de470dc322d2f57d912ae444 Mon Sep 17 00:00:00 2001 From: Tom Witkowski Date: Tue, 18 Feb 2020 12:02:42 +0100 Subject: [PATCH 6/6] fix README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index a767d71..87389e1 100644 --- a/README.md +++ b/README.md @@ -101,8 +101,6 @@ Trashmail::extend('custom_provider', static function (Container $app): ProviderC }); ``` - - ## Changelog Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.