Mercurial > hg > isophonics-drupal-site
diff core/modules/jsonapi/src/JsonApiResource/LinkCollection.php @ 18:af1871eacc83
Update to Drupal core 8.7.1
author | Chris Cannam |
---|---|
date | Thu, 09 May 2019 15:33:08 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/modules/jsonapi/src/JsonApiResource/LinkCollection.php Thu May 09 15:33:08 2019 +0100 @@ -0,0 +1,199 @@ +<?php + +namespace Drupal\jsonapi\JsonApiResource; + +use Drupal\Component\Assertion\Inspector; + +/** + * Contains a set of JSON:API Link objects. + * + * @internal JSON:API maintains no PHP API. The API is the HTTP API. This class + * may change at any time and could break any dependencies on it. + * + * @see https://www.drupal.org/project/jsonapi/issues/3032787 + * @see jsonapi.api.php + */ +final class LinkCollection implements \IteratorAggregate { + + /** + * The links in the collection, keyed by unique strings. + * + * @var \Drupal\jsonapi\JsonApiResource\Link[] + */ + protected $links; + + /** + * The link context. + * + * All links objects exist within a context object. Links form a relationship + * between a source IRI and target IRI. A context is the link's source. + * + * @var \Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel|\Drupal\jsonapi\JsonApiResource\ResourceObject + * + * @see https://tools.ietf.org/html/rfc8288#section-3.2 + */ + protected $context; + + /** + * LinkCollection constructor. + * + * @param \Drupal\jsonapi\JsonApiResource\Link[] $links + * An associated array of key names and JSON:API Link objects. + * @param \Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel|\Drupal\jsonapi\JsonApiResource\ResourceObject $context + * (internal use only) The context object. Use the self::withContext() + * method to establish a context. This should be done automatically when + * a LinkCollection is passed into a context object. + */ + public function __construct(array $links, $context = NULL) { + assert(Inspector::assertAll(function ($key) { + return static::validKey($key); + }, array_keys($links))); + assert(Inspector::assertAll(function ($link) { + return $link instanceof Link || is_array($link) && Inspector::assertAllObjects($link, Link::class); + }, $links)); + assert(is_null($context) || Inspector::assertAllObjects([$context], JsonApiDocumentTopLevel::class, ResourceObject::class)); + ksort($links); + $this->links = array_map(function ($link) { + return is_array($link) ? $link : [$link]; + }, $links); + $this->context = $context; + } + + /** + * {@inheritdoc} + */ + public function getIterator() { + assert(!is_null($this->context), 'A LinkCollection is invalid unless a context has been established.'); + return new \ArrayIterator($this->links); + } + + /** + * Gets a new LinkCollection with the given link inserted. + * + * @param string $key + * A key for the link. If the key already exists and the link shares an href + * with an existing link with that key, those links will be merged together. + * @param \Drupal\jsonapi\JsonApiResource\Link $new_link + * The link to insert. + * + * @return static + * A new LinkCollection with the given link inserted or merged with the + * current set of links. + */ + public function withLink($key, Link $new_link) { + assert(static::validKey($key)); + $merged = $this->links; + if (isset($merged[$key])) { + foreach ($merged[$key] as $index => $existing_link) { + if (Link::compare($existing_link, $new_link) === 0) { + $merged[$key][$index] = Link::merge($existing_link, $new_link); + return new static($merged, $this->context); + } + } + } + $merged[$key][] = $new_link; + return new static($merged, $this->context); + } + + /** + * Whether a link with the given key exists. + * + * @param string $key + * The key. + * + * @return bool + * TRUE if a link with the given key exist, FALSE otherwise. + */ + public function hasLinkWithKey($key) { + return array_key_exists($key, $this->links); + } + + /** + * Establishes a new context for a LinkCollection. + * + * @param \Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel|\Drupal\jsonapi\JsonApiResource\ResourceObject $context + * The new context object. + * + * @return static + * A new LinkCollection with the given context. + */ + public function withContext($context) { + return new static($this->links, $context); + } + + /** + * Gets the LinkCollection's context object. + * + * @return \Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel|\Drupal\jsonapi\JsonApiResource\ResourceObject + * The LinkCollection's context. + */ + public function getContext() { + assert(!is_null($this->context), 'A LinkCollection is invalid unless a context has been established.'); + return $this->context; + } + + /** + * Filters a LinkCollection using the provided callback. + * + * @param callable $f + * The filter callback. The callback has the signature below. + * + * @code + * boolean callback(string $key, \Drupal\jsonapi\JsonApiResource\Link $link, mixed $context)) + * @endcode + * + * @return \Drupal\jsonapi\JsonApiResource\LinkCollection + * A new, filtered LinkCollection. + */ + public function filter(callable $f) { + $links = iterator_to_array($this); + $filtered = array_reduce(array_keys($links), function ($filtered, $key) use ($links, $f) { + if ($f($key, $links[$key], $this->context)) { + $filtered[$key] = $links[$key]; + } + return $filtered; + }, []); + return new LinkCollection($filtered, $this->context); + } + + /** + * Merges two LinkCollections. + * + * @param \Drupal\jsonapi\JsonApiResource\LinkCollection $a + * The first link collection. + * @param \Drupal\jsonapi\JsonApiResource\LinkCollection $b + * The second link collection. + * + * @return \Drupal\jsonapi\JsonApiResource\LinkCollection + * A new LinkCollection with the links of both inputs. + */ + public static function merge(LinkCollection $a, LinkCollection $b) { + assert($a->getContext() === $b->getContext()); + $merged = new LinkCollection([], $a->getContext()); + foreach ($a as $key => $links) { + $merged = array_reduce($links, function (self $merged, Link $link) use ($key) { + return $merged->withLink($key, $link); + }, $merged); + } + foreach ($b as $key => $links) { + $merged = array_reduce($links, function (self $merged, Link $link) use ($key) { + return $merged->withLink($key, $link); + }, $merged); + } + return $merged; + } + + /** + * Ensures that a link key is valid. + * + * @param string $key + * A key name. + * + * @return bool + * TRUE if the key is valid, FALSE otherwise. + */ + protected static function validKey($key) { + return is_string($key) && !is_numeric($key) && strpos($key, ':') === FALSE; + } + +}