9 use seekat\API\ContentType\Handler
;
10 use seekat\Exception\InvalidArgumentException
;
11 use seekat\Exception\UnexpectedValueException
;
13 final class ContentType
{
20 * Content type abbreviation
26 * Content type handler map
29 static private $types = [];
32 * Register a content type handler
34 static function register(Handler
$handler) {
35 foreach ($handler->types() as $type) {
36 self
::$types[$type] = $handler;
41 * Check whether a handler is registered for a particular content type
42 * @param string $type The (abbreviated) content type
45 static function registered(string $type) : bool {
46 return isset(self
::$types[$type]);
50 * Unregister a content type handler
53 static function unregister(string $type) {
54 unset(self
::$types[$type]);
63 function __construct(int $version = 3, string $type = null) {
64 $this->version
= $version;
66 $this->setContentType($this->extractTypeFromParams(new Params($type)));
71 * @param Header $contentType
73 function setContentTypeHeader(Header
$contentType) {
74 $this->type
= $this->extractTypeFromHeader($contentType);
78 * @param string $contentType
80 function setContentType(string $contentType) {
81 $this->type
= $this->extractType($contentType);
87 function getVersion() : int {
88 return $this->version
;
92 * Get the (abbreviated) content type name
95 function getType() : string {
100 * Get the (full) content type
103 function getContentType() : string {
104 return $this->composeType($this->type
);
111 function apply(API
$api) : API
{
112 return $api->withHeader("Accept", $this->getContentType());
116 * Decode a response message's body according to its content type
119 * @throws UnexpectedValueException
121 function decode(Body
$data) {
122 $type = $this->getType();
123 if (static::registered($type)) {
124 return self
::$types[$type]->decode($data);
126 throw new UnexpectedValueException("Unhandled content type '$type'");
130 * Encode a request message's body according to its content type
133 * @throws UnexpectedValueException
135 function encode($data) : Body
{
136 $type = $this->getType();
137 if (static::registered($type)) {
138 return self
::$types[$type]->encode($data);
140 throw new UnexpectedValueException("Unhandled content type '$type'");
143 private function composeType(string $type) : string {
144 $part = "[^()<>@,;:\\\"\/\[\]?.=[:space:][:cntrl:]]+";
145 if (preg_match("/^$part\/$part\$/", $type)) {
149 switch (substr($type, 0, 1)) {
158 return "application/vnd.github.v{$this->version}$type";
161 private function extractType(string $type) : string {
162 return preg_replace("/
163 (?:application\/(?:vnd\.github(?:\.v{$this->version})?)?)
166 | (?:\.[^.+]+)?\+? (json)
170 private function extractTypeFromParams(Params
$params) : string {
171 return $this->extractType(current(array_keys($params->params
)));
174 private function extractTypeFromHeader(Header
$header) : string {
175 if (strcasecmp($header->name
, "Content-Type")) {
176 throw new InvalidArgumentException(
177 "Expected Content-Type header, got ". $header->name
);
179 return $this->extractTypeFromParams($header->getParams());
183 ContentType
::register(new Handler\Text
);
184 ContentType
::register(new Handler\Json
);
185 ContentType
::register(new Handler\Base64
);
186 ContentType
::register(new Handler\Stream
);