Regex to allow anything but whitespace?

looking to make a password regex that has a 6 min char limit, no max char limiit, and anything allowed but whitespace - would this be it?

$regex = ‘[\S]{6}’;

also, is this a bad password regex? heh. was just looking at some ‘big’ sites and they seem to allow the same thing.

also, how would I have the same regex, but adding the requirement that there be at least one letter and at least one number/special character in any position

update to first regex

$regex = ‘[\S]{6,}’;

think that one should be correct now… I am trying to do some tutorials/reading but it is somewhat confusing. heh

I think I came up with a solution for a password regex with the following requirements:

  1. must contain at least 1 letter (any case)
  2. must contain at least 1 number
  3. can contain NO whitespace

$regex = ‘^(?=.[A-Za-z0-9])[\S]$’

Would that be right? If so, how can I change it so that it can contain at least 1 number OR any special character. I’m getting close I think!

Ugh nevermind that seems to be allowing anything but no whitespace. More reading required. hehe

Bingo: (?=.[a-zA-Z])(?=.[\d\W])[\S]*$

at the moment any letter+any digit goes through, any letter+any special character goes through. just need to figure out how to make it allow any digit+any special character.

Sometimes, it’s easier breaking the process down into individual processes…


<?php
error_reporting(-1);
ini_set('display_errors', true);

function isValidPassword($password){
  $isRequiredLength   = strlen($password) > 5;
  $hasNoWhitespace    = false === strpos($password, ' ');
  $hasUppercaseLetter = 1 === preg_match('~[A-Z]~', $password);
  $hasLowercaseLetter = 1 === preg_match('~[a-z]~', $password);
  $hasNumeric         = 1 === preg_match('~[0-9]~', $password);
  return (bool)$isRequiredLength && $hasNoWhitespace && $hasUppercaseLetter && $hasLowercaseLetter && $hasNumeric;
}

$password = 'Anthony012345';

var_dump(
  isValidPassword($password)
); #bool(true)

When coming up with posers like this I find it very useful to create some test cases in an array - that way everyone knows what we are talking about.


$a[0] = '**********';
$a[1] = '111111111111';
$a[2] = 'Abc11';
$a[3] = 'ABCDDDDDDD';
$a[4] = 'A';
$a[5] = '(**55**)';
$a[6] = '1111a_aa';
$a[7] = '';
$a[8] = 123;
$a[9] = '<Is this bad stuff?>';
$a[10] = 'Abc12345';
// and so on.

If I have understood, that should be all fails and the last one a pass?

Anthony you are too good. I will put that in and try it out, and I think that I can add my special char requirement quite easily as well!

And Cups,


$a[0] = '**********'; // fail
$a[1] = '111111111111'; // fail
$a[2] = 'Abc11'; // pass
$a[3] = 'ABCDDDDDDD'; // fail
$a[4] = 'A'; // fail
$a[5] = '(**55**)'; // pass
$a[6] = '1111a_aa'; // pass
$a[7] = ''; // fail
$a[8] = 123; // fail
$a[9] = '<Is this bad stuff?>'; // pass
$a[10] = 'Abc12345'; // pass

I might write a little script to test this out like you suggest too, still curious how to do this via one regex just because I was up reading all night. hah.

This regex here:

(?=.[a-zA-Z])(?=.[\d\W])[\S]*$

Actually works to do almost everything I need. It allows ANYTHING except whitespace, but requires one letter and one number, or one letter and one special character -anywhere- in the password. Pretty good… but…

I wanted to make it so that one number and one special character passes as well…

Hope it’s not stupid to allow ANY character except whitespace…

Silly question maybe, but what have you got against whitespace? Some of us like to use multiple words or make phrases in our passwords.

I’ll be honest, I have no idea what I’m doing and I need a password regex so I did the ultimate newbie thing :blush: and looked at what some big popular sites allow/don’t allow in their passwords… the only common thing was no whitespace, everything else was acceptable.

re: spaces in passwords - I read this last week all about password strengths this is fun, might lend you an insight.

Link my suggestion up with Anthony’s function like so:


