ReactPHP HTTP
Asynchronous HTTP client in ReactPHP ecosystem. Achieves high-concurrency HTTP communication through event-driven architecture. Optimized for non-blocking I/O, streaming, and real-time communication. Supports high-performance application development beyond traditional PHP constraints.
GitHub Overview
reactphp/http
Event-driven, streaming HTTP client and server implementation for ReactPHP.
Topics
Star History
Library
ReactPHP HTTP
Overview
ReactPHP HTTP is "an asynchronous HTTP client and server library for PHP" developed as a core component of the ReactPHP ecosystem. It enables high-performance HTTP communication through event-driven, non-blocking I/O, efficiently handling concurrent connections and long-lived connections that were difficult with traditional PHP. With modern API design based on PSR-7 message abstraction and Promise-based asynchronous patterns, it provides innovative solutions for application development requiring massive HTTP request processing and real-time communication.
Details
ReactPHP HTTP 2025 edition is a mature library with over 10 years of development experience as a pioneer in asynchronous HTTP communication for PHP. Its design based on ReactPHP's event loop enables efficient handling of thousands of simultaneous HTTP connections in a single process. It provides complete support for PSR-7 HTTP message interfaces and standard HTTP request/response processing. Advanced features include streaming processing for efficient large file transmission, WebSocket support, and custom middleware support. Promise/A+ compliant asynchronous patterns enable building high-performance web applications that were difficult to achieve with traditional blocking I/O.
Key Features
- Event-Driven Architecture: Non-blocking I/O through ReactPHP event loop
- PSR-7 Compliance: Complete support for standard HTTP message interfaces
- Promise-Based: Intuitive API design for asynchronous processing
- Streaming Support: Efficient transmission and reception of large data
- High Concurrency: Processing thousands of connections in a single process
- Extensibility: Custom middleware and plugin support
Pros and Cons
Pros
- Enables true asynchronous HTTP processing impossible with traditional PHP
- Event-driven design provides extremely high memory efficiency and processing performance
- High compatibility with other PHP libraries through PSR-7 compliance
- Intuitive and readable asynchronous code through Promise patterns
- Complete integration with ReactPHP ecosystem for extensibility
- Excellent scalability suited for massive concurrent request processing
Cons
- Relatively high learning curve for asynchronous programming
- Paradigm significantly different from traditional PHP development patterns
- Debugging and troubleshooting can become complex
- Not all PHP libraries support asynchronous operations
- Potential overhead for small-scale synchronous processing
- Error handling can become complex with Promise chains
Reference Pages
Code Examples
Installation and Basic Setup
# Installation via Composer
composer require react/http react/socket
# Development dependencies
composer require --dev react/http react/promise-timer
# Full ReactPHP ecosystem
composer require react/socket react/stream react/promise react/http
# Project initialization example
mkdir reactphp-http-project
cd reactphp-http-project
composer init
composer require react/http psr/http-message
Basic HTTP Client Functionality
<?php
require 'vendor/autoload.php';
use React\EventLoop\Loop;
use React\Http\Browser;
use React\Promise\Promise;
use Psr\Http\Message\ResponseInterface;
// Initialize event loop
$loop = Loop::get();
// Create HTTP browser (client)
$browser = new Browser($loop);
// Basic GET request
function simpleGetRequest($browser) {
echo "=== Basic GET Request ===\n";
$promise = $browser->get('https://api.example.com/users');
$promise->then(
function (ResponseInterface $response) {
echo "Status: " . $response->getStatusCode() . "\n";
echo "Headers: " . json_encode($response->getHeaders(), JSON_PRETTY_PRINT) . "\n";
echo "Body: " . $response->getBody() . "\n\n";
return $response;
},
function (Exception $error) {
echo "Error: " . $error->getMessage() . "\n\n";
}
);
return $promise;
}
// Multiple concurrent GET requests
function concurrentGetRequests($browser) {
echo "=== Concurrent GET Requests ===\n";
$urls = [
'https://api.example.com/users',
'https://api.example.com/posts',
'https://api.example.com/comments'
];
$promises = [];
foreach ($urls as $url) {
$promises[] = $browser->get($url)->then(
function (ResponseInterface $response) use ($url) {
echo "Completed: $url (Status: " . $response->getStatusCode() . ")\n";
return [
'url' => $url,
'status' => $response->getStatusCode(),
'body' => (string) $response->getBody()
];
},
function (Exception $error) use ($url) {
echo "Failed: $url - " . $error->getMessage() . "\n";
return null;
}
);
}
// Wait for all requests to complete
return \React\Promise\all($promises)->then(
function ($results) {
echo "All requests completed successfully!\n";
echo "Results count: " . count(array_filter($results)) . "\n\n";
return $results;
}
);
}
// Request with timeout
function requestWithTimeout($browser) {
echo "=== Request with Timeout ===\n";
$promise = $browser->withTimeout(5.0)->get('https://api.example.com/slow-endpoint');
return $promise->then(
function (ResponseInterface $response) {
echo "Request completed within timeout\n";
echo "Status: " . $response->getStatusCode() . "\n\n";
return $response;
},
function (Exception $error) {
if ($error instanceof \React\Promise\Timer\TimeoutException) {
echo "Request timed out after 5 seconds\n\n";
} else {
echo "Request failed: " . $error->getMessage() . "\n\n";
}
}
);
}
// Main execution
$browser = new Browser($loop);
// Sequential execution example
simpleGetRequest($browser)
->then(function() use ($browser) {
return concurrentGetRequests($browser);
})
->then(function() use ($browser) {
return requestWithTimeout($browser);
})
->then(function() use ($loop) {
echo "All examples completed. Stopping event loop.\n";
$loop->stop();
});
// Start event loop
echo "Starting ReactPHP HTTP client examples...\n";
$loop->run();
POST/PUT/DELETE Requests and Authentication
<?php
require 'vendor/autoload.php';
use React\EventLoop\Loop;
use React\Http\Browser;
use Psr\Http\Message\ResponseInterface;
$loop = Loop::get();
$browser = new Browser($loop);
// POST request (sending JSON data)
function postJsonRequest($browser) {
echo "=== POST JSON Request ===\n";
$userData = [
'name' => 'John Doe',
'email' => '[email protected]',
'age' => 30
];
$headers = [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer your-access-token',
'User-Agent' => 'ReactPHP-HTTP-Client/1.0'
];
$promise = $browser->post(
'https://api.example.com/users',
$headers,
json_encode($userData)
);
return $promise->then(
function (ResponseInterface $response) {
echo "POST Status: " . $response->getStatusCode() . "\n";
echo "Response: " . $response->getBody() . "\n\n";
return $response;
},
function (Exception $error) {
echo "POST Error: " . $error->getMessage() . "\n\n";
}
);
}
// Form data POST
function postFormRequest($browser) {
echo "=== POST Form Request ===\n";
$formData = http_build_query([
'username' => 'testuser',
'password' => 'testpass123',
'remember' => '1'
]);
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
'Content-Length' => strlen($formData)
];
return $browser->post('https://api.example.com/login', $headers, $formData)
->then(
function (ResponseInterface $response) {
echo "Form POST Status: " . $response->getStatusCode() . "\n";
// Cookie retrieval example
if ($response->hasHeader('Set-Cookie')) {
$cookies = $response->getHeader('Set-Cookie');
echo "Received Cookies: " . implode(', ', $cookies) . "\n";
}
echo "Login Response: " . $response->getBody() . "\n\n";
return $response;
},
function (Exception $error) {
echo "Form POST Error: " . $error->getMessage() . "\n\n";
}
);
}
// PUT request (data update)
function putRequest($browser) {
echo "=== PUT Request ===\n";
$updateData = [
'name' => 'John Doe (Updated)',
'email' => '[email protected]',
'age' => 31
];
$headers = [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer your-access-token'
];
return $browser->put(
'https://api.example.com/users/123',
$headers,
json_encode($updateData)
)->then(
function (ResponseInterface $response) {
echo "PUT Status: " . $response->getStatusCode() . "\n";
echo "Updated User: " . $response->getBody() . "\n\n";
return $response;
},
function (Exception $error) {
echo "PUT Error: " . $error->getMessage() . "\n\n";
}
);
}
// DELETE request
function deleteRequest($browser) {
echo "=== DELETE Request ===\n";
$headers = [
'Authorization' => 'Bearer your-access-token'
];
return $browser->delete('https://api.example.com/users/123', $headers)
->then(
function (ResponseInterface $response) {
$status = $response->getStatusCode();
echo "DELETE Status: $status\n";
if ($status === 204) {
echo "User successfully deleted\n\n";
} else {
echo "Delete Response: " . $response->getBody() . "\n\n";
}
return $response;
},
function (Exception $error) {
echo "DELETE Error: " . $error->getMessage() . "\n\n";
}
);
}
// Authenticated request chain
function authenticatedRequestChain($browser) {
echo "=== Authenticated Request Chain ===\n";
// 1. Login
$loginData = ['username' => 'admin', 'password' => 'admin123'];
return $browser->post(
'https://api.example.com/auth/login',
['Content-Type' => 'application/json'],
json_encode($loginData)
)->then(
function (ResponseInterface $response) use ($browser) {
$loginResponse = json_decode($response->getBody(), true);
$token = $loginResponse['access_token'] ?? null;
if (!$token) {
throw new Exception('Login failed: No access token received');
}
echo "Login successful, token received\n";
// 2. Access protected resource
return $browser->get(
'https://api.example.com/admin/users',
['Authorization' => "Bearer $token"]
);
}
)->then(
function (ResponseInterface $response) {
echo "Protected resource access successful\n";
echo "Admin Users: " . $response->getBody() . "\n\n";
return $response;
}
)->otherwise(
function (Exception $error) {
echo "Authentication chain failed: " . $error->getMessage() . "\n\n";
}
);
}
// Main execution
postJsonRequest($browser)
->then(function() use ($browser) {
return postFormRequest($browser);
})
->then(function() use ($browser) {
return putRequest($browser);
})
->then(function() use ($browser) {
return deleteRequest($browser);
})
->then(function() use ($browser) {
return authenticatedRequestChain($browser);
})
->then(function() use ($loop) {
echo "All POST/PUT/DELETE examples completed.\n";
$loop->stop();
});
$loop->run();
Streaming and File Processing
<?php
require 'vendor/autoload.php';
use React\EventLoop\Loop;
use React\Http\Browser;
use React\Stream\WritableResourceStream;
use React\Stream\ReadableResourceStream;
use Psr\Http\Message\ResponseInterface;
$loop = Loop::get();
$browser = new Browser($loop);
// Processing streaming responses
function streamingResponse($browser) {
echo "=== Streaming Response ===\n";
return $browser->requestStreaming('GET', 'https://api.example.com/large-dataset')
->then(
function (ResponseInterface $response) {
echo "Streaming response started (Status: " . $response->getStatusCode() . ")\n";
$body = $response->getBody();
// Read data from stream
$dataReceived = 0;
$body->on('data', function ($chunk) use (&$dataReceived) {
$dataReceived += strlen($chunk);
echo "Received chunk: " . strlen($chunk) . " bytes (Total: $dataReceived bytes)\n";
// Process chunk data here
// e.g., save to database, write to file, etc.
});
$body->on('end', function () use (&$dataReceived) {
echo "Streaming completed. Total received: $dataReceived bytes\n\n";
});
$body->on('error', function (Exception $error) {
echo "Stream error: " . $error->getMessage() . "\n\n";
});
return $response;
},
function (Exception $error) {
echo "Streaming request failed: " . $error->getMessage() . "\n\n";
}
);
}
// File download
function downloadFile($browser, $url, $localPath) {
echo "=== File Download ===\n";
echo "Downloading: $url\n";
$file = fopen($localPath, 'w');
if (!$file) {
echo "Cannot open file for writing: $localPath\n\n";
return \React\Promise\resolve();
}
$fileStream = new WritableResourceStream($file, $browser->getLoop());
return $browser->requestStreaming('GET', $url)
->then(
function (ResponseInterface $response) use ($fileStream, $localPath) {
echo "Download started (Status: " . $response->getStatusCode() . ")\n";
if ($response->hasHeader('Content-Length')) {
$contentLength = $response->getHeaderLine('Content-Length');
echo "File size: $contentLength bytes\n";
}
$body = $response->getBody();
$downloaded = 0;
$body->on('data', function ($chunk) use ($fileStream, &$downloaded) {
$fileStream->write($chunk);
$downloaded += strlen($chunk);
if ($downloaded % 10240 === 0) { // Progress every 10KB
echo "Downloaded: $downloaded bytes\n";
}
});
$body->on('end', function () use ($fileStream, $localPath, &$downloaded) {
$fileStream->end();
echo "Download completed: $localPath ($downloaded bytes)\n\n";
});
$body->on('error', function (Exception $error) use ($fileStream) {
$fileStream->close();
echo "Download error: " . $error->getMessage() . "\n\n";
});
return $response;
}
);
}
// File upload
function uploadFile($browser, $url, $filePath) {
echo "=== File Upload ===\n";
echo "Uploading: $filePath\n";
if (!file_exists($filePath)) {
echo "File not found: $filePath\n\n";
return \React\Promise\resolve();
}
$fileSize = filesize($filePath);
echo "File size: $fileSize bytes\n";
$file = fopen($filePath, 'r');
$fileStream = new ReadableResourceStream($file, $browser->getLoop());
$headers = [
'Content-Type' => 'application/octet-stream',
'Content-Length' => $fileSize,
'Authorization' => 'Bearer your-upload-token'
];
return $browser->requestStreaming('POST', $url, $headers, $fileStream)
->then(
function (ResponseInterface $response) use ($filePath) {
echo "Upload response (Status: " . $response->getStatusCode() . ")\n";
echo "Upload response body: " . $response->getBody() . "\n\n";
return $response;
},
function (Exception $error) use ($filePath) {
echo "Upload failed for $filePath: " . $error->getMessage() . "\n\n";
}
);
}
// Processing large data in chunks
function processLargeDataInChunks($browser) {
echo "=== Large Data Chunk Processing ===\n";
$processedChunks = 0;
$buffer = '';
return $browser->requestStreaming('GET', 'https://api.example.com/large-json-array')
->then(
function (ResponseInterface $response) use (&$processedChunks, &$buffer) {
echo "Starting large data processing\n";
$body = $response->getBody();
$body->on('data', function ($chunk) use (&$processedChunks, &$buffer) {
$buffer .= $chunk;
// Split data by newlines and process
$lines = explode("\n", $buffer);
$buffer = array_pop($lines); // Keep last incomplete line in buffer
foreach ($lines as $line) {
if (trim($line)) {
// Process each line as JSON
$data = json_decode($line, true);
if ($data) {
$processedChunks++;
echo "Processed item $processedChunks: " . ($data['id'] ?? 'unknown') . "\n";
// Perform actual data processing here
// e.g., save to database, transform data, etc.
}
}
}
});
$body->on('end', function () use (&$processedChunks, &$buffer) {
// Process remaining data
if (trim($buffer)) {
$data = json_decode($buffer, true);
if ($data) {
$processedChunks++;
echo "Processed final item: " . ($data['id'] ?? 'unknown') . "\n";
}
}
echo "Large data processing completed. Total items: $processedChunks\n\n";
});
return $response;
}
);
}
// Main execution
streamingResponse($browser)
->then(function() use ($browser) {
return downloadFile($browser, 'https://example.com/sample-file.zip', '/tmp/downloaded-file.zip');
})
->then(function() use ($browser) {
return uploadFile($browser, 'https://api.example.com/upload', '/tmp/test-upload.txt');
})
->then(function() use ($browser) {
return processLargeDataInChunks($browser);
})
->then(function() use ($loop) {
echo "All streaming examples completed.\n";
$loop->stop();
});
$loop->run();
Error Handling and Retry Functionality
<?php
require 'vendor/autoload.php';
use React\EventLoop\Loop;
use React\Http\Browser;
use React\Promise\Timer\TimeoutException;
use Psr\Http\Message\ResponseInterface;
$loop = Loop::get();
$browser = new Browser($loop);
// Comprehensive error handling
function comprehensiveErrorHandling($browser) {
echo "=== Comprehensive Error Handling ===\n";
return $browser->get('https://api.example.com/unreliable-endpoint')
->then(
function (ResponseInterface $response) {
$status = $response->getStatusCode();
echo "Response received with status: $status\n";
// HTTP status code specific handling
if ($status >= 200 && $status < 300) {
echo "Success: Request completed successfully\n";
return $response;
} elseif ($status >= 400 && $status < 500) {
throw new Exception("Client error: HTTP $status - " . $response->getReasonPhrase());
} elseif ($status >= 500) {
throw new Exception("Server error: HTTP $status - " . $response->getReasonPhrase());
} else {
throw new Exception("Unexpected status code: $status");
}
}
)->otherwise(
function (Exception $error) {
echo "Error occurred: " . get_class($error) . "\n";
echo "Error message: " . $error->getMessage() . "\n";
// Error type specific handling
if ($error instanceof TimeoutException) {
echo "Error type: Request timeout\n";
} elseif ($error instanceof \React\Socket\ConnectionException) {
echo "Error type: Connection error\n";
} elseif ($error instanceof \InvalidArgumentException) {
echo "Error type: Invalid request parameters\n";
} else {
echo "Error type: General error\n";
}
echo "Error handling completed\n\n";
throw $error; // Re-throw if needed
}
);
}
// Request with retry functionality
function requestWithRetry($browser, $url, $maxRetries = 3, $backoffDelay = 1.0) {
echo "=== Request with Retry Logic ===\n";
echo "Attempting request: $url\n";
$attempt = 0;
$makeRequest = function() use ($browser, $url, $maxRetries, $backoffDelay, &$attempt, &$makeRequest) {
$attempt++;
echo "Attempt $attempt/$maxRetries\n";
return $browser->withTimeout(10.0)->get($url)
->then(
function (ResponseInterface $response) use ($attempt) {
$status = $response->getStatusCode();
echo "Request successful on attempt $attempt (Status: $status)\n\n";
return $response;
}
)->otherwise(
function (Exception $error) use ($attempt, $maxRetries, $backoffDelay, $makeRequest, $url) {
echo "Attempt $attempt failed: " . $error->getMessage() . "\n";
// Check if error is retryable
$shouldRetry = false;
if ($error instanceof TimeoutException) {
$shouldRetry = true;
echo "Timeout error - retryable\n";
} elseif ($error instanceof \React\Socket\ConnectionException) {
$shouldRetry = true;
echo "Connection error - retryable\n";
} elseif (method_exists($error, 'getResponse')) {
$response = $error->getResponse();
if ($response && $response->getStatusCode() >= 500) {
$shouldRetry = true;
echo "Server error - retryable\n";
}
}
if ($shouldRetry && $attempt < $maxRetries) {
$delay = $backoffDelay * pow(2, $attempt - 1); // Exponential backoff
echo "Retrying in $delay seconds...\n";
return \React\Promise\Timer\sleep($delay, $browser->getLoop())
->then($makeRequest);
} else {
echo "Max retries reached or non-retryable error\n\n";
throw $error;
}
}
);
};
return $makeRequest();
}
// Circuit breaker pattern
class CircuitBreaker {
private $failureCount = 0;
private $lastFailureTime = null;
private $state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
private $maxFailures;
private $recoveryTimeout;
public function __construct($maxFailures = 5, $recoveryTimeout = 60) {
$this->maxFailures = $maxFailures;
$this->recoveryTimeout = $recoveryTimeout;
}
public function call($browser, $url) {
echo "Circuit breaker state: {$this->state}\n";
if ($this->state === 'OPEN') {
if (time() - $this->lastFailureTime > $this->recoveryTimeout) {
$this->state = 'HALF_OPEN';
echo "Circuit breaker transitioning to HALF_OPEN\n";
} else {
echo "Circuit breaker is OPEN - request blocked\n\n";
return \React\Promise\reject(new Exception('Circuit breaker is OPEN'));
}
}
return $browser->get($url)
->then(
function (ResponseInterface $response) {
// Success - reset failure count
$this->failureCount = 0;
if ($this->state === 'HALF_OPEN') {
$this->state = 'CLOSED';
echo "Circuit breaker recovered - state: CLOSED\n";
}
return $response;
}
)->otherwise(
function (Exception $error) {
// Failure - increment counter
$this->failureCount++;
$this->lastFailureTime = time();
echo "Request failed (failure count: {$this->failureCount})\n";
if ($this->failureCount >= $this->maxFailures) {
$this->state = 'OPEN';
echo "Circuit breaker opened due to excessive failures\n";
}
throw $error;
}
);
}
}
function circuitBreakerExample($browser) {
echo "=== Circuit Breaker Pattern ===\n";
$circuitBreaker = new CircuitBreaker(3, 30);
// Test circuit breaker with multiple requests
$promises = [];
for ($i = 1; $i <= 7; $i++) {
$promises[] = $circuitBreaker->call($browser, 'https://api.example.com/unstable-endpoint')
->then(
function (ResponseInterface $response) use ($i) {
echo "Request $i succeeded\n";
return $response;
},
function (Exception $error) use ($i) {
echo "Request $i failed: " . $error->getMessage() . "\n";
return null; // Continue with other requests
}
);
}
return \React\Promise\all($promises)->then(
function ($results) {
echo "Circuit breaker test completed\n\n";
return $results;
}
);
}
// Main execution
comprehensiveErrorHandling($browser)
->otherwise(function() { return null; }) // Catch errors and continue
->then(function() use ($browser) {
return requestWithRetry($browser, 'https://api.example.com/sometimes-fails', 3, 1.0);
})
->otherwise(function() { return null; }) // Catch errors and continue
->then(function() use ($browser) {
return circuitBreakerExample($browser);
})
->then(function() use ($loop) {
echo "All error handling examples completed.\n";
$loop->stop();
});
$loop->run();
HTTP Server Implementation
<?php
require 'vendor/autoload.php';
use React\EventLoop\Loop;
use React\Http\HttpServer;
use React\Http\Message\Response;
use React\Socket\SocketServer;
use Psr\Http\Message\ServerRequestInterface;
$loop = Loop::get();
// Create and configure HTTP server
function createHttpServer($loop) {
echo "=== ReactPHP HTTP Server ===\n";
$server = new HttpServer($loop, function (ServerRequestInterface $request) {
$method = $request->getMethod();
$path = $request->getUri()->getPath();
$query = $request->getQueryParams();
echo "Request: $method $path\n";
// Routing logic
switch ($path) {
case '/':
return new Response(200, ['Content-Type' => 'application/json'],
json_encode(['message' => 'Welcome to ReactPHP HTTP Server!', 'timestamp' => time()]));
case '/users':
if ($method === 'GET') {
return handleGetUsers($query);
} elseif ($method === 'POST') {
return handleCreateUser($request);
}
break;
case '/health':
return new Response(200, ['Content-Type' => 'application/json'],
json_encode(['status' => 'healthy', 'uptime' => time()]));
case '/streaming':
return handleStreamingResponse();
default:
if (preg_match('/^\/users\/(\d+)$/', $path, $matches)) {
$userId = $matches[1];
if ($method === 'GET') {
return handleGetUser($userId);
} elseif ($method === 'PUT') {
return handleUpdateUser($userId, $request);
} elseif ($method === 'DELETE') {
return handleDeleteUser($userId);
}
}
break;
}
// 404 Not Found
return new Response(404, ['Content-Type' => 'application/json'],
json_encode(['error' => 'Not Found', 'path' => $path]));
});
return $server;
}
// Get users list
function handleGetUsers($query) {
$page = $query['page'] ?? 1;
$limit = $query['limit'] ?? 10;
$users = [
['id' => 1, 'name' => 'John Doe', 'email' => '[email protected]'],
['id' => 2, 'name' => 'Jane Smith', 'email' => '[email protected]'],
['id' => 3, 'name' => 'Bob Johnson', 'email' => '[email protected]'],
];
return new Response(200, ['Content-Type' => 'application/json'], json_encode([
'users' => array_slice($users, ($page - 1) * $limit, $limit),
'pagination' => [
'page' => (int)$page,
'limit' => (int)$limit,
'total' => count($users)
]
]));
}
// Get specific user
function handleGetUser($userId) {
$users = [
1 => ['id' => 1, 'name' => 'John Doe', 'email' => '[email protected]', 'created_at' => '2025-01-01T00:00:00Z'],
2 => ['id' => 2, 'name' => 'Jane Smith', 'email' => '[email protected]', 'created_at' => '2025-01-02T00:00:00Z'],
3 => ['id' => 3, 'name' => 'Bob Johnson', 'email' => '[email protected]', 'created_at' => '2025-01-03T00:00:00Z'],
];
if (isset($users[$userId])) {
return new Response(200, ['Content-Type' => 'application/json'],
json_encode($users[$userId]));
} else {
return new Response(404, ['Content-Type' => 'application/json'],
json_encode(['error' => 'User not found', 'user_id' => $userId]));
}
}
// Create user
function handleCreateUser(ServerRequestInterface $request) {
$body = (string) $request->getBody();
$data = json_decode($body, true);
if (!$data || !isset($data['name']) || !isset($data['email'])) {
return new Response(400, ['Content-Type' => 'application/json'],
json_encode(['error' => 'Invalid request data', 'required' => ['name', 'email']]));
}
$newUser = [
'id' => rand(1000, 9999),
'name' => $data['name'],
'email' => $data['email'],
'created_at' => date('c')
];
return new Response(201, ['Content-Type' => 'application/json'],
json_encode($newUser));
}
// Update user
function handleUpdateUser($userId, ServerRequestInterface $request) {
$body = (string) $request->getBody();
$data = json_decode($body, true);
if (!$data) {
return new Response(400, ['Content-Type' => 'application/json'],
json_encode(['error' => 'Invalid JSON data']));
}
$updatedUser = [
'id' => (int)$userId,
'name' => $data['name'] ?? 'Unknown',
'email' => $data['email'] ?? '[email protected]',
'updated_at' => date('c')
];
return new Response(200, ['Content-Type' => 'application/json'],
json_encode($updatedUser));
}
// Delete user
function handleDeleteUser($userId) {
// Actual deletion logic would go here
return new Response(204, [], ''); // No Content
}
// Streaming response
function handleStreamingResponse() {
return new Response(200, ['Content-Type' => 'text/plain'],
\React\Stream\ReadableResourceStream::fromIterator([
"Starting stream...\n",
"Data chunk 1\n",
"Data chunk 2\n",
"Data chunk 3\n",
"Stream completed.\n"
]));
}
// Start server
function startServer($loop) {
$server = createHttpServer($loop);
$socket = new SocketServer('127.0.0.1:8080', $loop);
$server->listen($socket);
echo "HTTP Server started on http://127.0.0.1:8080\n";
echo "Available endpoints:\n";
echo " GET / - Welcome message\n";
echo " GET /health - Health check\n";
echo " GET /users - List users\n";
echo " POST /users - Create user\n";
echo " GET /users/{id} - Get specific user\n";
echo " PUT /users/{id} - Update user\n";
echo " DELETE /users/{id} - Delete user\n";
echo " GET /streaming - Streaming response\n\n";
echo "Press Ctrl+C to stop the server\n";
return $server;
}
// Start the server
startServer($loop);
$loop->run();