Posts

Ajax in WordPress

The purpose of this article is to show how to use AJAX when creating themes and plugins in WordPress.

AJAX in WordPress admin panel

Ever since AJAX was built into the WP admin panel, it’s been very convenient to use AJAX functionality in plugins. A small example. Everything is done in one file (plugin file or functions.php theme file).

Add javascript

First, add the javascript code to the admin panel page that will send the AJAX request.

<?php
//add_action('admin_print_scripts', 'my_action_javascript'); // such a connection will not always work
add_action('admin_print_footer_scripts', 'my_action_javascript', 99);
function my_action_javascript() {
	?>
	<script>
	jQuery(document).ready(function($) {
		var data = {
			action: 'my_action',
			whatever: 1234
		};

		// since version 2.8 'ajaxurl' is always defined in the admin panel
		jQuery.post( ajaxurl, data, function(response) {
			alert('Received from the server: ' + response);
		});
	});
	</script>
	<?php
}
?>

Since version 2.8, the ajaxurl variable in javascript is defined globally on all pages of the admin. Use it in js code as a link to the AJAX request handler file. Usually it is /wp-admin/admin-ajax.php. This variable is not defined in the theme (template). To use it in the front end, you need to define it yourself. See below for how to do this.

Create a PHP function

Now, let’s create a PHP function that will process the passed AJAX request. To do this, add the following code to functions.php (possible in the plugin):

add_action( 'wp_ajax_my_action', 'my_action_callback' );
function my_action_callback() {
	$whatever = intval( $_POST['whatever'] );

	$whatever += 10;
	echo $whatever;

	wp_die(); // the exit is necessary so that there is nothing superfluous in the answer, only that it returns the function
}

Here we cling to a wp_ajax_my_action hook – this is a dynamic hook and it looks like this: wp_ajax_(action), where instead of (action) the value of the variable passed in the first code is inserted: action = my_action.

That’s all.

The above example is enough to start using AJAX in the WordPress admin panel.

[blue]If possible, always use wp_die() instead of die() or exit() in the AJAX request processing function. This way you will get better integration with WordPress and in case of code errors you will get data about them.[/blue]

AJAX in WordPress Frontend

The first thing to check is whether the jQuery library is installed on the site.

In the front end (external part of the site) you need to use another hook to handle AJAX requests: wp_ajax_nopriv_(action). This hook, unlike wp_ajax_(action), is triggered for unauthorized users.

That is, to create a request handler for all users: authorized and non-authorized, a PHP function must be attached to two hooks at once:

add_action('wp_ajax_(action)', 'my_action_callback');
add_action('wp_ajax_nopriv_(action)', 'my_action_callback');

[blue]‘wp_ajax_nopriv_(action)’ can be omitted if you do not want the AJAX request to be processed for unauthorized users.[/blue]

Variable ajaxurl

Let me remind you that the ajaxurl variable is only in the admin panel and it is not in the front of the site (front-end), so it needs to be defined (created). But we will call it in another way – myajax.url, for the front it is more convenient, because in this way it will be possible to add more data related to the AJAX request to the myajax object.

The correct way to create such a variable is to use the wp_localize_script() function.

// We connect the localization at the very end of the scripts connected to the output, so that the script 'twentyfifteen-script' to which we connect is definitely added to the output queue.
// code can be inserted anywhere in the functions.php theme
add_action( 'wp_enqueue_scripts', 'myajax_data', 99 );
function myajax_data(){

	// The first parameter 'twentyfifteen-script' means that the code will be attached to the script with the ID 'twentyfifteen-script'
	// 'twentyfifteen-script' must be added to the output queue, otherwise WP will not understand where to insert the localization code
	// usually this code needs to be added to functions.php in the place where the scripts are connected, after the specified script
	wp_localize_script( 'twentyfifteen-script', 'myajax', 
		array(
			'url' => admin_url('admin-ajax.php')
		)
	);  
}

As a result, we get in the head part of the site right before the ‘twentyfifteen-script’ script:

<script type='text/javascript'>
/* <![CDATA[ */
var myajax = {"url":"http://wptest.ru/wp-admin/admin-ajax.php"};
/* ]]> */
</script>
<script type='text/javascript' src='https://wptest.ru/wp-content/themes/twentyfifteen/js/functions.js?ver=20150330'></script>

The AJAX theory is over, now everything is as for the admin part, only instead of ajaxurl we specify myajax.url and we need to attach the handler function to another hook wp_ajax_nopriv_(action).

