soycharliente Posted April 1, 2010 Share Posted April 1, 2010 Trying to write a regex for checking a password. and I'm getting an error. Requirements are 6-16 characters, Letters, numbers, and !@#$%^&*()-_ are acceptable. I feel like it's something wrong with the escaping. I assumed I had to escape: $^*(). function pwCheck($password, $min, $max) { $regex = '/^[A-Za-z0-9!@#\$%\^&\*\(\)_-\.]{'.$min.','.$max.'}$/'; //echo $regex; if (preg_match($regex, $password)) { return FALSE; } return TRUE; } Warning: preg_match() [function.preg-match]: Compilation failed: range out of order in character class at offset 29 in functions.php on line 123456789 Don't have much experience/knowledge with regex yet. Can anyone help me spot my mistake(s)? Thanks. Quote Link to comment https://forums.phpfreaks.com/topic/197198-regex-a-password/ Share on other sites More sharing options...
salathe Posted April 1, 2010 Share Posted April 1, 2010 The issue is how you try to allow the hyphen character (-) in the character class ([...]). Hyphens can denote ranges of characters (e.g. A-Z for uppercase letters) and only if not denoting a range will the hyphen be interpreted as wanting to match itself. In your regex, the problem lies at _-\. which is trying to allow the underscore, hypen or dot characters but the regex engine tries to interpret it as a range from underscore to dot. In the ASCII character set underscore comes after dot (as Z comes after A) so it correctly detects that the range is invalid (equally, Z-A is invalid) and issues the warning message. So a solution? You could either escape the hyphen (\-) to make sure that it will not be interpreted as part of a range, or you can adopt a common technique of placing the hypen at the end of the character class (_\.-]). The latter works because there is no character after it so the hyphen cannot be interpreted as specifying a range. Since you're learning, there are also a few other points to consider but which don't related directly to the error that you got. First, many of the characters within the character class do not need to be escaped. It could equally be [A-Za-z0-9!@#$%^&*()_.-] Of course, when in doubt, escaping those characters generally won't do any harm but it is good to learn when escaping is necessary compared to when it won't hurt. Secondly, this is something that many people don't learn until quite late in their learning, is that the $ anchors the pattern to the end of the subject string (in your case, the email address). It matches the very end of the string or if the very last character is a newline (in PHP "\n") just before that newline. You probably don't want to allow emails like example@example.org\n which currently would be allowed. Again there are a couple of fixes: either use \z in place of the dollar, or keep the dollar and use the D pattern modifier. For more info on regex in PHP start here. Quote Link to comment https://forums.phpfreaks.com/topic/197198-regex-a-password/#findComment-1035150 Share on other sites More sharing options...
soycharliente Posted April 1, 2010 Author Share Posted April 1, 2010 Hey thanks for the reply and the link. I was trying to filter though this site for helping me, but I will check out what seems to be the actual manual for it! I found a bunch of other examples online and it seems like they all wrapped their character sets and string length limitations in parenthesis, so I did that too. I took out the ^ and $. Is the \A and \Z what you were talking about for the line starting and ending? I also didn't escape the special chars but put the hypen (-) at the end because of what you said. I read something in your link that seemed to validate it as well: All non-alphanumeric characters other than \, -, ^ (at the start) and the terminating ] are non-special in character classes, but it does no harm if they are escaped. The pattern terminator is always special and must be escaped when used within an expression. I updated my function and it seems to be working as expected now. function pwCheck($password, $min, $max) { $regex = '/\A([A-Za-z0-9!@#$%^&*()_.-]{'.$min.','.$max.'})\Z/'; //echo $regex; if (preg_match($regex, $password)) { return FALSE; } return TRUE; } I guess one issue that I didn't think about is how to handle forcing three different character sets/classes (i.e. at least one of each upper, lower, digit, special, etc or however many of whichever ones). I feel like THAT issue would be better solved with something as simple as an array and looping through each character to see if it's in one of the arrays and checking how many array I got a TRUE result from. I say that because I'm not fully aware of what regex can do yet. So hopefully I'll get there at some point. Quote Link to comment https://forums.phpfreaks.com/topic/197198-regex-a-password/#findComment-1035313 Share on other sites More sharing options...
salathe Posted April 1, 2010 Share Posted April 1, 2010 Great to see you getting stuck in, the "forcing three different character sets" can be done with a single regex (combined with your current one) but involves slightly more advanced techniques. Good luck pushing forward with regex and if you have questions feel free to ask. Quote Link to comment https://forums.phpfreaks.com/topic/197198-regex-a-password/#findComment-1035330 Share on other sites More sharing options...
Psycho Posted April 2, 2010 Share Posted April 2, 2010 Personally I would do this with two regex expressions just to make it easy to work with, but it can be done in one. I'll give you both solutions. These will validate that the password: - meets the minimum and maximum lengths - only contains the allowable characters - has at least one character from each "class" (upper, lower, num&special) Example One: function pwCheck($password, $min, $max) { $validChars = "/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9!@#$%^&*()_.-])[A-Za-z0-9!@#$%^&*()_.-]{{$min},{$max}}$/"; return (preg_match($validChars, $password)); } Example Two: function pwCheck($password, $min, $max) { $validChars = "/^[A-Za-z0-9!@#$%^&*()_.-]{{$min},{$max}}$/"; $classCheck = '/(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9!@#$%^&*()_.-])/'; return (preg_match($validChars, $password) && preg_match($classCheck, $password)); } The key of the "class" check is a non-consuming regular expression: (?=.*[a-z]) This means match any lower case letter that follows any other character. By using one of these for each class you get what you are after. Quote Link to comment https://forums.phpfreaks.com/topic/197198-regex-a-password/#findComment-1036184 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.