Zend\Feed\Reader, Guzzle und PSR-7

Zend\Feed ist eine mächtige Bibliothek zum Auffinden, Lesen und Schreiben von RSS-Feeds in verschiedenen Formaten (uns interessieren hier nur das Auffinden und Lesen). Standardmäßig verwendet Zend\Feed\Reader\Reader – eine Sammlung statischer Methoden für den Zugriff auf die Funktionalität zum Lesen und Auffinden von Feeds – einen Zend\Http\Client, um die HTTP-Kommunikation abzuwickeln. Leider hat Zend\Http\Client einige Probleme im praktischen Einsatz, etwa: „Incorrect handling of negative MaxAge cookie attribute“ oder „ChunkedBody decoding with Curl Adapter“ oder einige andere.
Deshalb haben wir versucht, Zend\Feed\Reader\Reader mit einer anderen HTTP-Client-Bibliothek zu integrieren, die bereits Teil des Projekts war: Guzzle. Guzzle ist ein leistungsfähiger HTTP-Client und unterstützt PSR-7 – den PHP-FIG-Standard für HTTP-Message-Interfaces. Auf Basis dieses Standards funktionierte die Integration wie am Schnürchen, denn in Zend\Feed\Reader ist eine kaum dokumentierte PSR-7-Kompatibilitätsschicht enthalten – der Zend\Feed\Reader\Http\Psr7ResponseDecorator.
Um Guzzle in Zend\Feed\Reader\Reader zu integrieren, war allerdings nur eine Klasse nötig – eine Implementierung von Zend\Feed\Reader\Http\ClientInterface, einem der einfachsten Interfaces, die man sich vorstellen kann:
namespace Zend\Feed\Reader\Http;
interface ClientInterface
{
/**
* Make a GET request to a given URI
*
* @param string $uri
* @return ResponseInterface
*/
public function get($uri);
}
Unser AppBundle\Feed\GuzzleClient fiel am Ende so einfach aus:
namespace AppBundle\Feed;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface as GuzzleClientInterface;
use Zend\Feed\Reader\Http\ClientInterface as FeedReaderHttpClientInterface;
use Zend\Feed\Reader\Http\Psr7ResponseDecorator;
/**
* Class GuzzleClient
*
* @package AppBundle\Feed
*/
class GuzzleClient implements FeedReaderHttpClientInterface
{
/**
* @var GuzzleClientInterface
*/
private $client;
/**
* GuzzleClient constructor.
*
* @param GuzzleClientInterface|null $client
*/
public function __construct(GuzzleClientInterface $client = null)
{
$this->client = $client ?: new Client();
}
/**
* {@inheritdoc}
*/
public function get($uri)
{
$response = $this->client->request('GET', $uri);
return new Psr7ResponseDecorator($response);
}
}
GuzzleHttp\ClientInterface::request() liefert ein Psr\Http\Message\ResponseInterface zurück, das direkt in eine Instanz von Zend\Feed\Reader\Http\Psr7ResponseDecorator gesteckt werden kann.
Nun konnten wir den HTTP-Client von Zend\Feed\Reader\Reader einfach durch unseren neuen Guzzle-Decorator ersetzen:
Zend\Feed\Reader\Reader::setHttpClient(new AppBundle\Feed\GuzzleClient());
Das war’s – das Auffinden von Feeds (Zend\Feed\Reader\Reader::findFeedLinks($uri)) und das Lesen von Feeds (Zend\Feed\Reader\Reader::import($uri)) kommunizieren nun über die robustere Guzzle-Bibliothek statt über Zend\Http\Client.