Problem/Motivation
The file.mime_type.guesser employs the service collector pattern to collection all services tagged as mime_type_guesser and the calls each of the guessers until one of them returns a guess. In doing so it accounts for the documented behavior of mime type guessers, as MimeTypeGuesserInterface::guess() documents the return value as:
string|null The mime type or NULL, if none could be guessed
However, the default (and only) mime type guesser in core (ExtensionMimeTypeGuesser) does not follow this behavior and itself returns a default mime type (application/octet-stream) if the extension of the file is not a known one. Thus, any mime type guesser that would otherwise come afterwards is ignored.
The concrete use case is to employ Symfony's FileBinaryMimeTypeGuesser to determine the file's mime type by introspecting the file itself. The service collector pattern suggests that this should be easily possible, but it is not.
Proposed resolution
No longer return a default value from ExtensionMimeTypeGuesser::guess(). Note that because this is a behavioral change, this is targeted for 9.1, even though it is a bug report. (This assessment was not coordinated with release managers and, thus, may be invalid in either direction.)
The default value could either be returned by the service collector itself, if none of the guessers returned anything, or we could add a FallbackMimeTypeGuesser that always returns the default and has a very low priority allowing others to intervene before the fallback, but after ExtensionMimeTypeGuesser.
| Comment | File | Size | Author |
|---|
Issue fork drupal-3156672
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
tstoecklerNote that I added #3055193: [Symfony 5] The "Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser" class is deprecated since Symfony 4.3, use "Symfony\Component\Mime\MimeTypes" instead. as a related issue because it touches the same code that would/will be touched here and seems likely to land in the near future, but as far as I can tell it is orthogonal in the sense that that would not actually fix this issue or require a different fix here.
Comment #3
tstoecklerComment #4
tstoecklerFound #2388749: Register symfony's mime guessers if they are supported which is related to the use-case in the issue summary. In that patch the binary mime type guesser has a higher priority than the extension one, which is why this issue hasn't come up, I assume.
Comment #6
pieterdcThanks for sharing this info.
I created a small patch to make ExtensionMimeTypeGuesser::guess() adhere to its interface.
This will probably break some existing tests but it does fix the fallback to other mime_type_guesser services.
Comment #8
pieterdcThe failed tests need fixing.
Next to the already outlined benefit (in the ticket description), this change also allows to differentiate between a detected (guessed) 'application/octet-stream' mime type and no detected one.
Comment #9
longwaveThis seems like the best approach, as it avoids breaking backward compatibility while fixing this issue. Sites could also opt out by removing the fallback service if they wanted to for some reason.
We could also add a deprecation warning if the fallback is triggered to warn users that the default will be removed in Drupal 10.
Comment #11
longwaveImplemented a fallback guesser so other guessers can be inserted in the chain.
The existing test conveniently covers both the full chain and the existing extension guesser in separate cases so we can reuse that to test both the fallback and the NULL return.
The NULL return also means we have to fix the return type in the proxy class at the same time, which is currently missing the nullable part.
Comment #14
pieterdcTesting a patch from this issue with Drupal core 9.4.x, you also need the patch from #3190265-28: guessMimeType declaration not compatible with Symfony interface otherwise Drupal won't allow a NULL return value.
Comment #16
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #20
joegraduateRe-rolled #11 in MR !8288.
Comment #21
smustgrave commentedFor backwards compatibility concern can we add a deprecation for the return with a CR that says in D12 it will return null.
Comment #22
joegraduateComment #23
kim.pepperAs per https://www.drupal.org/about/core/policies/core-change-policies/drupal-d... we can't really trigger a deprecation warning or use the @deprecated annotation. Instead we need to document the behaviour change in the docblock, and add a @todo to switch to returning NULL in the next major.
Comment #24
kim.pepperFor example:
and
// @todo https://www.drupal.org/node/3156672 Return NULL in Drupal 12.0.0.Comment #25
kim.pepperTried a new approach to add a constructor param to trigger a deprecation.
Comment #26
kim.pepperHiding patch files
Comment #27
smustgrave commentedChanged deprecation to 12 vs 11. But deprecation looks good.
Comment #28
longwaveWhy do we need the deprecation here? The fallback means the end result is the same if you use the mime type guesser service properly, IMHO if you are calling file.mime_type.guesser.extension directly you are on your own.
Comment #29
kim.pepper@longwave I thought we were doing this issue to fix the problem of guessers that come after.
However, I think we can use priority to allow workarounds.
Reverted.
Comment #30
kim.pepperHmm. I'm having second thoughts about reverting. I think we need to return NULL.
Comment #31
kim.pepperSorry for all the noise. I think I went back and forth with this too many times, and made changes I shouldn't have.
Comment #32
alexpottI'm wondering whether adding a fallback guesser is really necessary and whether the best place to add the fallback is in \Drupal\Core\File\MimeType\MimeTypeGuesser::guessMimeType()
Comment #33
kim.pepperAgree there is no need for a
FallbackMimeTypeGuesserif we can return'application/octet-stream'by default from\Drupal\Core\File\MimeType\MimeTypeGuesser::guessMimeType()Comment #34
tstoecklerI don't really have an opinion on which approach is better, but this looks good to me as well. Updated the change notice for the new approach, so as far as I can tell this is good to go again.
Comment #35
alexpott@tstoeckler the advantage of this approach is now we don't need to be concerned with enforcing which mime type guesser comes last. Also less services.
Comment #37
longwaveGiven this is a bug fix but also a minor behaviour change let's not commit this to 10.3, but I think it can go in 10.4 and 11.0.
Committed and pushed cc9baee953 to 11.x and 98eea842a3 to 11.0.x and 2d42a30bba to 10.4.x. Thanks!
Will update and publish the change record.
Comment #41
xjmDiscussed with @longwave and @alexpott. It's weird to have a behavior change of any kind in 10.4 and 11.0 but not 10.3 -- 10.4 is supposed to be a maintenance minor that limits disruptive bugfixes, and 11.0 is almost-RC-ish as of the week this was committed.
We agreed the disruption is minimal enough to backport to 10.3.x, which resolves potential 10.3/11.0 parity issues with this API. There is a CR, so the few projects that might be affected can get the info they need.
If this does cause more serious issues for contrib or sites, please open a followup issue linking this one and tag it "Contributed project blocker" if appropriate. Thanks!
Comment #42
mondrakeFTR, Sophron module was adjusted to mirror this.
Comment #44
mxr576FTR, fixing this issue created an E_DEPRECATED warning elsewhere which has not been spotted by the test suite on D.o: #3466462: Fix handling of unknown file extensions in FileMediaFormatterBase
Although it breaks functional tests on downstream... ;S #3467081: E_DEPRECATED warnings makes functional tests fail
Comment #45
xjmAmending attribution.