55namespace Membrane \OpenAPIReader \ValueObject \Valid \V30 ;
66
77use Membrane \OpenAPIReader \Exception \InvalidOpenAPI ;
8- use Membrane \OpenAPIReader \OpenAPIVersion ;
98use Membrane \OpenAPIReader \ValueObject \Partial ;
109use Membrane \OpenAPIReader \ValueObject \Valid \Enum \In ;
1110use Membrane \OpenAPIReader \ValueObject \Valid \Enum \Style ;
@@ -67,17 +66,8 @@ public function __construct(
6766 $ parameter ->required
6867 );
6968
70- $ this ->style = $ this ->validateStyle (
71- $ identifier ,
72- $ this ->in ,
73- $ parameter ->style ,
74- );
75-
76- $ this ->explode = $ parameter ->explode ?? $ this ->style ->defaultExplode ();
77-
78- if (isset ($ parameter ->schema ) !== empty ($ parameter ->content )) {
69+ isset ($ parameter ->schema ) === empty ($ parameter ->content ) ?:
7970 throw InvalidOpenAPI::mustHaveSchemaXorContent ($ parameter ->name );
80- }
8171
8272 if (isset ($ parameter ->schema )) {
8373 $ this ->content = [];
@@ -95,7 +85,14 @@ public function __construct(
9585 );
9686 }
9787
98- $ this ->checkStyleSuitability ();
88+ $ this ->style = $ this ->validateStyle (
89+ $ identifier ,
90+ $ this ->getSchema (),
91+ $ this ->in ,
92+ $ parameter ->style ,
93+ );
94+
95+ $ this ->explode = $ parameter ->explode ?? $ this ->style ->defaultExplode ();
9996 }
10097
10198 public function getSchema (): Schema
@@ -129,24 +126,42 @@ public function isSimilar(Parameter $other): bool
129126 mb_strtolower ($ this ->name ) === mb_strtolower ($ other ->name );
130127 }
131128
132- public function canConflict (Parameter $ other ): bool
129+ public function canConflictWith (Parameter $ other ): bool
133130 {
134- if (
135- $ this ->in !== $ other ->in || // parameter can be identified by differing location
136- $ this ->style !== $ other ->style || // parameter can be identified by differing style
137- $ this ->in !== In::Query
138- ) {
139- return false ;
140- }
131+ return ($ this ->canCauseConflict () && $ other ->isVulnerableToConflict ()) ||
132+ ($ this ->isVulnerableToConflict () && $ other ->canCauseConflict ());
133+ }
134+
135+ private function canCauseConflict (): bool
136+ {
137+ return $ this ->in === In::Query &&
138+ $ this ->style === Style::Form &&
139+ $ this ->explode &&
140+ $ this ->getSchema ()->canBe (Type::Object);
141+ }
141142
142- return match ($ this ->style ) {
143- Style::Form =>
144- $ this ->explode &&
145- $ other ->explode &&
146- $ this ->getSchema ()->canBe (Type::Object),
147- Style::PipeDelimited, Style::SpaceDelimited =>
148- !$ this ->getSchema ()->canOnlyBePrimitive (),
149- default => false ,
143+ private function isVulnerableToConflict (): bool
144+ {
145+ /**
146+ * @todo once schemas account for minItems and minProperties keywords.
147+ * pipeDelimited and spaceDelimited are also vulnerable if:
148+ * type:array and minItems <= 1
149+ * this is because there would be no delimiter to distinguish it from a form parameter
150+ *
151+ * form would not be vulnerable if:
152+ * explode:false
153+ * and...
154+ * type:object and minProperties > 1
155+ * or ...
156+ * type:array and minItems > 1
157+ * this is because there would be a delimiter to distinguish it from an exploding parameter
158+ */
159+
160+ return $ this ->in === In::Query && match ($ this ->style ) {
161+ Style::Form => true ,
162+ Style::PipeDelimited,
163+ Style::SpaceDelimited => $ this ->getSchema ()->canBePrimitive (),
164+ default => false
150165 };
151166 }
152167
@@ -174,6 +189,7 @@ private function validateRequired(
174189
175190 private function validateStyle (
176191 Identifier $ identifier ,
192+ Schema $ schema ,
177193 In $ in ,
178194 ?string $ style
179195 ): Style {
@@ -187,6 +203,20 @@ private function validateStyle(
187203 $ style ->isAllowed ($ in ) ?:
188204 throw InvalidOpenAPI::parameterIncompatibleStyle ($ identifier );
189205
206+ $ style !== Style::DeepObject || $ schema ->canOnlyBe (Type::Object) ?:
207+ throw InvalidOpenAPI::deepObjectMustBeObject ($ identifier );
208+
209+ if (
210+ in_array ($ style , [Style::SpaceDelimited, Style::PipeDelimited]) &&
211+ $ schema ->canBePrimitive ()
212+ ) {
213+ $ this ->addWarning (
214+ "style: $ style ->value , is not allowed to be primitive. " .
215+ 'In these instances style:form is recommended. ' ,
216+ Warning::UNSUITABLE_STYLE
217+ );
218+ }
219+
190220 return $ style ;
191221 }
192222
@@ -218,20 +248,4 @@ private function validateContent(
218248 ),
219249 ];
220250 }
221-
222- private function checkStyleSuitability (): void
223- {
224- foreach (Type::casesForVersion (OpenAPIVersion::Version_3_0) as $ type ) {
225- if (
226- $ this ->getSchema ()->canBe ($ type ) &&
227- $ this ->style ->isSuitableFor ($ type )
228- ) {
229- return ;
230- }
231- }
232- $ this ->addWarning (
233- 'unsuitable style for primitive data types ' ,
234- Warning::UNSUITABLE_STYLE
235- );
236- }
237251}
0 commit comments