<?php
namespace Can\RestBundle\EventListener;
use Can\RestBundle\CanRestBundle;
use Can\RestBundle\Http\Method;
use Can\RestBundle\Mapping\MetadataStore;
use Can\RestBundle\Negotiation\NegotiationConfiguration;
use Can\RestBundle\Negotiation\NegotiationHeader;
use Can\RestBundle\URI\ServiceNode;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
/**
* Adds the response Content-like headers.
*
* @group negotiation
*
* @package can/rest-bundle
* @author lechecacharro <lechecacharro@gmail.com>
*/
class ContentHeadersListener
{
/**
* @var NegotiationConfiguration
*/
private $configuration;
/**
* @var MetadataStore
*/
private $store;
/**
* ContentHeadersListener constructor.
*
* @param NegotiationConfiguration $configuration
* @param MetadataStore $store
*/
public function __construct(NegotiationConfiguration $configuration, MetadataStore $store)
{
$this->configuration = $configuration;
$this->store = $store;
}
/**
* @param ResponseEvent $event
*/
public function onResponse(ResponseEvent $event): void
{
if (! $this->configuration->isEnabled()) {
return;
}
$request = $event->getRequest();
if (! $request->attributes->get(CanRestBundle::ATTR_SERVICE_ZONE)) {
return;
}
$response = $event->getResponse();
// Don't add content headers to empty responses
if ($response->isEmpty() || empty($response->getContent())) {
return;
}
if ($charset = $request->attributes->get(CanRestBundle::ATTR_NEGOTIATION_CHARSET)) {
$response->setCharset($charset);
}
//if ($encoding = $request->attributes->get(CanRestBundle::ATTR_NEGOTIATION_ENCODING)) {
//$response->headers->set(NegotiationHeader::CONTENT_ENCODING, $encoding);
//}
if ($language = $request->attributes->get(CanRestBundle::ATTR_NEGOTIATION_LANGUAGE)) {
$response->headers->set(NegotiationHeader::CONTENT_LANGUAGE, $language);
}
if ($mediaType = $request->attributes->get(CanRestBundle::ATTR_NEGOTIATION_MEDIA_TYPE)) {
$response->headers->set(NegotiationHeader::CONTENT_TYPE, $mediaType);
if ($this->configuration->isPreventBrowserSniffing()) {
$response->headers->set(NegotiationHeader::X_CONTENT_TYPE_OPTIONS, 'nosniff');
}
}
$serviceNode = $request->attributes->get(CanRestBundle::ATTR_SERVICE_NODE);
if (! $serviceNode instanceof ServiceNode) {
return;
}
if ($this->configuration->isAdvertiseAcceptedPatches() && $this->currentNodeSupportsPatch($serviceNode)) {
$patchFormats = $this->configuration->getPatchFormats();
if ($patchFormats) {
$response->headers->set(NegotiationHeader::ACCEPT_PATCH, implode(',', array_merge(...array_values($patchFormats))));
}
}
if ($this->configuration->isAdvertiseAcceptedSearches() && $this->currentNodeSupportsSearch($serviceNode)) {
$searchFormats = $this->configuration->getSearchFormats();
if ($searchFormats) {
$response->headers->set(NegotiationHeader::ACCEPT_SEARCH, implode(',', array_merge(...array_values($searchFormats))));
}
}
}
/**
* @param ServiceNode $node
*
* @return bool
*/
protected function currentNodeSupportsPatch(ServiceNode $node): bool
{
if ($node->isCollection()) {
// We DO NOT support collection patches
return false;
}
if (! $this->store->hasMetadataFor($node)) {
return false;
}
return $this->store->getMetadataFor($node)->supportsMethod(Method::PATCH);
}
/**
* @param ServiceNode $node
*
* @return bool
*/
protected function currentNodeSupportsSearch(ServiceNode $node): bool
{
if (! $this->store->hasMetadataFor($node)) {
return false;
}
return $this->store->getMetadataFor($node)->supportsOperation(Method::SEARCH, $node->isCollection());
}
}