We’re scoping an upcoming project that will require the development of a large php web application within an existing wordpress site – this is for our use only and we’ve no intention of trying to package it for other users; so interoperability isn’t really a concern as long as we can continue to apply WordPress updates as they’re released without breaking our application.
Our initial thought is to code the application as a single php file located within the theme folder as a ‘Specialised Page Template‘.
The main reason for this is to make use of the Role, Capability & Authentication features already available within WordPress.
This will be a relatively large web application consisting of bespoke objects, classes & 1000’s of lines of bespoke php, JQuery and TSQL code.
Part of this web application will be a large back-end of ajax functions (120 approx.) – my question is related to this ajax backend file.
We’d prefer to handle logged in vs logged out behaviour at the ajax level ourself as we’ll be adding extra functionality & authentication on ajax requests based on role and capability restrictions.
Our Question:
If we write our own ajax.php file, which wp files do we need to require_once()
to ensure access to wordpress core functions & objects (such as is_user_logged_in()
, current_user_can()
& $current_user
etc)
Whats our problem with admin-ajax.php I hear you ask?
A typical example of an ajax request we’ll be making is to update a single value in the database, that’s going to consist of 3 simple steps:
- Check user is authenticated, if not, return a 403 header and exit();
- Initialise our applications database class & execute tsql statement
- return 200 header
Very simple as you can see & could be done with a little clientside jquery and 3 lines of server side php, to do the same with admin-ajax we need to dig through wp_localize_script, add_action, wp_enqueue_script before we even get to writing the client-side JQuery (or have we misunderstood?) – it just seems a little “overkill”?
Custom implementation vs. Standard API usage
Using the WP AJAX API is the way to go. First off, you get access to the complete set of WP APIs, you can leverage standard jQuery
$.ajax()
and similar API calls and you are running standard conform, gaining access to all knowledge spread around the web with articles or for e.g. in answers on this site. In the short term, you might think you are faster, but in the long term you are just cutting yourself off of all help sources.Example: Debugging
You will have to implement custom debugging routes, while WordPress AJAX has lots of sources that will help you getting around it.
External Database
The best thing to do is to use the
$wpdb
class.More info in this answer.
Overhead?
The main “problem”/performance drawback when using WP AJAX is that every request to
admin-ajax.php
actually reloads the whole WordPress core. This file can be replaced, as shown by @Rarst in this answer with a custom file that reduces the impact and only loads what you need.Custom Application
As I am doing it on a day to day basis: It’s no problem developing your application outside of WordPress and then simply pulling it in via Composer (or whatever package manager you are using) and loading it from a simple single file in your plugins folder that has nothing than a call to require our autoloader, a controller and a header comment that declares it as a plugin.
That’s all you need.
Web Applications
If you are running an Angular.js, Ember or Backbone WeApp it’s no problem. WP AJAX will work with that without a problem.
Security
WordPress reuses its nonces, which is a well known fact. This does not really expose anything or open a security whole, but you can go one step further and generate a new nonce [for every request as well], making your calls … ubersecure.
Basics
As it doesn’t make sense to repeat other answers, simply read through ajax, look at my example GitHub Gist or my other GitHub Gist that shows different implementations to show that callbacks can be attached in any possible way.
Overall, your plugin won’t do much than the following:
You just register that and your AJAX callback and you are done:
Note: Comprehensive example Gist on how to sanitize using
filter_var_array()
.And you AJAX.js file will look close to the following:
There’s not really much more to do aside from filling in the gaps/your application logic into what is shown above in code.
This is an add on to @kaiser answer, read that before.
To be honest
wp_localize_script
,add_action
are the best part of Ajax API, and really, they are an help, not a problem.Let’s imagine you have 120 ajax functions. Even if you don’t use
admin-ajax.php
see again your workflow:the “core” part is “execute sql statement” that is an action, one among 120.
How do you choose the action to perform? Do you want to create 200 ajax entry points? I hope no. I think you’ll create a single entry point file, and using a request argument,
$_POST['action']
or similar, you’ll decide which action to perform.Using
'wp_ajax_*'
and'wp_ajax_nopriv_*'
you already have a built-in code that dispatch a request to the related action performer (an object method, a callback, etc..).Moreover, if you use only
'wp_ajax_*'
without'wp_ajax_nopriv_*'
you already have the authentication routine included, no need to do anything else (or maybe you can use'wp_ajax_nopriv_*'
to send the 403 header).Let’s talk about
wp_localize_script
.First o later you’ll need to pass variables form PHP to javascript. How do you will handle this task? Putting javascript inside the php files and use
echo
inside the js code? I hope no.Actually
wp_localize_script
gives you a convenient way to do the trick, without having to write custom code and concentrate your efforts on write your business logic.First place where you’ll need to pass data from PHP to js is when you need to implement CSRF protection.
Your 3 points workflow above in fact is not complete nor secure: authentication is done via cookie and so vulnerable to Cross-site request forgery.
The way WordPress prevents this sort of attacks are nonces that are short-lived random strings that can’t be predicted by attackers. These nonces are generated in PHP how do you want to pass that variables to js?
Now the bad part: yes, WordPress ajax is slow. As @kaiser said in his answer you can create your own entry point avoiding to load things that you do not need, like widgets, themes, and other things. But in that file I suggests to trigger all the WordPress hooks and, in general, try to keep WordPress compatibility. Why?
It’s simple: even if you don’t want to distribuite your code can arrive a day that you want to use a WordPress plugin: there are thousand over there… is not fine having the possibility to use some plugins to do things instead of reinvent the wheel everytime you need something?