AcceptHeader.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpFoundation;
  11. // Help opcache.preload discover always-needed symbols
  12. class_exists(AcceptHeaderItem::class);
  13. /**
  14. * Represents an Accept-* header.
  15. *
  16. * An accept header is compound with a list of items,
  17. * sorted by descending quality.
  18. *
  19. * @author Jean-François Simon <contact@jfsimon.fr>
  20. */
  21. class AcceptHeader
  22. {
  23. /**
  24. * @var AcceptHeaderItem[]
  25. */
  26. private array $items = [];
  27. private bool $sorted = true;
  28. /**
  29. * @param AcceptHeaderItem[] $items
  30. */
  31. public function __construct(array $items)
  32. {
  33. foreach ($items as $item) {
  34. $this->add($item);
  35. }
  36. }
  37. /**
  38. * Builds an AcceptHeader instance from a string.
  39. */
  40. public static function fromString(?string $headerValue): self
  41. {
  42. $parts = HeaderUtils::split($headerValue ?? '', ',;=');
  43. return new self(array_map(function ($subParts) {
  44. static $index = 0;
  45. $part = array_shift($subParts);
  46. $attributes = HeaderUtils::combine($subParts);
  47. $item = new AcceptHeaderItem($part[0], $attributes);
  48. $item->setIndex($index++);
  49. return $item;
  50. }, $parts));
  51. }
  52. /**
  53. * Returns header value's string representation.
  54. */
  55. public function __toString(): string
  56. {
  57. return implode(',', $this->items);
  58. }
  59. /**
  60. * Tests if header has given value.
  61. */
  62. public function has(string $value): bool
  63. {
  64. return isset($this->items[$value]);
  65. }
  66. /**
  67. * Returns given value's item, if exists.
  68. */
  69. public function get(string $value): ?AcceptHeaderItem
  70. {
  71. return $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? null;
  72. }
  73. /**
  74. * Adds an item.
  75. *
  76. * @return $this
  77. */
  78. public function add(AcceptHeaderItem $item): static
  79. {
  80. $this->items[$item->getValue()] = $item;
  81. $this->sorted = false;
  82. return $this;
  83. }
  84. /**
  85. * Returns all items.
  86. *
  87. * @return AcceptHeaderItem[]
  88. */
  89. public function all(): array
  90. {
  91. $this->sort();
  92. return $this->items;
  93. }
  94. /**
  95. * Filters items on their value using given regex.
  96. */
  97. public function filter(string $pattern): self
  98. {
  99. return new self(array_filter($this->items, fn (AcceptHeaderItem $item) => preg_match($pattern, $item->getValue())));
  100. }
  101. /**
  102. * Returns first item.
  103. */
  104. public function first(): ?AcceptHeaderItem
  105. {
  106. $this->sort();
  107. return $this->items ? reset($this->items) : null;
  108. }
  109. /**
  110. * Sorts items by descending quality.
  111. */
  112. private function sort(): void
  113. {
  114. if (!$this->sorted) {
  115. uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) {
  116. $qA = $a->getQuality();
  117. $qB = $b->getQuality();
  118. if ($qA === $qB) {
  119. return $a->getIndex() > $b->getIndex() ? 1 : -1;
  120. }
  121. return $qA > $qB ? -1 : 1;
  122. });
  123. $this->sorted = true;
  124. }
  125. }
  126. }