seacms-api/src/JsonResponse.php
2023-03-02 16:44:25 +01:00

241 lines
5.7 KiB
PHP

<?php
// SPDX-License-Identifier: EUPL-1.2
// Authors: see README.md
namespace SeaCMS\Api;
use JsonSerializable;
use SeaCMS\Api\Cookies;
use Throwable;
/**
* Response for http response
*/
class JsonResponse implements JsonSerializable
{
/**
* HTTP Codes
* @var array
*/
const HTTP_CODES = [
200 => 'OK',
301 => 'Moved Permanently',
302 => 'Found',
304 => 'Not Modified',
307 => 'Temporary Redirect',
308 => 'Permanent Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
408 => 'Request Timeout',
500 => 'Internal Server Error',
501 => 'Not Implemented',
503 => 'Service Unavailable',
];
/**
* cookies to send
* @var null|Cookies
*/
protected $cookies;
/**
* HTTP CODE
* @var int
*/
protected $code;
/**
* content as array
* @var array
*/
protected $content;
/**
* headers
* @var array
*/
protected $headers;
public function __construct(int $code, array $content, array $headers = [],?Cookies $cookies = null){
$this->code = array_key_exists($code, self::HTTP_CODES) ? $code : 501; // default
$this->content = $content;
$this->headers = array_merge([
'Content-Type' => 'application/json',
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Headers' => 'X-Requested-With, Location, Slug, Accept, Content-Type',
'Access-Control-Expose-Headers' => 'Location, Slug, Accept, Content-Type',
'Access-Control-Allow-Methods' => 'POST, GET, OPTIONS, DELETE, PUT, PATCH',
'Access-Control-Max-Age' => '86400'
], $headers);
$this->cookies = $cookies;
}
/**
* set Cookies instance
* @param Cookies $cookies
*/
public function setCookies(Cookies $cookies)
{
$this->cookies = $cookies;
}
/**
* merge in content
* @param array $newContent
* @return $this
*/
public function mergeInContent(array $newContent){
$this->content = array_merge(
$this->content,
$newContent
);
}
/**
* prepend in content
* @param array $newContent
* @return $this
*/
protected function prependInContent(array $newContent){
$this->content = array_merge(
$newContent,
$this->content
);
}
/**
* set code
* @param int
*/
public function setCode(int $code)
{
$this->code = $code;
}
/* === Getters === */
/**
* return code
* @return int
*/
public function getCode(): int
{
return $this->code;
}
/**
* return content
* @return array
*/
public function getContent(): array
{
return $this->content;
}
/* === === */
/**
* Sends HTTP headers.
*
* @return $this
*/
public function sendHeaders(): JsonResponse
{
// headers have already been sent by the developer
if (JsonResponse::canSendHeaders()) {
// headers
foreach ($this->headers as $name => $value) {
header($name.': '.$value);
}
// cookies
if (!is_null($this->cookies)){
$this->cookies->sendCookiesOnce();
}
// status
$statusText = self::HTTP_CODES[$this->code];
$protocol = !empty($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
header("$protocol {$this->code} $statusText");
}
return $this;
}
/**
* test if headers can be sent
* @return bool
*/
public static function canSendHeaders(): bool
{
return !headers_sent() && !in_array(php_sapi_name(), ['cli', 'cli-server',' phpdbg'], true);
}
/**
* send headers and return output
* @return string
*/
public function send(): string
{
return $this->preparedOutput()->prepareStatusText()->sendHeaders()->returnContent();
}
/**
* check if output is JSONSerializable
* @return JsonResponse $this
*/
protected function preparedOutput(): JsonResponse
{
try {
if (!empty($GLOBALS['errorMessages'])){
$this->mergeInContent(['errorMessages'=>$GLOBALS['errorMessages']]);
}
json_encode($this->content);
} catch (Throwable $th) {
$this->code = 500;
$this->content = ['code' => 500, 'reason' =>"Not possible to JSONSerialize content"];
}
return $this;
}
/**
* prepare statusText
* @return JsonResponse $this
*/
protected function prepareStatusText(): JsonResponse
{
if (!array_key_exists($this->code,self::HTTP_CODES)){
$previousCode = intval($this->code);
$this->code = 501;
$this->prependInContent(['code' => 501, 'reason' =>"Wanted code ($previousCode) is not implemented !"]);
}
return $this;
}
/**
* return content as String
* @return string
*/
protected function returnContent(): string
{
return json_encode($this->getContent());
}
/**
* export class as array
* @return array
*/
public function jsonSerialize(): mixed
{
return [
'code' => $this->getCode(),
'content' => $this->getContent(),
'headers' => $this->headers,
];
}
}