Commit 5058fa38 authored by webchick's avatar webchick

Issue #1447736 by rbayliss, mikeytown2, effulgentsia, kim.pepper, Fabianx,...

Issue #1447736 by rbayliss, mikeytown2, effulgentsia, kim.pepper, Fabianx, boombatower: Adopt Guzzle library to replace drupal_http_request().
parent 7ac0584c
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
"symfony/yaml": "2.1.*", "symfony/yaml": "2.1.*",
"twig/twig": "1.8.*", "twig/twig": "1.8.*",
"doctrine/common": "2.3.*", "doctrine/common": "2.3.*",
"guzzle/http": "3.*",
"kriswallsmith/assetic": "1.1.*" "kriswallsmith/assetic": "1.1.*"
}, },
"minimum-stability": "alpha" "minimum-stability": "alpha"
......
This diff is collapsed.
...@@ -79,6 +79,16 @@ public function build(ContainerBuilder $container) { ...@@ -79,6 +79,16 @@ public function build(ContainerBuilder $container) {
->addArgument(new Reference('database')) ->addArgument(new Reference('database'))
->addArgument(new Reference('keyvalue')); ->addArgument(new Reference('keyvalue'));
$container->register('http_client_simpletest_subscriber', 'Drupal\Core\Http\Plugin\SimpletestHttpRequestSubscriber');
$container->register('http_default_client', 'Guzzle\Http\Client')
->addArgument(NULL)
->addArgument(array(
'curl.CURLOPT_TIMEOUT' => 30.0,
'curl.CURLOPT_MAXREDIRS' => 3,
))
->addMethodCall('addSubscriber', array(new Reference('http_client_simpletest_subscriber')))
->addMethodCall('setUserAgent', array('Drupal (+http://drupal.org/)'));
// Register the EntityManager. // Register the EntityManager.
$container->register('plugin.manager.entity', 'Drupal\Core\Entity\EntityManager'); $container->register('plugin.manager.entity', 'Drupal\Core\Entity\EntityManager');
......
<?php
/**
* @file
* Contains Drupal\Core\Http\Plugin\SimpletestHttpRequestSubscriber
*/
namespace Drupal\Core\Http\Plugin;
use Guzzle\Common\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Subscribe to HTTP requests and override the User-Agent header if the request
* is being dispatched from inside a simpletest.
*/
class SimpletestHttpRequestSubscriber implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return array('request.before_send' => 'onBeforeSendRequest');
}
/**
* Event callback for request.before_send
*/
public function onBeforeSendRequest(Event $event) {
// If the database prefix is being used by SimpleTest to run the tests in a copied
// database then set the user-agent header to the database prefix so that any
// calls to other Drupal pages will run the SimpleTest prefixed database. The
// user-agent is used to ensure that multiple testing sessions running at the
// same time won't interfere with each other as they would if the database
// prefix were stored statically in a file or database variable.
$test_info = &$GLOBALS['drupal_test_info'];
if (!empty($test_info['test_run_id'])) {
$event['request']->setHeader('User-Agent', drupal_generate_test_ua($test_info['test_run_id']));
}
}
}
...@@ -5,6 +5,24 @@ ...@@ -5,6 +5,24 @@
* Install, update and uninstall functions for the aggregator module. * Install, update and uninstall functions for the aggregator module.
*/ */
/**
* Implements hook_requirements().
*/
function aggregator_requirements($phase) {
$t = get_t();
$has_curl = function_exists('curl_init');
$requirements = array();
$requirements['curl'] = array(
'title' => $t('cURL'),
'value' => $has_curl ? $t('Enabled') : $t('Not found'),
);
if (!$has_curl) {
$requirements['curl']['severity'] = REQUIREMENT_ERROR;
$requirements['curl']['description'] = $t('The Aggregator module could not be installed because the PHP <a href="@curl_url">cURL</a> library is not available.', array('@curl_url' => 'http://php.net/manual/curl.setup.php'));
}
return $requirements;
}
/** /**
* Implements hook_schema(). * Implements hook_schema().
*/ */
......
...@@ -23,7 +23,6 @@ function aggregator_aggregator_parse($feed) { ...@@ -23,7 +23,6 @@ function aggregator_aggregator_parse($feed) {
// Filter the input data. // Filter the input data.
if (aggregator_parse_feed($feed->source_string, $feed)) { if (aggregator_parse_feed($feed->source_string, $feed)) {
$modified = empty($feed->http_headers['last-modified']) ? 0 : strtotime($feed->http_headers['last-modified']);
// Prepare the channel data. // Prepare the channel data.
foreach ($channel as $key => $value) { foreach ($channel as $key => $value) {
...@@ -35,14 +34,10 @@ function aggregator_aggregator_parse($feed) { ...@@ -35,14 +34,10 @@ function aggregator_aggregator_parse($feed) {
$image[$key] = trim($value); $image[$key] = trim($value);
} }
$etag = empty($feed->http_headers['etag']) ? '' : $feed->http_headers['etag'];
// Add parsed data to the feed object. // Add parsed data to the feed object.
$feed->link = !empty($channel['link']) ? $channel['link'] : ''; $feed->link = !empty($channel['link']) ? $channel['link'] : '';
$feed->description = !empty($channel['description']) ? $channel['description'] : ''; $feed->description = !empty($channel['description']) ? $channel['description'] : '';
$feed->image = !empty($image['url']) ? $image['url'] : ''; $feed->image = !empty($image['url']) ? $image['url'] : '';
$feed->etag = $etag;
$feed->modified = $modified;
// Clear the page and block caches. // Clear the page and block caches.
cache_invalidate_tags(array('content' => TRUE)); cache_invalidate_tags(array('content' => TRUE));
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
use Drupal\aggregator\Plugin\FetcherInterface; use Drupal\aggregator\Plugin\FetcherInterface;
use Drupal\Core\Annotation\Plugin; use Drupal\Core\Annotation\Plugin;
use Drupal\Core\Annotation\Translation; use Drupal\Core\Annotation\Translation;
use Guzzle\Http\Exception\BadResponseException;
/** /**
* Defines a default fetcher implementation. * Defines a default fetcher implementation.
...@@ -28,44 +29,31 @@ class DefaultFetcher implements FetcherInterface { ...@@ -28,44 +29,31 @@ class DefaultFetcher implements FetcherInterface {
* Implements Drupal\aggregator\Plugin\FetcherInterface::fetch(). * Implements Drupal\aggregator\Plugin\FetcherInterface::fetch().
*/ */
function fetch(&$feed) { function fetch(&$feed) {
$request = drupal_container()->get('http_default_client')->get($feed->url);
$feed->source_string = FALSE; $feed->source_string = FALSE;
// Generate conditional GET headers. // Generate conditional GET headers.
$headers = array();
if ($feed->etag) { if ($feed->etag) {
$headers['If-None-Match'] = $feed->etag; $request->addHeader('If-None-Match', $feed->etag);
} }
if ($feed->modified) { if ($feed->modified) {
$headers['If-Modified-Since'] = gmdate(DATE_RFC1123, $feed->modified); $request->addHeader('If-Modified-Since', gmdate(DATE_RFC1123, $feed->modified));
} }
// Request feed. try {
$result = drupal_http_request($feed->url, array('headers' => $headers)); $response = $request->send();
$feed->source_string = $response->getBody(TRUE);
$feed->etag = $response->getEtag();
$feed->modified = strtotime($response->getLastModified());
$feed->http_headers = $response->getHeaders();
// Process HTTP response code. return TRUE;
switch ($result->code) { }
case 304: catch (BadResponseException $e) {
break; $response = $e->getResponse();
case 301: watchdog('aggregator', 'The feed from %site seems to be broken due to "%error".', array('%site' => $feed->title, '%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase()), WATCHDOG_WARNING);
$feed->url = $result->redirect_url; drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->title, '%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase())));
// Do not break here. return FALSE;
case 200:
case 302:
case 307:
if (!isset($result->data)) {
$result->data = '';
}
if (!isset($result->headers)) {
$result->headers = array();
}
$feed->source_string = $result->data;
$feed->http_headers = $result->headers;
break;
default:
watchdog('aggregator', 'The feed from %site seems to be broken due to "%error".', array('%site' => $feed->title, '%error' => $result->code . ' ' . $result->error), WATCHDOG_WARNING);
drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->title, '%error' => $result->code . ' ' . $result->error)));
} }
return !($feed->source_string === FALSE);
} }
} }
...@@ -162,14 +162,14 @@ public function loadClass($class) ...@@ -162,14 +162,14 @@ public function loadClass($class)
*/ */
public function findFile($class) public function findFile($class)
{ {
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ('\\' == $class[0]) { if ('\\' == $class[0]) {
$class = substr($class, 1); $class = substr($class, 1);
} }
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if (false !== $pos = strrpos($class, '\\')) { if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name // namespaced class name
$classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR;
...@@ -202,6 +202,6 @@ public function findFile($class) ...@@ -202,6 +202,6 @@ public function findFile($class)
return $file; return $file;
} }
$this->classMap[$class] = false; return $this->classMap[$class] = false;
} }
} }
...@@ -17,6 +17,10 @@ ...@@ -17,6 +17,10 @@
'Symfony\\Component\\DependencyInjection' => $vendorDir . '/symfony/dependency-injection/', 'Symfony\\Component\\DependencyInjection' => $vendorDir . '/symfony/dependency-injection/',
'Symfony\\Component\\ClassLoader' => $vendorDir . '/symfony/class-loader/', 'Symfony\\Component\\ClassLoader' => $vendorDir . '/symfony/class-loader/',
'SessionHandlerInterface' => $vendorDir . '/symfony/http-foundation/Symfony/Component/HttpFoundation/Resources/stubs', 'SessionHandlerInterface' => $vendorDir . '/symfony/http-foundation/Symfony/Component/HttpFoundation/Resources/stubs',
'Guzzle\\Stream' => $vendorDir . '/guzzle/stream/',
'Guzzle\\Parser' => $vendorDir . '/guzzle/parser/',
'Guzzle\\Http' => $vendorDir . '/guzzle/http/',
'Guzzle\\Common' => $vendorDir . '/guzzle/common/',
'Doctrine\\Common' => $vendorDir . '/doctrine/common/lib/', 'Doctrine\\Common' => $vendorDir . '/doctrine/common/lib/',
'Assetic' => $vendorDir . '/kriswallsmith/assetic/src/', 'Assetic' => $vendorDir . '/kriswallsmith/assetic/src/',
); );
...@@ -6,9 +6,15 @@ ...@@ -6,9 +6,15 @@
class ComposerAutoloaderInit class ComposerAutoloaderInit
{ {
private static $loader;
public static function getLoader() public static function getLoader()
{ {
$loader = new \Composer\Autoload\ClassLoader(); if (null !== static::$loader) {
return static::$loader;
}
static::$loader = $loader = new \Composer\Autoload\ClassLoader();
$vendorDir = dirname(__DIR__); $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
......
This diff is collapsed.
<?php
namespace Guzzle\Common;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Class that holds an event dispatcher
*/
class AbstractHasDispatcher implements HasDispatcherInterface
{
/**
* @var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* {@inheritdoc}
*/
public static function getAllEvents()
{
return array();
}
/**
* {@inheritdoc}
*/
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
{
$this->eventDispatcher = $eventDispatcher;
return $this;
}
/**
* {@inheritdoc}
*/
public function getEventDispatcher()
{
if (!$this->eventDispatcher) {
$this->eventDispatcher = new EventDispatcher();
}
return $this->eventDispatcher;
}
/**
* {@inheritdoc}
*/
public function dispatch($eventName, array $context = array())
{
$this->getEventDispatcher()->dispatch($eventName, new Event($context));
}
/**
* {@inheritdoc}
*/
public function addSubscriber(EventSubscriberInterface $subscriber)
{
$this->getEventDispatcher()->addSubscriber($subscriber);
return $this;
}
}
<?php
namespace Guzzle\Common;
use Guzzle\Common\Exception\InvalidArgumentException;
/**
* Key value pair collection object
*/
class Collection implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface
{
/**
* @var array Data associated with the object.
*/
protected $data;
/**
* Constructor
*
* @param array $data Associative array of data to set
*/
public function __construct(array $data = null)
{
$this->data = $data ?: array();
}
/**
* Create a new collection from an array, validate the keys, and add default values where missing
*
* @param array $config Configuration values to apply.
* @param array $defaults Default parameters
* @param array $required Required parameter names
*
* @return self
* @throws InvalidArgumentException if a parameter is missing
*/
public static function fromConfig(array $config = null, array $defaults = null, array $required = null)
{
$collection = new self($defaults);
foreach ((array) $config as $key => $value) {
$collection->set($key, $value);
}
foreach ((array) $required as $key) {
if ($collection->hasKey($key) === false) {
throw new InvalidArgumentException("Config must contain a '{$key}' key");
}
}
return $collection;
}
/**
* Add a value to a key. If a key of the same name has already been added, the key value will be converted into an
* array and the new value will be pushed to the end of the array.
*
* @param string $key Key to add
* @param mixed $value Value to add to the key
*
* @return Collection Returns a reference to the object.
*/
public function add($key, $value)
{
if (!array_key_exists($key, $this->data)) {
$this->data[$key] = $value;
} elseif (is_array($this->data[$key])) {
$this->data[$key][] = $value;
} else {
$this->data[$key] = array($this->data[$key], $value);
}
return $this;
}
/**
* Removes all key value pairs
*
* @return Collection
*/
public function clear()
{
$this->data = array();
return $this;
}
/**
* Return the number of keys
*
* @return integer
*/
public function count()
{
return count($this->data);
}
/**
* Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns
* true, the current value from input is returned into the result Collection. The Closure must accept three
* parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value.
*
* @param \Closure $closure Closure evaluation function
* @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection
*
* @return Collection
*/
public function filter(\Closure $closure, $static = true)
{
$collection = ($static) ? new static() : new self();
foreach ($this->data as $key => $value) {
if ($closure($key, $value)) {
$collection->add($key, $value);
}
}
return $collection;
}
/**
* Get an iterator object
*
* @return array
*/
public function getIterator()
{
return new \ArrayIterator($this->data);
}
/**
* Get a specific key value.
*
* @param string $key Key to retrieve.
*
* @return mixed|null Value of the key or NULL
*/
public function get($key)
{
return isset($this->data[$key]) ? $this->data[$key] : null;
}
/**
* Get all or a subset of matching key value pairs
*
* @param array $keys Pass an array of keys to retrieve only a subset of key value pairs
*
* @return array Returns an array of all matching key value pairs
*/
public function getAll(array $keys = null)
{
return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data;
}
/**
* {@inheritdoc}
*/
public function toArray()
{
return $this->data;
}
/**
* Get all keys in the collection
*
* @return array
*/
public function getKeys()
{
return array_keys($this->data);
}
/**
* Returns whether or not the specified key is present.
*
* @param string $key The key for which to check the existence.
*
* @return bool
*/
public function hasKey($key)
{
return array_key_exists($key, $this->data);
}
/**
* Case insensitive search the keys in the collection
*
* @param string $key Key to search for
*
* @return bool|string Returns false if not found, otherwise returns the key
*/
public function keySearch($key)
{
foreach (array_keys($this->data) as $k) {
if (!strcasecmp($k, $key)) {
return $k;
}
}
return false;
}
/**
* Checks if any keys contains a certain value
*
* @param string $value Value to search for
*
* @return mixed Returns the key if the value was found FALSE if the value was not found.
*/
public function hasValue($value)
{
return array_search($value, $this->data);
}
/**
* Returns a Collection containing all the elements of the collection after applying the callback function to each
* one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a
* modified value
*
* @param \Closure $closure Closure to apply
* @param array $context Context to pass to the closure
* @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection
*
* @return Collection
*/
public function map(\Closure $closure, array $context = array(), $static = true)
{
$collection = $static ? new static() : new self();
foreach ($this as $key => $value) {
$collection->add($key, $closure($key, $value, $context));
}
return $collection;
}
/**
* Add and merge in a Collection or array of key value pair data.
*
* @param Collection|array $data Associative array of key value pair data
*
* @return Collection Returns a reference to the object.
*/
public function merge($data)
{
if ($data instanceof self) {
$data = $data->getAll();
} elseif (!is_array($data)) {
return $this;
}
if (empty($this->data)) {
$this->data = $data;
} else {
foreach ($data as $key => $value) {
$this->add($key, $value);
}
}
return $this;
}
/**
* ArrayAccess implementation of offsetExists()
*
* @param string $offset Array key
*
* @return bool
*/
public function offsetExists($offset)
{
return $this->hasKey($offset) !== false;
}
/**
* ArrayAccess implementation of offsetGet()
*
* @param string $offset Array key
*
* @return null|mixed
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* ArrayAccess implementation of offsetGet()
*
* @param string $offset Array key
* @param mixed $value Value to set
*/
public function offsetSet($offset, $value)
{
$this->set($offset, $value);
}
/**
* ArrayAccess implementation of offsetUnset()
*
* @param string $offset Array key
*/
public function offsetUnset($offset)
{
$this->remove($offset);
}