The best way to prevent brute force attacks from succeeding is requiring users to register with good, hard-to-guess passwords: containing letters, numbers, and punctuation; both
upper and lowercase; words not in the dictionary; at least eight characters long, etc. Also, don’t give indications as to why a login failed: saying that a username and password combination isn’t correct gives away nothing, but saying that a username isn’t right or that the password isn’t right for that username says too much.
To stop a brute force attack in its tracks, you could also limit the number of incorrect login attempts by a given IP address. IP addresses do change frequently, but in a brute force attack, the same IP address would be trying to login multiple times in a matter of minutes. You would have to track incorrect logins by IP address, and then, after X number of invalid attempts, block that IP address for 24 hours (or something). Or, if you didn’t want to go that far, you could use an “incremental delay” defense: each incorrect login from the same IP address creates an added delay in the response (use PHP’s sleep() function to create the delay). Humans might not notice or be bothered by such delays, but automated attacks most certainly would.