Lass die Nutzer des SDK ihren eigenen HTTP-Client nutzen

· Mathieu Santostefano · 5 Minuten zum Lesen
3 dog heads

Befreie dich von starren Abhängigkeiten in deinen PHP-SDKs. Erfahre, wie du die Standards PSR-7, PSR-17 und PSR-18 zusammen mit PHP-HTTP/Discovery nutzt, um deinen Benutzern die Verwendung ihres bevorzugten HTTP-Clients zu ermöglichen – sei es Guzzle, Symfony HttpClient oder ein anderes Tool. Ein Muss für PHP- und Symfony-Entwickler.

Dieser Artikel behandelt praktische Techniken für SDK-Entwickler, die es Benutzern ermöglichen, ihren eigenen HTTP-Client zu verwenden, statt ihnen die Nutzung eines Clients aufzuzwingen, den sie nicht gewählt haben. Er richtet sich auch an Benutzer, die ihren bevorzugten HTTP-Client innerhalb eines SDKs verwenden möchten, das diesen bereits unterstützt.

Du kennst wahrscheinlich den Dschungel der HTTP-Clients, die im PHP-Ökosystem existieren. Guzzle, Symfony HttpClient, Buzz, CakePHP HTTP usw. Als SDK-Maintainer ist es ein Albtraum, Support für all diese Pakete bereitzustellen. Glücklicherweise gibt es Abstraktionsschichten und Techniken, die uns dabei helfen, die große Mehrheit davon zu berücksichtigen. Lass uns gemeinsam herausfinden, wie das funktioniert!

Genesis

Vor ein paar Monaten besuchte ich den AFUP Day 2025, eine eintägige französische Konferenz. Insbesondere besuchte ich einen Vortrag meines Freundes Nicolas Grekas über HTTP-Clients in PHP. Der Vortrag war nicht ganz so, wie ich ihn mir vorgestellt hatte. Er versetzte das Publikum in die Lage eines SDK-Entwicklers, der vor der Herausforderung steht, einen HTTP-Client in seine Quellcode-Abhängigkeiten zu integrieren.

  • Welchen HTTP-Client?

  • Was, wenn ein SDK-Benutzer bereits einen HTTP-Client in seinen Abhängigkeiten installiert hat?

  • Wie kann ein SDK-Benutzer seinen eigenen HTTP-Client anstelle des von mir bereitgestellten verwenden?

Die meisten dieser Fragen hat Nicolas auf elegante Weise beantwortet. Lass uns in dieses faszinierende Thema eintauchen!

Hier ist eine Aufzeichnung seines Vortrags von der API Platform Con 2024:

API Platform Conference 2024 - Nicolas Grekas - Consuming HTTP APIs in PHP the Right Way!

Statte dich mit den richtigen Werkzeugen aus

Zunächst möchte ich dir einige PSRs (PHP Standard Recommendations) sowie einige nicht so „magische” PHP-Pakete vorstellen, die uns bei unserer Suche helfen können.

PSR

Zunächst sind hier einige PSRs. Falls du damit nicht vertraut bist, betrachte sie als beliebte technische Empfehlungen für Bibliotheksentwickler und deinen eigenen Anwendungscode. Diese PSRs decken viele Themen ab, z. B. Codierungsstil, Autoloading, Cache, Container, Clock und das, woran wir heute interessiert sind: HTTP.

PSR-7: HTTP Message

Diese beschreibt Schnittstellen für Request und Response (sowie einige „Untertypen“ wie Stream, UploadedFile usw.) und wird von PSR-18 als Argumenttyp und Rückgabetyp für die Methode sendRequest verwendet.

PSR-18: HTTP Client

Diese PSR kann wie folgt zusammengefasst werden: Es wird eine gemeinsame Schnittstelle zum Senden von PSR-7-Anfragen und Zurückgeben von PSR-7-Antworten bereitgestellt.

Sie stellt eine Client-Schnittstelle und drei Exception-Schnittstellen bereit, um verschiedene Arten von Fehlern darzustellen.

PSR-17: HTTP Factories

Diese PSR stellt Factories für alle HTTP-Nachrichtenschnittstellen bereit, die in PSR-7 definiert sind. Wie diese Factories genutzt werden können, sehen wir weiter unten.

PHP-Pakete

PSR/HTTP-Client