// target strings as an array
$a[0] = '**********';
$a[1] = '111111111111';
$a[2] = 'Abc11';
$a[3] = 'ABCDDDDDDD';
$a[4] = 'A';
$a[5] = '(**55**)';
$a[6] = '1111a_aa';
$a[7] = '';
$a[8] = 123;
$a[9] = '<Is this bad stuff?>';
$a[10] = 'Abc12345';

// a candidate function
function isValidPassword($password){
  $isRequiredLength   = strlen($password) > 5;
  $hasNoWhitespace    = false === strpos($password, ' ');
  $hasUppercaseLetter = 1 === preg_match('~[A-Z]~', $password);
  $hasLowercaseLetter = 1 === preg_match('~[a-z]~', $password);
  $hasNumeric         = 1 === preg_match('~[0-9]~', $password);
  return (bool)$isRequiredLength && $hasNoWhitespace && $hasUppercaseLetter && $hasLowercaseLetter && $hasNumeric;
}

// go through the array and see if the outcome matches expectations
foreach( $a as $k=>$password){
  if( isValidPassword($password) ){
  echo "test no $k is a pass <br />";
  }
}

In this case only no 10 is a pass, which is what I imagined you wanted.

ps Part of the idea of posting this suggestion was also to make it easier for anyone else to paste the array ($a) into their IDE or editor and test their ideas, or perhaps to go on adding test cases - its just a way of getting more people to respond to your challenge.


<?php
// target strings as an array
$a[0] = '**********'; // fail
$a[1] = '111111111111'; // fail
$a[2] = 'Abc11'; // pass
$a[3] = 'abc11'; // pass
$a[4] = 'A'; // fail
$a[5] = '(**55**)'; // pass
$a[6] = '1111a_aa'; // pass
$a[7] = ''; // fail
$a[8] = 123; // fail
$a[9] = '<Is this bad stuff?>'; // fail
$a[10] = 'Abc12345'; // pass
$a[11] = 'z$'; // pass
$a[12] = '2$'; // pass

// a candidate function
function isValidPassword($password){
  $hasNoWhitespace = false === strpos($password, ' ');
  $hasLetter = 1 === preg_match('~[a-zA-Z]~', $password);
  $hasNumeric = 1 === preg_match('~[0-9]~', $password);
  $hasSpecialChar = 1 === preg_match('~[how to define special characters here?]~', $password);
  return (bool)$hasNoWhitespace && $hasLetter && $hasNumeric
}

// go through the array and see if the outcome matches expectations
foreach( $a as $k=>$password){
  if( isValidPassword($password) ){
  echo "$k is a pass <br />";
  }
} 
?>

I commented str length out purely for testing and changed the array contents a bit. Oh and combined upper/lower case into just $hasLetter.

I’m currently trying to work out how to define ‘special characters’ in the regex for the $hasSpecialChar variable…

Also, I just read your link Cups and that was very interesting, not to mention surprising… I will seriously consider rethinking my anti-whitespace regex - easy to remove it. :slight_smile:

fixed this part of the code, my research skills are terrible apparently, should have been easy to find:


function isValidPassword($password){
  $hasNoWhitespace = false === strpos($password, ' ');
  $hasLetter = 1 === preg_match('~[a-zA-Z]~', $password);
  $hasNumeric = 1 === preg_match('~[0-9]~', $password);
  $hasSpecialChar = 1 === preg_match('~[\\W]~', $password);
  return (bool)$hasNoWhitespace && $hasLetter && $hasNumeric && $hasSpecialChar;
}

foreach( $a as $k=>$password){
  if( isValidPassword($password) ){
  echo "$k is a pass <br />";
  }
}
?>

now I just need to know how to fix the return so it works like the following:


($hasLetter && $hasNumeric) || ($hasLetter && hasSpecialchar) || ($hasNumeric && $hasSpecialChar)

If that makes sense…

I’ll be straight up honest I have near zero experience writing functions, I really need to learn. But I tried to solve my problem by editing Anthony’s code to the following:


<?php
$a[0] = '**********';
$a[1] = '111111111111';
$a[2] = 'Abc11';
$a[3] = 'ABCDDDDDDD';
$a[4] = 'A';
$a[5] = '(**55**)';
$a[6] = '1111a_aa';
$a[7] = '';
$a[8] = 123;
$a[9] = '<Is this bad stuff?>';
$a[10] = 'Abc12345';
$a[11] = 'a-$_';
$a[12] = 'abc123';
$a[13] = 'ABC123';
$a[14] = 'a2n¦ò¡¥';

