diff --git a/README.md b/README.md index 34580fe..338b415 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,9 @@ $ composer test Please see our [contributing guide](http://docs.php-http.org/en/latest/development/contributing.html). +## Cretids + +Thanks to [Cuzzle](https://github.com/namshi/cuzzle) for inpiration for the `CurlCommandFormatter`. ## Security diff --git a/spec/Formatter/CurlCommandFormatterSpec.php b/spec/Formatter/CurlCommandFormatterSpec.php new file mode 100644 index 0000000..50fd374 --- /dev/null +++ b/spec/Formatter/CurlCommandFormatterSpec.php @@ -0,0 +1,61 @@ +shouldHaveType('Http\Message\Formatter\CurlCommandFormatter'); + } + + function it_is_a_formatter() + { + $this->shouldImplement('Http\Message\Formatter'); + } + + function it_formats_the_request(RequestInterface $request, UriInterface $uri, StreamInterface $body) + { + $request->getUri()->willReturn($uri); + $request->getBody()->willReturn($body); + + $uri->withFragment('')->shouldBeCalled()->willReturn('http://foo.com/bar'); + $request->getMethod()->willReturn('GET'); + $request->getProtocolVersion()->willReturn('1.1'); + + $request->getHeaders()->willReturn(['foo'=>['bar', 'baz']]); + $request->getHeaderLine('foo')->willReturn('bar, baz'); + + $this->formatRequest($request)->shouldReturn('curl \'http://foo.com/bar\' -H \'foo: bar, baz\''); + } + + function it_formats_post_request(RequestInterface $request, UriInterface $uri, StreamInterface $body) + { + $request->getUri()->willReturn($uri); + $request->getBody()->willReturn($body); + + $body->__toString()->willReturn('body " data'." test' bar"); + $body->getSize()->willReturn(1); + $body->isSeekable()->willReturn(true); + $body->rewind()->willReturn(true); + + $uri->withFragment('')->shouldBeCalled()->willReturn('http://foo.com/bar'); + $request->getMethod()->willReturn('POST'); + $request->getProtocolVersion()->willReturn('2.0'); + + $request->getHeaders()->willReturn([]); + + $this->formatRequest($request)->shouldReturn("curl 'http://foo.com/bar' --http2 --request POST --data 'body \" data test'\'' bar'"); + } + + function it_does_nothing_for_response(ResponseInterface $response) + { + $this->formatResponse($response)->shouldReturn(''); + } +} diff --git a/src/Formatter/CurlCommandFormatter.php b/src/Formatter/CurlCommandFormatter.php new file mode 100644 index 0000000..5364ccc --- /dev/null +++ b/src/Formatter/CurlCommandFormatter.php @@ -0,0 +1,80 @@ + + */ +class CurlCommandFormatter implements Formatter +{ + /** + * {@inheritdoc} + */ + public function formatRequest(RequestInterface $request) + { + $command = sprintf('curl %s', escapeshellarg((string) $request->getUri()->withFragment(''))); + if ($request->getProtocolVersion() === '1.0') { + $command .= ' --http1.0'; + } elseif ($request->getProtocolVersion() === '2.0') { + $command .= ' --http2'; + } + + $method = strtoupper($request->getMethod()); + if ('HEAD' === $method) { + $command .= ' --head'; + } elseif ('GET' !== $method) { + $command .= ' --request '.$method; + } + + $command .= $this->getHeadersAsCommandOptions($request); + + $body = $request->getBody(); + if ($body->getSize() > 0) { + if (!$body->isSeekable()) { + return 'Cant format Request as cUrl command if body stream is not seekable.'; + } + $command .= sprintf(' --data %s', escapeshellarg($body->__toString())); + $body->rewind(); + } + + return $command; + } + + /** + * {@inheritdoc} + */ + public function formatResponse(ResponseInterface $response) + { + return ''; + } + + /** + * @param RequestInterface $request + * + * @return string + */ + private function getHeadersAsCommandOptions(RequestInterface $request) + { + $command = ''; + foreach ($request->getHeaders() as $name => $values) { + if ('host' === strtolower($name) && $values[0] === $request->getUri()->getHost()) { + continue; + } + + if ('user-agent' === strtolower($name)) { + $command .= sprintf('-A %s', escapeshellarg($values[0])); + continue; + } + + $command .= sprintf(' -H %s', escapeshellarg($name.': '.$request->getHeaderLine($name))); + } + + return $command; + } +}