Dieses Paket enthält den Code im Zusammenhang mit PSR-18 (ClientInterface und drei Exception-Schnittstellen). Genau das brauchen wir, um unseren Code zu typisieren und uns nicht auf eine konkrete Implementierung zu verlassen.

php-http/discovery

Es handelt sich dabei sowohl um eine Codebibliothek als auch um ein Composer-Plugin (seit Version 1.17). Es ist für die automatische Erkennung und Installation von Paketen zuständig, die konkrete Implementierungen von PSR-17- und PSR-18-Schnittstellen bereitstellen.

Es funktioniert zusammen mit den nächsten beiden Paketen.

psr/http-client-implementation & psr/http-factory-implementation

Diese beiden Pakete sind etwas Besonderes, da es sich um virtuelle Pakete handelt. Sie existieren nur im Packagist-Register und werden von „echten” Paketen verwendet, um anzuzeigen, dass sie konforme Implementierungen für PSR-17- und/oder PSR-18-Schnittstellen bereitstellen.

Ein kurzer Hinweis zum Konzept der virtuellen Pakete: Stelle es dir wie PHP-Interfaces vor, aber auf Paketebene. In diesem Beispiel zeigt das virtuelle Paket psr/http-client-implementation in einer SDK-composer.json an, dass das SDK jedes Paket benötigt, das eine konkrete Implementierung von PSR-18 bereitstellt.

Da „psr/http-client” direkt von „psr/http-message” abhängt, müssen wir nicht davon abhängig sein, um auf Nachrichtenschnittstellen wie RequestInterface, ResponseInterface usw. zu verweisen.

Eine letzte nützliche Eigenschaft ist die provide-Eigenschaft des Composer-Schemas. Hier ist eine konkrete Verwendung dieser Eigenschaft:

  • Maintainer des Pakets symfony/http-client können diese Eigenschaft in die „composer.json”-Datei aufnehmen, um anderen Entwicklern mitzuteilen, dass das Paket eine konkrete PSR-18-Implementierung bereitstellt.

"provide": {
    "psr/http-client-implementation": "1.0",
},
  • Als SDK-Entwickler kannst du das virtuelle Paket psr/http-client-implementation zusammen mit php-http/discovery anfordern. Letzteres sorgt als Plugin dafür, dass das SDK eine PSR-18-Implementierung benötigt. Es wird dann in der composer.json der Anwendung, in der es installiert ist, nach einer suchen, indem es die provide-Eigenschaft jeder Abhängigkeit liest.

  • Ein Bild sagt bekanntlich mehr als tausend Worte. Hier ist eines:

Scheme showing how HTTP client works with PSRsDank PHP-HTTP/HTTP-Discovery, das von Foo/SDK benötigt wird, können die Entwickler beider Anwendungen lediglich ihren bevorzugten HTTP-Client und Foo/SDK anfordern. Die Erkennungsfunktion sucht nach jedem Paket, das von der composer.json der Anwendung benötigt wird und psr/http-client-implementation bereitstellt.

Um die Erklärung zu vereinfachen, lasse ich das virtuelle Paket psr/http-factory-implementation hier bewusst weg, das Prinzip ist jedoch dasselbe.

Bereite deinen Code vor

SDK-Codebeispiel

<?php

use Http\Discovery\Psr17Factory;
use Http\Discovery\Psr18ClientDiscovery;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;

namespace Foo\SDK;

final readonly class Api
{
    private const string BASE_URI = 'https://example.com';

    public function __construct(
        private ?ClientInterface $client = null,
        private ?RequestFactoryInterface $requestFactory = null,
        private ?StreamFactoryInterface $streamFactory = null,
    ) {
        $this->client = $client ?: Psr18ClientDiscovery::find();

        $this->factory = new Psr17Factory(
            requestFactory: $requestFactory,
            streamFactory: $streamFactory,
        );
    }

    public function callApi(array $data): ResponseInterface
    {
        $body = $this->factory->createStream(json_encode($data));

        $request = $this->factory->createRequest('POST', self::BASE_URI . '/api/bar')
            ->withHeader('Content-Type', 'application/json')
            ->withBody($body)
        ;

        return $this->client->sendRequest($request);
    }
}

Hier ist ein grundlegendes Beispiel einer Foo-Klasse, die von einem SDK bereitgestellt wird und in ihrem eigenen Code PSR-18- sowie PSR-17-Schnittstellen verwendet. Dadurch haben SDK-Benutzer die Freiheit, ihren eigenen HTTP-Client mitzubringen.

  • automatisch, dank Psr18ClientDiscovery::find()

  • oder manuell durch Übergabe eines $client-Arguments an den Konstruktor.

