Memory Leak in plugin action

Depending on what exactly you’re trying to achieve, I agree with Tom’s comment, that a WP-CLI command might be better. The advantage is that the command runs from php directly on the server (usually has no max execution time, loads different php.ini, etc.) and you don’t need to involve the webserver.


If that is not possible, the next best way is probably to create a custom REST endpoint. WordPress has a class WP_REST_Controller, usually I write classes that extend this and work from there. For simplicity the following example is not using inheritence, but I try to keep to the same lingo.

1. Register new route

New/custom routes are registered via register_rest_route() like so

$version = 1;
$namespace = sprintf('acme/v%u', $version);
$base="/import";

\register_rest_route(
    $namespace,
    $base,
    [
        [
            'methods' => \WP_REST_Server::CREATABLE,
                         // equals ['POST','PUT','PATCH']

            'callback' => [$this, 'import_csv'],
            'permission_callback' => [$this, 'get_import_permissions_check'],
            'args' => [],
            // used for OPTIONS calls, left out for simplicity's sake
        ],
    ]
);

This will create a new route that you can call via

http://www.example.com/wp-json/acme/v1/import/
   default REST start-^       ^       ^
       namespace with version-|       |-base

2. Define permissions check

Maybe you need authentication? Use nonces?

public function get_import_permissions_check($request)
{
    //TODO: implement
    return true;
}

3. Create your actual endpoint callback

The method previously defined gets passed a WP_REST_Request object, use that to access request body, etc. To stay consistent, it is usually best to return a WP_REST_Response instead of custom printing of JSON or similar.

public function import_csv($request)
{
    $data = [];
    // do stuff
    return new \WP_REST_Response($data, 200);
}

If you do all of this in OOP style, you’ll get the following class

class Import_CSV
{

    /**
     * register routes for this controller
     */
    public function register_routes()
    {
        $version = 1;
        $namespace = sprintf('acme/v%u', $version);
        $base="/import";

        \register_rest_route(
            $namespace,
            $base,
            [
                [
                    'methods' => \WP_REST_Server::CREATABLE,
                    'callback' => [$this, 'import_csv'],
                    'permission_callback' => [$this, 'get_import_permissions_check'],
                    'args' => [],
                ],
            ]
        );
    }

    /**
     * endpoint for POST|PUT|PATCH /acme/v1/import
     */
    public function import_csv($request)
    {
        $data = [];
        // do stuff
        return new \WP_REST_Response($data, 200);
    }

    /**
     * check if user is permitted to access the import route
     */
    public function get_import_permissions_check($request)
    {
        //TODO: implement
        return true;
    }

}

But .. still 404? Yes, simply defining the class sadly doesn’t work (no autoloading by default 🙁 ), so we need to run register_routes() like so (in your plugin file)

require_once 'Import_CSV.php';
add_action('rest_api_init', function(){
    $import_csv = new \Import_CSV;
    $import_csv->register_routes();
});

Leave a Comment