352 lines
6.0 KiB
PHP
352 lines
6.0 KiB
PHP
<?php
|
||
/**
|
||
* Akeeba Engine
|
||
*
|
||
* @package akeebaengine
|
||
* @copyright Copyright (c)2006-2024 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||
* @license GNU General Public License version 3, or later
|
||
*/
|
||
|
||
namespace Akeeba\S3;
|
||
|
||
use Akeeba\S3\Exception\PropertyNotFound;
|
||
use Akeeba\S3\Response\Error;
|
||
use SimpleXMLElement;
|
||
|
||
// Protection against direct access
|
||
defined('AKEEBAENGINE') || die();
|
||
|
||
/**
|
||
* 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
|
||
*/
|
||
public function setBody($body, bool $rawResponse = false): void
|
||
{
|
||
$this->body = null;
|
||
|
||
if (empty($body))
|
||
{
|
||
return;
|
||
}
|
||
|
||
$this->body = $body;
|
||
|
||
$this->finaliseBody($rawResponse);
|
||
}
|
||
|
||
public function resetBody(): void
|
||
{
|
||
$this->body = null;
|
||
}
|
||
|
||
public function addToBody(string $data): void
|
||
{
|
||
if (empty($this->body))
|
||
{
|
||
$this->body = '';
|
||
}
|
||
|
||
$this->body .= $data;
|
||
}
|
||
|
||
public function finaliseBody(bool $rawResponse = false): void
|
||
{
|
||
if (!$this->hasBody())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!isset($this->headers['type']))
|
||
{
|
||
$this->headers['type'] = 'text/plain';
|
||
}
|
||
|
||
if (
|
||
!$rawResponse
|
||
&& is_string($this->body)
|
||
&&
|
||
(
|
||
($this->headers['type'] == 'application/xml')
|
||
|| (substr($this->body, 0, 5) == '<?xml')
|
||
)
|
||
)
|
||
{
|
||
$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(
|
||
500,
|
||
(string) $this->body->Code . ':' . (string) $this->body->Message
|
||
);
|
||
|
||
if (isset($this->body->Resource))
|
||
{
|
||
$this->error->setResource((string) $this->body->Resource);
|
||
}
|
||
}
|
||
}
|
||
}
|