Beachte, dass Psr17Factory intern Psr17FactoryDiscovery-Methoden aufruft, um vorhandene konkrete Implementierungen benötigter Factories zu finden, falls null an den Konstruktor übergeben wird.

Code-Beispiel des Benutzers

<?php

namespace App;

use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpClient\Psr18Client;

final readonly class FooRelatedService
{
    public function callBar(array $data): void
    {
        // Inside Foo/SDK/Api, Psr18ClientDiscovery will seek for a concrete implementation of PSR-18
        $fooApi = new Foo\SDK\Api();
        $response = $foo->callApi($data);

        // your logic
    }

    public function callBarWithMyOwnHttpClient(array $data): void
    {
        // Standalone instanciation, but dependency injection could be used here.
        $myOwnHttpClient = new Psr18Client(HttpClient::create());

        $fooApi = new Foo\SDK\Api(client: $myOwnHttpClient);
        $response = $foo->callApi($data);

        // your logic
    }

    public function callBarWithMyOwnHttpClientAndFactories(array $data): void
    {
        // Psr18Client implements ClientInterface, RequestFactoryInterface, StreamFactoryInterface and others
        $myOwnHttpClientAndFactories = new Psr18Client(HttpClient::create());

        $fooApi = new Foo\SDK\Api(
            client: $myOwnHttpClientAndFactories,
            requestFactory: $myOwnHttpClientAndFactories
            streamFactory: $myOwnHttpClientAndFactories
        );
        $response = $foo->callApi($data);

        // your logic
    }
}

Es folgt ein einfaches Beispiel für den Code eines SDK-Benutzers. Dabei lässt das SDK den passenden HTTP-Client selbst finden (Methode callBar) oder es wird manuell ein Client übergeben (Methode callBarWithMyOwnHttpClient). Es ist sogar möglich, den Client und die Factories zu übergeben (Methode callBarWithMyOwnHttpClientAndFactories). Bevor du diese Objekte übergibst, kannst du sie selbstverständlich für deine Bedürfnisse konfigurieren (Basis-URI, Header usw.).

Solange das SDK auf Schnittstellen basiert, steht es dem SDK-Benutzer völlig frei, eigene Schnittstellenimplementierungen zu erstellen und diese an das SDK-API-Objekt zu übergeben.

Weiterführende Informationen

Selbstverständlich haben wir hier nur an der Oberfläche des Themas gekratzt. Während der Entwicklung eines SDKs können viele weitere Fragen aufkommen, insbesondere, wenn du dich zu einem bestimmten Zeitpunkt auf eine spezifische Funktion eines HTTP-Clients verlassen musst, die nicht von PSR-18 abgedeckt wird.

Du könntest dich beispielsweise fragen, was passiert, wenn ein SDK-Benutzer keinen kompatiblen HTTP-Client in seinen Anwendungsabhängigkeiten hat. Solltest du standardmäßig einen bereitstellen? Welchen? Wie kann man ihn bereitstellen? Wie testet man als SDK-Maintainer seinen Code?

Wenn dir dieser Artikel gefallen hat und du tiefer in das Thema eintauchen möchtest, lass es uns wissen! Vielleicht folgt ein zweiter Teil, um dieses erste Kapitel zu ergänzen!

Ressourcen

Du möchtest diese Strategie in dein eigenes SDK implementieren, weißt aber nicht, wie du anfangen sollst?

Der Entwickler von Symfony, SensioLabs, bietet spezialisierte Beratung und Schulungen an. Damit unterstützt er Ihr Team dabei, Best Practices für die moderne PHP-Entwicklung und HTTP-Client-Integration zu erlernen.

Das könnte dich auch interessieren

Fabien Potencier
Elise Hamimi

SymfonyCon Amsterdam 2025: Unser Rückblick und die Highlights

Nach einer legendären ersten Ausgabe im Jahr 2019 feierte die SymfonyCon ihr großes Comeback in Amsterdam. Von Anfang an war die Energie einer mit Spannung erwarteten Konferenz zu spüren: mehr als 1.200 Teilnehmer, 39 Nationalitäten, das größte Treffen der Symfony-Community des Jahres, großartige Entdeckungen ... und eine ausgelassene Atmosphäre. Dieses Jahr war etwas ganz Besonderes, denn es war das 20-jährige Jubiläum von Symfony. SensioLabs war dabei: Wir berichten Ihnen ausführlich über unsere Erfahrungen dort!

