23

I am currently writing a small script that checks the contents of each string.

I was wondering what the REGEX would be to make sure that a string has a letter (upper or lower), a digit, and a special character?

Here is what I know so far (whcih isn't much):

if(preg_match('/^[a-zA-Z0-9]+$/i', $string)):

Help would be great!

Thank You!

Ry-
  • 218,210
  • 55
  • 464
  • 476
Peter Stuart
  • 2,362
  • 7
  • 42
  • 73

6 Answers6

57

The easiest (and probably best) way is to do three separate checks with preg_match:

$containsLetter  = preg_match('/[a-zA-Z]/',    $string);
$containsDigit   = preg_match('/\d/',          $string);
$containsSpecial = preg_match('/[^a-zA-Z\d]/', $string);

// $containsAll = $containsLetter && $containsDigit && $containsSpecial
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • @rybo111: No, they can’t. `A-z` contains more characters than `a-zA-Z`. – Ry- Mar 12 '16 at 22:02
  • 1
    @rybo111: Please stop editing my answer to be less clear. Nobody “requested if()”, and it’s not like you can’t use variables in one. – Ry- Mar 13 '16 at 21:23
  • What if I want to do something if it doesn't contain letter and numbers, instead of contain it? – Freedo Jun 03 '20 at 08:58
  • @Freedo: `!$containsLetter` https://www.php.net/manual/en/language.operators.logical.php – Ry- Jun 03 '20 at 09:00
14

You can use positive lookahead to create a single regex:

$strongPassword = preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$%^&]).*$/');
//                                              special characters  ^^^^
Igor Korkhov
  • 8,283
  • 1
  • 26
  • 31
13

I have found great answer here with explanation to make sure that a given string contains at least one character from each of the following categories.

Lowercase character, Uppercase character, Digit, Symbol

^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*(_|[^\w])).+$

A short explanation:

^ // the start of the string

(?=.*[a-z]) // use positive look ahead to see if at least one lower case letter exists

(?=.*[A-Z]) // use positive look ahead to see if at least one upper case letter exists

(?=.*\d) // use positive look ahead to see if at least one digit exists

(?=.*[_\W]) // use positive look ahead to see if at least one underscore or non-word character exists

.+ // gobble up the entire string

$ // the end of the string

Hope that help you.

Mark van Lent
  • 12,641
  • 4
  • 30
  • 52
Emad Samir Zaki
  • 393
  • 2
  • 10
5

False (chosen answer above - Thanks!) had the a pretty easy way to wrap your head around it (if you aren't too familiar with regex) and come up with what works for you.

I just put this in to elaborate a bit:

(you can paste it at http://phptester.net/index.php?lang=en to work with it)

<?php

$pass="abc1A";

$ucl = preg_match('/[A-Z]/', $pass); // Uppercase Letter
$lcl = preg_match('/[a-z]/', $pass); // Lowercase Letter
$dig = preg_match('/\d/', $pass); // Numeral
$nos = preg_match('/\W/', $pass); // Non-alpha/num characters (allows underscore)

if($ucl) {
    echo "Contains upper case!<br>";
}

if($lcl) {
    echo "Contains lower case!<br>";
}

if($dig) {
    echo "Contains a numeral!<br>";
}

// I negated this if you want to dis-allow non-alphas/num:
if(!$nos) {
    echo "Contains no Symbols!<br>"; 
}

if ($ucl && $lcl && $dig && !$nos) { // Negated on $nos here as well
    echo "<br>All Four Pass!!!";
} else {
    echo "<br>Failure...";
}

?>
Scott Hallett
  • 51
  • 1
  • 1
4

It may be best to use 3 distinct regexs to do this, since you would need to match 6 different possibilities, depending on where your special characters are in your string. But if you want to do it in one regex, and your special characters are, say, [+?@], it is possible:

$string = "abc@123";
$regex = "/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$%^&]).*$/"
if (preg_match($regex, $string)) {
   // special characters
}
Bruno Ribeiro
  • 1,280
  • 16
  • 21
arc
  • 584
  • 2
  • 5
3

A letter is \pL, a number is \pN and a special char is [what you want], here I assume it is not a letter and not a number, so the regex looks like:

/^(?=.*?\pL)(?=.*?\pN)(?=.*[^\pL\pN])/
Toto
  • 89,455
  • 62
  • 89
  • 125