* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace KTXC\Http\Request; use KTXC\Http\HeaderUtils; // Help opcache.preload discover always-needed symbols class_exists(RequestHeaderAcceptItem::class); /** * Represents an Accept-* header. * * An accept header is compound with a list of items, * sorted by descending quality. * * @author Jean-François Simon */ class RequestHeaderAccept { /** * @var RequestHeaderAcceptItem[] */ private array $items = []; private bool $sorted = true; /** * @param RequestHeaderAcceptItem[] $items */ public function __construct(array $items) { foreach ($items as $item) { $this->add($item); } } /** * Builds an AcceptHeader instance from a string. */ public static function fromString(?string $headerValue): self { $parts = HeaderUtils::split($headerValue ?? '', ',;='); return new self(array_map(function ($subParts) { static $index = 0; $part = array_shift($subParts); $attributes = HeaderUtils::combine($subParts); $item = new RequestHeaderAcceptItem($part[0], $attributes); $item->setIndex($index++); return $item; }, $parts)); } /** * Returns header value's string representation. */ public function __toString(): string { return implode(',', $this->items); } /** * Tests if header has given value. */ public function has(string $value): bool { return isset($this->items[$value]); } /** * Returns given value's item, if exists. */ public function get(string $value): ?RequestHeaderAcceptItem { return $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? null; } /** * Adds an item. * * @return $this */ public function add(RequestHeaderAcceptItem $item): static { $this->items[$item->getValue()] = $item; $this->sorted = false; return $this; } /** * Returns all items. * * @return RequestHeaderAcceptItem[] */ public function all(): array { $this->sort(); return $this->items; } /** * Filters items on their value using given regex. */ public function filter(string $pattern): self { return new self(array_filter($this->items, fn (RequestHeaderAcceptItem $item) => preg_match($pattern, $item->getValue()))); } /** * Returns first item. */ public function first(): ?RequestHeaderAcceptItem { $this->sort(); return $this->items ? reset($this->items) : null; } /** * Sorts items by descending quality. */ private function sort(): void { if (!$this->sorted) { uasort($this->items, function (RequestHeaderAcceptItem $a, RequestHeaderAcceptItem $b) { $qA = $a->getQuality(); $qB = $b->getQuality(); if ($qA === $qB) { return $a->getIndex() > $b->getIndex() ? 1 : -1; } return $qA > $qB ? -1 : 1; }); $this->sorted = true; } } }