add_action( 'wp_authenticate', 'my_front_end_login_fail', 1, 2 );
function my_front_end_login_fail( $user, $pwd ) {
}
Did you noticed that the hook wp_authenticate
takes two arguments? And did you know that you have to pass the number of arguments with the add_action()
call?
From time to time you should have a look at the core files:
$ignore_codes = array('empty_username', 'empty_password');
if (is_wp_error($user) && !in_array($user->get_error_code(), $ignore_codes) ) {
do_action('wp_login_failed', $username);
}
The wp_login_failed
hook is only executed when the username or userpassword is not empty.
So. Let's start from the beginning and cleanup some things:
add_action( 'wp_authenticate', 'my_front_end_login_fail', 1, 2 );
add_action( 'wp_login_failed', 'my_front_end_login_fail', 1, 1 );
function my_front_end_login_fail( $user, $pwd = '' ) {
// reasons to stop here
// $user is not empty
// $user is not a wp-error object
// $pwd is not empty
if ( ! empty( $user ) && ! empty( $pwd ) && ! is_wp_error( $user ) )
return false;
// if a referer is set, use it. else setup the standard login file
$referrer = ( isset( $_SERVER['HTTP_REFERER'] ) && ! empty( $_SERVER['HTTP_REFERER'] ) ) ?
$_SERVER['HTTP_REFERER'] : home_url( 'wp-login.php' ); // take the safe one, use home_url()
/*
* since PHP5 we can parse an url
* @see http://php.net/manual/en/function.parse-url.php
*
* parse_url( 'http://www.example.com/wp-login.php?login=failed&foo=bar' ) gives us something like that:
*
* array (
* 'scheme' => 'http'
* 'host' => 'www.example.com'
* 'path' => '/wp-login.php'
* 'query' => 'login=failed&foo=bar'
* )
*/
$parsed_url = parse_url( $referrer );
/*
* Another fine function is parse_str()
* @see: http://php.net/manual/en/function.parse-str.php
*
* parse( 'login=failed&foo=bar', $query ); results in
* array(
* 'login' => 'failed'
* 'foo' => 'bar'
* )
*
*/
parse_str( $parsed_url['query'], $query );
// if there's a valid referrer, and it's not the default log-in screen
if ( ! strstr( $parsed_url['path'], 'wp-login' ) && ! strstr( $parsed_url['path'], 'wp-admin' ) ) {
// already has the failed don't appened it again
$redirect_to = $referrer;
if( ! isset( $query['login'] ) || 'failed' !== $query['login'] ) {
// add the failed
// but never ever use a simple string concaternation
// what will result if the referer is 'example.com?foo=bar'?
// it will result in 'example.com?foo=bar?login=failed' OUTCH!
$redirect_to = add_query_arg( array( 'login' => 'failed_empty' ), $referrer );
}
// you don't want to redirect to google or somewhere else, you want to redirect to your
// own domain. so use wp_safe_redirect()
wp_safe_redirect( $redirect_to );
exit;
}
}
This is a bit tricky. We hook the function into two different actions. The first action passes two arguments, the second one only one. Our callback always takes two arguments, but if only one is passed, the second one is predefined with an empty string.
The rest of the function is explained in the comments.