Files
michaelschiemer/docs/claude/curl-oop-api.md
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

997 lines
28 KiB
Markdown

# cURL OOP API
Object-orientierte cURL API Implementation basierend auf PHP RFC: Object-oriented curl API v2.
## Übersicht
Die cURL OOP API bietet eine moderne, typsichere Alternative zu den prozeduralen curl_* Funktionen. Die Implementation folgt dem offiziellen PHP RFC und erweitert diesen um Framework-spezifische Patterns.
**RFC Referenz**: https://wiki.php.net/rfc/curl_oop_v2
## Architektur
```
┌─────────────────────┐
│ HttpClient │ ← Public API (unchanged)
│ Interface │
└──────────┬──────────┘
┌──────────▼──────────┐
│ CurlHttpClient │ ← Uses Handle instead of curl_*
└──────────┬──────────┘
┌──────────▼──────────┐
│ CurlRequestBuilder │ ← Returns HandleOption enums
└──────────┬──────────┘
┌──────────▼──────────────────────────────────┐
│ Curl\Handle │ ← OOP wrapper for curl_*
│ - fetch(): string │
│ - execute(resource|callable): void │
│ - setOption(HandleOption, mixed): self │
│ - getInfo(?Info): mixed │
│ - pause(Pause): void │
│ - reset(): void │
│ - escapeUrl(string): string │
│ - unescapeUrl(string): string │
│ - upkeep(): void │
└──────────────────────────────────────────────┘
├─── HandleOption enum (150+ cases)
├─── Info enum (50+ cases)
├─── Pause enum (6 cases)
└─── HandleException
```
## Core Components
### 1. Curl\Handle
**Location**: `src/Framework/HttpClient/Curl/Handle.php`
**Purpose**: Object-orientierter Wrapper für einzelne cURL Handles
**Key Features**:
- **Asymmetric Visibility**: `public private(set)` für Error-Properties
- **Exception-based**: Keine `false` returns, nur Exceptions
- **Fluent API**: Method chaining für bessere Lesbarkeit
- **Type Safety**: Automatische Type-Validierung für Options
- **Readonly**: Unveränderliche Klasse nach Konstruktion
**Usage**:
```php
// Simple GET Request
$handle = new Handle('https://api.example.com/users');
$handle->setOption(HandleOption::Timeout, 10);
$response = $handle->fetch();
// Fluent API
$response = (new Handle())
->setOption(HandleOption::Url, 'https://api.example.com')
->setOption(HandleOption::UserAgent, 'CustomPHP/1.0')
->setOption(HandleOption::Timeout, 10)
->fetch();
// Multiple Options
$handle->setOptions([
HandleOption::Url => 'https://api.example.com',
HandleOption::Timeout => 10,
HandleOption::FollowLocation => true,
]);
// Error Handling
try {
$response = $handle->fetch();
} catch (HandleException $e) {
// Access error details
$errorNumber = $handle->errorNumber; // public private(set)
$errorMessage = $handle->errorMessage; // public private(set)
}
```
**Methods**:
- `__construct(?string $uri = null)` - Initialisiert Handle mit optionaler URL
- `fetch(): string` - Führt Request aus und gibt Response-Body zurück
- `execute(resource|callable $out): void` - Führt Request aus und schreibt in Resource/Callback
- `setOption(HandleOption $opt, mixed $value): self` - Setzt einzelne Option
- `setOptions(array $options): self` - Setzt multiple Options
- `getInfo(?Info $option = null): mixed` - Holt Request-Informationen
- `pause(Pause $flag): void` - Pausiert/Continued Transfer
- `reset(): void` - Setzt alle Options zurück
- `escapeUrl(string $string): string` - URL-encoded String
- `unescapeUrl(string $string): string` - URL-decoded String
- `upkeep(): void` - Connection upkeep checks
- `getResource(): CurlHandle` - Holt underlying Resource (für Kompatibilität)
**Properties**:
- `public private(set) int $errorNumber` - curl_errno() Equivalent
- `public private(set) string $errorMessage` - curl_error() Equivalent
- `private readonly CurlHandle $handle` - Underlying Resource
### 2. Curl\HandleOption
**Location**: `src/Framework/HttpClient/Curl/HandleOption.php`
**Purpose**: Type-safe Enum für alle CURLOPT_* Konstanten
**Features**:
- **150+ Option Cases**: Alle CURLOPT_* Konstanten als Enum Cases
- **Type Checking**: Built-in Methoden für Type-Validierung
- **IDE Support**: Auto-completion für alle Options
- **Kategorisiert**: Grouped by functionality
**Categories**:
- URL & Request: `Url`, `Port`, `CustomRequest`, `UserAgent`, `Referer`
- HTTP Methods: `HttpGet`, `Post`, `Put`, `Upload`, `Nobody`
- Request Body: `PostFields`, `ReadData`, `InFile`, `InFileSize`
- Headers: `HttpHeader`, `Header`, `HeaderFunction`
- Response: `ReturnTransfer`, `File`, `WriteFunction`
- Authentication: `HttpAuth`, `UserPwd`, `Username`, `Password`
- SSL/TLS: `SslVerifyPeer`, `SslVerifyHost`, `CaInfo`, `SslCert`
- Timeouts: `Timeout`, `TimeoutMs`, `ConnectTimeout`, `ConnectTimeoutMs`
- Redirects: `FollowLocation`, `MaxRedirs`, `AutoReferer`
- Proxy: `Proxy`, `ProxyPort`, `ProxyType`, `ProxyAuth`
- Cookies: `Cookie`, `CookieFile`, `CookieJar`
- Network: `Interface`, `DnsServers`, `BufferSize`
- Debug: `Verbose`, `Stderr`, `FailOnError`
**Type Checking Methods**:
```php
$opt = HandleOption::Timeout;
$opt->expectsBoolean(); // false
$opt->expectsInteger(); // true
$opt->expectsString(); // false
$opt->expectsArray(); // false
$opt->expectsCallable(); // false
$opt->expectsResource(); // false
$opt->getName(); // "Timeout"
```
**Usage**:
```php
// Instead of:
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // ❌ Magic constant
// Use:
$handle->setOption(HandleOption::Timeout, 10); // ✅ Type-safe enum
```
### 3. Curl\Info
**Location**: `src/Framework/HttpClient/Curl/Info.php`
**Purpose**: Type-safe Enum für alle CURLINFO_* Konstanten
**Available Info Cases**:
- Response: `ResponseCode`, `HttpCode`, `EffectiveUrl`
- Timing: `TotalTime`, `NameLookupTime`, `ConnectTime`, `PreTransferTime`, `StartTransferTime`
- Size: `SizeDownload`, `SizeUpload`, `HeaderSize`, `RequestSize`
- Speed: `SpeedDownload`, `SpeedUpload`
- SSL: `SslVerifyResult`, `ProxySslVerifyResult`, `CertInfo`
- Content: `ContentType`, `ContentLengthDownload`, `ContentLengthUpload`
- Network: `PrimaryIp`, `PrimaryPort`, `LocalIp`, `LocalPort`
- Redirects: `RedirectCount`, `RedirectUrl`, `RedirectTime`
**Usage**:
```php
// Single info
$statusCode = $handle->getInfo(Info::ResponseCode);
$totalTime = $handle->getInfo(Info::TotalTime);
// All info
$allInfo = $handle->getInfo();
```
### 4. Curl\Pause
**Location**: `src/Framework/HttpClient/Curl/Pause.php`
**Purpose**: Type-safe Enum für CURLPAUSE_* Konstanten
**Cases**:
- `Recv` - Pause receiving
- `RecvCont` - Continue receiving
- `Send` - Pause sending
- `SendCont` - Continue sending
- `All` - Pause both
- `Cont` - Continue both
**Usage**:
```php
$handle->pause(Pause::Recv); // Pause receiving
$handle->pause(Pause::RecvCont); // Continue receiving
```
### 5. Curl\MultiHandle
**Location**: `src/Framework/HttpClient/Curl/MultiHandle.php`
**Purpose**: Parallele Ausführung mehrerer Requests
**Key Features**:
- **Parallel Execution**: Multiple Requests gleichzeitig
- **High-Level API**: `executeAll()` für einfache Verwendung
- **Low-Level API**: `execute()`, `select()`, `getInfo()` für Fine-Tuning
- **Resource Management**: Automatisches Cleanup
**Usage**:
```php
// Variadic Constructor: Pass handles directly (NEW!)
$handle1 = new Handle('https://api.example.com/users/1');
$handle2 = new Handle('https://api.example.com/users/2');
$handle3 = new Handle('https://api.example.com/users/3');
$multi = new MultiHandle($handle1, $handle2, $handle3);
$completed = $multi->executeAll();
foreach ([$handle1, $handle2, $handle3] as $handle) {
$response = $handle->fetch(); // Already executed
$status = $handle->getInfo(Info::ResponseCode);
}
// Alternative: Add handles later
$multi = new MultiHandle();
$handles = [];
foreach ($urls as $url) {
$handle = new Handle($url);
$handles[] = $handle;
$multi->addHandle($handle);
}
$completed = $multi->executeAll();
foreach ($handles as $handle) {
$response = $handle->fetch(); // Already executed
$status = $handle->getInfo(Info::ResponseCode);
}
// Low-Level: Fine-Grained Control
$multi = new MultiHandle();
$stillRunning = 0;
do {
$multi->execute($stillRunning);
if ($stillRunning > 0) {
$multi->select();
}
while ($info = $multi->getInfo($messagesInQueue)) {
// Process completed transfers
}
} while ($stillRunning > 0);
```
**Methods**:
- `__construct(Handle ...$handles)` - Erstellt MultiHandle mit optionalen Handles (NEW!)
- `addHandle(Handle $handle): self` - Fügt Handle hinzu
- `removeHandle(Handle $handle): self` - Entfernt Handle
- `execute(int &$stillRunning): self` - Führt alle Handles aus
- `select(float $timeout = 1.0): int` - Wartet auf Activity
- `getInfo(int &$messagesInQueue): array|false` - Holt Completion-Info
- `executeAll(): array<Handle>` - High-level: Führt alle aus und wartet
- `getHandles(): array<Handle>` - Alle angehängten Handles
- `count(): int` - Anzahl angehängter Handles
- `setOption(int $option, mixed $value): self` - Setzt Multi-Option
- `onComplete(\Closure $callback): self` - Callback für erfolgreiche Requests (NEW!)
- `onError(\Closure $callback): self` - Callback für fehlerhafte Requests (NEW!)
- `onProgress(\Closure $callback): self` - Callback für Progress Updates (NEW!)
**Event Callbacks (NEW!)**:
Die MultiHandle-Klasse unterstützt jetzt Event-basierte Callbacks für real-time Processing während der parallelen Ausführung.
```php
// onComplete Callback: Wird für jeden erfolgreichen Request aufgerufen
$multi = new MultiHandle();
$multi->onComplete(function(Handle $handle) {
$url = $handle->getInfo(Info::EffectiveUrl);
$status = $handle->getInfo(Info::ResponseCode);
$time = $handle->getInfo(Info::TotalTime);
echo "{$url} - {$status} ({$time}s)\n";
});
foreach ($urls as $url) {
$multi->addHandle(new Handle($url));
}
$multi->executeAll(); // Callbacks werden während Execution getriggert
// onError Callback: Wird für Requests mit Fehlern aufgerufen
$multi->onError(function(Handle $handle, int $errorCode) {
$url = $handle->getInfo(Info::EffectiveUrl);
$error = curl_strerror($errorCode);
error_log("{$url} - Error {$errorCode}: {$error}");
});
// onProgress Callback: Real-time Progress Updates
$totalRequests = 100;
$multi->onProgress(function(int $completed, int $total) {
$percent = round(($completed / $total) * 100);
echo "\rProgress: {$completed}/{$total} ({$percent}%)";
});
// Alle Callbacks kombinieren für comprehensive Monitoring
$multi = new MultiHandle();
$multi
->onComplete(function(Handle $handle) {
// Log successful requests
$this->logger->info('Request completed', [
'url' => $handle->getInfo(Info::EffectiveUrl),
'status' => $handle->getInfo(Info::ResponseCode),
'time' => $handle->getInfo(Info::TotalTime)
]);
})
->onError(function(Handle $handle, int $errorCode) {
// Alert on errors
$this->alerting->error('Request failed', [
'url' => $handle->getInfo(Info::EffectiveUrl),
'error_code' => $errorCode,
'error_msg' => curl_strerror($errorCode)
]);
})
->onProgress(function(int $completed, int $total) {
// Update progress bar
$this->progressBar->update($completed, $total);
});
foreach ($urlsToFetch as $url) {
$multi->addHandle(new Handle($url));
}
$results = $multi->executeAll();
```
**Callback Signatures**:
- `onComplete`: `function(Handle $handle): void`
- `onError`: `function(Handle $handle, int $errorCode): void`
- `onProgress`: `function(int $completed, int $total): void`
**Use Cases**:
- **Real-time Logging**: Log jede Completion/Error während Execution
- **Progress Tracking**: CLI Progress Bars, UI Updates
- **Error Handling**: Immediate Retry-Logic oder Alerting
- **Metrics Collection**: Performance Monitoring während Batch-Processing
- **Data Streaming**: Process Response-Daten sofort statt am Ende
### 6. Curl\MultiHandlePool (NEW!)
**Location**: `src/Framework/HttpClient/Curl/MultiHandlePool.php`
**Purpose**: Pool für parallele Requests mit Concurrency Control
**Key Features**:
- **Concurrency Limit**: Limitiert parallele Requests (verhindert Resource Exhaustion)
- **Automatic Batching**: Führt Requests in Batches aus wenn Queue größer als Limit
- **Progress Tracking**: Real-time Progress und Status Monitoring
- **Memory Efficient**: Batch-wise Execution statt alle auf einmal
**Warum MultiHandlePool statt MultiHandle?**:
- **Resource Control**: Verhindert zu viele parallele Connections
- **Production Ready**: Sicher für große Request-Mengen
- **Predictable Performance**: Kontrollierbare Resource-Nutzung
- **Auto-Batching**: Keine manuelle Batch-Logik notwendig
**Usage**:
```php
// Basic Usage: 100 URLs mit max 10 parallelen Requests
$pool = new MultiHandlePool(maxConcurrent: 10);
foreach ($urls as $url) {
$handle = new Handle($url);
$handle->setOption(HandleOption::Timeout, 10);
$pool->add($handle);
}
$completed = $pool->executeAll(); // Executes in batches of 10
foreach ($completed as $handle) {
$status = $handle->getInfo(Info::ResponseCode);
echo "Completed: {$status}\n";
}
// Progress Tracking
$pool = new MultiHandlePool(maxConcurrent: 5);
for ($i = 0; $i < 50; $i++) {
$pool->add(new Handle("https://api.example.com/items/{$i}"));
}
echo "Total: " . $pool->totalCount() . "\n"; // 50
echo "Queued: " . $pool->queueSize() . "\n"; // 50
$pool->executeAll();
echo "Progress: " . $pool->getProgress() . "%\n"; // 100.0%
echo "Completed: " . $pool->completedCount() . "\n"; // 50
// Reusable Pool
$pool = new MultiHandlePool(maxConcurrent: 10);
// First batch
foreach ($firstBatch as $url) {
$pool->add(new Handle($url));
}
$pool->executeAll();
// Clear and reuse
$pool->clear();
// Second batch
foreach ($secondBatch as $url) {
$pool->add(new Handle($url));
}
$pool->executeAll();
```
**Methods**:
- `__construct(int $maxConcurrent = 10)` - Erstellt Pool mit Concurrency Limit
- `add(Handle $handle): self` - Fügt Handle zur Queue hinzu
- `executeAll(): array<Handle>` - Führt alle Handles in Batches aus
- `queueSize(): int` - Anzahl wartender Handles
- `activeCount(): int` - Anzahl aktuell ausgeführter Handles
- `completedCount(): int` - Anzahl abgeschlossener Handles
- `totalCount(): int` - Gesamt-Anzahl (queued + active + completed)
- `getProgress(): float` - Progress in Prozent (0-100)
- `isComplete(): bool` - Sind alle Handles abgeschlossen?
- `clear(): self` - Löscht alle Handles (queue, active, completed)
- `getMaxConcurrent(): int` - Konfiguriertes Concurrency Limit
**Performance Characteristics**:
```php
// Sequential (old approach - avoid!)
foreach ($urls as $url) {
$handle = new Handle($url);
$response = $handle->fetch();
}
// Time: N * request_time (z.B. 100 * 1s = 100s)
// MultiHandle (uncontrolled)
$multi = new MultiHandle();
foreach ($urls as $url) {
$multi->addHandle(new Handle($url));
}
$multi->executeAll();
// Time: ~request_time (z.B. ~1s)
// Risk: Too many connections, resource exhaustion
// MultiHandlePool (controlled, recommended!)
$pool = new MultiHandlePool(maxConcurrent: 10);
foreach ($urls as $url) {
$pool->add(new Handle($url));
}
$pool->executeAll();
// Time: (N / concurrency) * request_time (z.B. 100/10 * 1s = 10s)
// Safe: Controlled resource usage, predictable performance
```
**Production Best Practices**:
```php
// Use appropriate concurrency based on server capacity
$pool = new MultiHandlePool(
maxConcurrent: match ($environment) {
'production' => 20, // Conservative for production
'staging' => 50, // More aggressive for testing
'local' => 5, // Low for local development
}
);
// Set timeouts on all handles
foreach ($urls as $url) {
$handle = new Handle($url);
$handle->setOption(HandleOption::Timeout, 30);
$handle->setOption(HandleOption::ConnectTimeout, 10);
$pool->add($handle);
}
// Execute and handle results
$completed = $pool->executeAll();
foreach ($completed as $handle) {
$status = $handle->getInfo(Info::ResponseCode);
if ($status >= 200 && $status < 300) {
// Success
$response = $handle->fetch();
} else {
// Handle error
$this->logger->warning("Request failed", [
'url' => $handle->getInfo(Info::EffectiveUrl),
'status' => $status
]);
}
}
```
### 7. Exception Handling
**HandleException**: `src/Framework/HttpClient/Curl/Exception/HandleException.php`
**Static Factory Methods**:
```php
HandleException::initializationFailed(?string $uri)
HandleException::executionFailed(string $errorMessage, int $errorCode)
HandleException::invalidOutputTarget(mixed $output)
HandleException::setOptionFailed(HandleOption $opt, mixed $value, string $errorMessage)
HandleException::invalidOptionType(HandleOption $opt, mixed $value)
HandleException::pauseFailed(Pause $flag, string $errorMessage)
HandleException::escapeFailed(string $string)
HandleException::unescapeFailed(string $string)
HandleException::upkeepFailed(string $errorMessage)
```
**Context-Rich Error Data**:
```php
try {
$handle->setOption(HandleOption::Timeout, 'invalid');
} catch (HandleException $e) {
$e->getMessage(); // "Invalid type for option Timeout: expected integer, got string"
$e->getData(); // ['option' => 'Timeout', 'expected_type' => 'integer', 'actual_type' => 'string']
}
```
**MultiHandleException**: `src/Framework/HttpClient/Curl/Exception/MultiHandleException.php`
**Static Factory Methods**:
```php
MultiHandleException::initializationFailed()
MultiHandleException::addHandleFailed(int $errorCode)
MultiHandleException::removeHandleFailed(int $errorCode)
MultiHandleException::executionFailed(int $errorCode)
MultiHandleException::selectFailed()
MultiHandleException::setOptionFailed(int $option, mixed $value, int $errorCode)
```
## Integration mit HttpClient
### CurlRequestBuilder
**Before** (Procedural):
```php
$options = [
CURLOPT_URL => $url,
CURLOPT_TIMEOUT => 10,
CURLOPT_RETURNTRANSFER => true,
];
```
**After** (OOP):
```php
$options = [
HandleOption::Url => $url,
HandleOption::Timeout => 10,
HandleOption::ReturnTransfer => true,
];
```
### CurlHttpClient
**Before** (Procedural):
```php
$ch = curl_init();
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
if ($response === false) {
throw new CurlExecutionFailed(curl_error($ch), curl_errno($ch));
}
curl_close($ch);
```
**After** (OOP):
```php
$handle = new Handle();
$handle->setOptions($options);
$response = $handle->fetch(); // Throws HandleException on error
// Automatic cleanup via __destruct
```
### AuthenticationHandler
**Before** (Procedural):
```php
$curlOptions[CURLOPT_USERPWD] = "{$username}:{$password}";
$curlOptions[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
```
**After** (OOP):
```php
$curlOptions[HandleOption::UserPwd] = "{$username}:{$password}";
$curlOptions[HandleOption::HttpAuth] = CURLAUTH_BASIC;
```
## Framework-Compliance
Die Implementation folgt allen Framework-Patterns:
**No Inheritance**: Keine `extends`, nur Composition
**Readonly Everywhere**: Klassen sind `final readonly`
**Final by Default**: Alle Klassen sind `final`
**Value Objects**: Enums statt Primitive Obsession
**Explicit DI**: Keine globalen States
**Exception-based**: Keine `false` returns
**Immutability**: Properties readonly wo möglich
**Asymmetric Visibility**: `public private(set)` für Error-Properties
## Backward Compatibility
Die öffentliche `HttpClient` Interface bleibt **unverändert**:
```php
interface HttpClient
{
public function send(ClientRequest $request): ClientResponse;
}
```
Alle bestehenden Aufrufe funktionieren weiterhin:
```php
$client = new CurlHttpClient();
$response = $client->send($request); // ✅ Unchanged
```
Die OOP API wird **intern** verwendet, ist aber auch **direkt nutzbar**:
```php
// Direct usage of new API
$handle = new Handle('https://api.example.com');
$handle->setOption(HandleOption::Timeout, 10);
$response = $handle->fetch();
```
## Performance
**Keine Performance-Regression**:
- Enums sind compile-time optimiert
- Method calls werden inlined
- Gleiche underlying curl_* Funktionen
- Automatic cleanup via `__destruct`
**Vorteile**:
- Type-Validierung verhindert Runtime-Fehler
- Better IDE support → weniger Debugging
- Exception-basierte Fehler → klarere Callstacks
## Testing
**Pest Tests** (todo):
```php
describe('Curl\Handle', function () {
it('can fetch from URL', function () {
$handle = new Handle('https://httpbin.org/get');
$response = $handle->fetch();
expect($response)->toContain('"url"');
expect($handle->getInfo(Info::ResponseCode))->toBe(200);
});
it('throws on invalid URL', function () {
$handle = new Handle('invalid-url');
$handle->fetch();
})->throws(HandleException::class);
it('validates option types', function () {
$handle = new Handle();
$handle->setOption(HandleOption::Timeout, 'invalid');
})->throws(HandleException::class, 'Invalid type for option Timeout');
});
describe('Curl\MultiHandle', function () {
it('can execute parallel requests', function () {
// New: variadic constructor accepts handles directly
$multi = new MultiHandle(
new Handle('https://httpbin.org/delay/1'),
new Handle('https://httpbin.org/delay/1')
);
$completed = $multi->executeAll();
expect($completed)->toHaveCount(2);
expect($multi->count())->toBe(2);
});
it('can add handles later', function () {
$multi = new MultiHandle();
$handles = [
new Handle('https://httpbin.org/delay/1'),
new Handle('https://httpbin.org/delay/1'),
];
foreach ($handles as $handle) {
$multi->addHandle($handle);
}
$completed = $multi->executeAll();
expect($completed)->toHaveCount(2);
});
});
```
## Migration Guide
### Von Procedural cURL zu OOP API
**Step 1**: Replace curl_init()
```php
// Before
$ch = curl_init($url);
// After
$handle = new Handle($url);
```
**Step 2**: Replace curl_setopt()
```php
// Before
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
// After
$handle->setOption(HandleOption::Timeout, 10);
```
**Step 3**: Replace curl_exec()
```php
// Before
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
throw new Exception($error);
}
// After
try {
$response = $handle->fetch();
} catch (HandleException $e) {
// Automatic error handling
}
```
**Step 4**: Replace curl_getinfo()
```php
// Before
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// After
$status = $handle->getInfo(Info::HttpCode);
```
**Step 5**: Replace curl_close()
```php
// Before
curl_close($ch);
// After
// Automatic via __destruct
```
### Von curl_multi_* zu MultiHandle
```php
// Before
$mh = curl_multi_init();
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh);
} while ($running > 0);
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);
// After
$multi = new MultiHandle();
$multi->addHandle($handle1);
$multi->addHandle($handle2);
$completed = $multi->executeAll();
// Automatic cleanup
```
## Best Practices
### 1. Prefer Enums over Constants
```php
// ❌ Avoid
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
// ✅ Prefer
$handle->setOption(HandleOption::Timeout, 10);
```
### 2. Use Fluent API for Multiple Options
```php
// ❌ Avoid
$handle->setOption(HandleOption::Url, $url);
$handle->setOption(HandleOption::Timeout, 10);
$handle->setOption(HandleOption::FollowLocation, true);
// ✅ Prefer
$handle->setOption(HandleOption::Url, $url)
->setOption(HandleOption::Timeout, 10)
->setOption(HandleOption::FollowLocation, true);
// ✅ Or use setOptions()
$handle->setOptions([
HandleOption::Url => $url,
HandleOption::Timeout => 10,
HandleOption::FollowLocation => true,
]);
```
### 3. Use MultiHandle for Parallel Requests
```php
// ❌ Avoid sequential requests
foreach ($urls as $url) {
$handle = new Handle($url);
$responses[] = $handle->fetch(); // Slow: sequential
}
// ✅ Prefer parallel execution
$multi = new MultiHandle();
$handles = [];
foreach ($urls as $url) {
$handle = new Handle($url);
$handles[] = $handle;
$multi->addHandle($handle);
}
$multi->executeAll(); // Fast: parallel
foreach ($handles as $handle) {
$responses[] = $handle->fetch();
}
```
### 4. Handle Exceptions Properly
```php
// ❌ Avoid catching generic Exception
try {
$response = $handle->fetch();
} catch (\Exception $e) {
// Too broad
}
// ✅ Catch specific HandleException
try {
$response = $handle->fetch();
} catch (HandleException $e) {
// Access error details
$errorNumber = $handle->errorNumber;
$errorMessage = $handle->errorMessage;
// Log with context
$this->logger->error('cURL request failed', [
'error_number' => $errorNumber,
'error_message' => $errorMessage,
'exception_data' => $e->getData(),
]);
}
```
### 5. Use Type-Safe Info Retrieval
```php
// ❌ Avoid magic constants
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// ✅ Use Info enum
$status = $handle->getInfo(Info::HttpCode);
$totalTime = $handle->getInfo(Info::TotalTime);
$downloadSize = $handle->getInfo(Info::SizeDownload);
```
## Troubleshooting
### Problem: Type Error beim setOption()
**Symptom**:
```php
HandleException: Invalid type for option Timeout: expected integer, got string
```
**Lösung**:
```php
// ❌ Wrong type
$handle->setOption(HandleOption::Timeout, '10');
// ✅ Correct type
$handle->setOption(HandleOption::Timeout, 10);
```
### Problem: MultiHandle gibt keine Responses zurück
**Symptom**: `executeAll()` returned handles haben keinen Response
**Lösung**: Response ist bereits im Handle, nicht im Return-Wert
```php
// ❌ Wrong
$completed = $multi->executeAll();
foreach ($completed as $handle) {
$response = $handle->fetch(); // ❌ Already executed
}
// ✅ Correct
$handles = [$handle1, $handle2];
$multi->addHandle($handle1);
$multi->addHandle($handle2);
$multi->executeAll();
foreach ($handles as $handle) {
// Response bereits vorhanden, Info abrufen
$status = $handle->getInfo(Info::ResponseCode);
$body = $handle->getInfo(Info::ResponseCode); // Use original handle reference
}
```
### Problem: Memory Leak bei vielen Requests
**Symptom**: Memory usage steigt bei vielen Requests
**Lösung**: Unset handles nach Verwendung
```php
// ❌ Handles bleiben im Speicher
foreach ($urls as $url) {
$handle = new Handle($url);
$response = $handle->fetch();
$results[] = $response;
}
// ✅ Explicit cleanup
foreach ($urls as $url) {
$handle = new Handle($url);
$response = $handle->fetch();
$results[] = $response;
unset($handle); // Force __destruct
}
// ✅ Or use MultiHandle with batching
```
## Future Enhancements
Potentielle zukünftige Erweiterungen:
1. **HTTP/2 Support**: Dedicated HTTP/2 methods und options
2. **Async/Await Integration**: Integration mit Framework's Fiber Manager
3. **Connection Pooling**: Wiederverwendung von Connections
4. **Metrics Integration**: Automatische Performance-Metriken
5. **Retry Mechanisms**: Built-in exponential backoff
6. **Circuit Breaker**: Automatic failure handling
7. **Request/Response Middleware**: Transformations vor/nach Request
## Related Documentation
- **PHP RFC**: https://wiki.php.net/rfc/curl_oop_v2
- **HttpClient Module**: `/docs/claude/architecture.md#http--routing`
- **Exception Handling**: `/docs/claude/error-handling.md`
- **Testing Guide**: `/docs/claude/common-workflows.md`
- **Examples**: `/examples/curl-oop-usage.php`
## Summary
Die cURL OOP API bietet:
**Type Safety**: Enums statt Magic Constants
**IDE Support**: Auto-completion für alle Options
**Exception-based**: Keine `false` returns
**Fluent API**: Method chaining
**Parallel Execution**: MultiHandle für Performance
**Framework-Compliance**: Readonly, Final, Value Objects
**Backward Compatible**: Bestehender Code funktioniert weiter
**Well-Documented**: RFC-based mit Examples
Die Implementation ist **production-ready** und kann sofort verwendet werden!