purewater2011 / claude-code-sdk
PHP SDK for Claude Code
Installs: 2
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/purewater2011/claude-code-sdk
Requires
- php: >=7.4
- ext-json: *
- symfony/process: ^4.4|^5.0|^6.0|^7.0
Requires (Dev)
- orchestra/testbench: ^6.0|^7.0
- pestphp/pest: ^1.22
- phpstan/phpstan: ^1.4
- spatie/ray: ^1.28
Suggests
- laravel/framework: Required for Laravel integration (^10.0|^11.0|^12.0)
This package is auto-updated.
Last update: 2025-10-09 08:18:51 UTC
README
PHP wrapper for Claude Code. Fun fact: This SDK was built by using Claude Code to port its own Python SDK to PHP. 🤖
Stream Claude's responses, track token usage, hook into events, and integrate AI coding assistance into your PHP apps.
Installation
composer require purewater2011/claude-code-sdk
Make sure Claude Code CLI is installed:
npm install -g @anthropic-ai/claude-code
PHP 7.4+ Compatibility
This SDK is fully compatible with PHP 7.4 and higher using modern PHP patterns:
Key Features
Fluent Interface - Chain methods for clean configuration:
$options = Options::create() ->systemPrompt('You are a helpful assistant') ->allowedTools(['Read', 'Write']) ->maxTurns(5);
PermissionMode - Use static methods:
PermissionMode::default() // Default permission mode PermissionMode::acceptEdits() // Auto-accept edits PermissionMode::bypassPermissions() // Bypass all permission checks
MCP Server Config - Easy server configuration:
MCPServerConfig::stdio('node', ['server.js']) MCPServerConfig::sse('https://api.example.com/sse') MCPServerConfig::http('https://api.example.com/mcp')
Note: All examples in this README use the fluent interface pattern, which is the recommended approach for PHP 7.4+. See PHP74_MIGRATION.md for detailed compatibility information.
Quick Start
Basic Usage
<?php use Purewater2011\ClaudeCode\ClaudeCode; use Purewater2011\ClaudeCode\Types\Messages\AssistantMessage; use Purewater2011\ClaudeCode\Types\Messages\SystemMessage; use Purewater2011\ClaudeCode\Types\Messages\ResultMessage; use Purewater2011\ClaudeCode\Types\ContentBlocks\TextBlock; // Send a query to Claude $messages = ClaudeCode::query("What files are in the current directory?"); // Process the streaming response foreach ($messages as $message) { if ($message instanceof SystemMessage) { echo "[SYSTEM] {$message->subtype}\n"; } elseif ($message instanceof AssistantMessage) { foreach ($message->content as $block) { if ($block instanceof TextBlock) { echo "[CLAUDE] {$block->text}\n"; } } } elseif ($message instanceof ResultMessage) { echo "[DONE] Cost: \${$message->totalCostUsd} | Time: {$message->durationMs}ms\n"; } }
With Options
use Purewater2011\ClaudeCode\ClaudeCode; use Purewater2011\ClaudeCode\Types\Config\Options; use Purewater2011\ClaudeCode\Types\Enums\PermissionMode; // Create options using fluent interface $options = Options::create() ->systemPrompt('You are a helpful coding assistant') ->allowedTools(['Read', 'Write', 'Edit']) ->permissionMode(PermissionMode::acceptEdits()) ->maxTurns(5); /** * A generator is returned, allowing you to stream messages as they are generated. * @var Generator<Purewater2011\ClaudeCode\Types\Messages\Message> $messages */ $messages = ClaudeCode::query("Help me refactor this code", $options); foreach ($messages as $message) { if ($message instanceof AssistantMessage) { foreach ($message->content as $block) { if ($block instanceof TextBlock) { echo "[CLAUDE] {$block->text}\n"; } } } elseif ($message instanceof ResultMessage) { echo "[DONE] Total Cost: \${$message->totalCostUsd}\n"; } }
Usage in Laravel
Configuration
Publish the configuration file:
php artisan vendor:publish --tag=claude-code-config
Configure your settings in config/claude-code.php
or use environment variables:
CLAUDE_CODE_SYSTEM_PROMPT="You are a Laravel expert" CLAUDE_CODE_ALLOWED_TOOLS=Read,Write,Edit CLAUDE_CODE_PERMISSION_MODE=acceptEdits CLAUDE_CODE_MODEL=claude-3-sonnet
Using the Facade
use Purewater2011\ClaudeCode\Laravel\Facades\ClaudeCode; // Simple query $messages = ClaudeCode::query("Create a new Laravel controller"); // With custom options $options = ClaudeCode::options() ->systemPrompt("You are a Laravel expert") ->allowedTools(['Read', 'Write', 'Edit']) ->maxTurns(10); $messages = ClaudeCode::query("Help me build a REST API", $options);
Dependency Injection
use Illuminate\Support\Facades\App; class MyService { private $claude; public function __construct() { $this->claude = App::make('claude-code'); } public function generateCode(string $prompt): void { $messages = $this->claude->query($prompt); foreach ($messages as $message) { // Process messages... } } }
Configuration Options
Options
Configure Claude Code using the fluent interface:
use Purewater2011\ClaudeCode\Types\Config\Options; use Purewater2011\ClaudeCode\Types\Config\MCPServerConfig; use Purewater2011\ClaudeCode\Types\Enums\PermissionMode; $options = Options::create() ->systemPrompt('You are a helpful assistant') ->appendSystemPrompt('Always be concise') ->allowedTools(['Read', 'Write', 'Edit', 'Bash']) ->disallowedTools(['Delete']) ->permissionMode(PermissionMode::acceptEdits()) ->permissionPromptToolName('MyCustomPrompt') ->continueConversation(true) ->resume('session-abc123') ->maxTurns(10) ->model('claude-3-sonnet') ->cwd('/path/to/project') ->mcpServers([ 'my-server' => MCPServerConfig::stdio('node', ['server.js']) ]);
Available Options:
Method | Description | Example |
---|---|---|
systemPrompt(string) |
Set context for Claude | ->systemPrompt('You are a PHP expert') |
appendSystemPrompt(string) |
Append to system prompt | ->appendSystemPrompt('Be concise') |
allowedTools(array) |
Tools Claude can use | ->allowedTools(['Read', 'Write']) |
disallowedTools(array) |
Tools Claude cannot use | ->disallowedTools(['Bash']) |
permissionMode(PermissionMode) |
Permission handling | ->permissionMode(PermissionMode::acceptEdits()) |
maxTurns(int) |
Max conversation turns | ->maxTurns(10) |
model(string) |
Claude model to use | ->model('claude-3-sonnet') |
cwd(string) |
Working directory | ->cwd('/path/to/project') |
mcpServers(array) |
MCP server configs | See MCP section below |
interceptors(array) |
Event interceptors | See Interceptors section below |
## Message Types
The SDK provides strongly-typed message classes with proper DTOs:
### SystemMessage
System events with typed data for initialization:
```php
use Purewater2011\ClaudeCode\Types\Messages\SystemMessage;
use Purewater2011\ClaudeCode\Types\Config\SystemInitData;
if ($message instanceof SystemMessage && $message->subtype === 'init') {
// Strongly typed init data
$initData = $message->data; // SystemInitData instance
echo "Session ID: {$initData->sessionId}\n";
echo "Model: {$initData->model}\n";
echo "Tools: " . implode(', ', $initData->tools) . "\n";
echo "Working Directory: {$initData->cwd}\n";
}
AssistantMessage
Contains content blocks with Claude's responses:
use Purewater2011\ClaudeCode\Types\Messages\AssistantMessage; use Purewater2011\ClaudeCode\Types\ContentBlocks\TextBlock; use Purewater2011\ClaudeCode\Types\ContentBlocks\ToolUseBlock; use Purewater2011\ClaudeCode\Types\ContentBlocks\ToolResultBlock; foreach ($message->content as $block) { if ($block instanceof TextBlock) { echo "Text: {$block->text}\n"; } elseif ($block instanceof ToolUseBlock) { echo "Tool: {$block->name} with " . json_encode($block->input) . "\n"; } elseif ($block instanceof ToolResultBlock) { echo "Result: {$block->content} (Error: " . ($block->isError ? 'Yes' : 'No') . ")\n"; } }
UserMessage
User input and tool feedback:
use Purewater2011\ClaudeCode\Types\Messages\UserMessage; use Purewater2011\ClaudeCode\Types\ContentBlocks\ToolResultBlock; // Simple text message $message = new UserMessage("Hello Claude!"); // Tool feedback (from Claude's perspective) if (is_array($message->content)) { foreach ($message->content as $block) { if ($block instanceof ToolResultBlock) { echo "Tool feedback: {$block->content}\n"; echo "Tool ID: {$block->toolUseId}\n"; echo "Is Error: " . ($block->isError ? 'Yes' : 'No') . "\n"; } } }
ResultMessage
Session completion with usage metrics:
use Purewater2011\ClaudeCode\Types\Messages\ResultMessage; echo "Total Cost: \${$message->totalCostUsd}\n"; echo "Duration: {$message->durationMs}ms\n"; echo "API Time: {$message->durationApiMs}ms\n"; echo "Turns: {$message->numTurns}\n"; echo "Session ID: {$message->sessionId}\n"; echo "Input Tokens: {$message->usage['input_tokens']}\n"; echo "Output Tokens: {$message->usage['output_tokens']}\n";
MCP Server Configuration
Configure Model Context Protocol servers:
use Purewater2011\ClaudeCode\Types\Config\Options; use Purewater2011\ClaudeCode\Types\Config\MCPServerConfig; $options = Options::create() ->mcpServers([ // Stdio server 'filesystem' => MCPServerConfig::stdio( 'node', ['mcp-server-filesystem.js'], ['NODE_ENV' => 'production'] ), // SSE server 'weather' => MCPServerConfig::sse( 'https://api.example.com/mcp/sse', ['Authorization' => 'Bearer token'] ), // HTTP server 'database' => MCPServerConfig::http( 'https://api.example.com/mcp', ['API-Key' => 'secret'] ) ]);
MCP Server Types:
// Stdio server (local process) MCPServerConfig::stdio( 'node', // command ['server.js'], // args ['NODE_ENV' => 'production'] // env (optional) ); // SSE server (server-sent events) MCPServerConfig::sse( 'https://api.example.com/sse', // url ['Authorization' => 'Bearer token'] // headers (optional) ); // HTTP server MCPServerConfig::http( 'https://api.example.com/mcp', // url ['API-Key' => 'secret'] // headers (optional) );
Interceptors
Note: This is NOT related to or the same as Claude Code Hooks
The SDK supports interceptors that allow you to tap into various events during the Claude Code lifecycle. This is useful for logging, monitoring, debugging, or building real-time UI updates.
Available Hook Points
onQueryStart
- Fired when a query beginsonRawMessage
- Fired when raw JSON is received from Claude Code CLIonMessageParsed
- Fired after a message is parsed into a typed objectonQueryComplete
- Fired when the query completes successfullyonError
- Fired when an error occurs
Basic Usage
use Purewater2011\ClaudeCode\ClaudeCode; use Purewater2011\ClaudeCode\Types\Config\Options; // Simple logging interceptor $logger = function($event, $data) { echo "[{$event}] " . json_encode($data) . PHP_EOL; }; $options = Options::create() ->interceptors([$logger]); $messages = ClaudeCode::query("Help me code", $options);
Example Interceptors
File Logger
use Purewater2011\ClaudeCode\Examples\Interceptors\FileLoggerInterceptor; $options = Options::create() ->interceptors([ new FileLoggerInterceptor('/tmp/claude.log') ]);
Metrics Collector
use Purewater2011\ClaudeCode\Examples\Interceptors\MetricsInterceptor; $options = Options::create() ->interceptors([ new MetricsInterceptor() // Tracks token usage, costs, and timing ]);
Webhook Dispatcher
use Purewater2011\ClaudeCode\Examples\Interceptors\WebhookInterceptor; $options = Options::create() ->interceptors([ new WebhookInterceptor('https://api.example.com/claude-events') ]);
Custom Interceptor
// Token usage tracker $totalTokens = 0; $tokenTracker = function($event, $data) use (&$totalTokens) { if ($event === 'onMessageParsed' && $data['message'] instanceof ResultMessage) { $totalTokens += $data['message']->usage['input_tokens'] ?? 0; $totalTokens += $data['message']->usage['output_tokens'] ?? 0; } }; // Real-time progress indicator $progressTracker = function($event, $data) { switch ($event) { case 'onQueryStart': echo "Starting query: {$data['prompt']}\n"; break; case 'onMessageParsed': echo "."; // Progress dot for each message break; case 'onQueryComplete': echo "\nQuery completed!\n"; break; } }; $options = Options::create() ->interceptors([$tokenTracker, $progressTracker]);
Laravel Integration
// In a service provider $this->app->bind(Options::class, function ($app) { $interceptors = []; // Add logging if enabled if (config('claude-code.logging.enabled')) { $interceptors[] = new FileLoggerInterceptor( storage_path('logs/claude-code.log') ); } // Add metrics collection if (config('claude-code.metrics.enabled')) { $interceptors[] = new MetricsInterceptor(); } return Options::create() ->interceptors($interceptors) ->systemPrompt(config('claude-code.system_prompt')) ->model(config('claude-code.model')); });
Error Handling
The SDK provides specific exception types for different failure scenarios:
use Purewater2011\ClaudeCode\ClaudeCode; use Purewater2011\ClaudeCode\Exceptions\CLINotFoundException; use Purewater2011\ClaudeCode\Exceptions\CLIConnectionException; use Purewater2011\ClaudeCode\Exceptions\ProcessException; try { $messages = ClaudeCode::query("Help me code"); } catch (CLINotFoundException $e) { echo "Claude Code CLI not found. Install with: npm install -g @anthropic-ai/claude-code"; } catch (CLIConnectionException $e) { echo "Failed to connect to Claude Code: {$e->getMessage()}"; } catch (ProcessException $e) { echo "Process failed with exit code {$e->exitCode}: {$e->stderr}"; }
License
This SDK is open-source software licensed under the MIT license.