AJAX code example for the front end

<?php

add_action( 'wp_enqueue_scripts', 'myajax_data', 99 );
function myajax_data(){

	wp_localize_script('twentyfifteen-script', 'myajax', 
		array(
			'url' => admin_url('admin-ajax.php')
		)
	);  

}

add_action('wp_footer', 'my_action_javascript', 99); // for the front
function my_action_javascript() {
	?>
	<script type="text/javascript" >
	jQuery(document).ready(function($) {
		var data = {
			action: 'my_action',
			whatever: 1234
		};

		// 'ajaxurl' is not defined at the front, so we added its analogue using wp_localize_script().
		jQuery.post( myajax.url, data, function(response) {
			alert('Получено с сервера: ' + response);
		});
	});
	</script>
	<?php
}

add_action('wp_ajax_my_action', 'my_action_callback');
add_action('wp_ajax_nopriv_my_action', 'my_action_callback');
function my_action_callback() {
	$whatever = intval( $_POST['whatever'] );

	echo $whatever + 10;

	// the exit is necessary so that there is nothing superfluous in the answer, only that it returns the function
	wp_die();
}

The code is designed for twentyfifteen. You can insert the code into the functions.php of the theme.

This code will work for any theme, the only thing to do is to change the name of the main script of the theme twentyfifteen-script that connects after jquery.

Logical AJAX hook connection

I did not complicate the reading and did not say how to properly connect AJAX through hooks in the code. However, everything that is written below is not necessary, because it will work this way, but it is recommended.

Functions handlers set by hooks:

  • wp_ajax_(action)
  • wp_ajax_nopriv_(action)

Both hooks always satisfy the wp_doing_ajax() condition:

if( wp_doing_ajax() ){}

// up to WP 4.7 
if( defined('DOING_AJAX') ){}

So the hooks themselves need to be connected only if this condition works.

Using this rule, you can not connect hooks where this makes no sense. For example, when generating a template page or admin page. This small detail will add more logic to the code, and in some cases can eliminate bugs.

An example of how it is recommended to connect all AJAX hooks.

// only connect AJAX handlers when it makes sense
if( wp_doing_ajax() ){
	add_action('wp_ajax_myaction', 'ajax_handler');
	add_action('wp_ajax_nopriv_myaction', 'ajax_handler');
}

// or so up to WP 4.7
if( defined('DOING_AJAX') ){
	add_action('wp_ajax_myaction', 'ajax_handler');
	add_action('wp_ajax_nopriv_myaction', 'ajax_handler');
}

In this case, hooks will be connected only during the AJAX request and will not be connected with a simple visit to the frontend, admin panel, REST or CRON request.

Let me remind you that the data sent from the frontend to the wp-admin/admin-ajax.php file is processed by the arbitrary function ajax_handler() specified in the hook, whether the user is authorized or not.

Use nonce and check rights

There is no urgent need to test an AJAX request if it is not potentially dangerous. For example, when he just receives some data. But when a request deletes or updates data, it simply needs to be additionally protected with nonce code and access control.

Developers are often too lazy to put such protection, getting the most unexpected result. Unscrupulous users can somehow force the user with the rights to do what they need and eventually harm the site on which you worked for many months, years.

There are two types of protection that should be used in AJAX requests in most cases.

1. Nonce code (random code)

Nonce is a unique string that is created and used once – a one-time number. Nonce check is used when you want to make sure that the request was sent from the specified “place”.

In WordPress, the functions wp_create_nonce() and check_ajax_referer() are the basic functions for creating and then checking nonce code. With their help, we will create nonce protection for AJAX requests.

To begin with, we will create nonce code:

add_action( 'wp_enqueue_scripts', 'myajax_data', 99 );
function myajax_data(){

	wp_localize_script('twentyfifteen-script', 'myajax', 
		array(
			'url' => admin_url('admin-ajax.php'),
			'nonce' => wp_create_nonce('myajax-nonce')
		)
	);  

}

twentyfifteen-script is the name of the main script of the topic (see above), which is connected to the site using wp_enqueue_script().

Then, in the AJAX query, we add a variable with nonce code:

var ajaxdata = {
	action     : 'myajax-submit',
	nonce_code : myajax.nonce
};
jQuery.post( myajax.url, ajaxdata, function( response ) {
	alert( response );
});

Now, in processing the request, you should check the nonce code:

