index/src/MediaType.php

163 lines
4.6 KiB
PHP

<?php
// MediaType.php
// Created: 2022-02-10
// Updated: 2023-02-01
namespace Index;
use InvalidArgumentException;
use Stringable;
class MediaType implements Stringable, IComparable, IEquatable {
private string $category = '';
private string $kind = '';
private string $suffix = '';
private array $params = [];
public function __construct(string $category, string $kind, string $suffix, array $params) {
$this->category = $category;
$this->kind = $kind;
$this->suffix = $suffix;
$this->params = $params;
}
public function getCategory(): string {
return $this->category;
}
public function getKind(): string {
return $this->kind;
}
public function getSuffix(): string {
return $this->suffix;
}
public function getParams(): array {
return $this->params;
}
public function hasParam(string $name): bool {
return isset($this->params[$name]);
}
public function getParam(string $name, int $filter = FILTER_DEFAULT, $options = null) {
if(!isset($this->params[$name]))
return null;
return filter_var($this->params[$name], $filter, $options);
}
public function getCharset(): string {
return $this->getParam('charset', FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) ?? 'utf-8';
}
public function getQuality(): float {
return max(min(round($this->getParam('q', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION) ?? 1, 2), 1), 0);
}
public function compareCategory(string $category): int {
return ($this->category === '*' || $category === '*')
? 0 : strcmp($this->category, $category);
}
public function matchCategory(string $category): bool {
return $this->category === '*' || $category === '*' || $this->category === $category;
}
public function compareKind(string $kind): int {
return ($this->kind === '*' || $kind === '*')
? 0 : strcmp($this->kind, $kind);
}
public function matchKind(string $kind): bool {
return $this->kind === '*' || $kind === '*' || $this->kind === $kind;
}
public function compareSuffix(string $suffix): int {
return strcmp($this->suffix, $suffix);
}
public function matchSuffix(string $suffix): bool {
return $this->suffix === $suffix;
}
public function compare(mixed $other): int {
if(!($other instanceof MediaType)) {
try {
$other = self::parse((string)$other);
} catch(InvalidArgumentException $ex) {
return -1;
}
}
if(($match = self::compareCategory($other->category)) !== 0)
return $match;
if(($match = self::compareKind($other->kind)) !== 0)
return $match;
return 0;
}
public function equals(mixed $other): bool {
if(!($other instanceof MediaType)) {
try {
$other = self::parse((string)$other);
} catch(InvalidArgumentException $ex) {
return false;
}
}
if(!$this->matchCategory($other->category))
return false;
if(!$this->matchKind($other->kind))
return false;
return true;
}
public function __toString(): string {
$string = $this->category . '/' . $this->kind;
if(!empty($this->suffix))
$string .= '+' . $this->suffix;
if(!empty($this->params))
foreach($this->params as $key => $value) {
$string .= ';';
if(is_string($key))
$string .= $key . '=';
$string .= $value;
}
return $string;
}
public static function parse(string $mediaTypeStr): MediaType {
$parts = explode(';', $mediaTypeStr);
if(empty($parts))
throw new InvalidArgumentException('Invalid media type supplied.');
$mediaTypeStr = array_shift($parts);
$mediaTypeParts = explode('/', $mediaTypeStr, 2);
$category = $mediaTypeParts[0];
$kindSplit = explode('+', $mediaTypeParts[1] ?? '', 2);
$kind = $kindSplit[0];
$suffix = $kindSplit[1] ?? '';
$params = [];
foreach($parts as $part) {
$paramSplit = explode('=', trim($part), 2);
$params[trim($paramSplit[0])] = trim($paramSplit[1] ?? '', " \n\r\t\v\0\"");
}
return new MediaType($category, $kind, $suffix, $params);
}
public static function fromPath(string $path): MediaType {
return self::parse(mime_content_type($path));
}
}