Created
August 29, 2025 01:08
-
-
Save ursuleacv/2919a4b1abce0ab5678ef7e7c1269db1 to your computer and use it in GitHub Desktop.
PHP class to mask or anonymize PII data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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