View source
<?php
namespace Drupal\feeds\Feeds\Fetcher;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\feeds\Exception\EmptyFeedException;
use Drupal\feeds\FeedInterface;
use Drupal\feeds\Plugin\Type\ClearableInterface;
use Drupal\feeds\Plugin\Type\Fetcher\FetcherInterface;
use Drupal\feeds\Plugin\Type\PluginBase;
use Drupal\feeds\Result\HttpFetcherResult;
use Drupal\feeds\StateInterface;
use Drupal\feeds\Utility\Feed;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\RequestOptions;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
class HttpFetcher extends PluginBase implements ClearableInterface, FetcherInterface, ContainerFactoryPluginInterface {
protected $client;
protected $cache;
protected $fileSystem;
public function __construct(array $configuration, $plugin_id, array $plugin_definition, ClientInterface $client, CacheBackendInterface $cache, FileSystemInterface $file_system) {
$this->client = $client;
$this->cache = $cache;
$this->fileSystem = $file_system;
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('http_client'), $container
->get('cache.feeds_download'), $container
->get('file_system'));
}
public function fetch(FeedInterface $feed, StateInterface $state) {
$sink = $this->fileSystem
->tempnam('temporary://', 'feeds_http_fetcher');
$sink = $this->fileSystem
->realpath($sink);
$cache_key = $this
->useCache() ? $this
->getCacheKey($feed) : FALSE;
$response = $this
->get($feed
->getSource(), $sink, $cache_key);
if ($response
->getStatusCode() == Response::HTTP_NOT_MODIFIED) {
$state
->setMessage($this
->t('The feed has not been updated.'));
throw new EmptyFeedException();
}
return new HttpFetcherResult($sink, $response
->getHeaders());
}
protected function get($url, $sink, $cache_key = FALSE) {
$url = Feed::translateSchemes($url);
$options = [
RequestOptions::SINK => $sink,
];
if (isset($this->client
->getConfig('headers')['User-Agent'])) {
$options[RequestOptions::HEADERS]['User-Agent'] = $this->client
->getConfig('headers')['User-Agent'];
}
if ($cache_key && ($cache = $this->cache
->get($cache_key))) {
if (isset($cache->data['etag'])) {
$options[RequestOptions::HEADERS]['If-None-Match'] = $cache->data['etag'];
}
if (isset($cache->data['last-modified'])) {
$options[RequestOptions::HEADERS]['If-Modified-Since'] = $cache->data['last-modified'];
}
}
try {
$response = $this->client
->get($url, $options);
} catch (RequestException $e) {
$args = [
'%site' => $url,
'%error' => $e
->getMessage(),
];
throw new \RuntimeException($this
->t('The feed from %site seems to be broken because of error "%error".', $args));
}
if ($cache_key) {
$this->cache
->set($cache_key, array_change_key_case($response
->getHeaders()));
}
return $response;
}
protected function useCache() {
return !$this->configuration['always_download'];
}
protected function getCacheKey(FeedInterface $feed) {
return $feed
->id() . ':' . hash('sha256', $feed
->getSource());
}
public function clear(FeedInterface $feed, StateInterface $state) {
$this
->onFeedDeleteMultiple([
$feed,
]);
}
public function defaultConfiguration() {
return [
'auto_detect_feeds' => FALSE,
'use_pubsubhubbub' => FALSE,
'always_download' => FALSE,
'fallback_hub' => '',
'request_timeout' => 30,
];
}
public function onFeedDeleteMultiple(array $feeds) {
foreach ($feeds as $feed) {
$this->cache
->delete($this
->getCacheKey($feed));
}
}
}