Partially hide email address in PHP

Viewed 35003

I am building a simple friend/buddy system, and when someone tries to search for new friends, I want to show partially hidden email addresses, so as to give an idea about who the user might be, without revealing the actual details.

So I want abcdlkjlkjk@hotmail.com to become abcdl******@hotmail.com.

As a test I wrote:

<?php
$email = "abcdlkjlkjk@hotmail.com";

$em = explode("@",$email);
$name = $em[0];
$len = strlen($name);
$showLen = floor($len/2);
$str_arr = str_split($name);
for($ii=$showLen;$ii<$len;$ii++){
    $str_arr[$ii] = '*';
}
$em[0] = implode('',$str_arr); 
$new_name = implode('@',$em);
echo $new_name;

This works, but I was wondering if there was any easier/shorter way of applying the same logic? Like a regex maybe?

18 Answers

I m using femich answer above and tweak it a bit for my

function mask_email($email, $char_shown_front = 1, $char_shown_back = 1)
{
    $mail_parts = explode('@', $email);
    $username = $mail_parts[0];
    $len = strlen($username);

    if ($len < $char_shown_front or $len < $char_shown_back) {
        return implode('@', $mail_parts);
    }

    //Logic: show asterisk in middle, but also show the last character before @
    $mail_parts[0] = substr($username, 0, $char_shown_front)
        . str_repeat('*', $len - $char_shown_front - $char_shown_back)
        . substr($username, $len - $char_shown_back, $char_shown_back);

    return implode('@', $mail_parts);
}

test123@gmail.com -> t*****3@gmail.com

you can pass in the number of character to show in the front and in the back

Very simple RegExp way:

$email = preg_replace('/\B[^@.]/', '*', $email)

Results:

john@smith.com: j***@s*****.c**

abcdef@example.org: a*****@e******.o**

abcdef: a*****

Another variant that was heavily influenced by the answers already shared.

This has two key extra benefits:

  • It keeps the first characters after a defined set of delimiters, making it more readable while still preserving privacy.
  • It works with longer domain endings such as .org.uk and .com.au

Example: firstname.lastname@example.co.uk becomes f********.l*******@e*****.c*.u*

function mask_email( $email ) {
    
    $masked = '';

    $show_next = true;

    foreach ( str_split( $email ) as $chr ) {

      if ( $show_next ) {
        $masked .= $chr;
        $show_next = false;
      }
      else if ( in_array( $chr, array('.', '@', '+') ) ) {
        $masked .= $chr;
        $show_next = true;
      }
      else {
        $masked .= '*';
        $show_next = false;
      }

    }

    return $masked;
  }

Here is version with only 2 lines (if you remove function stuff).

<?php
function censor_email($str,$amount=2, $char='*') {
    list($local, $domain)=explode("@",$str);
    return substr($local,0,$amount).str_repeat($char,strlen($local)-$amount)."@".$domain;
}
?>
function maskEmail($email) {
    preg_match('/^.?(.*)?.@.+$/', $email, $matches);
    return str_replace($matches[1], str_repeat('*', strlen($matches[1])), $email);
}

echo maskEmail('abcdefgh@example.com')
echo maskEmail('abh@example.com')
echo maskEmail('ah@example.com')
echo maskEmail('a@example.com')

returns

a******h@example.com
a*h@example.com
ah@example.com
a@example.com

This is what I did, as I required exact number of string count same as plain email.

This function only shows first & last two characters before "@"

function mask_email($email)
{
    $em   = explode("@",$email);
    $len  = strlen($em[0]);
    $substr_count = 1;
    if($len > 6)
        $substr_count = 2;
    
    $first = substr($em[0], 0,$substr_count);
    $last = substr($em[0], -$substr_count);
    $no_of_star = $len - ($substr_count * 2);
    
    return $first.str_repeat('*', $no_of_star).$last."@".end($em);   
}
Related