WP Manifestindependent plugin directory
★ 2stars
1forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/triggerfishab/rest-ajax-plugin/archive/refs/heads/master.zip

Utilizes WordPress REST API (instead of admin-ajax.php). This plugin works as a controller for the AJAX handlers.

AJAX handlers lives in app/AjaxHandler. All files in app/AjaxHandler uses the App\AjaxHandler namespace. AJAX handlers can be defined in two ways, and are called "method" or "class" based (AJAX) handlers.

  1. A public static method in the default AJAX handler class DefaultHandler.php. This is how a method based handler is defined.
  2. It's own file and handler class. This is how a class based handler is defined.

The class of a class based handler must extend the abstract Triggerfish\REST_Ajax\AbstractAjaxHandler class and implement a public method called __getData.

More in-depth information

Actions and handlers does not need to be registered like tf_add_ajax_handler or add_action('wp_ajax_XXX'). The request is automatically mapped to a class or a method, specific to the current action. All is mapped by the action sent in the request. Automatic templating can be achieved.

Request flow:

  1. A class named like the action in StudlyCase will be searched in the following namespace, App\AjaxHandler. If such a class is found and has a public method named __getData, the flow will jump to 3. This is what is called "class based" handler below.

  2. If a class cannot be found by 1, the fallback will be searched for. The fallback is a public static method named like the action in camelCase in App\AjaxHandler\DefaultHandler. This is what is called "method based" handler below.

  3. Automatic templating. If the data from 1 or 2 is an array, a template named like the action in kebab-case will be searched for in a directory called "ajax" in the views directory.

    But if the handler is "class based", the class can define a public method named __template that return the preferred template's path. This will take precedence over, and fall back to, the template in the "ajax" directory from above.

    The data from 1 or 2 will be injected as the template will be included with the App\template function.

Actions:

  • tf/ajax/before
  • tf/ajax/before/action=XX
  • tf/ajax/after/action=XX
  • tf/ajax/after/action=XX

Filters:

  • tf/ajax/result
  • tf/ajax/result/action=XX
  • tf/ajax/template_paths
  • tf/ajax/template_paths/action=XX

JSON request body and handler params

For POST requests with Content-Type: application/json, WordPress exposes the decoded body via WP_REST_Request::get_json_params(). Class-based handlers merge those values into the usual query/form params (later JSON keys override) and store the result on AbstractAjaxHandler::$params, so nested structures are real PHP arrays instead of stdClass objects (which avoids foreach type issues on PHP 8+).

The keys searchArgs and taxonomyFilters are normalized: existing arrays are kept as-is; objects are converted to arrays via JSON encode/decode; any other type becomes an empty array [].

Polylang

When Polylang is active, the controller sets the current language for the AJAX request in a context-aware way: REST contexts (PLL_REST_Request) use the language model only (no URL chooser). Frontend contexts use PLL_Choose_Lang_Url and the preferred language. Other Polylang setups fall back to resolving the language from the model.

REST error responses

Failures are returned as REST errors with these code values (useful when debugging in the browser network panel or logs):

Code When
rest_ajax_exception Uncaught exception or error while handling the request
rest_ajax_validate Exception during action validation
rest_ajax_template Exception while rendering the template
no_handler No callable handler was resolved (should be rare)

When WP_DEBUG is true, the error message may include the underlying exception message.

Version and releases

The plugin header in rest-ajax-plugin.php carries the current version. Release builds use a git tag (for example 1.2.0); keep the header in sync when tagging.

1.2.0

  • Merge JSON body parameters into class-based handler params; normalize searchArgs / taxonomyFilters for PHP 8.
  • Polylang: correct handling for REST (PLL_REST_Request) vs frontend.
  • Harden REST flow with try/catch, structured WP_Error responses, and safer handler class validation (invalid base class fails validation instead of E_USER_ERROR).

Releases

9 releases.

Tag
Published
1.2.0 latest
Mar 25, 2026 6mo ago
Mar 15, 2024 2 years ago
Mar 13, 2024 2 years ago
Dec 13, 2022 3 years ago
Nov 18, 2022 3 years ago
Sep 8, 2022 4 years ago
Feb 7, 2020 6 years ago
Oct 15, 2019 6 years ago
Oct 9, 2019 7 years ago

These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.

B grade

Security

REST Ajax 1.2.0 · audited by WP Registry

Medium-severity findings.

1 medium
Audited release
1.2.0
Findings
1
Worst severity
medium
Content hash
8705fa7290f337168e2e879c…

Findings

  • medium Unauthenticated dynamic-dispatch REST endpoint with no auth or nonce layer

    missing_permission_callback

    Controller::registerRESTRoute() registers theme/v1/ajax for GET and POST with permission_callback '__return_true'. The 'action' parameter selects a class (\App\AjaxHandler\Studly(action), constructed during validation) or any public static method on \App\AjaxHandler\DefaultHandler (camelCase(action)), and all request params are passed to it via call_user_func_array. There is no capability, nonce or allow-list mechanism, so every handler a theme implements, plus any public static helper on DefaultHandler, is reachable by anonymous users. This acts as an amplifier for any state-changing or data-returning theme handler.

    src/Controller.php:64-103

    Recommendation

    Add an opt-in permission model: let handler classes declare a permission method (default deny for writes), verify a wp_rest/action nonce for POST, and restrict method-based dispatch to an explicit allow-list instead of any public static method.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.