20

The regex below is to validate a username.

  • Must be between 4-26 characters long

  • Start with atleast 2 letters

  • Can only contain numbers and one underscore and one dot

I have this so far, but isn't working.

$username = $_POST['username'];
if (!eregi("^([a-zA-Z][0-9_.]){4,26}$",$username)) {
    return false;
} else {
    echo "username ok";
}
3
  • 9
    preg_match is the preferred way to perform regular expression matches. eregi was deprecated as of PHP 5.3 and its use is "highly discouraged": http://php.net/eregi
    – rojoca
    Commented Sep 19, 2010 at 20:51
  • Didn't no that, thanks for link
    – Elliott
    Commented Sep 19, 2010 at 20:54
  • Please give some examples of valid and invalid user names. Your rules are contradictory (at least 2 letters vs only numbers).
    – kennytm
    Commented Sep 19, 2010 at 21:54

3 Answers 3

40

You could use the regex

/^(?=[a-z]{2})(?=.{4,26})(?=[^.]*\.?[^.]*$)(?=[^_]*_?[^_]*$)[\w.]+$/iD

as in

<?php

$username=$_POST['username'];

if (!preg_match('/^(?=[a-z]{2})(?=.{4,26})(?=[^.]*\.?[^.]*$)(?=[^_]*_?[^_]*$)[\w.]+$/iD',
                $username))
{
 return false;
}
else
{
 echo "username ok";
}

?>
  • The ^(?=[a-z]{2}) ensure the string "Start with atleast 2 letters".
  • The (?=.{4,26}) ensure it "Must be between 4-26 characters long".
  • The (?=[^.]*\.?[^.]*$) ensures the following characters contains at most one . until the end.
  • Similarly (?=[^_]*_?[^_]*$) ensures at most one _.
  • The [\w.]+$ commits the match. It also ensures only alphanumerics, _ and . will be involved.

(Note: this regex assumes hello_world is a valid user name.)

10
  • Make it start with more that two letters and it will be perfect :) "Start with atleast 2 letters". Commented Sep 19, 2010 at 21:20
  • But at the sametime it can only contains numbers, so the last chars must be only "\d._" and there can't be any number in the first part. Here is one based on your previous regex : Commented Sep 19, 2010 at 21:27
  • /^(?=.{4,26})(?=[a-z]{2,}[^a-z]*$)(?=[^.]*\.?[^.]*$)(?=[^.]*\.?[^.]*$)(?=[^_]*_?[^_]*$)[\w._]*$/i - Each lookhead is in the order of the asked contraints. Commented Sep 19, 2010 at 21:30
  • i have made: ^(([a-z]{2})([a-z0-9]{2,24}))$ and works great apart from i cant get the single dot to work. anyone wish to extend go ahead.
    – RobertPitt
    Commented Sep 19, 2010 at 21:38
  • 2
    Do not forget to add the /D modifier. This is important because $ will allow a final newline in the string, e.g. "eviluser\n".
    – Geert
    Commented Sep 20, 2010 at 5:58
6

You are asking for a lot in one regex, this may be possible but it is not the best way, you want to let the user know why their username is erroring out.

function checkUsername($username) {
    $username = trim($username);
    if (empty($username)) {
        return "username was left blank.";
    }elseif (strlen($username) < 4) {
        return "username was too short";
    }elseif (strlen($username) > 26) {
        return "username was too long";
    }elseif (!preg_match('~^[a-z]{2}~i', $username)) {
        return "username must start with two letters";
    }elseif (preg_match('~[^a-z0-9_.]+~i', $username)) {
        return "username contains invalid characters.";
    }elseif (substr_count($username, ".") > 1) {
        return "username may only contain one or less periods.";
    }elseif (substr_count($username, "_") > 1) {
        return "username may only contain one or less underscores.";
    }

    return true;
} 

Then to check you would do:

$validUsername = checkUsername($username);

if ($validusername !== true) {
     echo "An error occured: " . $validUsername;
}

This way the user knows there was an error and can fix it easier instead of being blind to what the error is. Also you should stop using ereg it has been depreciated use preg_match instead.

NOTE the use of the !== to check, this also checks type since it can return a string (which would return true with a loose comparison). Just an FYI.

6
  • Thanks, it only echo'd "username ok" as that was sent back to the jquery function. I did have a list of the requirements the username needs, but I guess your way is better. I will give it ago :)
    – Elliott
    Commented Sep 19, 2010 at 21:02
  • Added the 2 starting letters requirement.
    – Jim
    Commented Sep 19, 2010 at 21:04
  • 1
    preg_match('~[a-z0-9_.]+~i', $username) will return 1 for foo@bar as [a-z0-9_.]+ matches foo.
    – Gumbo
    Commented Sep 19, 2010 at 21:04
  • Thanks for catching that, added the start and ending anchors to fix it.
    – Jim
    Commented Sep 19, 2010 at 21:16
  • I tested it, works fine but the underscore and periods dont work if I have one. Also I can have special characters such as '# [ ] ` etc
    – Elliott
    Commented Sep 19, 2010 at 22:07
0

The part where you said:

and one underscore and one dot

would steer me away from regular expressions to be honest!

I would do something like:

if(!eregi('^[a-zA-Z]{2}$', substr(0,2))) {
    // .... check for only 2 dots here later ....
}
2
  • Simply because it would be far far easier to write a some for/else code at this point. I'm sure there is a regex for it but it'll be complex! Commented Sep 19, 2010 at 20:42
  • ☢☢☢ 2020 / php.net: "Warning This function was DEPRECATED in PHP 5.3.0, and REMOVED in PHP 7.0.0. "
    – John
    Commented Oct 1, 2020 at 17:25

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.