class LinkCollectionNormalizer in Drupal 9
Same name and namespace in other branches
- 8 core/modules/jsonapi/src/Normalizer/LinkCollectionNormalizer.php \Drupal\jsonapi\Normalizer\LinkCollectionNormalizer
Normalizes a LinkCollection object.
The JSON:API specification has the concept of a "links collection". A links collection is a JSON object where each member of the object is a "link object". Unfortunately, this means that it is not possible to have more than one link for a given key.
When normalizing more than one link in a LinkCollection with the same key, a unique and random string is appended to the link's key after a double dash (--) to differentiate the links. See this class's hashByHref() method for details.
This may change with a later version of the JSON:API specification.
@internal JSON:API maintains no PHP API since its API is the HTTP API. This class may change at any time and this will break any dependencies on it.
Hierarchy
- class \Drupal\serialization\Normalizer\NormalizerBase implements \Symfony\Component\Serializer\SerializerAwareInterface, CacheableNormalizerInterface uses \Symfony\Component\Serializer\SerializerAwareTrait
- class \Drupal\jsonapi\Normalizer\NormalizerBase
- class \Drupal\jsonapi\Normalizer\LinkCollectionNormalizer
- class \Drupal\jsonapi\Normalizer\NormalizerBase
Expanded class hierarchy of LinkCollectionNormalizer
See also
https://www.drupal.org/project/drupal/issues/3032787
1 file declares its use of LinkCollectionNormalizer
- LinkCollectionNormalizerTest.php in core/
modules/ jsonapi/ tests/ src/ Kernel/ Normalizer/ LinkCollectionNormalizerTest.php
1 string reference to 'LinkCollectionNormalizer'
- jsonapi.services.yml in core/
modules/ jsonapi/ jsonapi.services.yml - core/modules/jsonapi/jsonapi.services.yml
1 service uses LinkCollectionNormalizer
File
- core/
modules/ jsonapi/ src/ Normalizer/ LinkCollectionNormalizer.php, line 34
Namespace
Drupal\jsonapi\NormalizerView source
class LinkCollectionNormalizer extends NormalizerBase {
/**
* The normalizer $context key name for the key of an individual link.
*
* @var string
*/
const LINK_KEY = 'jsonapi_links_object_link_key';
/**
* The normalizer $context key name for the context object of the link.
*
* @var string
*/
const LINK_CONTEXT = 'jsonapi_links_object_context';
/**
* {@inheritdoc}
*/
protected $supportedInterfaceOrClass = LinkCollection::class;
/**
* A random string to use when hashing links.
*
* This string is unique per instance of a link collection, but always the
* same within it. This means that link key hashes will be non-deterministic
* for outside observers, but two links within the same collection will always
* have the same hash value.
*
* This is not used for cryptographic purposes.
*
* @var string
*/
protected $hashSalt;
/**
* The current user making the request.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* LinkCollectionNormalizer constructor.
*
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(AccountInterface $current_user = NULL) {
if (is_null($current_user)) {
@trigger_error('Calling ' . __METHOD__ . '() without the $current_user argument is deprecated in drupal:9.2.0 and will be required in drupal:10.0.0.', E_USER_DEPRECATED);
$current_user = \Drupal::currentUser();
}
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = []) {
assert($object instanceof LinkCollection);
$normalized = [];
/** @var \Drupal\jsonapi\JsonApiResource\Link $link */
foreach ($object as $key => $links) {
$is_multiple = count($links) > 1;
foreach ($links as $link) {
$link_key = $is_multiple ? sprintf('%s--%s', $key, $this
->hashByHref($link)) : $key;
$attributes = $link
->getTargetAttributes();
$normalization = array_merge([
'href' => $link
->getHref(),
], !empty($attributes) ? [
'meta' => $attributes,
] : []);
// Checking access on links is not about access to the link itself;
// it is about whether the current user has access to the route that is
// *targeted* by the link. This is done on a "best effort" basis. That
// is, some links target routes that depend on a request to determine if
// they're accessible or not. Some other links might target routes to
// which the current user will clearly not have access, in that case
// this code proactively removes those links from the response.
$access = $link
->getUri()
->access($this->currentUser, TRUE);
$cacheability = CacheableMetadata::createFromObject($link)
->addCacheableDependency($access);
$normalized[$link_key] = $access
->isAllowed() ? new CacheableNormalization($cacheability, $normalization) : new CacheableOmission($cacheability);
}
}
return CacheableNormalization::aggregate($normalized);
}
/**
* Hashes a link using its href and its target attributes, if any.
*
* This method generates an unpredictable, but deterministic, 7 character
* alphanumeric hash for a given link.
*
* The hash is unpredictable because a random hash salt will be used for every
* request. The hash is deterministic because, within a single request, links
* with the same href and target attributes (i.o.w. duplicates) will generate
* equivalent hash values.
*
* @param \Drupal\jsonapi\JsonApiResource\Link $link
* A link to be hashed.
*
* @return string
* A 7 character alphanumeric hash.
*/
protected function hashByHref(Link $link) {
// Generate a salt unique to each instance of this class.
if (!$this->hashSalt) {
$this->hashSalt = Crypt::randomBytesBase64();
}
// Create a dictionary of link parameters.
$link_parameters = [
'href' => $link
->getHref(),
] + $link
->getTargetAttributes();
// Serialize the dictionary into a string.
foreach ($link_parameters as $name => $value) {
$serialized_parameters[] = sprintf('%s="%s"', $name, implode(' ', (array) $value));
}
// Hash the string.
$b64_hash = Crypt::hashBase64($this->hashSalt . implode('; ', $serialized_parameters));
// Remove any dashes and underscores from the base64 hash and then return
// the first 7 characters.
return substr(str_replace([
'-',
'_',
], '', $b64_hash), 0, 7);
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
CacheableNormalizerInterface:: |
constant | Name of key for bubbling cacheability metadata via serialization context. | ||
LinkCollectionNormalizer:: |
protected | property | The current user making the request. | |
LinkCollectionNormalizer:: |
protected | property | A random string to use when hashing links. | |
LinkCollectionNormalizer:: |
protected | property |
The interface or class that this Normalizer supports. Overrides NormalizerBase:: |
|
LinkCollectionNormalizer:: |
protected | function | Hashes a link using its href and its target attributes, if any. | |
LinkCollectionNormalizer:: |
constant | The normalizer $context key name for the context object of the link. | ||
LinkCollectionNormalizer:: |
constant | The normalizer $context key name for the key of an individual link. | ||
LinkCollectionNormalizer:: |
public | function | ||
LinkCollectionNormalizer:: |
public | function | LinkCollectionNormalizer constructor. | |
NormalizerBase:: |
protected | property |
List of formats which supports (de-)normalization. Overrides NormalizerBase:: |
|
NormalizerBase:: |
protected | function | Adds cacheability if applicable. | |
NormalizerBase:: |
protected | function |
Checks if the provided format is supported by this normalizer. Overrides NormalizerBase:: |
|
NormalizerBase:: |
protected static | function | Rasterizes a value recursively. | |
NormalizerBase:: |
public | function | Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::supportsDenormalization() | 1 |
NormalizerBase:: |
public | function | 1 |