diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a89388..895f061 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### Added + +- Add the `always_prepend` option to `AddPathPlugin`. +- The `BaseUriPlugin` can now passthru options for the underlying `AddPathPlugin`. + ### Deprecated - The `debug_plugins` option for `PluginClient` is deprecated and will be removed in 2.0. Use the decorator design pattern instead like in [ProfilePlugin](https://github.com/php-http/HttplugBundle/blob/de33f9c14252f22093a5ec7d84f17535ab31a384/Collector/ProfilePlugin.php). diff --git a/spec/Plugin/AddPathPluginSpec.php b/spec/Plugin/AddPathPluginSpec.php index a7ec9b5..1a6c2da 100644 --- a/spec/Plugin/AddPathPluginSpec.php +++ b/spec/Plugin/AddPathPluginSpec.php @@ -61,4 +61,23 @@ function it_throws_exception_on_empty_path(UriInterface $host) $this->beConstructedWith($host); $this->shouldThrow('\LogicException')->duringInstantiation(); } + + function it_adds_path_only_if_required( + RequestInterface $request, + UriInterface $host, + UriInterface $uri + ) { + $host->getPath()->shouldBeCalled()->willReturn('/api'); + + $request->getUri()->shouldBeCalled()->willReturn($uri); + $request->withUri($uri)->shouldNotBeCalled(); + + $uri->withPath('/api/api/users')->shouldNotBeCalled(); + $uri->getPath()->shouldBeCalled()->willReturn('/api/users'); + + $this->beConstructedWith($host, [ + 'always_prepend' => false, + ]); + $this->handleRequest($request, function () {}, function () {}); + } } diff --git a/spec/Plugin/BaseUriPluginSpec.php b/spec/Plugin/BaseUriPluginSpec.php index 2faf769..fc09218 100644 --- a/spec/Plugin/BaseUriPluginSpec.php +++ b/spec/Plugin/BaseUriPluginSpec.php @@ -99,4 +99,22 @@ function it_replaces_domain_and_adds_path( $this->beConstructedWith($host, ['replace' => true]); $this->handleRequest($request, function () {}, function () {}); } + + function it_adds_path_only_if_required( + RequestInterface $request, + UriInterface $host, + UriInterface $uri + ) { + $host->getHost()->shouldBeCalled()->willReturn('example.com'); + $host->getPath()->shouldBeCalled()->willReturn('/api'); + + $request->getUri()->shouldBeCalled()->willReturn($uri); + $request->withUri($uri)->shouldNotBeCalled(); + + $uri->getHost()->shouldBeCalled()->willReturn('example.com'); + $uri->getPath()->shouldBeCalled()->willReturn('/api/users'); + + $this->beConstructedWith($host, [], ['always_prepend' => false]); + $this->handleRequest($request, function () {}, function () {}); + } } diff --git a/src/Plugin/AddPathPlugin.php b/src/Plugin/AddPathPlugin.php index 18fdf52..c226d54 100644 --- a/src/Plugin/AddPathPlugin.php +++ b/src/Plugin/AddPathPlugin.php @@ -5,6 +5,7 @@ use Http\Client\Common\Plugin; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\UriInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; /** * Prepend a base path to the request URI. Useful for base API URLs like http://domain.com/api. @@ -18,10 +19,19 @@ final class AddPathPlugin implements Plugin */ private $uri; + /** + * @var bool + */ + private $alwaysPrepend; + /** * @param UriInterface $uri + * @param array $config { + * + * @var bool $alwaysPrepend Set to true to always prepend the path even if the request path start with that path. + * } */ - public function __construct(UriInterface $uri) + public function __construct(UriInterface $uri, array $config = []) { if ($uri->getPath() === '') { throw new \LogicException('URI path cannot be empty'); @@ -32,6 +42,12 @@ public function __construct(UriInterface $uri) } $this->uri = $uri; + + $resolver = new OptionsResolver(); + $this->configureOptions($resolver); + $options = $resolver->resolve($config); + + $this->alwaysPrepend = $options['always_prepend']; } /** @@ -39,10 +55,26 @@ public function __construct(UriInterface $uri) */ public function handleRequest(RequestInterface $request, callable $next, callable $first) { - $request = $request->withUri($request->getUri() - ->withPath($this->uri->getPath().$request->getUri()->getPath()) - ); + $prepend = $this->uri->getPath(); + $path = $request->getUri()->getPath(); + + if ($this->alwaysPrepend || substr($path, 0, strlen($prepend)) !== $prepend) { + $request = $request->withUri($request->getUri() + ->withPath($prepend.$path) + ); + } return $next($request); } + + /** + * @param OptionsResolver $resolver + */ + private function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'always_prepend' => true, + ]); + $resolver->setAllowedTypes('always_prepend', 'bool'); + } } diff --git a/src/Plugin/BaseUriPlugin.php b/src/Plugin/BaseUriPlugin.php index 2c2a775..b91d1fd 100644 --- a/src/Plugin/BaseUriPlugin.php +++ b/src/Plugin/BaseUriPlugin.php @@ -26,13 +26,14 @@ final class BaseUriPlugin implements Plugin /** * @param UriInterface $uri Has to contain a host name and cans have a path. * @param array $hostConfig Config for AddHostPlugin. @see AddHostPlugin::configureOptions + * @param array $pathConfig Config for AddPassPlugin. @see AddPathPlugin::configureOptions */ - public function __construct(UriInterface $uri, array $hostConfig = []) + public function __construct(UriInterface $uri, array $hostConfig = [], array $pathConfig = []) { $this->addHostPlugin = new AddHostPlugin($uri, $hostConfig); if (rtrim($uri->getPath(), '/')) { - $this->addPathPlugin = new AddPathPlugin($uri); + $this->addPathPlugin = new AddPathPlugin($uri, $pathConfig); } }