Mehr erfahren
The SensioLabs team celebrating the 20th anniversary of Symfony with balloons
Jules Daunay

Die Geschichte geht weiter: SensioLabs feiert 20 Jahre Symfony

Die Zeit vergeht wie im Flug – besonders, wenn man an der Zukunft der Entwicklung schreibt! Das SensioLabs-Team hat gerade die 20 Kerzen des Symfony-Frameworks ausgeblasen. Wir haben den Anlass im Büro gefeiert, doch die Party ist noch nicht vorbei. Das Datum für eine XXL-Feier steht bereits fest: die SymfonyCon Amsterdam vom 27. bis 28. November 2025.

Mehr erfahren
PHP 8.5 URI extension
Oskar Stark

PHP 8.5's neue URI-Erweiterung: Ein Game-Changer für URL-Parsing

PHP 8.5 führt eine leistungsstarke neue URI-Erweiterung ein, die die URL-Verarbeitung modernisiert. Mit Unterstützung für RFC 3986 und WHATWG-Standards bietet die neue Uri-Klasse unveränderliche Objekte, fluent Interfaces und korrekte Validierung - und behebt alle Einschränkungen der veralteten parse_url()-Funktion. Dieser Leitfaden zeigt praktische Vorher/Nachher-Beispiele und erklärt, wann welcher Standard zu verwenden ist.

Mehr erfahren
Open in new tab
Silas Joisten

Die Tab-Falle: Warum das Erzwingen neuer Tabs eine schlechte UX ist

Wir haben es alle schon getan — target="_blank" zu einem Link hinzugefügt, um „Benutzern zu helfen", auf unserer Website zu bleiben. Aber was sich wie eine harmlose Bequemlichkeit anfühlt, führt oft zu Verwirrung, beeinträchtigt die Barrierefreiheit und birgt versteckte Sicherheitsrisiken.

Mehr erfahren
Blue sign on a building with several Now What? letters
Thibaut Chieux

Wie man Nachrichten beim Aufbau asynchroner Anwendungen mit dem Symfony-Messenger priorisiert

Die asynchrone Verarbeitung bietet Vorteile wie entkoppelte Prozesse und schnellere Reaktionszeiten. Die Verwaltung von Nachrichtenprioritäten kann jedoch zu einer Herausforderung werden. Bei Aufgaben, die vom Zurücksetzen von Passwörtern bis hin zu komplexen Exporten reichen, ist die rechtzeitige Zustellung kritischer Nachrichten unerlässlich. Dieser Artikel befasst sich mit häufigen Problemen bei der asynchronen Verarbeitung und zeigt Lösungen mit Symfony Messenger auf, mit denen Sie Ihre Anwendung ohne umfangreiches Refactoring optimieren können.

Mehr erfahren
SensioLabs University Courses announcing the new level 3 Master training course now available
Jules Daunay

Wir stellen vor: Mastering Symfony 7

Wenn du deine Symfony-Kenntnisse verbessern möchtest, ist der neue Level-3 Trainingskurs bei SensioLabs vielleicht das Richtige für dich! Du meisterst komplexe Themen, optimierst die Leistung und wirst zum Symfony-Experten.

Mehr erfahren
PHP 8.5
Oskar Stark

Was ist neu in PHP 8.5: Ein umfassender Überblick

PHP 8.5 wird im November 2025 veröffentlicht und bringt mehrere nützliche neue Features und Verbesserungen mit sich. Diese Version konzentriert sich auf die Verbesserung der Developer Experience, neue Utility-Funktionen und bessere Debugging-Möglichkeiten.

Mehr erfahren
Two images: on the left many cars stuck in a traffic jam with the sign "All directions" above, on the right a blue car moving forward alone on the highway with the sign "Service Subscriber" and a Symfony logo above
Steven Renaux

Symfony Lazy Services mit Stil – Steigere deine Entwicklererfahrung mit Service Subscribers

Steigere die Performance und Developer Experience (DX) deiner Symfony-App! Erfahre, wie du Service Subscribers und Traits für das verzögerte Laden von Services verwendest, um die sofortige Instanziierung zu reduzieren, Abhängigkeiten zu vereinfachen und modularen, wartbaren Code zu schreiben.

Mehr erfahren
Image