laravel-hash-list
Keep the list of people you must not email, without keeping their email addresses.
v1.0.0Read the docsGitHub · coming soonPackagist · coming soon
Laravel Hash List
Keep the list of people you must not email, without keeping their email addresses.
composer require keaton-php/laravel-hash-list
php artisan vendor:publish --tag="hash_list-migrations"
php artisan migrateThe service provider is discovered automatically, and the migration creates one table, hash_list_entries. At the end of this README, two organisations share their do-not-contact lists as hashes (one of them 100,000 long), check everyone against both before reaching out, and neither receives the other's addresses in plain text, or stores one.
The plain version
Say people can ask you to stop emailing them. The obvious way to remember them is a list, and in_array() to check it before you send:
<?php
$unsubscribed = ['jane@example.com', 'sam@example.com'];
foreach (['jane@example.com', 'riley@example.com'] as $email) {
echo $email, ': ', in_array($email, $unsubscribed, true) ? 'skip' : 'send', PHP_EOL;
}
jane@example.com: skip
riley@example.com: sendFor a handful of addresses, that is fine.
Where it breaks down
The trouble is that one email address has many spellings. Here is one address, the way four different parts of an app might hand it to you:
<?php
$unsubscribed = ['jane@example.com'];
$spellings = [
'jane@example.com',
'Jane@Example.com',
' jane@example.com',
'Jane Doe <jane@example.com>',
];
foreach ($spellings as $email) {
printf("%-30s %s\n", "'{$email}'", in_array($email, $unsubscribed, true) ? 'skip' : 'send');
}
'jane@example.com' skip
'Jane@Example.com' send
' jane@example.com' send
'Jane Doe <jane@example.com>' sendThree of the four go out to someone who asked you to stop. And there are two more problems:
- Scale. The list has to be in memory to be checked. At a hundred thousand addresses, that is a lot to load for every send.
- Privacy. The list is a pile of real people's addresses: the people who least want to hear from you. And when another organisation shares its list with you, it is often not addresses at all but hashes of them, which
in_array()can't compare with an address.
The same thing with Hash List
HashList::add() puts an address on the list, and HashList::has() asks whether an address is on it:
<?php
use Keaton\HashList\Facades\HashList;
HashList::add('jane@example.com');
$spellings = [
'jane@example.com',
'Jane@Example.com',
' jane@example.com',
'Jane Doe <jane@example.com>',
];
foreach ($spellings as $email) {
printf("%-30s %s\n", "'{$email}'", HashList::has($email) ? 'skip' : 'send');
}
'jane@example.com' skip
'Jane@Example.com' skip
' jane@example.com' skip
'Jane Doe <jane@example.com>' skipAll four spellings are caught, the check is one indexed database query, and what is stored is a hash of the address, not the address.