0

When running Axios in V8Js all requests fail as XMLHttpRequest is not available.

How can we make axios request work server side?

Felix Eve
  • 3,811
  • 3
  • 40
  • 50

1 Answers1

1

It's possible to bind PHP functions or classes to JavaScript with V8Js.

For example in PHP you can:

$v8 = new \V8Js();

$v8->sayHello = function($name) {
    print('Hello ' . $name);
};

$js = 'PHP.sayHello('Bob')';

$v8->executeString($js);

When executed this will produce the string 'Hello Bob'.

So, know this we can create the XMLHttpRequest in PHP and then bind it to JS in the same way.

First we need to create the XMLHttpRequest class to actually make the requests. For this I'm using Guzzle.

<?php

namespace App\Http\Helpers;

use GuzzleHttp\Client;

class XMLHttpRequest
{
    protected $url;

    protected $method;

    public $status;

    public $statusText;

    public $readyState;

    public $responseData;

    public $responseHeaders;

    public $onreadystatechange;

    public function open($method, $url)
    {
        $this->method   = $method;
        $this->url      = $url;
    }

    public function send($data = '')
    {
        $headers = [];
        // Here I am using a Laravel function to fetch the session id but this could be replaced
        if($sessionId = request()->session()->getId()) {
            // Set whatever auth values are needed for your application
            $headers['Cookie'] = 'session=' . $sessionId;
        }

        $options = [
            'http_errors' => false,
            'headers'     => $headers,
            'body'        => $data
        ];

        $client = new Client();

        $response = $client->request($this->method, $this->url, $options);

        $this->responseHeaders = $response->getHeaders();
        $this->responseData    = (string) $response->getBody();
        $this->status          = $response->getStatusCode();
        $this->readyState      = 4;
        $this->statusText      = $response->getReasonPhrase();

        if (is_callable($this->onreadystatechange)) {
            call_user_func($this->onreadystatechange);
        }

    }

}

Then after creating a V8Js instance we can attach the new library:

$v8 = new \V8Js();

$v8->XMLHttpRequest = function () {
    return new XMLHttpRequest;
};

Now we need to tell Axios to use our library. We can do this by first creating an adapter:

const XMLHttpRequest = PHP.XMLHttpRequest;
const adapter = (config) => {
    return new Promise(function(resolve, reject) {
        const xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = () => {
            const response = {
                data: xhttp.responseData,
                status: xhttp.status,
                statusText: xhttp.statusText,
                headers: xhttp.responseHeaders,
                config: config,
                request: {}
            };
            settle(resolve, reject, response);
        };

        xhttp.open(config.method, config.baseURL + config.url);
        xhttp.send();
    })
}

And then adding an interceptor to set the adapter and the baseURL:

axios.interceptors.request.use(config => {
    config.baseURL = 'https://YOUR-URL.com'
    config.adapter = adapter
    return config
})

After this using a normal axios.post will work server side.

Felix Eve
  • 3,811
  • 3
  • 40
  • 50