AcceptHeader.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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. $index = 0;
  43. $parts = HeaderUtils::split($headerValue ?? '', ',;=');
  44. return new self(array_map(function ($subParts) use (&$index) {
  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, function (AcceptHeaderItem $item) use ($pattern) {
  100. return preg_match($pattern, $item->getValue());
  101. }));
  102. }
  103. /**
  104. * Returns first item.
  105. */
  106. public function first(): ?AcceptHeaderItem
  107. {
  108. $this->sort();
  109. return !empty($this->items) ? reset($this->items) : null;
  110. }
  111. /**
  112. * Sorts items by descending quality.
  113. */
  114. private function sort(): void
  115. {
  116. if (!$this->sorted) {
  117. uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) {
  118. $qA = $a->getQuality();
  119. $qB = $b->getQuality();
  120. if ($qA === $qB) {
  121. return $a->getIndex() > $b->getIndex() ? 1 : -1;
  122. }
  123. return $qA > $qB ? -1 : 1;
  124. });
  125. $this->sorted = true;
  126. }
  127. }
  128. }