When running Axios in V8Js all requests fail as XMLHttpRequest
is not available.
How can we make axios request work server side?
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.