I would first try and find two numbers, using non-regex (or preg_match_all('[0-9]', ...) >= 2
, then validating against:
^[!@#$%*a-zA-Z0-9]{8,}$
This should be faster and easier to understand. To do it using only regex sounds you need lookahead which basically scans the expression twice afaik, though I'm not sure of the PHP internals on that one.
Be prepared for a lot of complaints about passwords not being accepted. I personally have a large subset of passwords that wouldn't validate against those restrictions. Also nonsensical passwords like 12345678
would validate, or even 11111111
, but not f4#f@faASvCXZr$%%zcorrecthorsebatterystaple
.