Here is my completed working PHP class:
<?php
class Samba
{
var $conf, $error_mess, $http_status;
var $auth_method = "access_token";
var $retries = 0;
var $res;
const HTTP_STATUS_OK = "HTTP/1.1 200 OK";
const HTTP_STATUS_BAD_REQ = "HTTP/1.1 400 Bad Request";
const CURL_HTTP_CODE_UNAUTH = 401;
const CURL_HTTP_CODE_OK = 200;
const MAX_RETRIES = 5;
function __construct()
{
$s = new Settings('SAMBA_CONFIG');
$this->conf = $s;
return true;
}
private function auth_log($stat)
{
$fields = [
'auth_method',
'auth_user',
'auth_status'
];
$vals = [
$this->auth_method,
$this->conf->get_val('user'),
$stat
];
global $db;
$db->insert('api_auth_log', $fields, $vals);
}
private function check_status($h = null)
{
if ($h)
{
if (is_array($h))
{
$this->http_status = $h[0];
switch ($this->http_status)
{
case self::HTTP_STATUS_OK:
$this->auth_log('OK');
return true;
break;
case self::HTTP_STATUS_BAD_REQ:
if ($this->retries < self::MAX_RETRIES)
{
$this->auth_log('FAIL');
$this->conf->set('atoken', '');
$this->conf->set('rtoken', '');
$this->conf->update();
return $this->login();
}else{
$this->error_mess = "Max login retries [".self::MAX_RETRIES."] exceeded!";
return false;
}
break;
default:
$this->error_mess = "An HTTP error occurred [".$this->http_status."]";
return false;
}
}
}else{
$this->error_mess = "Failed to connect to POS server! [".$this->http_status."]";
return false;
}
}
private function build_url($endpoint)
{
return $this->conf->get_val('proto')
."://".$this->conf->get_val('host')
.":".$this->conf->get_val('port')
."/".$endpoint;
}
private function login()
{
$do_refresh = false;
if (!empty($this->conf->get_val('rtoken')))
{
$this->auth_method = "refresh_token";
$data = [
'grant_type' => $this->auth_method,
'refresh_token' => $this->conf->get_val('rtoken')
];
$do_refresh = true;
}else{
$this->auth_method = "password";
$data = [
'grant_type' => $this->auth_method,
'username' => $this->conf->get_val('user'),
'password' => $this->conf->get_val('pass')
];
}
$data['client_id'] = $this->conf->get_val('client_id');
$data['client_secret'] = $this->conf->get_val('client_secret');
$payload = http_build_query($data);
$opts = [
'http' => [
'method' => 'POST',
'ignore_errors' => true,
'header'=> "Content-type: application/x-www-form-urlencoded\r\n"
."Content-Length: " . strlen($payload) . "\r\n",
'content' => $payload
]
];
$c = stream_context_create($opts);
$res = file_get_contents($this->build_url('token'), false, $c);
if($this->check_status($http_response_header))
{
$json = json_decode($res);
$this->conf->set('atoken', $json->access_token);
$this->conf->set('rtoken', $json->refresh_token);
$this->conf->set('expires', time()+$json->expires_in);
if ($this->conf->update())
{
return true;
}else{
$this->error_mess = $this->conf->error_mess;
return false;
}
}
}
function query($q, $from_file=1)
{
// Load query file
if ($from_file)
{
$qf = GQL_DIR."/".$q.".graphql";
if (file_exists($qf))
{
$q = file_get_contents($qf);
}else{
$this->error_mess = "Query file not found [".$q."]";
return false;
}
}
$query = [
"query" => $q,
"variables" => null,
"operationName" => null
];
// Check if access token is expired
if (time() >= $this->conf->get_val('expires')) $this->login();
// CURL setup
$url = $this->build_url('api/graphql');
$method = 'POST';
$curl = curl_init();
curl_setopt_array($curl, array(
CURLINFO_HEADER_OUT => true,
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_POSTFIELDS => json_encode($query),
CURLOPT_HTTPHEADER => array(
"Authorization: Bearer ".$this->conf->get_val('atoken'),
'dataType: json',
'Content-Type: application/json'
),
));
$responseString = curl_exec($curl);
$info = curl_getinfo($curl);
//print_r($info); die();
// Check HTTP status
if ($info['http_code'] != self::CURL_HTTP_CODE_OK)
{
if ($info['http_code'] == self::CURL_HTTP_CODE_UNAUTH)
{
if (!$this->login()) return false;
return $this->query($q);
}else{
$this->error_mess = "An HTTP error occurred [".$info['http_code']."]";
return false;
}
}
curl_close($curl);
if ($responseString)
{
$responseObj = json_decode($responseString);
// Test for empty object
if (empty((array)$responseObj->data))
{
$this->error_mess = "Server returned no data!";
return false;
}
$jsonErrorMessage = '';
switch (json_last_error())
{
case JSON_ERROR_NONE:
break;
case JSON_ERROR_DEPTH:
$jsonErrorMessage = 'Maximum stack depth exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$jsonErrorMessage = 'Underflow or the modes mismatch';
break;
case JSON_ERROR_CTRL_CHAR:
$jsonErrorMessage = 'Unexpected control character found';
break;
case JSON_ERROR_SYNTAX:
$jsonErrorMessage = 'Malformed JSON';
break;
case JSON_ERROR_UTF8:
$jsonErrorMessage = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
default:
$jsonErrorMessage = 'JSON - Other';
break;
}
if (!empty($jsonErrorMessage))
{
$this->error_mess = $jsonErrorMessage;
return false;
}
if (!isset($responseObj->message) || empty($responseObj->message))
{
if (!$responseObj->errors)
{
$this->res = $responseObj;
return $this->res;
}
else
{
//LOG API ERROR
$this->error_mess = $responseObj->errors->message;
return false;
}
}
else
{
//LOG API ERROR
$this->error_mess = $responseObj->message;
return false;
}
}
else
{
//LOG NULL RESPONSE ERROR
$this->error_mess = "No response from server!";
return false;
}
}
}
?>