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”?

2 s
2

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.

class CustomDatabaseHandler extends wpdb
{
    // Overwrite what you need here
}

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.

<?php
/** Plugin Name: Custom Application as Plugin */

# Composer autoloader
include_once __DIR__.'/vendor/autoload.php';

add_action( 'plugins_loaded', function()
{
    // Initialize your plugin here
} );

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:

// Public or private?
// 'wp_enqueue_scripts"https://wordpress.stackexchange.com/"admin_enqueue_scripts'
// or only on login?
// 'login_enqueue_scripts'
add_action( 'wp_enqueue_scripts', function()
{
    $name="handle";
    wp_register_script(
        $name,
        plugins_url( 'assets/ajax.js', __FILE__ ),
        [ 'jquery' ],
        filemtime( plugins_dir_path( __FILE__ ).'assets/ajax.js' ),
        true
    );
    wp_enqueue_script( $name );
    wp_localize_script(
        $name,
        "{$name}Obj", // This string is what gives you access to below array
        [
            'ajaxurl'     => admin_url( 'admin-ajax.php' ),
            '_ajax_nonce' => wp_create_nonce( "{$name}_action" ),
            'action'      => "{$name}_action",
            'data'        => [ /* additional data as array */ ],
        ]
    );
} );

You just register that and your AJAX callback and you are done:

// Public or private?
add_action( "wp_ajax_{$name}_action", 'ajaxCb' );
add_action( "wp_ajax_nopriv_{$name}_action", 'ajaxCb' );

public function ajaxCb( $data )
{
    check_ajax_referer( $data['action'] );

    # @TODO sanitize data here:
    // filter_var() filter_var_array() and filter_input()
    # @TODO Custom logic here

    // Error?
    if ( is_wp_error( $thing ) )
        wp_send_json_error( $data );

    // Success!
    wp_send_json_success( $data );
}

Note: Comprehensive example Gist on how to sanitize using filter_var_array().

And you AJAX.js file will look close to the following:

/*global jQuery, $, handleObj */
( function( $, plugin ) {
    "use strict";
        var pass = $( '#password__field' ).val(),
            name = $( '#name__field' ).val();

        // @TODO other setup tasks for static vars in here

        $.ajax( {
            url  : plugin.ajaxurl,
            data : {
                action      : plugin.action,
                _ajax_nonce : plugin._ajax_nonce,
                userName    : name,
                passWord    : pass
            },
            beforeSend : function( d ) {
                // @TODO Add loading animation
            }
        } )
            .done( function( response, textStatus, jqXHR ) {
                // @TODO Clear loading animation
                if ( ! response.success ) {
                    // @TODO Custom error
                }
                else {
                    // @TODO success
                }
            } )
            .fail( function( jqXHR, textStatus, errorThrown ) {
                console.log( errorThrown );
            } );
} ( jQuery, handleObj || {} ) );

There’s not really much more to do aside from filling in the gaps/your application logic into what is shown above in code.

Leave a Reply

Your email address will not be published. Required fields are marked *