Skip to content
This repository was archived by the owner on Mar 20, 2025. It is now read-only.

Commit 2b0f999

Browse files
authored
Merge pull request #5 from elbgoods/ft-manager-driver-pattern
apply manager driver pattern
2 parents f481ec5 + 2685531 commit 2b0f999

File tree

11 files changed

+335
-69
lines changed

11 files changed

+335
-69
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
All notable changes to this package will be documented in this file.
44

5+
## v0.2.0
6+
7+
* switch to manager driver pattern
8+
* `\Elbgoods\TrashmailRule\TrashmailManager`
9+
* `\Elbgoods\TrashmailRule\Providers`
10+
* add service class `\Elbgoods\TrashmailRule\Trashmail`
11+
* add facade `\Elbgoods\TrashmailRule\Facades\Trashmail`
12+
* try-catch all errors in providers and skip this provider
13+
514
## v0.1.0
615

716
* initial release

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ If you want to pass some domains always you can add them to the whitelist. These
4343

4444
## Usage
4545

46+
## Validation Rule
47+
4648
This package provides a basic `TrashmailRule` which you can use. All more specific rules only extend this rule with a predefined `format`.
4749

4850
```php
@@ -60,6 +62,45 @@ $rule = new TrashmailRule(false);
6062
$rule->nullable();
6163
```
6264

65+
## Facade
66+
67+
You can also use the facade if you want to check any email address outside validation.
68+
This will run the same logic as the validation rule and runs all providers set in the config.
69+
70+
```php
71+
use Elbgoods\TrashmailRule\Facades\Trashmail;
72+
73+
Trashmail::isDisposable('example@elbgoods.de');
74+
```
75+
76+
## single Provider
77+
78+
You can also check using a single provider only.
79+
Keep in mind that all providers only accept the domain to check and not a full email address.
80+
The facade provides a method that returns the domain used in an email address.
81+
82+
```php
83+
use Elbgoods\TrashmailRule\Facades\Trashmail;
84+
85+
Trashmail::provider('config')->isDisposable(
86+
Trashmail::getDomain('example@elbgoods.de')
87+
);
88+
```
89+
90+
## custom Provider
91+
92+
If you want to add your own provider you can do so.
93+
94+
```php
95+
use Elbgoods\TrashmailRule\Facades\Trashmail;
96+
use Illuminate\Contracts\Container\Container;
97+
use Elbgoods\TrashmailRule\Contracts\ProviderContract;
98+
99+
Trashmail::extend('custom_provider', static function (Container $app): ProviderContract {
100+
return new CustomProvider();
101+
});
102+
```
103+
63104
## Changelog
64105

65106
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

config/trashmail.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
use GuzzleHttp\RequestOptions;
44

