Skip to content

Instantly share code, notes, and snippets.

@ursuleacv
Created August 29, 2025 01:08
Show Gist options
  • Save ursuleacv/2919a4b1abce0ab5678ef7e7c1269db1 to your computer and use it in GitHub Desktop.
Save ursuleacv/2919a4b1abce0ab5678ef7e7c1269db1 to your computer and use it in GitHub Desktop.
PHP class to mask or anonymize PII data
<?php
/**
* Class PIIMasker
*
* This class provides methods to mask or anonymize Personally Identifiable Information (PII).
* Masking involves partial replacement for readability, while anonymization uses hashing for irreversibility.
* It's recommended to use anonymization for storing data securely.
*/
class PIIMasker
{
/**
* Masks an email address by hiding parts of the username and domain.
* Example: [email protected] -> j***d***e***l@g****.c**
*
* @param string $email The original email address.
* @return string The masked email address.
*/
public function maskEmail(string $email): string
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $email; // Return as-is if invalid
}
list($username, $domain) = explode('@', $email);
$maskedUsername = $this->partialMask($username, 1, 1); // Show first and last char, mask middle
list($domainName, $tld) = explode('.', $domain);
$maskedDomain = $this->partialMask($domainName, 1, 1) . '.' . $this->partialMask($tld, 1, 1);
return $maskedUsername . '@' . $maskedDomain;
}
/**
* Anonymizes an email by hashing it (irreversible).
*
* @param string $email The original email address.
* @return string The hashed email (SHA-256).
*/
public function anonymizeEmail(string $email): string
{
return hash('sha256', $email);
}
/**
* Masks a phone number by hiding middle digits.
* Example: +1234567890 -> +123***790
*
* @param string $phone The original phone number.
* @return string The masked phone number.
*/
public function maskPhone(string $phone): string
{
// Remove non-digit characters for processing
$digits = preg_replace('/\D/', '', $phone);
if (strlen($digits) < 7) {
return $phone; // Too short, return as-is
}
// Keep area code and last few digits, mask middle
$visibleEnd = substr($digits, -3); // Last 3 digits
$visibleStart = substr($digits, 0, 3); // First 3 digits
$halfLength = (int)((strlen($digits) - 6) / 2); // LS digits to mask (approx half)
$masked = $visibleStart . str_repeat('*', $halfLength) . $visibleEnd;
return $phone ? preg_replace('/\d/', $masked, $phone, 1) : $masked; // Preserve format if possible
}
/**
* Anonymizes a phone number by hashing it.
*
* @param string $phone The original phone number.
* @return string The hashed phone number.
*/
public function anonymizePhone(string $phone): string
{
$digits = preg_replace('/\D/', '', $phone); // Normalize
return hash('sha256', $digits);
}
/**
* Masks a name by hiding middle characters in each word.
* Example: John Doe -> J*** D**
*
* @param string $name The original name.
* @return string The masked name.
*/
public function maskName(string $name): string
{
$words = explode(' ', $name);
$maskedWords = array_map(function($word) {
if (strlen($word) <= 2) {
return $word; // Too short, don't mask
}
return $this->partialMask($word, 1, 1);
}, $words);
return implode(' ', $maskedWords);
}
/**
* Anonymizes a name by hashing it.
*
* @param string $name The original name.
* @return string The hashed name.
*/
public function anonymizeName(string $name): string
{
return hash('sha256', strtolower(trim($name))); // Normalize case
}
/**
* Masks a SSN (Social Security Number) by showing only last 4 digits.
* Example: 123-45-6789 -> *** ** 6789
*
* @param string $ssn The original SSN.
* @return string The masked SSN.
*/
public function maskSSN(string $ssn): string
{
return preg_replace('/(\d{3})[^\d]?(\d{2})[^\d]?(\d{4})/', '***-**-$3', $ssn);
}
/**
* Anonymizes an SSN by hashing it (after stripping non-digits).
*
* @param string $ssn The original SSN.
* @return string The hashed SSN.
*/
public function anonymizeSSN(string $ssn): string
{
$digits = preg_replace('/\D/', '', $ssn);
return hash('sha256', $digits);
}
/**
* Utility method to partially mask a string.
* Shows first $showStart characters and last $showEnd characters, masks the rest.
*
* @param string $str The string to mask.
* @param int $showStart Number of characters to show at start.
* @param int $showEnd Number of characters to show at end.
* @return string The partially masked string.
*/
private function partialMask(string $str, int $showStart = 1, int $showEnd = 1): string
{
$len = strlen($str);
if ($len <= $showStart + $showEnd) {
return $str; // No masking needed
}
$maskLength = $len - $showStart - $showEnd;
return substr($str, 0, $showStart) . str_repeat('*', $maskLength) . substr($str, -$showEnd);
}
/**
* General mask method: determines data type and applies appropriate masking.
* Supports 'email', 'phone', 'name', 'ssn'.
*
* @param string $data The data to mask.
* @param string $type The type of data ('email', 'phone', 'name', 'ssn').
* @return string The masked data.
*/
public function mask(string $data, string $type): string
{
switch (strtolower($type)) {
case 'email':
return $this->maskEmail($data);
case 'phone':
return $this->maskPhone($data);
case 'name':
return $this->maskName($data);
case 'ssn':
return $this->maskSSN($data);
default:
return $data; // Unknown type, return as-is
}
}
/**
* General anonymize method: determines data type and applies hashing.
* Supports the same types as mask().
*
* @param string $data The data to anonymize.
* @param string $type The type of data ('email', 'phone', 'name', 'ssn').
* @return string The hashed data.
* @throws InvalidArgumentException If type is unknown.
*/
public function anonymize(string $data, string $type): string
{
switch (strtolower($type)) {
case 'email':
return $this->anonymizeEmail($data);
case 'phone':
return $this->anonymizePhone($data);
case 'name':
return $this->anonymizeName($data);
case 'ssn':
return $this->anonymizeSSN($data);
default:
throw new InvalidArgumentException("Unknown data type: $type");
}
}
}
// Example usage:
$masker = new PIIMasker();
// Mask examples
echo $masker->maskEmail('[email protected]') . PHP_EOL; // j***d***e***l@g****.c**
echo $masker->maskPhone('+1234567890') . PHP_EOL; // +123***890 (simplified for brevity)
echo $masker->maskName('John Doe') . PHP_EOL; // J*** D**
echo $masker->maskSSN('123-45-6789') . PHP_EOL; // ***-**-6789
// Anonymize examples (hashes will be different each run if hashing is used)
echo $masker->anonymizeEmail('[email protected]') . PHP_EOL; // a hash like 'e9c8...'
echo $masker->anonymizePhone('+1234567890') . PHP_EOL; // a hash like 'f1d2...'
// General methods
echo $masker->mask('[email protected]', 'email') . PHP_EOL;
echo $masker->anonymize('Jane Doe', 'name') . PHP_EOL;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment