Permalink
Browse files

[http-foudation] Better accept header parsing

  • Loading branch information...
1 parent d8f6021 commit 6b601bd9a639ca141ebfcc3b6b91c2dbde334726 @jfsimon jfsimon committed with fabpot Oct 24, 2012
View
@@ -0,0 +1,22 @@
+UPGRADE FROM 2.1 to 2.2
+=======================
+
+#### Deprecations
+
+ * The `Request::splitHttpAcceptHeader()` is deprecated and will be removed in 2.3.
+
+ You should now use the `AcceptHeader` class which give you fluent methods to
+ parse request accept-* headers. Some examples:
+
+ ```
+ $accept = AcceptHeader::fromString($request->headers->get('Accept'));
+ if ($accept->has('text/html') {
+ $item = $accept->get('html');
+ $charset = $item->getAttribute('charset', 'utf-8');
+ $quality = $item->getQuality();
+ }
+
+ // accepts items are sorted by descending quality
+ $accepts = AcceptHeader::fromString($request->headers->get('Accept'))->all();
+
+ ```
@@ -0,0 +1,172 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Represents an Accept-* header.
+ *
+ * An accept header is compound with a list of items,
+ * sorted by descending quality.
+ *
+ * @author Jean-François Simon <contact@jfsimon.fr>
+ */
+class AcceptHeader
+{
+ /**
+ * @var AcceptHeaderItem[]
+ */
+ private $items = array();
+
+ /**
+ * @var bool
+ */
+ private $sorted = true;
+
+ /**
+ * Constructor.
+ *
+ * @param AcceptHeaderItem[] $items
+ */
+ public function __construct(array $items)
+ {
+ foreach ($items as $item) {
+ $this->add($item);
+ }
+ }
+
+ /**
+ * Builds an AcceptHeader instance from a string.
+ *
+ * @param string $headerValue
+ *
+ * @return AcceptHeader
+ */
+ public static function fromString($headerValue)
+ {
+ $index = 0;
+
+ return new self(array_map(function ($itemValue) use (&$index) {
lisachenko
lisachenko Jun 23, 2013

It will be better to use LSB: return new static(...) here to allow customization and testing.

Baachi
Baachi Jun 26, 2013 Contributor

Please open a PR/issue for that. Comment on a 8 month commit is not the right way to do it.

+ $item = AcceptHeaderItem::fromString($itemValue);
+ $item->setIndex($index++);
+
+ return $item;
+ }, preg_split('/\s*(?:,*("[^"]+"),*|,*(\'[^\']+\'),*|,+)\s*/', $headerValue, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)));
+ }
+
+ /**
+ * Returns header value's string representation.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return implode(',', $this->items);
+ }
+
+ /**
+ * Tests if header has given value.
+ *
+ * @param string $value
+ *
+ * @return Boolean
+ */
+ public function has($value)
+ {
+ return isset($this->items[$value]);
+ }
+
+ /**
+ * Returns given value's item, if exists.
+ *
+ * @param string $value
+ *
+ * @return AcceptHeaderItem|null
+ */
+ public function get($value)
+ {
+ return isset($this->items[$value]) ? $this->items[$value] : null;
+ }
+
+ /**
+ * Adds an item.
+ *
+ * @param AcceptHeaderItem $item
+ *
+ * @return AcceptHeader
+ */
+ public function add(AcceptHeaderItem $item)
+ {
+ $this->items[$item->getValue()] = $item;
+ $this->sorted = false;
+
+ return $this;
+ }
+
+ /**
+ * Returns all items.
+ *
+ * @return AcceptHeaderItem[]
+ */
+ public function all()
+ {
+ $this->sort();
+
+ return $this->items;
+ }
+
+ /**
+ * Filters items on their value using given regex.
+ *
+ * @param string $pattern
+ *
+ * @return AcceptHeader
+ */
+ public function filter($pattern)
+ {
+ return new self(array_filter($this->items, function (AcceptHeaderItem $item) use ($pattern) {
+ return preg_match($pattern, $item->getValue());
+ }));
+ }
+
+ /**
+ * Returns first item.
+ *
+ * @return AcceptHeaderItem|null
+ */
+ public function first()
+ {
+ $this->sort();
+
+ return !empty($this->items) ? current($this->items) : null;
+ }
+
+ /**
+ * Sorts items by descending quality
+ */
+ private function sort()
+ {
+ if (!$this->sorted) {
+ uasort($this->items, function ($a, $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;
+ }
+ }
+}
Oops, something went wrong.

0 comments on commit 6b601bd

Please sign in to comment.