55
return [
6+
'providers' => [
7+
'config',
8+
'dead_letter',
9+
],
10+
611
/*
712
* This package can load a remote blacklist from https://www.dead-letter.email
813
*/

src/Contracts/ProviderContract.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Elbgoods\TrashmailRule\Contracts;
4+
5+
interface ProviderContract
6+
{
7+
public function isDisposable(string $domain): ?bool;
8+
}

src/Facades/Trashmail.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Elbgoods\TrashmailRule\Facades;
4+
5+
use Closure;
6+
use Elbgoods\TrashmailRule\Contracts\ProviderContract;
7+
use Elbgoods\TrashmailRule\Trashmail as TrashmailService;
8+
use Elbgoods\TrashmailRule\TrashmailManager;
9+
use Illuminate\Support\Facades\Facade;
10+
11+
/**
12+
* @method static bool isDisposable(string $email)
13+
* @method static ProviderContract provider(string $provider)
14+
* @method static string getDomain(string $email)
15+
* @method static TrashmailManager extend(string $provider, Closure $creator)
16+
*/
17+
class Trashmail extends Facade
18+
{
19+
protected static function getFacadeAccessor(): string
20+
{
21+
return TrashmailService::class;
22+
}
23+
}

src/Providers/ConfigProvider.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Elbgoods\TrashmailRule\Providers;
4+
5+
use Elbgoods\TrashmailRule\Contracts\ProviderContract;
6+
7+
class ConfigProvider implements ProviderContract
8+
{
9+
protected array $whitelist;
10+
protected array $blacklist;
11+
12+
public function __construct(array $whitelist, array $blacklist)
13+
{
14+
$this->whitelist = $whitelist;
15+
$this->blacklist = $blacklist;
16+
}
17+
18+
public function isDisposable(string $domain): ?bool
19+
{
20+
if (in_array($domain, $this->whitelist)) {
21+
return false;
22+
}
23+
24+
if (in_array($domain, $this->blacklist)) {
25+
return true;
26+
}
27+
28+
return null;
29+
}
30+
}

src/Providers/DeadLetterProvider.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace Elbgoods\TrashmailRule\Providers;
4+
5+
use Elbgoods\TrashmailRule\Contracts\ProviderContract;
6+
use Illuminate\Contracts\Cache\Factory as CacheFactory;
7+
use Illuminate\Contracts\Cache\Repository as CacheRepository;
8+
9+
class DeadLetterProvider implements ProviderContract
10+
{
11+
protected const BLACKLIST_URL = 'https://www.dead-letter.email/blacklist_flat.json';
12+
13+
protected array $config;
14+
protected CacheFactory $cache;
15+
16+
public function __construct(array $config, CacheFactory $cache)
17+
{
18+
$this->config = $config;
19+
$this->cache = $cache;
20+
}
21+
22+
public function isDisposable(string $domain): ?bool
23+
{
24+
if (! $this->config['enabled']) {
25+
return null;
26+
}
27+
28+
if (in_array($this->hashDomain($domain), $this->getBlacklist())) {
29+
return true;
30+
}
31+
32+
return null;
33+
}
34+
35+
protected function getBlacklist(): array
36+
{
37+
if (! $this->config['cache']['enabled']) {
38+
return $this->loadDeadLetter();
39+
}
40+
41+
$cache = $this->getCache();
42+
43+
$key = $this->config['cache']['key'];
44+
45+
if ($cache->has($key)) {
46+
return json_decode($cache->get($key), true);
47+
}
48+
49+
$blacklist = $this->loadDeadLetter();
50+
51+
$cache->put(
52+
$key,
53+
json_encode($blacklist),
54+
$this->config['cache']['ttl']
55+
);
56+
57+
return $blacklist;
58+
}
59+
60+
protected function loadDeadLetter(): array
61+
{
62+
$response = guzzle(
63+
self::BLACKLIST_URL,
64+
$this->config['guzzle']
65+
)->request('GET', '');
66+
67+
$body = $response->getBody()->getContents();
68+
69+
return json_decode($body, true);
70+
}
71+
72+
protected function hashDomain(string $domain): string
73+
{
74+
return hash('sha1', hash('sha1', $domain));
75+
}
76+
77+
protected function getCache(): CacheRepository
78+
{
79+
return $this->cache->store($this->config['cache']['store']);
80+
}
81+
}

src/Rules/TrashmailRule.php

Lines changed: 2 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22

33
namespace Elbgoods\TrashmailRule\Rules;
44

5-
use Illuminate\Cache\Repository as CacheRepository;
5+
use Elbgoods\TrashmailRule\Facades\Trashmail;
66
use Illuminate\Contracts\Validation\Rule;
77
use Illuminate\Support\Facades\Lang;
88
use Illuminate\Support\Str;
99

1010
class TrashmailRule implements Rule
1111
{
12-
protected const BLACKLIST_URL = 'https://www.dead-letter.email/blacklist_flat.json';
13-
1412
protected bool $required;
1513

1614
public function __construct(bool $required = true)
@@ -45,16 +43,7 @@ public function passes($attribute, $value): bool
4543
return false;
4644
}
4745

48-
$domain = trim(mb_strtolower(Str::after($value, '@')));
49-
50-
if (in_array($domain, config('trashmail.whitelist'))) {
51-
return true;
52-
}
53-
54-
return ! in_array(
55-
$this->hashDomain($domain),
56-
$this->getBlacklist()
57-
);
46+
return ! Trashmail::isDisposable($value);
5847
}
5948

