@@ -1225,7 +1225,7 @@ public static function getMimeTypes(string $format): array
12251225 /**
12261226 * Gets the format associated with the mime type.
12271227 */
1228- public function getFormat (?string $ mimeType ): ?string
1228+ public function getFormat (?string $ mimeType, bool $ subtypeFallback = false ): ?string
12291229 {
12301230 $ canonicalMimeType = null ;
12311231 if ($ mimeType && false !== $ pos = strpos ($ mimeType , '; ' )) {
@@ -1245,6 +1245,27 @@ public function getFormat(?string $mimeType): ?string
12451245 }
12461246 }
12471247
1248+ if (!$ canonicalMimeType ) {
1249+ $ canonicalMimeType = $ mimeType ;
1250+ }
1251+
1252+ if (str_starts_with ($ canonicalMimeType , 'application/ ' ) && str_contains ($ canonicalMimeType , '+ ' )) {
1253+ $ suffix = substr (strrchr ($ canonicalMimeType , '+ ' ), 1 );
1254+ if (isset (static ::getStructuredSuffixFormats ()[$ suffix ])) {
1255+ return static ::getStructuredSuffixFormats ()[$ suffix ];
1256+ }
1257+ }
1258+
1259+ if ($ subtypeFallback && str_contains ($ canonicalMimeType , '/ ' )) {
1260+ [, $ subtype ] = explode ('/ ' , $ canonicalMimeType , 2 );
1261+ if (str_starts_with ($ subtype , 'x- ' )) {
1262+ $ subtype = substr ($ subtype , 2 );
1263+ }
1264+ if (!str_contains ($ subtype , '+ ' )) {
1265+ return $ subtype ;
1266+ }
1267+ }
1268+
12481269 return null ;
12491270 }
12501271
@@ -1917,6 +1938,41 @@ protected static function initializeFormats(): void
19171938 'atom ' => ['application/atom+xml ' ],
19181939 'rss ' => ['application/rss+xml ' ],
19191940 'form ' => ['application/x-www-form-urlencoded ' , 'multipart/form-data ' ],
1941+ 'soap ' => ['application/soap+xml ' ],
1942+ 'problem ' => ['application/problem+json ' ],
1943+ 'hal ' => ['application/hal+json ' , 'application/hal+xml ' ],
1944+ 'jsonapi ' => ['application/vnd.api+json ' ],
1945+ 'yaml ' => ['text/yaml ' , 'application/x-yaml ' ],
1946+ 'wbxml ' => ['application/vnd.wap.wbxml ' ],
1947+ 'pdf ' => ['application/pdf ' ],
1948+ 'csv ' => ['text/csv ' ],
1949+ ];
1950+ }
1951+
1952+ /**
1953+ * Structured MIME suffix fallback formats
1954+ *
1955+ * This mapping is used when no exact MIME match is found in $formats.
1956+ * It enables handling of types like application/soap+xml → 'xml'.
1957+ *
1958+ * @see https://datatracker.ietf.org/doc/html/rfc6839
1959+ * @see https://datatracker.ietf.org/doc/html/rfc7303
1960+ *
1961+ * @return array<string, string>
1962+ */
1963+ private static function getStructuredSuffixFormats (): array
1964+ {
1965+ return [
1966+ 'json ' => 'json ' ,
1967+ 'xml ' => 'xml ' ,
1968+ 'xhtml ' => 'html ' ,
1969+ 'cbor ' => 'cbor ' ,
1970+ 'zip ' => 'zip ' ,
1971+ 'ber ' => 'asn1 ' ,
1972+ 'der ' => 'asn1 ' ,
1973+ 'tlv ' => 'tlv ' ,
1974+ 'wbxml ' => 'xml ' ,
1975+ 'yaml ' => 'yaml ' ,
19201976 ];
19211977 }
19221978
0 commit comments