toMilliseconds() / $concurrentUsers : 0; // Spawn concurrent user processes $userProcesses = []; for ($i = 0; $i < $concurrentUsers; $i++) { // Ramp-up delay if ($rampUpDelay > 0) { usleep((int) ($rampUpDelay * 1000)); } $userProcesses[] = $this->spawnUserProcess( userId: $i, endpoint: $endpoint, requestCount: $requestsPerUser ); } // Wait for all user processes to complete foreach ($userProcesses as $userId => $process) { $userResults = $this->waitForUserProcess($process); $results[$userId] = $userResults; foreach ($userResults['errors'] ?? [] as $error) { $errors[] = $error; } } $endTime = microtime(true); $totalTime = ($endTime - $startTime) * 1000; // milliseconds return $this->analyzeResults($results, $errors, $totalTime); } /** * Spawn a user process that makes requests * * @return array Process information */ private function spawnUserProcess( int $userId, string $endpoint, int $requestCount ): array { $responseTimes = []; $statusCodes = []; $errors = []; for ($i = 0; $i < $requestCount; $i++) { $requestStart = microtime(true); try { $result = $this->makeRequest($endpoint); $requestEnd = microtime(true); $responseTime = ($requestEnd - $requestStart) * 1000; $responseTimes[] = $responseTime; $statusCodes[] = $result['status_code']; if ($result['status_code'] >= 400) { $errors[] = [ 'user_id' => $userId, 'request_num' => $i, 'status_code' => $result['status_code'], 'error' => $result['error'] ?? 'HTTP error' ]; } } catch (\Exception $e) { $errors[] = [ 'user_id' => $userId, 'request_num' => $i, 'error' => $e->getMessage() ]; } } return [ 'user_id' => $userId, 'response_times' => $responseTimes, 'status_codes' => $statusCodes, 'errors' => $errors ]; } /** * Make HTTP request to endpoint */ private function makeRequest(string $endpoint): array { $url = $this->baseUrl . $endpoint; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_HTTPHEADER => [ 'User-Agent: Mozilla/5.0 (Load Test)', 'Accept: application/json' ], CURLOPT_TIMEOUT => 30 ]); $response = curl_exec($ch); $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); return [ 'response' => $response, 'status_code' => $statusCode, 'error' => $error ?: null ]; } /** * Wait for user process to complete (placeholder for actual process handling) */ private function waitForUserProcess(array $process): array { return $process; } /** * Analyze results from all users */ private function analyzeResults(array $results, array $errors, float $totalTime): LoadTestResult { $allResponseTimes = []; $allStatusCodes = []; foreach ($results as $userResult) { $allResponseTimes = array_merge($allResponseTimes, $userResult['response_times']); $allStatusCodes = array_merge($allStatusCodes, $userResult['status_codes']); } sort($allResponseTimes); $totalRequests = count($allResponseTimes); $avgResponseTime = array_sum($allResponseTimes) / $totalRequests; $minResponseTime = min($allResponseTimes); $maxResponseTime = max($allResponseTimes); $medianResponseTime = $this->calculateMedian($allResponseTimes); $p95ResponseTime = $this->calculatePercentile($allResponseTimes, 95); $p99ResponseTime = $this->calculatePercentile($allResponseTimes, 99); $requestsPerSecond = ($totalRequests / $totalTime) * 1000; $successfulRequests = count(array_filter($allStatusCodes, fn($code) => $code >= 200 && $code < 300)); $errorRate = ($totalRequests - $successfulRequests) / $totalRequests * 100; return new LoadTestResult( totalRequests: $totalRequests, totalTimeMs: $totalTime, avgResponseTimeMs: $avgResponseTime, minResponseTimeMs: $minResponseTime, maxResponseTimeMs: $maxResponseTime, medianResponseTimeMs: $medianResponseTime, p95ResponseTimeMs: $p95ResponseTime, p99ResponseTimeMs: $p99ResponseTime, requestsPerSecond: $requestsPerSecond, successfulRequests: $successfulRequests, errorRate: $errorRate, errors: $errors ); } private function calculateMedian(array $values): float { $count = count($values); $middle = floor($count / 2); if ($count % 2 === 0) { return ($values[$middle - 1] + $values[$middle]) / 2; } return $values[$middle]; } private function calculatePercentile(array $values, int $percentile): float { $count = count($values); $index = ceil($count * ($percentile / 100)) - 1; return $values[(int) max(0, min($index, $count - 1))]; } }