phpstringformatting

Hint or partially hide email address with stars (*) in PHP


I have this mail address abcedf@example.com.

How to convert it into this mail address a****f@example.com

I tried using strpos and get @ but I cannot get middle values and change it to ****.


Solution

  • At first, I thought that strpos() on @ would get me the length of the "local" part of the address and I could use substr_replace() to simply inject the asterisks BUT a valid email address can have multiple @ in the local part AND multibyte support is a necessary inclusion for any real-world project. This means that mb_strpos() would be an adequate replacement for strpos(), but there isn't yet a native mb_substr_replace() function in php, so the convolution of devising a non-regex snippet became increasingly unattractive.

    If you want to see my original answer, you can check the edit history, but I no longer endorse its use. My original answer also detailed how other answers on this page fail to obfuscate email addresses which have 1 or 2 characters in the local substring. If you are considering using any other answers, but sure to test against a@example.com and ab@example.com as preliminary unit tests.

    My snippet to follow DOES NOT validate an email address; it is assumed that your project will use appropriate methods to validate the address before bothering to allow it into your system. The power/utility of this snippet is that it is multibyte-safe and it will add asterisks in all scenarios and when there is only a single character in the local part, the leading character is repeated before the @ so that the mutated address is harder to guess. Oh, and the number of asterisks to be added is declared as a variable for simpler maintenance.

    Code: (Demo) (PHP7.4+ Demo) (Regex Demo)

    $minFill = 4;
    echo preg_replace_callback(
             '/^(.)(.*?)([^@]?)(?=@[^@]+$)/u',
             function ($m) use ($minFill) {
                  return $m[1]
                         . str_repeat("*", max($minFill, mb_strlen($m[2], 'UTF-8')))
                         . ($m[3] ?: $m[1]);
             },
             $email
         );
    

    Input/Output:

    'a@example.com'              => 'a****a@example.com',
    'ab@example.com'             => 'a****b@example.com',
    'abc@example.com'            => 'a****c@example.com',
    'abcd@example.com'           => 'a****d@example.com',
    'abcde@example.com'          => 'a****e@example.com',
    'abcdef@example.com'         => 'a****f@example.com',
    'abcdefg@example.com'        => 'a*****g@example.com',
    'Ф@example.com'              => 'Ф****Ф@example.com',
    'ФѰ@example.com'             => 'Ф****Ѱ@example.com',
    'ФѰД@example.com'            => 'Ф****Д@example.com',
    'ФѰДӐӘӔӺ@example.com'        => 'Ф*****Ӻ@example.com',
    '"a@tricky@one"@example.com' => '"************"@example.com',
    

    Regex-planation:

    /            #pattern delimiter
    ^            #start of string
    (.)          #capture group #1 containing the first character
    (.*?)        #capture group #2 containing zero or more characters (lazy, aka non-greedy)
    ([^@]?)      #capture group #3 containing an optional single non-@ character
    (?=@[^@]+$)  #require that the next character is @ then one or more @ until the end of the string
    /            #pattern delimiter
    u            #unicode/multibyte pattern modifier
    

    Callback explanation:


    For anyone seeking a less sophisticated approach, the following preg_replace() call will replace the local part of the email from the second (non-last) character to the second-last (non-first) charcter in a 1-to-1 fashion using the \G (continue) metacharacter. There are fringe cases where this technique fails to redact any characters -- so use with caution.

    echo preg_replace("/(?:^.\K|\G(?!^))[^@](?!@)/u", '*', $email);
    

    Input/Output:

    'a@example.com'              => 'a@example.com',               // not good
    'ab@example.com'             => 'ab@example.com',              // not good
    'abc@example.com'            => 'a*c@example.com',
    'abcd@example.com'           => 'a**d@example.com',
    'abcde@example.com'          => 'a***e@example.com',
    'abcdef@example.com'         => 'a****f@example.com',
    'abcdefg@example.com'        => 'a*****g@example.com',
    'Ф@example.com'              => 'Ф@example.com',               // not good
    'ФѰ@example.com'             => 'ФѰ@example.com',              // not good
    'ФѰД@example.com'            => 'Ф*Д@example.com',
    'ФѰДӐӘӔӺ@example.com'        => 'Ф*****Ӻ@example.com',
    '"a@tricky@one"@example.com' => '"a@tricky@one"@example.com',  // not good