diff --git a/Describer/ParameterRefMergeDescriber.php b/Describer/ParameterRefMergeDescriber.php
new file mode 100644
index 0000000..bebf8d3
--- /dev/null
+++ b/Describer/ParameterRefMergeDescriber.php
@@ -0,0 +1,79 @@
+getPaths() as $path) {
+ /** @var Operation $operation */
+ foreach ($path->getOperations() as $operation) {
+ $this->checkOperation($api, $operation);
+ }
+ }
+ }
+
+ /**
+ * This method removes parameters that also have a ref version as they will be duplicated otherwise.
+ */
+ private function checkOperation(Swagger $api, Operation $operation)
+ {
+ $parametersToRemove = [];
+
+ /** @var Parameter[] $globalParams */
+ $globalParams = $api->getParameters();
+ /** @var Parameters|Parameter[] $currentParams */
+ $currentParams = $operation->getParameters();
+
+ foreach ($currentParams as $parameter) {
+ $ref = $parameter->getRef();
+
+ if (null === $ref) {
+ // we only concern ourselves with '$ref' parameters
+ continue;
+ }
+
+ $ref = \mb_substr($ref, 13); // trim the '#/parameters/' part of ref
+ if (!isset($globalParams[$ref])) {
+ // this really shouldn't happen, if it does there will be other failures elsewhere, so just ignore here
+ continue;
+ }
+
+ $refParameter = $globalParams[$ref];
+ // param ids are in form {name}/{in}
+ $refParameterId = \sprintf('%s/%s', $refParameter->getName(), $refParameter->getIn());
+ if ($currentParams->has($refParameterId)) {
+ // if we got here it means a duplicate parameter is directly defined, schedule it for removal
+ $parametersToRemove[] = $currentParams->get($refParameterId);
+ }
+ }
+
+ foreach ($parametersToRemove as $parameterToRemove) {
+ $currentParams->remove($parameterToRemove);
+ }
+ }
+}
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index 978930e..c6ad025 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -32,6 +32,10 @@
+
+
+
+
diff --git a/Tests/Describer/AbstractDescriberTest.php b/Tests/Describer/AbstractDescriberTest.php
index b1cca2d..f73dcb3 100644
--- a/Tests/Describer/AbstractDescriberTest.php
+++ b/Tests/Describer/AbstractDescriberTest.php
@@ -12,10 +12,12 @@
namespace Nelmio\ApiDocBundle\Tests\Describer;
use EXSyst\Component\Swagger\Swagger;
+use Nelmio\ApiDocBundle\Describer\DescriberInterface;
use PHPUnit\Framework\TestCase;
abstract class AbstractDescriberTest extends TestCase
{
+ /** @var DescriberInterface */
protected $describer;
protected function getSwaggerDoc(): Swagger
diff --git a/Tests/Describer/ParameterRefMergeDescriberTest.php b/Tests/Describer/ParameterRefMergeDescriberTest.php
new file mode 100644
index 0000000..bd2b666
--- /dev/null
+++ b/Tests/Describer/ParameterRefMergeDescriberTest.php
@@ -0,0 +1,64 @@
+ '2.0',
+ 'info' => ['title' => 'Ref Test'],
+ 'paths' => [
+ '/api/{version}/product' => [
+ 'get' => [
+ 'parameters' => [
+ [
+ '$ref' => '#/parameters/versionParam',
+ ],
+ [
+ 'name' => 'version',
+ 'in' => 'path',
+ 'required' => true,
+ 'type' => 'string',
+ 'pattern' => 'v\\d+',
+ ],
+ ],
+ ],
+ ],
+ ],
+ 'parameters' => [
+ 'versionParam' => [
+ 'name' => 'version',
+ 'in' => 'path',
+ 'required' => true,
+ 'type' => 'string',
+ ],
+ ],
+ ];
+ $api = new Swagger($apiDef);
+ $this->describer->describe($api);
+
+ $describedData = $api->toArray();
+ // only one parameter should remain as they were duplicates
+ $this->assertCount(1, $describedData['paths']['/api/{version}/product']['get']['parameters']);
+ }
+
+ protected function setUp()
+ {
+ $this->describer = new ParameterRefMergeDescriber();
+ }
+}