startTime = now(); $this->rawRequest = $rawRequest; $this->parsedRequest = $parsedRequest; $this->id = $this->getRequestId(); } /** * @inheritDoc */ public function jsonSerialize() { $data = [ 'id' => $this->id, 'performed_at' => $this->startTime->toDateTimeString(), 'duration' => $this->getDuration(), 'subdomain' => $this->detectSubdomain(), 'request' => [ 'raw' => $this->isBinary($this->rawRequest) ? 'BINARY' : $this->rawRequest, 'method' => $this->parsedRequest->getMethod(), 'uri' => $this->parsedRequest->getUri()->getPath(), 'headers' => $this->parsedRequest->getHeaders()->toArray(), 'body' => $this->isBinary($this->rawRequest) ? 'BINARY' : $this->parsedRequest->getContent(), 'query' => $this->parsedRequest->getQuery()->toArray(), 'post' => $this->getPostData(), 'curl' => $this->getRequestAsCurl(), 'additional_data' => $this->additionalData, ], ]; if ($this->parsedResponse) { try { $body = $this->parsedResponse->getBody(); } catch (\Exception $e) { $body = ''; } $data['response'] = [ 'raw' => $this->shouldReturnBody() ? $this->rawResponse : 'BINARY', 'status' => $this->parsedResponse->getStatusCode(), 'headers' => $this->parsedResponse->getHeaders()->toArray(), 'reason' => $this->parsedResponse->getReasonPhrase(), 'body' => $this->shouldReturnBody() ? $body : 'BINARY', ]; } return $data; } public function setAdditionalData(array $data) { $this->additionalData = array_merge($this->additionalData, $data); } public function getAdditionalData(): array { return $this->additionalData; } protected function isBinary(string $string): bool { return preg_match('~[^\x20-\x7E\t\r\n]~', $string) > 0; } protected function shouldReturnBody() { $contentType = Arr::get($this->parsedResponse->getHeaders()->toArray(), 'Content-Type'); return $contentType === 'application/json' || Str::is('text/*', $contentType) || Str::is('*javascript*', $contentType); } public function getRequest() { return $this->parsedRequest; } public function setResponse(string $rawResponse, Response $response) { $this->parsedResponse = $response; $this->rawResponse = $rawResponse; if (is_null($this->stopTime)) { $this->stopTime = now(); } } public function id(): string { return $this->id; } public function getRequestData(): ?string { return $this->rawRequest; } public function getResponse(): ?Response { return $this->parsedResponse; } public function getPostData() { $postData = []; $contentType = Arr::get($this->parsedRequest->getHeaders()->toArray(), 'Content-Type'); switch ($contentType) { case 'application/x-www-form-urlencoded': parse_str($this->parsedRequest->getContent(), $postData); $postData = collect($postData)->map(function ($value, $key) { return [ 'name' => $key, 'value' => $value, ]; })->toArray(); break; case 'application/json': $postData = collect(json_decode($this->parsedRequest->getContent(), true))->map(function ($value, $key) { return [ 'name' => $key, 'value' => $value, ]; })->values()->toArray(); break; default: $stream = fopen('php://temp', 'rw'); fwrite($stream, $this->rawRequest); rewind($stream); try { $document = new StreamedPart($stream); if ($document->isMultiPart()) { $postData = collect($document->getParts())->map(function (StreamedPart $part) { return [ 'name' => $part->getName(), 'value' => $part->isFile() ? null : $part->getBody(), 'is_file' => $part->isFile(), 'filename' => $part->isFile() ? $part->getFileName() : null, 'mime_type' => $part->isFile() ? $part->getMimeType() : null, ]; })->toArray(); } } catch (\Exception $e) { // } break; } return $postData; } protected function detectSubdomain() { return collect($this->parsedRequest->getHeaders()->toArray()) ->mapWithKeys(function ($value, $key) { return [strtolower($key) => $value]; })->get('x-original-host'); } protected function getRequestId() { return collect($this->parsedRequest->getHeaders()->toArray()) ->mapWithKeys(function ($value, $key) { return [strtolower($key) => $value]; })->get('x-expose-request-id', (string)Str::uuid()); } public function getDuration() { return $this->startTime->diffInMilliseconds($this->stopTime, false); } protected function getRequestAsCurl(): string { try { return (new CurlFormatter())->format(parse_request($this->rawRequest)); } catch (\Throwable $e) { return ''; } } }