Files
rank24.pl/autoload/RestClient3.php
2024-12-12 15:33:18 +01:00

324 lines
8.3 KiB
PHP

<?php
/**
* PHP 7.4-8.0 REST Client build with cURL
*
* @author Fabio Agostinho Boris <fabioboris@gmail.com>
* @added DataForSEO
*/
class RestClient {
public string $host; // the url to the rest server
public ?int $port = null;
public string $scheme;
public string $post_type = 'json';
public int $timeout = 60;
public int $connection_timeout = 10;
private ?string $token; // Auth token
private ?string $ba_user;
private ?string $ba_password;
private ?string $ba_ua;
public string $last_url = '';
public $last_response = null;
public $last_http_code = null;
public function __construct(string $host, string $token = null, string $ba_user = null, string $ba_password = null, string $ba_user_agent = null) {
$arr_h = parse_url($host);
if (isset($arr_h['port'])) {
$this->port = (int)$arr_h['port'];
$this->host = str_replace(":" . $this->port, "", $host);
} else {
$this->port = null;
$this->host = $host;
}
if (isset($arr_h['scheme'])) {
$this->scheme = $arr_h['scheme'];
}
$this->token = $token;
$this->ba_user = $ba_user;
$this->ba_password = $ba_password;
$this->ba_ua = $ba_user_agent;
}
/**
* Returns the absolute URL
*
* @param string $raw_headers
*/
private function http_parse_headers(string $raw_headers): array {
$headers = array();
$key = '';
foreach (explode("\n", $raw_headers) as $h) {
$h = explode(':', $h, 2);
if (isset($h[1])) {
if (!isset($headers[$h[0]])) {
$headers[$h[0]] = trim($h[1]);
} elseif (is_array($headers[$h[0]])) {
$headers[$h[0]] = array_merge($headers[$h[0]], array(trim($h[1])));
} else {
$headers[$h[0]] = array_merge(array($headers[$h[0]]), array(trim($h[1])));
}
$key = $h[0];
} else {
if (substr($h[0], 0, 1) == "\t") {
$headers[$key] .= "\r\n\t" . trim($h[0]);
} elseif (!$key) {
$headers[0] = trim($h[0]);
}
}
}
return $headers;
}
/**
* Returns the absolute URL
*
* @param string|null $url
* @return string
*/
private function url(string $url = null): string {
$_host = rtrim($this->host, '/');
$_url = ltrim($url, '/');
return "{$_host}:{$this->port}/{$_url}";
}
/**
* Returns the URL with encoded query string params
*
* @param string $url
* @param array|null $params
* @return string
*/
private function urlQueryString(string $url, array $params = null): string {
$qs = array();
if ($params) {
foreach ($params as $key => $value) {
$qs[] = "{$key}=" . urlencode($value);
}
}
$url = explode('?', $url);
if (isset($url[1])) {
$url_qs = $url[1];
}
$url = $url[0];
if (isset($url_qs)) {
$url = "{$url}?{$url_qs}";
}
if (count($qs)) {
return "{$url}?" . implode('&', $qs);
} else {
return $url;
}
}
/**
* Make an HTTP request using cURL
*
* @param string $verb
* @param string $url
* @param array $params
*/
private function request(string $verb, string $url, array $params = array()) {
$ch = curl_init(); // the cURL handler
$url = $this->url($url); // the absolute URL
$request_headers = array();
if (!empty($this->token)) {
$request_headers[] = "Authorization: {$this->token}";
}
if ((!empty($this->ba_user)) and (!empty($this->ba_password))) {
curl_setopt($ch, CURLOPT_USERPWD, $this->ba_user . ":" . $this->ba_password);
}
// encoded query string on GET
switch (true) {
case 'GET' == $verb:
$url = $this->urlQueryString($url, $params);
break;
case in_array($verb, array(
'POST',
'PUT',
'DELETE'
), false):
if ($this->post_type == 'json') {
$request_headers[] = 'Content-Type: application/json';
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
} else {
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
}
}
// set the URL
curl_setopt($ch, CURLOPT_URL, $url);
$this->last_url = $url;
// set the HTTP verb for the request
switch ($verb) {
case 'POST':
curl_setopt($ch, CURLOPT_POST, true);
break;
case 'PUT':
case 'DELETE':
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $verb);
}
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connection_timeout);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
if (!empty($this->ba_ua)) {
curl_setopt($ch, CURLOPT_USERAGENT, $this->ba_ua);
}
if (!empty($this->port)) {
curl_setopt($ch, CURLOPT_PORT, $this->port);
}
if ((!empty($this->scheme)) and ($this->scheme == 'https')) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
} else {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
}
$response = curl_exec($ch);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = $this->http_parse_headers(substr($response, 0, $header_size));
$response = substr($response, $header_size);
$http_code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$content_error = curl_error($ch);
//var_dump($content_error);
curl_close($ch);
if (strpos($content_type, 'json')) {
$response = json_decode($response, true);
}
$this->last_response = $response;
$this->last_http_code = $http_code;
switch (true) {
case 'GET' == $verb:
if ($http_code !== 200) {
if (is_array($response)) {
$this->throw_error($response, $http_code);
} else {
$this->throw_error(trim($content_error . "\n" . $response), $http_code);
}
}
return $response;
case in_array($verb, array(
'POST',
'PUT',
'DELETE'
), false):
if (($http_code !== 303) and ($http_code !== 200)) {
if (is_array($response)) {
$this->throw_error($response, $http_code);
} else {
$this->throw_error(trim($content_error . "\n" . $response), $http_code);
}
}
if ($http_code === 200) {
return $response;
} else {
return str_replace(rtrim($this->host, '/') . '/', '', $headers['Location']);
}
}
}
private function throw_error($response, $http_code) {
if (is_array($response) && array_key_exists('error', $response)) {
if ((isset($response['error']['message'])) and (isset($response['error']['code']))) {
if (is_array($response['error']['message'])) {
throw new RestClientException(implode("; ", $response['error']['message']), (int)$response['error']['code'], $http_code);
} else {
throw new RestClientException($response['error']['message'], (int)$response['error']['code'], $http_code);
}
} else {
throw new RestClientException(implode("; ", $response), 0, $http_code);
}
} else {
if (is_string($response)) {
return false;
// throw new RestClientException($response, 0, $http_code);
} else {
return false;
// throw new RestClientException(json_encode($response), 0, $http_code);
}
}
}
/**
* Make an HTTP GET request
*
* @param string $url
* @param array $params
*/
public function get($url, $params = array()) {
return $this->request('GET', $url, $params);
}
/**
* Make an HTTP POST request
*
* @param string $url
* @param array $params
*/
public function post($url, $params = array()) {
return $this->request('POST', $url, $params);
}
/**
* Make an HTTP PUT request
*
* @param string $url
* @param array $params
*/
public function put($url, $params = array()) {
return $this->request('PUT', $url, $params);
}
/**
* Make an HTTP DELETE request
*
* @param string $url
* @param array $params
*/
public function delete($url, $params = array()) {
return $this->request('DELETE', $url, $params);
}
}
class RestClientException extends Exception {
protected $http_code;
public function __construct(string $message, int $code = 0, int $http_code = 0, Exception $previous = null) {
$this->http_code = $http_code;
parent::__construct($message, $code, $previous);
}
/**
* @return int the http code error representation of the exception.
*/
public function getHttpCode() {
return $this->http_code;
}
/**
* @return string the string representation of the exception.
*/
public function __toString(): string {
return __CLASS__ . ": [{$this->code}]: {$this->message} (HTTP status code: {$this->http_code})\n";
}
}