Complex regex with preg_match_all()

Posted on 2012-08-13
Last Modified: 2012-08-13
Dear Experts,

I've been handed a request to enforce a criteria for a password policy.

I will the be the first to say regular expressions are not my strong suit. So, while this may not be as complex as it seems, it most certainly is to me.

The request is:

two of a capital, special character, or number.

I understand the individual checks:


It's how to combine them and create the AND/OR that slips past me.

I considered doing it by keeping track of the individual results and doing a comparison afterward, but it occurred to me there must be a way to do it in one expression.

So, my question is - is there a way to do this in one expression and, if so, how?

Alternately, would it be cleaner to check the $subject individually and compare the results afterwards?

I am trying desperately not to break the rules by asking more than one question with this question, but finding it extremely difficult. The reason is if an Expert with regular expressions says "do it individually" then there is no reason to pursue the other option. If the best approach is to combine the checks into one expression, of course this is the route I would like to pursue, and learn from.

Thank you in advance.

Question by:mwheeler_fsd
    LVL 34

    Accepted Solution

    I'll assume you mean this, logically:
    two of (a capital, special character, or number)
    rather than:
    two each of:
    1. capitals
    2. special characters
    3. numbers

    There's also a question of how you define special characters, as there are things like carriage returns and tabs to consider. I'll go by what's available on my (US) keyboard.


    Open in new window

    My code for testing:
    $subjects = array("asdf2j",
    "               "
    foreach ($subjects as $subject) {
      if (preg_match("/^(?=(.*?[A-Z\d~!@#\$%\^&*()_+{}|:\"<>?`\-=\[\]\\\\;\',.\/]){2})/",$subject,$m
      #if (preg_match("/^(?=(.*?[A-Z\d]){2})/",$subject,$matches))
        print "Subject '$subject' matched\n";
        print "Subject '$subject' didn't match\n";

    Open in new window

    Subject 'asdf2j' didn't match
    Subject 'FF' matched
    Subject '**' matched
    Subject 'jF' didn't match
    Subject 'F' didn't match
    Subject '' didn't match
    Subject '\\' matched
    Subject '\' didn't match
    Subject '               ' didn't match

    Open in new window

    LVL 74

    Assisted Solution

    by:käµfm³d 👽
    You may find my article, Regular Expression Lookaround Demystified, interesting to read. I give an example of setting up password requirements in the section "Real World Examples" (about half-way down).

    My offering is:

    preg_match('/^(?=[^A-Z0-9\W]*[A-Z0-9\W]+[^A-Z0-9\W]*[A-Z0-9\W])/', $subject, $matches);

    Open in new window

    I reused your "\W", but I agree with Terry's approach that you should explicitly define your "special characters". "\W" encompasses too many characters that I would not expect you would want in your passwords--including null characters! This is why Terry's last example would be matched by my pattern.

    Author Comment

    Yes, your assumption on the logic was correct. I had the opportunity to go through the spec on a conference call where that had been cleared up. When I posted the question, I cut and pasted it from the spec doc. Apologies for my assumptive oversight.

    In any event, thank you for your excellent solution!


    Author Comment

    I would also like to thank kaufmed for your insight, and will definitely check out your article.

    LVL 107

    Expert Comment

    by:Ray Paseur
    ... it occurred to me there must be a way to do it in one expression ...
    But why? Why not write simple, easy-to-understand code?

    But with that said, please bookmark a link to the REGEX cheat sheet, where Dave Child has some helpful information for those of us, myself included, who are thickheaded about regular expressions.

    HTH, ~Ray

    Author Closing Comment

    Dear TerryAtOpus and Kaufmed,

    Please accept my humble explanation for a redistribution of my earlier awarded points.

    I have been a member of EE for 8 years, and a programmer for 25 years. While I strive to follow the protocol, I was a bit hasty, and most certainly naive, about how to award points for your excellent answers. So much so, I consulted one of the outstanding mediators (who I will leave nameless, but greatly appreciated) as to what the proper protocol would be in this scenario, and my dilemma.

    He offered up a well balanced explanation of the different ways these situations could be approached and, after more consideration, I feel I must award kaufmed a portion of the points.

    The last thing I want to do is discourage or upset any of the Experts that work so hard to provide solutions, so please accept my apology for this. Here is my reasoning;

    TerryAtOpus - your answer was spot on, and even having to take some assumptions from my lack of clarity, you solved the issue at hand. However, kaufmed's additional link to his excellent article was very helpful for me in understanding why/how this works.

    I realize that the goal is to answer the question, but the additional foundation information from kaufmed helps me to improve as a programmer. I clearly understand that was not what I asked for, and I am hoping, with the deepest respect, you can understand my reasoning.

    I take this site very seriously and hope that both of you will understand why, and how, I arrived at this conclusion.

    Your time and dedication is greatly appreciated.

    With best regards,

    PS - Again through my own hard lesson learned, I must thank Ray_Paseur for his response as well. I apologize for this, and most certainly have a better understanding of the protocol and workings in this scenario. My naivete in this situation is quite humbling.
    LVL 74

    Expert Comment

    by:käµfm³d 👽
    Unexpected, but appreciated  = )

    Author Comment

    You are quite welcome and I hope my explanation is well received. Your article is excellent.

    Best regards,
    LVL 34

    Expert Comment

    by:Terry Woods
    Thanks, Mike, for caring about the people at the other end! It's appreciated!

    Author Comment

    Thank you SO much for understanding. A learning experience for me, and am truly appreciative of the gracious understanding.

    Best wishes to all,

    Featured Post

    Highfive Gives IT Their Time Back

    Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

    Join & Write a Comment

    Suggested Solutions

    Introduction Many web sites contain image galleries; a common design for these galleries includes a page with a collection of thumbnail images.  You can click on each of the thumbnail images to see the larger version of the image.  This is easily i…
    This article will explain how to display the first page of your Microsoft Word documents (e.g. .doc, .docx, etc...) as images in a web page programatically. I have scoured the web on a way to do this unsuccessfully. The goal is to produce something …
    The viewer will learn how to count occurrences of each item in an array.
    The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

    734 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    22 Experts available now in Live!

    Get 1:1 Help Now