code cleanup - #118
Conversation
There was a problem hiding this comment.
Pull request overview
This PR refactors file upload/download paths to use the SDK’s configured Guzzle client instead of hand-rolled curl calls, and updates tests to assert the outgoing HTTP requests.
Changes:
- Replaced
curl-based download logic inExportandSupportingDocumentTemplatewithConfiguration::getHttpClient()->request(...)andsink. - Reworked
EncryptedFile::upload()to use Guzzle multipart requests and removed the custom multipart builder (and the$boundaryparameter). - Added/updated unit tests to validate download/upload behavior via a configured Guzzle handler stack.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/SupportingDocumentTemplateTest.php | Adds a test asserting SupportingDocumentTemplate::download() uses the configured Guzzle client and writes to disk. |
| tests/EncryptedFileTest.php | Adds a test asserting multipart upload goes through the configured Guzzle client and adjusts signature expectations. |
| src/Item/SupportingDocumentTemplate.php | Replaces curl download with Guzzle request() + sink. |
| src/Item/Export.php | Replaces curl export download with Guzzle; simplifies gzip decompression using stream wrappers. |
| src/Item/EncryptedFile.php | Replaces manual multipart body assembly with Guzzle multipart; returns UploadResult from response. |
| src/Configuration.php | Stores the configured Guzzle client for reuse; removes getApiKey() / getBaseUri() helpers. |
Suppressed comments (2)
src/Item/EncryptedFile.php:69
- EncryptedFile::upload() no longer handles transport-level failures (DNS/connection timeouts, etc.). Guzzle will throw a GuzzleException in these cases, whereas the previous curl implementation returned false/0 and still produced an UploadResult. To keep behavior consistent and avoid unexpected exceptions for SDK consumers, catch RequestException/GuzzleException and convert it into an UploadResult (using the response if present).
$response = \Didww\Configuration::getHttpClient()->request('POST', $url, [
'headers' => [
'Api-Key' => $apiKey,
'X-DIDWW-API-Version' => $apiVersion,
'Accept' => 'application/json',
],
'multipart' => $multipart,
'http_errors' => false,
]);
return new UploadResult((string) $response->getBody(), $response->getStatusCode());
src/Configuration.php:51
- Configuration removed public getApiKey() and getBaseUri() methods. If these were part of the supported public API, this is a breaking change for SDK users. Consider reintroducing them as
@deprecatedwrappers to preserve backwards compatibility.
self::$documentClient = new \Swis\JsonApi\Client\DocumentClient($client, $responseParser);
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $destHandle = is_resource($dest) ? $dest : fopen($dest, 'w'); | ||
|
|
||
| try { | ||
| \Didww\Configuration::getHttpClient()->request('GET', $this->getAttributes()['url'], [ | ||
| 'sink' => $destHandle, | ||
| 'allow_redirects' => true, | ||
| // HTTP code >= 400 will throw a GuzzleException, same as CURLOPT_FAILONERROR before. | ||
| ]); | ||
| } catch (\GuzzleHttp\Exception\GuzzleException $e) { | ||
| return $e->getMessage(); | ||
| } |
| public static function getHttpClient(): \GuzzleHttp\ClientInterface | ||
| { | ||
| return self::$httpClient; | ||
| } |
| public static function upload(string $fingerprint, string $fileContent, ?string $description = null): UploadResult | ||
| { | ||
| $apiKey = \Didww\Configuration::getApiKey(); | ||
| $apiKey = \Didww\Configuration::getCredentials()->getApiKey(); | ||
| $apiVersion = \Didww\Configuration::getCredentials()->getVersion() ?? '2026-04-16'; | ||
| $baseUri = \Didww\Configuration::getBaseUri(); | ||
| $baseUri = \Didww\Configuration::getCredentials()->getEndpoint(); | ||
| $url = $baseUri.'/encrypted_files'; |
69396c0 to
2da2fe4
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (3)
src/Item/Export.php:132
- The request relies on the Guzzle client's default
http_errorssetting. If consumers pass['http_errors' => false]intoConfiguration::configure(), HTTP 4xx/5xx responses will no longer raise an exception and$errorwill incorrectly remainnull(changing behavior vs the previousCURLOPT_FAILONERROR). Explicitly enablehttp_errorsfor this request (and adjust the inline comment accordingly).
'sink' => $destHandle,
'allow_redirects' => true,
// HTTP code >= 400 will throw a GuzzleException, same as CURLOPT_FAILONERROR before.
]);
src/Item/Export.php:136
- Only
GuzzleExceptionis caught here. Any other throwable fromrequest()(e.g., invalid URL/URI or invalid sink) will bubble up as an exception, which breaks the method’s current “return true|string” error-reporting behavior. Consider catching\ThrowableafterGuzzleExceptionand returning the message like before.
} catch (\GuzzleHttp\Exception\GuzzleException $e) {
$error = $e->getMessage();
}
src/Configuration.php:26
getHttpClient()promises to returnClientInterface, butself::$httpClientis uninitialized untilconfigure()runs, so calling this early will currently produce aTypeError(returning null). Consider adding an explicit guard with a clear exception message to make misconfiguration easier to diagnose.
public static function getHttpClient()
{
return self::$httpClient;
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
src/Item/Export.php:126
download()implementation switched from cURL to Guzzle streaming, but the existing Export tests only cover the success path. It would be good to add a regression test for a failed HTTP response (e.g., 404/500) to assert thatdownload()returns an error and does not leave an error body written into the destination file.
try {
\Didww\Configuration::getHttpClient()->request('GET', $this->getAttributes()['url'], [
'headers' => [
'Api-Key' => $apiKey,
'User-Agent' => 'didww-php-sdk/'.\Didww\Client::sdkVersion(),
| try { | ||
| \Didww\Configuration::getHttpClient()->request('GET', $this->getAttributes()['url'], [ | ||
| 'headers' => [ | ||
| 'Api-Key' => $apiKey, | ||
| 'User-Agent' => 'didww-php-sdk/'.\Didww\Client::sdkVersion(), | ||
| 'X-DIDWW-API-Version' => \Didww\Configuration::getCredentials()->getVersion() ?? '2026-04-16', | ||
| ], | ||
| 'sink' => $destHandle, | ||
| 'allow_redirects' => true, | ||
| // HTTP code >= 400 will throw a GuzzleException, same as CURLOPT_FAILONERROR before. | ||
| ]); | ||
| $error = null; | ||
| } catch (\GuzzleHttp\Exception\GuzzleException $e) { | ||
| $error = $e->getMessage(); | ||
| } |
…lient() Adds the seam needed by Export::download()/downloadAndDecompress(), which route through the already-configured HTTP client instead of hand-rolling their own curl setup.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/Item/Export.php:126
download()relies on the configured Guzzle client defaults. If a consumer configures the client withhttp_errors => false, 4xx/5xx responses will no longer fail (curl previously usedCURLOPT_FAILONERROR). Also, if the exporturlattribute isnull/empty, Guzzle can throw a non-GuzzleException(e.g.,InvalidArgumentException), which currently bypasses the catch and can crash the call. Consider validating the URL and forcinghttp_errorsfor this request to preserve the previous behavior.
try {
\Didww\Configuration::getHttpClient()->request('GET', $this->getAttributes()['url'], [
'headers' => [
'Api-Key' => $apiKey,
'User-Agent' => 'didww-php-sdk/'.\Didww\Client::sdkVersion(),
src/Item/Export.php:138
- The comment about Guzzle's
sinkclosing the underlying resource is misleading here. Since this method accepts either a path or an already-open resource, the intent is: close the handle only when this method opened it, and leave caller-provided resources open.
// Guzzle's 'sink' stream wrapper already closes the underlying resource once
// the response body has been written to it.
Replace hand-rolled curl_init/curl_setopt_array/curl_exec with Guzzle's 'sink' request option via the shared configured HTTP client. Guzzle's sink stream wrapper closes the destination resource itself once the response body is written, so the method no longer needs (and would error on) its own fclose() for that resource.
…ecompress() Replace the manual gzopen/gzread(8192)/fwrite copy loop with PHP's built-in compress.zlib:// stream wrapper plus stream_copy_to_stream(), which does the same chunked read/write internally. Existing testDownloadAndDecompress already exercises the decompressed output, so no new test was needed.
|



Fixes a real gap: Export::download()/downloadAndDecompress() built their own curl handle from scratch, bypassing whatever the consumer passed to Configuration::configure($credentials, $httpClientConfig) — proxy, timeout, SSL options, custom middleware all silently didn't apply to downloads (and downloads had no timeout at all, so they could hang indefinitely). Routes both methods through the SDK's shared, already-configured Guzzle client instead, via a new Configuration::getHttpClient() seam, so consumer HTTP config now actually applies everywhere. Not a line-count reduction (net +7 lines — the seam is new code). No behavior changes for the happy path, all tests pass.