Skip to content

Commit

Permalink
Merge pull request #5 from elbgoods/ft-manager-driver-pattern
Browse files Browse the repository at this point in the history
apply manager driver pattern
  • Loading branch information
Gummibeer authored Feb 18, 2020
2 parents f481ec5 + 2685531 commit 2b0f999
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 69 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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('[email protected]');
```

## 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('[email protected]')
);
```

## 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.
Expand Down
5 changes: 5 additions & 0 deletions config/trashmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
8 changes: 8 additions & 0 deletions src/Contracts/ProviderContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Elbgoods\TrashmailRule\Contracts;

interface ProviderContract
{
public function isDisposable(string $domain): ?bool;
}
23 changes: 23 additions & 0 deletions src/Facades/Trashmail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

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
{
protected static function getFacadeAccessor(): string
{
return TrashmailService::class;
}
}
30 changes: 30 additions & 0 deletions src/Providers/ConfigProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Elbgoods\TrashmailRule\Providers;

use Elbgoods\TrashmailRule\Contracts\ProviderContract;

class ConfigProvider implements ProviderContract
{
protected array $whitelist;
protected array $blacklist;

public function __construct(array $whitelist, array $blacklist)
{
$this->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;
}
}
81 changes: 81 additions & 0 deletions src/Providers/DeadLetterProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace Elbgoods\TrashmailRule\Providers;

use Elbgoods\TrashmailRule\Contracts\ProviderContract;
use Illuminate\Contracts\Cache\Factory as CacheFactory;
use Illuminate\Contracts\Cache\Repository as CacheRepository;

class DeadLetterProvider implements ProviderContract
{
protected const BLACKLIST_URL = 'https://www.dead-letter.email/blacklist_flat.json';

protected array $config;
protected CacheFactory $cache;

public function __construct(array $config, CacheFactory $cache)
{
$this->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']);
}
}
71 changes: 2 additions & 69 deletions src/Rules/TrashmailRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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));
}
}
67 changes: 67 additions & 0 deletions src/Trashmail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Elbgoods\TrashmailRule;

use Closure;
use Elbgoods\TrashmailRule\Contracts\ProviderContract;
use Exception;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Illuminate\Support\Str;
use Psr\Log\LoggerInterface;

class Trashmail
{
protected ConfigRepository $config;
protected TrashmailManager $manager;
protected LoggerInterface $log;

public function __construct(
ConfigRepository $config,
TrashmailManager $manager,
LoggerInterface $log
) {
$this->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, '@')));
}
}
Loading

0 comments on commit 2b0f999

Please sign in to comment.