aboutsummaryrefslogtreecommitdiff
path: root/lib/classes/restapi/Response.php
blob: 4417979baf8be6ae0567af071408f17cfce98d6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
<?php
namespace RESTAPI;

/**
 * Response class for the rest api
 *
 * @author     <mlunzena@uos.de>
 * @license    GPL 2 or later
 * @since      Stud.IP 3.0
 * @deprecated Since Stud.IP 5.0. Will be removed in Stud.IP 6.0.
 */
class Response implements \ArrayAccess
{
    public $body, $status, $headers;

    /**
     * Constructor, sets vital information if provided.
     *
     * @param String $body    Body contents of the response, optional,
     *                        defaults to empty string
     * @param int    $status  HTTP status code, optional, defaults to 200
     * @param Array  $headers HTTP headers, optional, defaults to no headers
     */
    public function __construct($body = '', $status = 200, $headers = [])
    {
        $this->body = $body;
        $this->status = (int) $status;
        $this->headers = (array) $headers;
    }

    /**
     * Detects whether the response status is of success type (HTTP status 2xx)
     *
     * @return bool True if status is of success type, false otherwise
     */
    public function isSuccess()
    {
        return 200 <= $this->status && $this->status <= 299;
    }

    /**
     * Finishes the response with the given response renderer.
     *
     * @param Renderer\DefaultRenderer $content_renderer Used response renderer,
     *                                                   only applied if body is
     *                                                   not a callable closure
     */
    public function finish($content_renderer)
    {
        if (!is_callable($this->body)) {
            $content_renderer->render($this);
        }
    }

    /**
     * Sends the response.
     */
    public function output()
    {
        if (isset($this->status)) {
            if (mb_strpos(PHP_SAPI, 'cgi') === 0) {
                $this->sendHeader(sprintf('Status: %d %s', $this->status, $this->reason()));
            } else {
                $this->sendHeader(sprintf('HTTP/1.1 %d %s', $this->status, $this->reason()));
            }
        }

        foreach ($this->headers as $k => $v) {
            $this->sendHeader("$k: $v", false, $this->status);
        }

        if (is_callable($this->body)) {
            call_user_func($this->body);
        } else {
            echo $this->body;
        }
    }

    /**
     * Internally used function to actually send headers
     *
     * @param  string     the HTTP header
     * @param  bool       optional; TRUE if previously sent header should be
     *                    replaced - FALSE otherwise (default)
     * @param  integer    optional; the HTTP response code
     *
     * @return void
     */
    public function sendHeader($header, $replace = FALSE, $status = NULL) {
        if (isset($status)) {
            header($header, $replace, $status);
        }
        else {
            header($header, $replace);
        }
    }

    /**
     * Returns the reason phrase of this response according to RFC2616.
     *
     * @return string  the reason phrase for this response's status
     */
    public function reason() {
        $reason = [
            100 => 'Continue', 'Switching Protocols',
            200 => 'OK', 'Created', 'Accepted', 'Non-Authoritative Information',
                   'No Content', 'Reset Content', 'Partial Content',
            300 => 'Multiple Choices', 'Moved Permanently', 'Found', 'See Other',
                   'Not Modified', 'Use Proxy', '(Unused)', 'Temporary Redirect',
            400 => 'Bad Request', 'Unauthorized', 'Payment Required','Forbidden',
                   'Not Found', 'Method Not Allowed', 'Not Acceptable',
                   'Proxy Authentication Required', 'Request Timeout', 'Conflict',
                   'Gone', 'Length Required', 'Precondition Failed',
                   'Request Entity Too Large', 'Request-URI Too Long',
                   'Unsupported Media Type', 'Requested Range Not Satisfiable',
                   'Expectation Failed',
            500 => 'Internal Server Error', 'Not Implemented', 'Bad Gateway',
                   'Service Unavailable', 'Gateway Timeout',
                   'HTTP Version Not Supported'];

        return isset($reason[$this->status]) ? $reason[$this->status] : '';
    }

    // array access methods for headers

    /**
     * @todo Add bool return type when Stud.IP requires PHP8 minimal
     */
    #[ReturnTypeWillChange]
    public function offsetExists($offset)
    {
        return isset($this->headers[$offset]);
    }

    /**
     * @param $offset
     * @return mixed
     *
     * @todo Add mixed return type when Stud.IP requires PHP8 minimal
     */
    #[\ReturnTypeWillChange]
    public function offsetGet($offset)
    {
        return @$this->headers[$offset];
    }

    /**
     * @todo Add void return type when Stud.IP requires PHP8 minimal
     */
    #[ReturnTypeWillChange]
    public function offsetSet($offset, $value)
    {
        $this->headers[$offset] = $value;
    }

    /**
     * @todo Add void return type when Stud.IP requires PHP8 minimal
     */
    #[ReturnTypeWillChange]
    public function offsetUnset($offset)
    {
        unset($this->headers[$offset]);
    }
}