Validating password rules The following method is designed to return true if it passes all of the rules for a password. Does anyone see a way to improve it? Performance improvements are welcome for sake of education.

I already know password rules beside length are counter-productive.  It's not my choice.  Also, I know that performance is absolutely trivial here.  I only care about performance by way of educating myself.  The stuff I learn on non-critical optimization often helps me when I do have a bottleneck.  I will not be implementing something just because it is faster.

/**
 * Returns true if and only if a password passes the rules:
 *  - Must be at least 8 characters.
 *  - Must contain two of the following types:
 *    - Letters 
 *    - Numbers
 *    - Symbols (includes whitespace)
 * 
 * @param string $password The raw, unencrypted password.
 * @return bool 
 */
public function isValidPassword($password) {
    $length = strlen($password);

    if ($length &lt; 8) {
        return false;
    }

    $foundLetter = false;
    $foundNumber = false;
    $foundSymbol = false;

    for ($i = 0; $i &lt; $length; $i++) {
        $char = $length[$i];

        if (ctype_alpha($char)) {
            $foundLetter = true;
        }
        else if (ctype_digit($char)) {
            $foundNumber = true;
        }
        else if (ctype_punct($char) || ctype_space($char)) {
            $foundSymbol = true;
        }
    }

    return ($foundLetter &amp;&amp; $foundNumber)
        || ($foundLetter &amp;&amp; $foundSymbol)
        || ($foundNumber &amp;&amp; $foundSymbol);
}
Your solution does not honor multibyte characters, i.e. it will probably break for non-English languages.

Instead you could utilize PCRE (regular expressions) and Unicode character grounps:

$foundLetter = preg_match('(\pL)u', $password);
$foundNumber = preg_match('(\pN)u', $password);
$foundSymbol = preg_match('(???)u', $password);


Not sure what you define as a symbol, [\pP\p{Xps}] (Punctuation + Whitespace) would reflect your current version (I think). Maybe a better alternative would be [^\pL\pN] though, which just ensures one non alphanumeric letter.

In theory you could change your script into a one liner now:

return strlen($password) &gt;= 8 &amp;&amp; (
    preg_match('(\pL)u', $password)
  + preg_match('(\pN)u', $password)
  + preg_match('([^\pL\pN])u', $password)
) &gt;= 2;