2022-02-20 20:22:07 +00:00
|
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* Akeeba Engine
|
|
|
|
|
*
|
|
|
|
|
* @package akeebaengine
|
2023-12-19 02:28:16 +00:00
|
|
|
|
* @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
|
2022-02-20 20:22:07 +00:00
|
|
|
|
* @license GNU General Public License version 3, or later
|
|
|
|
|
*/
|
|
|
|
|
|
2023-12-19 02:28:16 +00:00
|
|
|
|
namespace Akeeba\S3;
|
2022-02-20 20:22:07 +00:00
|
|
|
|
|
2023-12-19 02:28:16 +00:00
|
|
|
|
use Akeeba\S3\Exception\PropertyNotFound;
|
|
|
|
|
use Akeeba\S3\Response\Error;
|
2022-02-20 20:22:07 +00:00
|
|
|
|
use SimpleXMLElement;
|
|
|
|
|
|
|
|
|
|
// Protection against direct access
|
2023-12-19 02:28:16 +00:00
|
|
|
|
defined('AKEEBAENGINE') || die();
|
2022-02-20 20:22:07 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Amazon S3 API response object
|
|
|
|
|
*
|
|
|
|
|
* @property Error $error Response error object
|
|
|
|
|
* @property string|SimpleXMLElement|null $body Body data
|
|
|
|
|
* @property int $code Response code
|
|
|
|
|
* @property array $headers Any headers we may have
|
|
|
|
|
*/
|
|
|
|
|
class Response
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Error object
|
|
|
|
|
*
|
|
|
|
|
* @var Error
|
|
|
|
|
*/
|
|
|
|
|
private $error = null;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Response body
|
|
|
|
|
*
|
|
|
|
|
* @var string|SimpleXMLElement|null
|
|
|
|
|
*/
|
|
|
|
|
private $body = null;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Status code of the response, e.g. 200 for OK, 403 for Forbidden etc
|
|
|
|
|
*
|
|
|
|
|
* @var int
|
|
|
|
|
*/
|
|
|
|
|
private $code = 0;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Response headers
|
|
|
|
|
*
|
|
|
|
|
* @var array
|
|
|
|
|
*/
|
|
|
|
|
private $headers = [];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Response constructor.
|
|
|
|
|
*/
|
|
|
|
|
public function __construct()
|
|
|
|
|
{
|
|
|
|
|
$this->error = new Error();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Is this an error response?
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function isError(): bool
|
|
|
|
|
{
|
|
|
|
|
return is_null($this->error) || $this->error->isError();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Does this response have a body?
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function hasBody(): bool
|
|
|
|
|
{
|
|
|
|
|
return !empty($this->body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the response error object
|
|
|
|
|
*
|
|
|
|
|
* @return Error
|
|
|
|
|
*/
|
|
|
|
|
public function getError(): Error
|
|
|
|
|
{
|
|
|
|
|
return $this->error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the response error object
|
|
|
|
|
*
|
|
|
|
|
* @param Error $error
|
|
|
|
|
*/
|
|
|
|
|
public function setError(Error $error): void
|
|
|
|
|
{
|
|
|
|
|
$this->error = $error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the response body
|
|
|
|
|
*
|
|
|
|
|
* If there is no body set up you get NULL.
|
|
|
|
|
*
|
|
|
|
|
* If the body is binary data (e.g. downloading a file) or other non-XML data you get a string.
|
|
|
|
|
*
|
|
|
|
|
* If the body was an XML string – the standard Amazon S3 REST API response type – you get a SimpleXMLElement
|
|
|
|
|
* object.
|
|
|
|
|
*
|
|
|
|
|
* @return string|SimpleXMLElement|null
|
|
|
|
|
*/
|
|
|
|
|
public function getBody()
|
|
|
|
|
{
|
|
|
|
|
return $this->body;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the response body. If it's a string we'll try to parse it as XML.
|
|
|
|
|
*
|
|
|
|
|
* @param string|SimpleXMLElement|null $body
|
|
|
|
|
*/
|
2023-12-19 02:28:16 +00:00
|
|
|
|
public function setBody($body, bool $rawResponse = false): void
|
2022-02-20 20:22:07 +00:00
|
|
|
|
{
|
|
|
|
|
$this->body = null;
|
|
|
|
|
|
|
|
|
|
if (empty($body))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->body = $body;
|
|
|
|
|
|
2023-12-19 02:28:16 +00:00
|
|
|
|
$this->finaliseBody($rawResponse);
|
2022-02-20 20:22:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function resetBody(): void
|
|
|
|
|
{
|
|
|
|
|
$this->body = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function addToBody(string $data): void
|
|
|
|
|
{
|
|
|
|
|
if (empty($this->body))
|
|
|
|
|
{
|
|
|
|
|
$this->body = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->body .= $data;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-19 02:28:16 +00:00
|
|
|
|
public function finaliseBody(bool $rawResponse = false): void
|
2022-02-20 20:22:07 +00:00
|
|
|
|
{
|
|
|
|
|
if (!$this->hasBody())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isset($this->headers['type']))
|
|
|
|
|
{
|
|
|
|
|
$this->headers['type'] = 'text/plain';
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-19 02:28:16 +00:00
|
|
|
|
if (
|
|
|
|
|
!$rawResponse
|
|
|
|
|
&& is_string($this->body)
|
|
|
|
|
&&
|
|
|
|
|
(
|
|
|
|
|
($this->headers['type'] == 'application/xml')
|
|
|
|
|
|| (substr($this->body, 0, 5) == '<?xml')
|
|
|
|
|
)
|
2022-02-20 20:22:07 +00:00
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
$this->body = simplexml_load_string($this->body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_object($this->body) && ($this->body instanceof SimpleXMLElement))
|
|
|
|
|
{
|
|
|
|
|
$this->parseBody();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the status code of the response
|
|
|
|
|
*
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public function getCode(): int
|
|
|
|
|
{
|
|
|
|
|
return $this->code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets the status code of the response
|
|
|
|
|
*
|
|
|
|
|
* @param int $code
|
|
|
|
|
*/
|
|
|
|
|
public function setCode(int $code): void
|
|
|
|
|
{
|
|
|
|
|
$this->code = $code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the response headers
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getHeaders(): array
|
|
|
|
|
{
|
|
|
|
|
return $this->headers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the response headers
|
|
|
|
|
*
|
|
|
|
|
* @param array $headers
|
|
|
|
|
*/
|
|
|
|
|
public function setHeaders(array $headers): void
|
|
|
|
|
{
|
|
|
|
|
$this->headers = $headers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set a single header
|
|
|
|
|
*
|
|
|
|
|
* @param string $name The header name
|
|
|
|
|
* @param string $value The header value
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
public function setHeader(string $name, string $value): void
|
|
|
|
|
{
|
|
|
|
|
$this->headers[$name] = $value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Does a header by this name exist?
|
|
|
|
|
*
|
|
|
|
|
* @param string $name The header to look for
|
|
|
|
|
*
|
|
|
|
|
* @return bool True if it exists
|
|
|
|
|
*/
|
|
|
|
|
public function hasHeader(string $name): bool
|
|
|
|
|
{
|
|
|
|
|
return array_key_exists($name, $this->headers);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Unset a response header
|
|
|
|
|
*
|
|
|
|
|
* @param string $name The header to unset
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
public function unsetHeader(string $name): void
|
|
|
|
|
{
|
|
|
|
|
if ($this->hasHeader($name))
|
|
|
|
|
{
|
|
|
|
|
unset ($this->headers[$name]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Magic getter for the protected properties
|
|
|
|
|
*
|
|
|
|
|
* @param string $name
|
|
|
|
|
*
|
|
|
|
|
* @return mixed
|
|
|
|
|
*/
|
|
|
|
|
public function __get(string $name)
|
|
|
|
|
{
|
|
|
|
|
switch ($name)
|
|
|
|
|
{
|
|
|
|
|
case 'error':
|
|
|
|
|
return $this->getError();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'body':
|
|
|
|
|
return $this->getBody();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'code':
|
|
|
|
|
return $this->getCode();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'headers':
|
|
|
|
|
return $this->getHeaders();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new PropertyNotFound("Property $name not found in " . get_class($this));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Magic setter for the protected properties
|
|
|
|
|
*
|
|
|
|
|
* @param string $name The name of the property
|
|
|
|
|
* @param mixed $value The value of the property
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
public function __set(string $name, $value): void
|
|
|
|
|
{
|
|
|
|
|
switch ($name)
|
|
|
|
|
{
|
|
|
|
|
case 'error':
|
|
|
|
|
$this->setError($value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'body':
|
|
|
|
|
$this->setBody($value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'code':
|
|
|
|
|
$this->setCode($value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'headers':
|
|
|
|
|
$this->setHeaders($value);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new PropertyNotFound("Property $name not found in " . get_class($this));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Scans the SimpleXMLElement body for errors and propagates them to the Error object
|
|
|
|
|
*/
|
|
|
|
|
protected function parseBody(): void
|
|
|
|
|
{
|
|
|
|
|
if (!in_array($this->code, [200, 204]) &&
|
|
|
|
|
isset($this->body->Code, $this->body->Message)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
$this->error = new Error(
|
2023-12-19 02:28:16 +00:00
|
|
|
|
500,
|
|
|
|
|
(string) $this->body->Code . ':' . (string) $this->body->Message
|
2022-02-20 20:22:07 +00:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (isset($this->body->Resource))
|
|
|
|
|
{
|
|
|
|
|
$this->error->setResource((string) $this->body->Resource);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|