6049
public function message(): string
@@ -71,60 +60,4 @@ public function isNullable(): bool
7160
{
7261
return ! $this->required;
7362
}
74-
75-
protected function getBlacklist(): array
76-
{
77-
$deadLetter = $this->getDeadLetter();
78-
79-
$blacklist = array_map([$this, 'hashDomain'], config('trashmail.blacklist'));
80-
81-
return array_merge($deadLetter, $blacklist);
82-
}
83-
84-
protected function getDeadLetter(): array
85-
{
86-
if (! config('trashmail.dead_letter.enabled')) {
87-
return [];
88-
}
89-
90-
if (! config('trashmail.dead_letter.cache.enabled')) {
91-
return $this->loadDeadLetter();
92-
}
93-
94-
/** @var CacheRepository $cache */
95-
$cache = app('cache')->store(config('trashmail.dead_letter.cache.store'));
96-
97-
$key = config('trashmail.dead_letter.cache.key');
98-
99-
if ($cache->has($key)) {
100-
return json_decode($cache->get($key), true);
101-
}
102-
103-
$blacklist = $this->loadDeadLetter();
104-
105-
$cache->put(
106-
$key,
107-
json_encode($blacklist),
108-
config('trashmail.dead_letter.cache.ttl')
109-
);
110-
111-
return $blacklist;
112-
}
113-
114-
protected function loadDeadLetter(): array
115-
{
116-
$response = guzzle(
117-
self::BLACKLIST_URL,
118-
config('trashmail.dead_letter.guzzle')
119-
)->request('GET', '');
120-
121-
$body = $response->getBody()->getContents();
122-
123-
return json_decode($body, true);
124-
}
125-
126-
protected function hashDomain(string $domain): string
127-
{
128-
return hash('sha1', hash('sha1', $domain));
129-
}
13063
}

src/Trashmail.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace Elbgoods\TrashmailRule;
4+
5+
use Closure;
6+
use Elbgoods\TrashmailRule\Contracts\ProviderContract;
7+
use Exception;
8+
use Illuminate\Contracts\Config\Repository as ConfigRepository;
9+
use Illuminate\Support\Str;
10+
use Psr\Log\LoggerInterface;
11+
12+
class Trashmail
13+
{
14+
protected ConfigRepository $config;
15+
protected TrashmailManager $manager;
16+
protected LoggerInterface $log;
17+
18+
public function __construct(
19+
ConfigRepository $config,
20+
TrashmailManager $manager,
21+
LoggerInterface $log
22+
) {
23+
$this->config = $config;
24+
$this->manager = $manager;
25+
$this->log = $log;
26+
}
27+
28+
public function isDisposable(string $email): bool
29+
{
30+
$domain = $this->getDomain($email);
31+
32+
$providers = $this->config->get('trashmail.providers');
33+
34+
foreach ($providers as $provider) {
35+
try {
36+
$isDisposable = $this->manager->driver($provider)->isDisposable($domain);
37+
} catch (Exception $exception) {
38+
$this->log->error($exception);
39+
40+
continue;
41+
}
42+
43+
if ($isDisposable === null) {
44+
continue;
45+
}
46+
47+
return $isDisposable;
48+
}
49+
50+
return false;
51+
}
52+
53+
public function provider(string $provider): ProviderContract
54+
{
55+
return $this->manager->driver($provider);
56+
}
57+
58+
public function extend(string $provider, Closure $creator): TrashmailManager
59+
{
60+
return $this->manager->extend($provider, $creator);
61+
}
62+
63+
public function getDomain(string $email): string
64+
{
65+
return trim(mb_strtolower(Str::after($email, '@')));
66+
}
67+
}

0 commit comments

Comments
 (0)