1<?php
2
3
4namespace ComboStrap;
5
6
7use TestRequest;
8
9class HttpResponse
10{
11    public const EXIT_KEY = 'exit';
12
13
14    const STATUS_NOT_FOUND = 404;
15    public const STATUS_ALL_GOOD = 200;
16    const STATUS_NOT_MODIFIED = 304;
17    const STATUS_PERMANENT_REDIRECT = 301;
18    public const STATUS_DOES_NOT_EXIST = 404;
19    public const STATUS_UNSUPPORTED_MEDIA_TYPE = 415;
20    public const STATUS_BAD_REQUEST = 400;
21    public const STATUS_INTERNAL_ERROR = 500;
22    public const STATUS_NOT_AUTHORIZED = 401;
23    const MESSAGE_ATTRIBUTE = "message";
24
25    /**
26     * @var int
27     */
28    private $status;
29
30    private $canonical = "support";
31    /**
32     * @var \Doku_Event
33     */
34    private $event;
35    /**
36     * @var array
37     */
38    private $headers = [];
39    private $msg;
40
41
42    /**
43     * Error constructor.
44     */
45    public function __construct($status, $msg)
46    {
47        $this->status = $status;
48        $this->msg = $msg;
49    }
50
51    public static function create(int $status, string $msg = null): HttpResponse
52    {
53        return new HttpResponse($status, $msg);
54    }
55
56
57    public function setEvent(\Doku_Event $event): HttpResponse
58    {
59        $this->event = $event;
60        return $this;
61    }
62
63    public function send($payload = null, $contentType = null)
64    {
65
66        if ($contentType != null) {
67            Http::setMime($contentType);
68        } else {
69            Http::setMime(Mime::PLAIN_TEXT);
70        }
71
72        // header should before the status
73        // because for instance a `"Location` header changes the status to 302
74        foreach ($this->headers as $header) {
75            header($header);
76        }
77
78        if ($this->status !== null) {
79            Http::setStatus($this->status);
80        } else {
81            $status = Http::getStatus();
82            if ($status === null) {
83                Http::setStatus(self::STATUS_INTERNAL_ERROR);
84                LogUtility::log2file("No status was set for this soft exit, the default was set instead", LogUtility::LVL_MSG_ERROR, $this->canonical);
85            }
86        }
87
88
89        /**
90         * Payload
91         */
92        if ($payload !== null) {
93            echo $payload;
94        }
95
96        /**
97         * Exit
98         */
99        if (!PluginUtility::isTest()) {
100            if ($this->status !== self::STATUS_ALL_GOOD && $this->msg !== null) {
101                // if this is a 304, there is no body, no message
102                LogUtility::log2file("Bad Http Response: $this->status : $this->msg", LogUtility::LVL_MSG_ERROR, $this->canonical);
103            }
104            exit;
105        } else {
106
107            /**
108             * Stop the propagation and prevent the default
109             */
110            if ($this->event !== null) {
111                $this->event->stopPropagation();
112                $this->event->preventDefault();
113            }
114
115            /**
116             * Add test info into the request
117             */
118            $testRequest = TestRequest::getRunning();
119
120            if ($testRequest !== null) {
121                $testRequest->addData(self::EXIT_KEY, $payload);
122            }
123
124            /**
125             * Output buffer
126             * Stop the buffer
127             * Test request starts a buffer at {@link TestRequest::execute()},
128             * it will capture the body until this point
129             */
130            ob_end_clean();
131            /**
132             * To avoid phpunit warning `Test code or tested code did not (only) close its own output buffers`
133             * and
134             * Send the output to the void
135             */
136            ob_start(function ($value) {
137            });
138
139        }
140    }
141
142    public function setCanonical($canonical): HttpResponse
143    {
144        $this->canonical = $canonical;
145        return $this;
146    }
147
148
149    public function addHeader(string $header): HttpResponse
150    {
151        $this->headers[] = $header;
152        return $this;
153    }
154
155    /**
156     * @param string|array $messages
157     */
158    public function sendMessage($messages)
159    {
160        if (is_array($messages) && sizeof($messages) == 0) {
161            $messages = ["No information, no errors"];
162        }
163        $message = json_encode(["message" => $messages]);
164        $this->send($message, Mime::JSON);
165
166    }
167
168}
169