function isValidPassword($password) {
  $hasNoWhitespace = false === strpos($password, ' ');
  $hasLetter = 1 === preg_match('~[a-zA-Z]~', $password);
  $hasNumeric = 1 === preg_match('~[0-9]~', $password);
  $hasSpecialChar = 1 === preg_match('~[\\W]~', $password);
  return (bool) ($hasNoWhitespace && $hasLetter && $hasNumeric) || ($hasNoWhitespace && $hasLetter && $hasSpecialchar) || ($hasNoWhitespace && $hasNumeric && $hasSpecialChar);
}

foreach( $a as $k=>$password){
  if( isValidPassword($password) ){
  echo "$k is a pass <br />";
  }
}
?>

it actually returns all the correct values! But I get a bunch of ‘Undefined variable: hasSpecialchar’ errors too. I know the return line above isn’t right, but I’m honestly just guessing here. :smiley: I tried rearranging brackets around etc and got the same results…

What am I doing wrong?

The last line of that function contains $hasSpecialchar, which should be $hasSpecialChar, with a capital C, so it’s the same as on the line that comes before it :slight_smile:

well I’m gonna go hide in a corner now. :blush: :blush:

wooooooops.

seems to work now!

Thank you Scallio :slight_smile:

Gonna bump this, I obviously got it to the originally intended working point, and currently the code allows any one of the following (with no whitespace (\S) allowed anywhere):

  1. letter+digit
  2. letter+specialchar
  3. digit+specialchar

But what if I wanted to add 3 alternate requirements that include ability to put a space in, while still keeping one of the three main requirements? ie:

  1. letter+digit+space
  2. letter+specialchar+space
  3. digit+specialchar+space

I tried changing the code to this:


$a[0] = 'a1';
$a[1] = 'A2';
$a[2] = 'a*';
$a[3] = 'A$';
$a[4] = '2$';
$a[5] = '2 '; // should not pass, but does
$a[6] = 'a '; // should not pass, but does
$a[7] = ' '; // should not pass, but does

function isValidPassword($password) {
	$hasSpace = 1 === preg_match('~[ ]~', $password);
	$hasLetter = 1 === preg_match('~[a-zA-Z]~', $password);
	$hasDigit = 1 === preg_match('~[\\d]~', $password);
	$hasSpecialChar = 1 === preg_match('~[\\W]~', $password);
	return (bool) ($hasLetter && $hasDigit) || ($hasLetter && $hasSpecialChar) || ($hasDigit && $hasSpecialChar) || ($hasLetter && $hasDigit && $hasSpace) || ($hasLetter && $hasSpecialChar && $hasSpace) || ($hasDigit && $hasSpecialChar && $hasSpace);
}

foreach( $a as $k=>$password){
  if( isValidPassword($password) ){
  echo "$k is a pass <br />";
  }
}

but, from what I can tell, spaces are counted in $hasSpecialChar under the \W. Thus letting letter+space and digit+space pass, without the correct requirements in 4/5/6.

Is there any way to use \W with the condition that spaces aren’t included in it?

Tried ~[\W\S]~ but the \W seems to override the \S.

I hope that all made sense.

To work out what and where this is going wrong you need to do some simple debugging by proving that what PHP is assessing matches your brains expectations (your brain will be wrong, as will ours).

The sooner you figure out how to do this, the less like voodoo programming this process will appear to be.

Add lines like:


var_dump( $hasSpace );

to your isValidPassword() function temporarily.

add a <hr /> too so you can see the blocks of var_dump()s. Identify which one is letting you down, then you might be able to work it out for yourself (this will start happening) else, come back with a “why does this one not work?” question.

$hasSpecialChar = 1 === preg_match('~[\\W]~', $password);

That’s definitely the one, \W is counting spaces as a ‘special character’.

$hasSpecialChar = 1 === preg_match('~[\\W]~', str_replace(' ','',$password));

:shifty:

Thanks StarLion, that does the trick. I suck at this! Everything (everything) works now.

Regex = complete.

Thanks to everyone in this thread! Believe it or not I did learn some new things, which is important to me.