# PHP SDK best practices

**Recommended practices for configuring, running, and maintaining the PHP SDK.**

## Keep the SDK up to date

Always use a recent version of the SDK to ensure you have the latest features, performance improvements, and security fixes.

To stay informed about new releases, watch the [GitHub repository](https://github.com/commercetools/commercetools-sdk-php-v2) or check [Packagist](https://packagist.org/packages/commercetools/commercetools-sdk) for new releases.

## Store credentials securely

Never hardcode credentials such as `clientId` and `clientSecret` in your source code. Always load them from environment variables or a secrets management service at runtime.

```php title="Load credentials from environment variables"
$authConfig = new ClientCredentialsConfig(
  new ClientCredentials(
    getenv('CTP_CLIENT_ID'),
    getenv('CTP_CLIENT_SECRET'),
    getenv('CTP_SCOPES')
  ),
  [],
  getenv('CTP_AUTH_URL') . '/oauth/token'
);
```

## Reuse the client

Building a client sets up an HTTP client, OAuth configuration, and any middleware you add. Create it once and reuse the same instance across requests instead of building a new client every time.

```php title="Build the client once and cache the instance"
class ClientService
{
    private ?ApiRequestBuilder $apiClient = null;

    public function getApiClient(): ResourceByProjectKey
    {
        if (!$this->apiClient) {
            $authConfig = new ClientCredentialsConfig(
                new ClientCredentials(
                    getenv('CTP_CLIENT_ID'),
                    getenv('CTP_CLIENT_SECRET'),
                    getenv('CTP_SCOPES')
                ),
                [],
                getenv('CTP_AUTH_URL') . '/oauth/token'
            );

            $client = ClientFactory::of()->createGuzzleClient(
                new Config([], getenv('CTP_API_URL')),
                $authConfig
            );

            $this->apiClient = new ApiRequestBuilder($client);
        }

        return $this->apiClient->withProjectKey(getenv('CTP_PROJECT_KEY'));
    }
}
```

If you're using a framework such as Laravel, register the class as a singleton so the container reuses one instance within the current application lifecycle:

```php title="Register the client service as a singleton in Laravel"
$this->app->singleton(ClientService::class, function ($app) {
    return new ClientService();
});
```

If your application runs as a long-lived worker process, such as Swoole or RoadRunner, instead of a traditional per-request PHP process, a cached client can reuse connections across requests and encounter the same kind of stale-connection errors as long-running services in other languages. Guzzle's cURL handler generally recovers from a reset connection automatically. If you see intermittent connection errors, recreate the client periodically or turn off connection reuse for the affected requests.

## Configure default middleware

The factory automatically installs correlation IDs. It adds authentication and reauthentication middleware when you pass a `ClientCredentialsConfig`, and logging middleware when you pass a PSR-3 logger as the third argument to `createGuzzleClient`. See the [middleware reference](/dev-tooling/php-sdk-middleware.md#middlewarefactory) for details.

```php title="Configure middleware when creating the client"
$client = ClientFactory::of()->createGuzzleClient(
  new Config([], getenv('CTP_API_URL')),
  $authConfig,
  $logger
);
```

## Logging

The SDK accepts any [PSR-3](https://www.php-fig.org/psr/psr-3/) compatible logger. If you're using a framework, pass its existing logger instead of creating a separate one, so SDK requests appear alongside the rest of your application logs:

```php title="Reuse your framework's logger"
use Illuminate\Support\Facades\Log;

$client = ClientFactory::of()->createGuzzleClient(
  new Config([], getenv('CTP_API_URL')),
  $authConfig,
  Log::getLogger()
);
```

## Handle errors safely

The SDK throws [ApiClientException](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-base/src/Exception/ApiClientException.php) for recoverable client errors (HTTP status codes `4xx`) and [ApiServerException](https://github.com/commercetools/commercetools-sdk-php-v2/blob/master/lib/commercetools-base/src/Exception/ApiServerException.php) for server errors (HTTP status codes `5xx`). Both extend Guzzle's `ClientException` and `ServerException`, so `getResponse()->getStatusCode()` returns the HTTP status, and `getResult()` returns the deserialized error body.

Catch these exceptions and map them to your own response. Avoid returning the caught exception object directly, since it can expose internal details such as the original request to API consumers.

```php title="Map SDK exceptions to a safe response"
use Commercetools\Exception\ApiClientException;
use Commercetools\Exception\ApiServerException;

try {
    $product = $apiRoot->products()->withId($id)->get()->execute();
} catch (ApiClientException | ApiServerException $e) {
    return response()->json([
        'message' => 'The commercetools API request failed.',
    ], $e->getResponse()->getStatusCode());
}
```

For more specific error handling, catch one of the SDK's narrower exception subclasses instead, such as `NotFoundException` or `ConcurrentModificationException`.

## Retrieve the raw API response

Use `execute()` when you want the SDK to deserialize the response into a model. Use `send()` and `getBody()` when you need the raw JSON, for example to log the full payload or pass it through to another system unchanged.

```php title="Get the raw API response"
$response = $apiRoot
  ->products()
  ->get()
  ->send();

$rawJson = (string) $response->getBody();
```

## Further resources

- [Middleware reference](/dev-tooling/php-sdk-middleware.md): configure retry, logging, correlation ID, and authentication middleware
- [Integration tests](https://github.com/commercetools/commercetools-sdk-php-v2/tree/master/test/integration/Api): working examples for common SDK operations

## Related pages

- [Area overview page with navigation](/dev-tooling.md)
- [Previous page: Middleware](/dev-tooling/php-sdk-middleware.md)
- [Search documentation and API specs](/search.md)