ProblemDetails   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 196
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 2
dl 0
loc 196
ccs 46
cts 46
cp 1
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 14 3
A getType() 0 4 1
A getTitle() 0 4 1
A getStatus() 0 4 1
A getDetail() 0 4 1
A getInstance() 0 4 1
A getExtensions() 0 4 1
A __toString() 0 4 1
A toJson() 0 4 1
C toArray() 0 22 7
A jsonSerialize() 0 4 1
A send() 0 5 1
1
<?php
2
declare(strict_types=1);
3
/**
4
 * Caridea
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
 * use this file except in compliance with the License. You may obtain a copy of
8
 * the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
 * License for the specific language governing permissions and limitations under
16
 * the License.
17
 *
18
 * @copyright 2015-2018 LibreWorks contributors
19
 * @license   Apache-2.0
20
 */
21
namespace Caridea\Http;
22
23
use Psr\Http\Message\UriInterface;
24
use Psr\Http\Message\ResponseInterface;
25
26
/**
27
 * An implementation of RFC 7807, Problem Details for HTTP APIs.
28
 *
29
 * @copyright 2015-2018 LibreWorks contributors
30
 * @license   Apache-2.0
31
 * @see https://tools.ietf.org/html/rfc7807
32
 */
33
class ProblemDetails implements \JsonSerializable
34
{
35
    const MIME_TYPE_JSON = 'application/problem+json';
36
    const REGEX_NAMES = '/^[a-z][a-z0-9_]{2,}$/i';
37
38
    /**
39
     * @var \Psr\Http\Message\UriInterface An absolute URI that identifies the problem type
40
     */
41
    protected $type;
42
    /**
43
     * @var string A short, human-readable summary of the problem type
44
     */
45
    protected $title;
46
    /**
47
     * @var int The HTTP status code generated by the origin server for this occurrence of the problem
48
     */
49
    protected $status;
50
    /**
51
     * @var string A human-readable explanation specific to this occurrence of the problem
52
     */
53
    protected $detail;
54
    /**
55
     * @var \Psr\Http\Message\UriInterface An absolute URI that identifies the specific occurrence of the problem
56
     */
57
    protected $instance;
58
    /**
59
     * @var array<string,mixed> Map of `string` names to `mixed` values
60
     */
61
    protected $extensions = [];
62
    /**
63
     * @var array Cached output
64
     */
65
    protected $output;
66
67
    /**
68
     * Creates a new ProblemDetails.
69
     *
70
     * The `extensions` parameter, if provided, must be an associative array
71
     * matching names to values. Each name must follow the guidelines in the
72
     * RFC, namely start with a letter, be at least three characters long, and
73
     * only contain alpha, digit, and the underscore. The values can be of any
74
     * type, but be aware that the values must be easily converted to JSON. You
75
     * pretty much always want these values to be immutable.
76
     *
77
     * @param \Psr\Http\Message\UriInterface|null $type An absolute URI that identifies the problem type
78
     * @param string|null $title A short, human-readable summary of the problem type
79
     * @param int $status The HTTP status code generated by the origin server for this occurrence of the problem
80
     * @param string|null $detail A human-readable explanation specific to this occurrence of the problem
81
     * @param \Psr\Http\Message\UriInterface|null $instance An absolute URI that identifies the specific occurrence of the problem
82
     * @param array $extensions Additional members to the ProblemDetails
83
     */
84 8
    public function __construct(UriInterface $type = null, string $title = null, int $status = 0, string $detail = null, \Psr\Http\Message\UriInterface $instance = null, array $extensions = [])
85
    {
86 8
        $this->type = $type;
87 8
        $this->title = $title;
88 8
        $this->status = $status;
89 8
        $this->detail = $detail;
90 8
        $this->instance = $instance;
91 8
        array_walk($extensions, function ($v, $k) {
92 3
            if (!preg_match(self::REGEX_NAMES, (string) $k) || property_exists($this, (string) $k)) {
93 2
                throw new \InvalidArgumentException("Invalid extension name: '$k'");
94
            }
95 8
        });
96 6
        $this->extensions = $extensions;
97 6
    }
98
99
    /**
100
     * Gets the absolute URI that identifies the problem type
101
     *
102
     * @return \Psr\Http\Message\UriInterface|null The problem type URI (or null)
103
     */
104 1
    public function getType(): ?UriInterface
105
    {
106 1
        return $this->type;
107
    }
108
109
    /**
110
     * Gets the short, human-readable summary of the problem type.
111
     *
112
     * @return string|null A summary of the problem type
113
     */
114 1
    public function getTitle(): ?string
115
    {
116 1
        return $this->title;
117
    }
118
119
    /**
120
     * Gets the HTTP status code generated by the origin server for this occurrence of the problem
121
     *
122
     * @return int The status code
123
     */
124 1
    public function getStatus(): int
125
    {
126 1
        return $this->status;
127
    }
128
129
    /**
130
     * Gets a human readable explanation specific to this occurrence of the problem.
131
     *
132
     * @return string|null An explanation specific to this occurrence of the problem
133
     */
134 1
    public function getDetail(): ?string
135
    {
136 1
        return $this->detail;
137
    }
138
139
    /**
140
     * Gets the absolute URI that identifies the specific occurrence of the problem.
141
     *
142
     * @return \Psr\Http\Message\UriInterface|null The URI that identifies the specific occurrence of the problem (or null)
143
     */
144 1
    public function getInstance(): ?UriInterface
145
    {
146 1
        return $this->instance;
147
    }
148
149
    /**
150
     * Gets an array of any extension members defined.
151
     *
152
     * @return array Any extension members, never null.
153
     */
154 1
    public function getExtensions(): array
155
    {
156 1
        return $this->extensions;
157
    }
158
159
    /**
160
     * Returns a JSON representation of this problem.
161
     *
162
     * @return string The JSON representation
163
     */
164 1
    public function __toString(): string
165
    {
166 1
        return $this->toJson();
167
    }
168
169
    /**
170
     * A JSON representation of this problem.
171
     *
172
     * @return string The JSON representation
173
     */
174 6
    public function toJson(): string
175
    {
176 6
        return json_encode($this->toArray());
177
    }
178
179
    /**
180
     * Gets a serializable representation of this problem.
181
     *
182
     * @return array<string,mixed> This problem detail as an associative array
183
     */
184 6
    public function toArray(): array
185
    {
186 6
        if (empty($this->output)) {
187
            $problem = [
188 6
                'type' => $this->type ? (string)$this->type : 'about:blank'
189
            ];
190 6
            if ($this->title) {
191 1
                $problem['title'] = $this->title;
192
            }
193 6
            if ($this->status) {
194 1
                $problem['status'] = $this->status;
195
            }
196 6
            if ($this->detail) {
197 1
                $problem['detail'] = $this->detail;
198
            }
199 6
            if ($this->instance) {
200 1
                $problem['instance'] = (string)$this->instance;
201
            }
202 6
            $this->output = array_merge($problem, $this->extensions);
203
        }
204 6
        return $this->output;
205
    }
206
207
    /**
208
     * Returns the JSON representation.
209
     *
210
     * @return array<string,mixed> This problem detail as an associative array
211
     */
212 3
    public function jsonSerialize()
213
    {
214 3
        return $this->toArray();
215
    }
216
217
    /**
218
     * Adds this problem detail to a PSR-7 HTTP response.
219
     *
220
     * @param ResponseInterface $response The HTTP response
221
     * @return ResponseInterface The new response
222
     */
223 1
    public function send(ResponseInterface $response): ResponseInterface
224
    {
225 1
        $response->getBody()->write(json_encode($this->toArray()));
226 1
        return $response->withHeader('Content-Type', self::MIME_TYPE_JSON);
227
    }
228
}
229