It is much simpler to separately validate individual password requirements than to create a single uber-expression to validate everything all at once.
if(
// mandatory matches
strlen($password) > $minlength && // enforce length
preg_match('/[a-z]/', $password) && // contains lowercase
preg_match('/[A-Z]/', $password) // contains uppercase
) {
$passed_count = 0;
if( preg_match('/[0-9]/', $password) ) { $passed_count++; } // contains digit
if( preg_match('/[^a-zA-Z0-9]/', $password) ) { $passed_count++; } // contains symbol
if( $passed_count > $min_passed ) {
// valid password
}
}
edited to illustrate mandatory/optional checks