16: The Claude PHP SDK

Chapter 16: The Claude PHP SDK
Section titled “Chapter 16: The Claude PHP SDK”Overview
Section titled “Overview”When building production applications with Claude in PHP, choosing the right SDK is critical. While Anthropic released an official SDK (claude-php/claude-php-sdk), it is currently in beta and has seen limited maintenance. In contrast, the community-driven Claude-PHP-SDK (claude-php/claude-php-sdk) has emerged as the standard for PHP developers.
This chapter explores why the community SDK is the preferred choice, its architecture (which mirrors the official Python SDK), and how to build robust applications using it.
Why Not the Official SDK?
Section titled “Why Not the Official SDK?”The official claude-php/claude-php-sdk package, while initially promising, has several drawbacks for production use:
- Maintenance Status: The official SDK is in beta and often lags behind API updates.
- Feature Parity: It lacks some features found in the official Python and TypeScript SDKs.
- Community Support: The PHP ecosystem has rallied around
claude-php/claude-php-sdkdue to its active development and faster release cycle.
The Claude-PHP-SDK aims for 1-to-1 feature parity with the official Python SDK, ensuring that PHP developers have access to the same capabilities, including latest model support, streaming, and tool use.
What You’ll Build
Section titled “What You’ll Build”By the end of this chapter, you will have created:
- A fully configured Claude client using the community SDK
- A robust service layer that handles logging and error management
- A streaming implementation for real-time responses
- Comprehensive tests using mocked clients
- A configuration strategy for production environments
Prerequisites
Section titled “Prerequisites”Before diving in, ensure you have:
- ✓ PHP 8.4+
- ✓ Composer installed
- ✓ Anthropic API key
- ✓ Completed Chapter 03: Your First Claude Request
Estimated Time: 45-60 minutes
Installing the SDK
Section titled “Installing the SDK”Remove the official SDK if you have it installed, and require the community package:
# Remove official SDK (if present)composer remove claude-php/claude-php-sdk
# Install the community Claude-PHP-SDKcomposer require claude-php/claude-php-sdkVerify installation:
composer show claude-php/claude-php-sdkSDK Architecture
Section titled “SDK Architecture”The Claude-PHP-SDK is designed to be simple and intuitive. Unlike the complex factory pattern of the official SDK, it uses a straightforward client instantiation approach.
Basic Usage
Section titled “Basic Usage”<?phpdeclare(strict_types=1);
require 'vendor/autoload.php';
use ClaudePhp\ClaudePhp;
// Simple instantiation$client = new ClaudePhp( apiKey: $_ENV['ANTHROPIC_API_KEY']);
// Direct method calls$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 100, 'messages' => [ ['role' => 'user', 'content' => 'Say hello from the SDK!'] ]]);
// Response is object-accessibleecho $response->content[0]->text;Why It Works
Section titled “Why It Works”The ClaudePhp class is the main entry point. It handles authentication, HTTP communication, and response parsing. The messages()->create() method maps directly to the /v1/messages endpoint.
Unlike the official SDK which returns typed objects, the community SDK returns array-accessible responses, giving you raw access to the data structure while still providing helper methods where necessary.
Working with Responses
Section titled “Working with Responses”The SDK returns responses that are easy to work with standard PHP array functions.
<?phpdeclare(strict_types=1);
require 'vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp(apiKey: $_ENV['ANTHROPIC_API_KEY']);
$response = $client->messages()->create([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [['role' => 'user', 'content' => 'Analyze PHP 8.4']]]);
// 1. Accessing Content$text = $response->content[0]->text;
// 2. Checking Usage (Cost Tracking)$inputTokens = $response->usage->inputTokens;$outputTokens = $response->usage->outputTokens;
// 3. Metadata$id = $response->id;$stopReason = $response->stopReason; // e.g., 'end_turn' or 'max_tokens'
echo "Generated {$outputTokens} tokens. Stop reason: {$stopReason}\n";Streaming Responses
Section titled “Streaming Responses”One of the most powerful features of the SDK is support for streaming. This allows you to process generation events in real-time, essential for chat interfaces.
<?phpdeclare(strict_types=1);
require 'vendor/autoload.php';
use ClaudePhp\ClaudePhp;
$client = new ClaudePhp(apiKey: $_ENV['ANTHROPIC_API_KEY']);
// Use messages()->stream() method for streaming responses$stream = $client->messages()->stream([ 'model' => 'claude-sonnet-4-5-20250929', 'max_tokens' => 1024, 'messages' => [['role' => 'user', 'content' => 'Write a haiku about code.']]]);
echo "Streaming response:\n";
foreach ($stream as $event) { // Handle content updates if (($event['type'] ?? null) === 'content_block_delta') { echo $event['delta']['text'] ?? ''; flush(); // Force output to terminal }}echo "\n";Why It Works
Section titled “Why It Works”The messages()->stream() method returns a generator that yields Server-Sent Events (SSE) as they arrive from Claude’s streaming API. You can iterate over this generator to handle events like message_start, content_block_delta (text chunks), and message_stop in real-time.
Service Layer Pattern
Section titled “Service Layer Pattern”Since the SDK doesn’t use a complex middleware system out of the box, the best practice is to wrap the client in a service class. This allows you to add logging, caching, and application-specific logic.
<?phpdeclare(strict_types=1);
namespace App\Services;
use ClaudePhp\ClaudePhp;use Psr\Log\LoggerInterface;
class ClaudeService{ public function __construct( private ClaudePhp $client, private LoggerInterface $logger ) {}
public function generate(string $prompt, array $options = []): string { $model = $options['model'] ?? 'claude-sonnet-4-5-20250929';
$this->logger->info('Claude Request', [ 'model' => $model, 'prompt_length' => strlen($prompt) ]);
try { $response = $this->client->messages()->create([ 'model' => $model, 'max_tokens' => $options['max_tokens'] ?? 1024, 'messages' => [ ['role' => 'user', 'content' => $prompt] ] ]);
$this->logger->info('Claude Response', [ 'id' => $response->id, 'tokens' => $response->usage ]);
return $response->content[0]->text;
} catch (\Exception $e) { $this->logger->error('Claude API Error', ['error' => $e->getMessage()]); throw $e; } }}Why It Works
Section titled “Why It Works”Wrapping the external SDK in your own service class (the Adapter Pattern) decouples your application from the specific SDK implementation. If you ever need to switch SDKs again or add global behavior like rate limiting, you only need to update this service class.
Error Handling
Section titled “Error Handling”The SDK throws specific exceptions that allow you to handle different failure scenarios gracefully.
<?phpdeclare(strict_types=1);
use ClaudePhp\ClaudePhp;use ClaudePhp\Exceptions\{ APIConnectionError, RateLimitError, AuthenticationError, APIStatusError};
$client = new ClaudePhp(apiKey: $_ENV['ANTHROPIC_API_KEY']);
try { $client->messages()->create([/* ... */]);} catch (RateLimitError $e) { // Handle 429 Too Many Requests // Implement backoff/retry logic here error_log("Rate limit hit: " . $e->getMessage());} catch (AuthenticationError $e) { // Handle invalid API key error_log("Authentication failed: " . $e->getMessage());} catch (APIConnectionError $e) { // Handle network/timeout issues error_log("Connection error: " . $e->getMessage());} catch (APIStatusError $e) { // Handle other 4xx/5xx errors error_log("API Error {$e->status_code}: {$e->message}");} catch (\Exception $e) { // Handle unexpected errors error_log("Unexpected error: " . $e->getMessage());}Testing with the SDK
Section titled “Testing with the SDK”Since the client is a simple class, you can mock it using PHPUnit’s mocking capabilities or wrap it in an interface.
<?phpdeclare(strict_types=1);
use PHPUnit\Framework\TestCase;use ClaudePhp\ClaudePhp;use App\Services\ClaudeService;use Psr\Log\LoggerInterface;
class ClaudeServiceTest extends TestCase{ public function testGenerateText(): void { // Mock the Client $mockClient = $this->createMock(ClaudePhp::class);
// Define expected response structure (as object) $mockResponse = (object) [ 'id' => 'msg_123', 'content' => [(object) ['text' => 'Mocked response']], 'usage' => (object) ['inputTokens' => 10, 'outputTokens' => 5] ];
// Mock the messages() method to return a mock that has create() $mockMessages = $this->createMock(\stdClass::class); $mockMessages->expects($this->once()) ->method('create') ->with($this->callback(function ($params) { return isset($params['model']) && str_contains($params['model'], 'claude-sonnet'); })) ->willReturn($mockResponse);
$mockClient->expects($this->once()) ->method('messages') ->willReturn($mockMessages);
// Mock Logger $mockLogger = $this->createMock(LoggerInterface::class);
// Instantiate service $service = new ClaudeService($mockClient, $mockLogger);
$result = $service->generate('Test prompt');
$this->assertEquals('Mocked response', $result); }}Best Practices Summary
Section titled “Best Practices Summary”- Prefer the Community SDK: It is actively maintained and follows the Python SDK’s feature set.
- Use Service Wrappers: Wrap the
ClaudePhpin your own classes to add application-specific logic. - Handle Object Responses: Remember that responses are objects, not arrays. Use
$response->content[0]->text. - Use Environment Variables: Never hardcode API keys; use
$_ENV['ANTHROPIC_API_KEY']for security. - Leverage Streaming: Use
$client->messages()->stream([...])method for real-time responses.
Troubleshooting
Section titled “Troubleshooting””Class ClaudePHP\ClaudePhp not found”
Section titled “”Class ClaudePHP\ClaudePhp not found””- Run
composer dump-autoload. - Ensure you installed
claude-php/claude-php-sdkand notclaude-php/claude-php-sdk.
”Undefined array key ‘text’”
Section titled “”Undefined array key ‘text’””- Responses can contain multiple content blocks. Always check
$response->content[0]->textor iterate through content blocks. - Ensure the API call succeeded before accessing keys.
”SSL Certificate Errors”
Section titled “”SSL Certificate Errors””- If you encounter SSL issues locally, ensure your PHP installation has a valid
curl.cainfoconfiguration.
Further Reading
Section titled “Further Reading”- Claude-PHP-SDK GitHub — Official repository and documentation
- Anthropic API Reference — API endpoint details
- Packagist Package — Version history and installation
Continue to Chapter 17: Building a Claude Service Class to apply these patterns in a robust architecture.