add_action( 'wp_ajax_nopriv_myajax-submit', 'myajax_submit' );
add_action( 'wp_ajax_myajax-submit', 'myajax_submit' );
function myajax_submit(){
	// check nonce code, if the check is not passed interrupt processing
	check_ajax_referer( 'myajax-nonce', 'nonce_code' );
	// or so
	if( ! wp_verify_nonce( $_POST['nonce_code'], 'myajax-nonce' ) ) die( 'Stop!');

	// process data and return
	echo 'Возвращаемые данные';

	// keep in mind to finish PHP
	wp_die();
}

check_ajax_referer() works based on wp_verify_nonce() and is essentially its wrapper for AJAX requests.

[blue]Note that in this case Nonce code is created in HTML code. This means that if you have the Page Caching Plug-in installed, this code may and probably will be out of date by the time of another AJAX request, because the HTML code is cached.[/blue]

2. Checking access rights

Here AJAX requests will be triggered only for users with a specified right, such as author. For all others, including unauthorized users, the AJAX query will return an error.

The special feature here is that non-authorized users should also see an error message in the AJAX query. For them it is therefore also necessary to process the request and to return the error message:

add_action( 'wp_ajax_nopriv_myajax-submit', 'myajax_submit' );
add_action( 'wp_ajax_myajax-submit', 'myajax_submit' );
function myajax_submit(){
	// check nonce code, if the check is not passed interrupt processing
	check_ajax_referer( 'myajax-nonce', 'nonce_code' );

	// current user does not have author rights or higher
	if( ! current_user_can('publish_posts') )
		die('This request is available to users with author or higher rights..')

	// OK. The user has the necessary rights!

	// We do what we need and display the data on the screen to return it to the script

	// Don't forget to exit.
	wp_die();
}

Enable caching for AJAX requests

By default, all AJAX requests are NOT cached by the browser for this PHP sets special headers with nocache_headers().

Most AJAX requests do not need to be cached because they need to return fresh data, but there are times when such caching can save resources and speed up the script. For example, if we have a complex product filter that users use all the time. Here it would be reasonable to cache all the results of the filter for a couple of hours, for example, but still the goods are not added at this speed…

To enable caching for the specified AJAX requests, see the second example of the nocache_headers() function.

Catch bugs, PHP errors

Problems can occur with an AJAX request and PHP errors. Notes or messages may change the returned result or cause a javascript error.

Debug (display errors on the screen)

Option #1

As a rule, requests are sent from your browser to a file. So to see the result of a request, bug or anything else, you can open the developer panel, select our request among many and see what it returned.

You can use the usual print_r() or var_dump() functions in your code to see what is in the necessary variables.

Option #2: enable error display in AJAX requests

WordPress by default does not show errors for AJAX requests even if the WP_DEBUG constant is enabled! You can see it in code of wp_debug_mode() function.

However, you can still enable this display, because on working projects we still have WP_DEBUG disabled and are afraid of nothing, but the bugs help us to catch it!

To enable the display of AJAX request errors, you need to insert such code into the functions.php theme file or the plugin. But it’s best to insert it as early as possible to see early errors, the best way is in MU plugins.

if( WP_DEBUG && WP_DEBUG_DISPLAY && (defined('DOING_AJAX') && DOING_AJAX) ){
	@ ini_set( 'display_errors', 1 );
}

Option #3: output data to a log file

If, as you write the code, you need to look into the $myvar variable, then you can still use such code in the ajax request handler:

error_log( print_r($myvar, true) );

As a result, the contents of the $myvar variable will be written to the server log file (error.log). In this way, you can run ajax and look into the log file.

Option #4: output PHP errors into a log file

To output PHP notes and errors to a log file, you must enable the WP_DEBUG_LOG constant. This log file will appear in the wp-content folder.

Option #5

If you cannot see the error message and need to work in developer mode, you can clear the buffer immediately before returning the data:

ob_clean();
echo $whatever;
die();

After that you need to see what the request returns via browser debug or something else.

Option #6

You can also use the FirePHP tool for debug, which writes errors to the browser console.

Error returning data

If the AJAX request to the wp-admin/admin-ajax.php file fails, then -1 or 0 will be returned.

  • -1 – error when checking the request. See check_ajax_referer() function.
  • 0 – request processing returned a blank result
  • 0 – is also returned by default in all other cases

/ July, 5 at 10:34

I create web projects, develop, optimize and promote websites. If you have any ideas or want to suggest something, then write to me and my team.

Top ↑

Leave a Reply