241 lines
5.7 KiB
PHP
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,
|
|
];
|
|
}
|
|
}
|