Guzzle 6 to Guzzle 7 Migration Guide
If you’re maintaining a PHP application that uses Guzzle 6, you’ve likely encountered situations where you need more modern features, better standards compliance, or simply want to stay current with PHP ecosystem evolution. Upgrading to Guzzle 7 is a practical decision many teams face—and we’ll walk you through exactly what that process entails, what changes you need to make, and how to approach the migration with confidence.
Before we dive into the specifics, let’s establish some context. Guzzle has been the de facto HTTP client for PHP for well over a decade. It provides a clean, expressive interface for making HTTP requests—a fundamental need for any application that communicates with external APIs or web services. With version 7, Guzzle embraces PHP Standards Recommendations (PSR) more fully than ever before, particularly PSR-18 (HTTP Client) and PSR-7 (HTTP Message Interfaces). For many teams, this means not just an upgrade but a shift toward more interoperable, standards-based code.
In this guide, we’ll cover:
We’ve established what we’re covering—now let’s talk about why you might choose to upgrade. The transition from Guzzle 6 to Guzzle 7 brings several noteworthy changes, though it’s worth noting upfront that the migration is generally straightforward; most codebases require minimal modifications. The key improvements are:
- PSR-18 and PSR-7 Compliance: Guzzle 7 fully implements PSR-18 (HTTP Client) and PSR-7 (HTTP Message Interfaces). What this means for you is greater interoperability—your code can work with other libraries that follow these same standards, and you gain access to a broader ecosystem of middleware and tools. If you’ve ever needed to swap out an HTTP client or integrate with a PSR-compliant library, this change simplifies those scenarios considerably.
- Refined Exception Hierarchy: The exception structure has been reorganized around a common
GuzzleExceptionbase class. This makes error handling more predictable—if you need to catch any Guzzle-related error, you can catch the base exception; if you need more specificity, you can catchRequestException,ConnectException,ClientException(4xx), orServerException(5xx). You’ll find yourself writing cleaner, more intentional exception handling code. - PHP 7.2.5+ Requirement: Guzzle 7 requires at least PHP 7.2.5. For many teams, this coincides with other modern PHP requirements; if your application already runs on PHP 7.4, 8.0, or newer, this isn’t a blocker. Of course, if you’re still on PHP 7.1 or older, you’ll need to plan a PHP upgrade alongside Guzzle—but that’s often a worthwhile endeavor for performance and security reasons anyway.
- Streamlined API: Some legacy options and behaviors have been removed or changed. This isn’t a breaking change for most users; the changes are carefully targeted at edge cases and outdated patterns. The API you use day-to-day—
get(),post(),send(),requestAsync()—remains familiar, just with cleaner internals.
One may wonder: how much work will this migration actually be? For many applications, the answer is surprisingly little—often just updating your composer.json and adjusting a few exception catch blocks. Though we’ll cover everything you need to know, chances are you’ll find the process smoother than you might expect.
Key Changes in Guzzle 7
Before we get into the migration steps, it’s worth understanding what actually changed—and why. Guzzle has evolved considerably since its initial release in 2009. Guzzle 5 brought PSR-7 compatibility; Guzzle 6 focused on performance and HTTP/2 support; and Guzzle 7 brings full PSR-18 compliance alongside a cleanup of legacy patterns accumulated over years of development. Let’s examine the concrete changes you’ll encounter.
PHP Version Requirement
Guzzle 7 requires PHP 7.2.5 or higher. If you are using an older version of PHP, you will need to upgrade your environment before you can use Guzzle 7.
This is a significant change from Guzzle 6, which supported PHP 5.5 and later. Most production PHP applications today run PHP 7.4 or newer—in fact, PHP 7.2 reached end-of-life in November 2021. If you’re still on PHP 7.1 or older, you’ll need to address that first. For context, the jump to PHP 7.2.5+ typically brings meaningful performance improvements and security fixes beyond just satisfying Guzzle’s requirement.
PSR-7 and PSR-18 Compliance
Originally, Guzzle defined its own interfaces for HTTP messages and clients. Over time, the PHP community developed standards through the PHP Framework Interop Group: PSR-7 for HTTP message interfaces (requests, responses, streams) and PSR-18 for HTTP client interfaces. Guzzle 6 was largely PSR-7 compatible, but Guzzle 7 aligns completely with both standards.
What this means in practice: your Guzzle 7 client implements Psr\Http\Client\ClientInterface, and requests/responses implement Psr\Http\Message\RequestInterface and Psr\Http\Message\ResponseInterface. You can type-hint against these interfaces in your own code, making it easier to swap Guzzle for another PSR-18 client if needed. We’ll see this play out in the request() method changes that follow.
Exception Handling
The exception hierarchy has been reorganized in Guzzle 7 to provide a cleaner, more logical structure. In Guzzle 6, RequestException was the base for most operational errors. In Guzzle 7, GuzzleException serves as the universal base, with RequestException and ConnectException as direct children.
Here’s a comparison of the old and new exception hierarchies:
Guzzle 6:
GuzzleHttp\Exception\RequestExceptionGuzzleHttp\Exception\ClientException(4xx errors)GuzzleHttp\Exception\ServerException(5xx errors)GuzzleHttp\Exception\BadResponseException
Guzzle 7:
GuzzleHttp\Exception\GuzzleExceptionGuzzleHttp\Exception\RequestExceptionGuzzleHttp\Exception\ClientException(4xx errors)GuzzleHttp\Exception\ServerException(5xx errors)GuzzleHttp\Exception\BadResponseException
GuzzleHttp\Exception\ConnectException
Practical implications: In Guzzle 6, if you were catching RequestException to handle all HTTP-related errors, you probably didn’t need to change anything—but now you have options. You can catch GuzzleException for everything, or catch more specific exceptions like ConnectException for connection-level problems (timeouts, DNS failures, refused connections). This refinement helps you write more precise error handling.
One may wonder: what happened to BadResponseException? It still exists, but now as a child of RequestException—so it covers non-2xx, non-4xx, non-5xx responses (like 3xx redirects that weren’t followed). This makes the hierarchy more complete.
request() Method and PSR-7 Integration
This is where PSR-7 compliance becomes tangible. In Guzzle 6, Client::request() accepted three arguments: the HTTP method, URI, and an array of options. In Guzzle 7, the method still exists for backward compatibility, but the preferred pattern is to create a Request object explicitly and pass it to send() or sendAsync().
Here’s what we mean:
Guzzle 6 (original pattern):
$client = new GuzzleHttp\Client();
$response = $client->request('GET', '/users', [
'query' => ['foo' => 'bar']
]);
Guzzle 7 (preferred PSR-7 pattern):
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
$client = new Client();
$request = new Request('GET', '/users?foo=bar');
$response = $client->send($request);
A few observations here. First, notice that we’re now explicitly creating a Request object from the guzzlehttp/psr7 package (installed automatically as a dependency of Guzzle 7). Second, the query string is embedded directly in the URI rather than passed as a separate option—this aligns with PSR-7’s design. Third, though the old request() method signature still works for backward compatibility, it’s considered legacy; the Guzzle team recommends using send() with a Request object for new code.
Of course, this raises a practical question: what if you have a lot of existing code using the old pattern? Do you need to rewrite everything at once? The good news is that the old API remains functional. You can migrate gradually, refactoring as you touch each part of your codebase. Though if you’re doing a major version upgrade anyway, it’s a reasonable time to embrace the new pattern—especially since it gives you PSR-7 compatibility benefits. We’ll show both approaches in the migration steps.
Removed and Deprecated Options
Some request options have been removed entirely; others have been renamed. The most common changes:
blocking: This option has been removed. All requests are now blocking by default. For asynchronous requests, you should use therequestAsync()orsendAsync()methods.save_to: This option has been renamed tosink.
Step-by-Step Migration Guide
Now that you understand the key changes, let’s walk through the migration process step by step.
1. Update Your composer.json
The first step is to update your composer.json file to require Guzzle 7.
{
"require": {
"guzzlehttp/guzzle": "^7.0"
}
}
Then, run composer update to install the new version.
2. Update Your Exception Handling
Next, you need to update your exception handling code to reflect the new exception hierarchy.
Guzzle 6:
try {
$client->request('GET', '/');
} catch (\GuzzleHttp\Exception\RequestException $e) {
// Handle the exception
}
Guzzle 7:
try {
$client->request('GET', '/');
} catch (\GuzzleHttp\Exception\GuzzleException $e) {
// Handle the exception
}
It is recommended to catch the more specific exceptions, like ClientException or ServerException, when possible.
3. Update Your Request Options
If you are using any of the removed or deprecated request options, you need to update them.
For example, if you were using the save_to option, you should replace it with sink.
Guzzle 6:
$client->request('GET', '/', [
'save_to' => '/path/to/file'
]);
Guzzle 7:
$client->request('GET', '/', [
'sink' => '/path/to/file'
]);
4. Test Your Application
After making these changes, it’s crucial to thoroughly test your application to ensure that everything is working as expected. Pay close attention to areas where you are making HTTP requests to external services.
Conclusion
Migrating from Guzzle 6 to Guzzle 7 is a straightforward process that offers significant benefits in terms of standards compliance, performance, and developer experience. By following the steps outlined in this guide, you can ensure a smooth and successful migration. Remember to update your composer.json, adjust your exception handling, and update any deprecated request options. And most importantly, don’t forget to test your application thoroughly after the upgrade.
Sponsored by Durable Programming
Need help with your PHP application? Durable Programming specializes in maintaining, upgrading, and securing PHP applications.
Hire Durable Programming