Problem/Motivation
With #1921558: Convert file_get_mimetype() to use Symfony MimeTypeGuessers, the function file_mimetype_mapping()
was removed. The mapping was placed as a protected property of the ExtensionMimeTypeGuesser
class, as its main purpose was for the guesser to use it. But there are cases when the mapping, or just the list of MIME types, should be used in itself, outside the context of guessing. For instance, a form in the File entity module should present known MIME types for the user.
Proposed resolution
Move the default mapping to a new service and class, Drupal\Core\File\MimeType\MimeTypeMapper
, with getter/setter methods.
Introduce an alterMapping()
method which invokes the mimetype
alter hook upon service instantiation to allow modules to play with MIME type<->extension mapping.
Change the ExtensionMimeTypeGuesser
class to use the new mapper; deprecate its ::setMapping
method.
Deprecate the file_mimetype_mapping
alter hook.
Add tests.
Add tests.
Remaining tasks
Commit
User interface changes
None
API changes
A new service file.mime_type.mapper
.
Beta phase evaluation
Issue category | Bug because functionality in previous version is now missing |
---|---|
Issue priority | Major because it is a small regression. In D7 one could access the mapping out of the context of guessing, and could eg get a list of all known mimetypes . After #1921558: Convert file_get_mimetype() to use Symfony MimeTypeGuessers its impossible without extending the class, because the mapping is a protected property |
Disruption |
None, a BC layer covers the case of custom/contrib classes extending from ExtensionMimeTypeGuesser .
|
Comment | File | Size | Author |
---|---|---|---|
#116 | 2311679-nr-bot.txt | 150 bytes | needs-review-queue-bot |
#103 | 2311679-103.patch | 53.13 KB | pcambra |
#98 | 2311679-98.patch | 74.21 KB | yogeshmpawar |
Issue fork drupal-2311679
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 #1
ArlaComment #2
ArlaThis patch moves the mapping from ExtensionMimeTypeGuesser to the new MimeTypeMapper, which is a service, and lets ExtensionMimeTypeGuesser use that mapper.
Comment #3
ParisLiakos CreditAttribution: ParisLiakos commentedthanks for opening the issue
I do not see the reason to have this public getter, just retrieve the mapper from the $this->container
Needs an interface, so that its swappable
Comment #4
ArlaAdjusted from the feedback in #3.
Comment #5
BerdirOne thing that we were discussing was if and how much of this logic should move to the mapper?
Right now, there is just getMapping(). But that's a very arbitrary structure, wouldn't it be nice to have getMimeTypes() too, if you just need those?
And if we go there, would it be useful if the mapper would possibly even have a method like getMimeTypeForExtension($extension)? Then this would be re-usable if you'd just have a extension and not an actual file. Not sure if we should consider that at all or in a separate issue...
Why do we need a setter? If you want to alter it, use the hook, this seems strange.
Comment #6
Arla2. I'm not sure why setMapping() would be needed, but it is tested in MimeTypeTest. I guess testing the alter hook would be more appropriate, and remove setMapping()?
Comment #7
ParisLiakos CreditAttribution: ParisLiakos commentedtotally agreed with 1.
2. is strange indeed and was there pre-conversion, see also #1921558-17: Convert file_get_mimetype() to use Symfony MimeTypeGuessers.
In other words, the hook will only fire if the default mapping is used: The setter overrides the hook.
I very much support to make the hook fire *always* and get rid of the setter...or the other way around
Comment #8
ArlaHere the Mapper has also getMimeTypes() and getMimeTypeForExtension(). The test for custom mapping is removed. Is it desirable to add a test for the alter hook?
The alter hook is only run on the first call to getMapping(). Unless the guesser is used before registering all hook implementations, this should not be a problem.
Comment #11
alexpott#2388749: Register symfony's mime guessers if they are supported is related.
Comment #12
mondrake#2388749: Register symfony's mime guessers if they are supported has been committed, and it removed
hook_file_mimetype_mapping_alter()
.Rerolled patch in #8 (see do-not-test patch), and removed the alter call here too. Interdiff against the reroll.
Would love to see this in as there was no straightforward way in contrib to map extensions to mimetypes before #2388749: Register symfony's mime guessers if they are supported and also hacks like this
now no longer (legitimately I'd say) work.
Comment #13
Dave ReidWhat is the reasoning for removing the alter hook? This hook has been *very* useful for contrib to easily provide support for new file types when adding them to core would take much longer.
Comment #14
mondrake@dave see #2388749-10: Register symfony's mime guessers if they are supported
Comment #16
mondrakeFixed PHPUnit test failure.
Comment #17
ParisLiakos CreditAttribution: ParisLiakos commentedI dont think we should expose the mapping array, it is implementation details.
Instead lets provide methods.
Also added a method addMapping($mimetype, $extension) which adds/overwrites mappings.
If we agree on it i ll go ahead and write tests
Comment #18
mondrake#17
+1, but then I suggest also to have a method to return supported extensions given a MIME type, that would be possible to be done elsewhere with the mapping array exposed, but not otherwise. Done here.
Great! It's what I was suggesting in #2388749-39: Register symfony's mime guessers if they are supported.
+1 :)
Comment #21
alexpott#2388749: Register symfony's mime guessers if they are supported has been reverted
Comment #22
mondrake#21 new patch coming soon.
Comment #23
mondrakeNew patch - combining parts of #8 and parts of #18. Interdiff against #8.
This keeps new methods introduced by @ParisLiakos in #17 and by me in #18, and keeps (but changed to protected) the getMapping method in #8 which is needed to allow the alter call to
hook_file_mimetype_mapping_alter()
. I'd say it would be probably better to change the signature of the hook to pass the MimeTypeMapper object and use addMapping() in hook implementations instead of letting hooks manipulate the array by reference, as pointed out in #17. But I guess that needs discussion.Also added some tests for
MimeTypeMapper::getExtensionsForMimeType()
andMimeTypeMapper::addMapping()
.Comment #24
ParisLiakos CreditAttribution: ParisLiakos commentedlets only call getMapping() once and store it in a local variable instead of doing unnecessary function calls in the same method
Good idea!
Comment #25
mondrake#24 done
Comment #26
ParisLiakos CreditAttribution: ParisLiakos commentedcan we add a comment here? why this is needed
and maybe add a small test case for this method
Maybe those docs should refer to the interface methods ;)
Comment #27
mondrake#26 done.
#26.1 - this is needed to make sure that alter hook is invoked before adding any mapping. Maybe I am overcomplicating, but it could be a case that code outside of the hook implementation add a mapping, then the first time any other method is called the hook would fire and may override that mapping.
Also fixed a couple of doc issues.
Comment #28
ParisLiakos CreditAttribution: ParisLiakos commentedNice, thanks..so i guess all we need now is a beta evaluation template and updating https://www.drupal.org/node/2258015 with alter hook change.
other than that, this is rtbc
Comment #29
mondrake#28 updated change record draft
https://www.drupal.org/node/2258015/revisions/view/8067869/8077401
can anybody take care of the beta eval?
Comment #30
ParisLiakos CreditAttribution: ParisLiakos commentedthank you
I think this is a bug since its a regression
Comment #31
alexpottThere's a weird recursion going on here when the alter hook is fired.
This seems pointless - just return $this for a fluent interface.
This is not quite correct anymore.
Comment #32
mondrakeThanks @alexpott.
#31.1 - different approach here, alter hook is invoked at construction time before any method is called. Not sure it is OK.
#31.2 - done
#31.3 - :)
#31.4 - changed
also changed a small doc issue 'Contains \Drupal...' instead of 'Contains Drupal'
Comment #33
ParisLiakos CreditAttribution: ParisLiakos commentedno, we should avoid doing stuff on the constructor other than injecting dependencis, especially invoking a hook.
which is fine since
is called before the hook is called, so the function will just return $this->mapping, it wont do anything else.
Comment #34
alexpottI agree that doing the alter in the constructor is wrong. The alter should only be fired when necessary but also the recursive call during an alter is also weird. Plus we've lost the ability to remove mappings.
Comment #35
mondrakeWe are indeed facing recursion here, because if we call e.g. getMimeTypeForExtension, this will call getMapping which, on first run, will invoke the hook. Hook implementations then will call e.g. addMapping who in turn will call again getMapping. So here we need measures to avoid endless loops. I made a change here to make things I hope more explicit, starting back from #27. If any idea how to circumvent this, feel free :)
Next - adding removeMapping and removeMimeType, will post a patch soon.
Comment #36
mondrakeAdded
::removeMapping()
and::removeMimeType()
methods.Comment #37
mondrakeI found an alternative, i.e. using the 'calls' parameter in service definition to execute the alter hook separately from other methods, but not in the constructor (kind of 'setter injection' according to Symfony docs). Sorry, I'm learning here...
Comment #38
alexpott@mondrake that looks like a really nice solution.
Comment #39
mondrake#2260061: Responsive image module does not support sizes/picture polyfill 2.2 just introduced the below
which is OKish, but a workaround. With the patch here this could become
is it something to be added to this issue?
Comment #40
mondrakeBased on @yched's comment in #2426757-5: responsive_image_build_source_attributes() calls mime type guesser over and over, added #39. Also added a strtolower($extension) in
MimeTypeMapper::addMapping
to keep consistency with other methods.Comment #43
mondrakeRerolled.
Comment #46
mondrakeAny reviewers?
We were at RTBC back in #30 and @alexpott's feedback in #31 was addressed, see #38 also.
BTW we also have a published change record which is giving this for done, so it would have to be at least set back to draft while this is sorted out.
Comment #47
mondrakelazy: true
tag to thefile.mime_type.mapper
service, to match mime type guessing services definitionhook_file_mimetype_mapping_alter
String::format
toSafeMarkup::format
inMimeTypeTest
Comment #48
slashrsm CreditAttribution: slashrsm commentedComment #49
Dave ReidComment #52
mondrakeReroll.
Comment #54
mondrakeAdded/updated the proxy classes.
Comment #56
ArlaComment #57
mondrakeIMO, this needs decision before RC:
hook_file_mimetype_mapping_alter()
signature (passing aMimeTypeMapperInterface
object instead of the internal mapping array) will be hard to get in during 8.0 because of BCComment #59
mondrakeRerolled, conficts due to #2534066: Allow selecting the original image when creating a responsive image style, then made the logic in
responsive_image_get_mime_type
simpler to follow, and added comments.Comment #60
mondrakeComment #61
mondrakeThis is a soft blocker for ImageMagick, see #2612590: Allow configuring in the UI the image formats supported by the toolkit. It would allow the ImageMagick toolkit to be configurable in terms of image formats accepted. Now the supported file extensions are hardcoded in
ImagemagickToolkit::getSupportedExtensions()
similarly to GD core toolkit, but the difference with ImageMagick is that if you want to add support for an image format you do not need to write specific code for that format as in GD - you just have to have a version of the executable that supports the format you need.Comment #62
mondrakeComment #64
mondrakeJust a reroll. However this is now no longer acceptable I think, it breaks BC. This issue is still blocking a stable release for the ImageMagick module.
Comment #65
mondrakeAn attempt to move on without BC break. This restores the
hook_file_mimetype_mapping_alter
to its original signature, still keeping the new servicefile.mime_type.mapper
.Comment #67
mondrakeComment #69
alexpottAre we sure this needs to be lazy? Given this is already injected into a lazy service that always needs it I'm not sure.
Comment #70
mondrake@alexpott thanks, patch here removes lazy flag and proxy class.
Comment #71
mondrakeComment #73
mondrakeWhoops..
Comment #75
mondrakeLet's see this.
Comment #76
mondrakeComment #77
mondrakeRemoved no longer needed file docblocks. Anyone up for a review?
Comment #79
mondrakeReroll after #2796329: \Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser maps the 'js' extension to the 'application/x-javascript' MIME type instead of 'application/javascript'.
Comment #80
mondrakeA problem with #79 was that, even if existing API were not changed, still any class that was extending from ExtensionMimeTypeGuesser and trying to access its protected $mapping and $defaultMapping properties would fail, since the mapping is moved to the new class.
Here I am using a magic __get method in ExtensionMimeTypeGuesser, so that any get attempt from ExtensionMimeTypeGuesser::$mapping or ExtensionMimeTypeGuesser::defaultMapping will be redirected to the mapper service, and fetched through two public methods there, introduced for BC and marked @internal.
Comment #81
mondrakeComment #82
mondrakeThis issue is pretty much stalled :(
I wonder whether it would make sense to take a different approach in the short/medium term, i.e. just add the methods that would be needed in contrib to the
ExtensionMimeTypeGuesser
service:and postpone introducing the separate service
MimeTypeMapper
to D9.Any feedback?
Comment #87
mondrakeComment #88
mondrakeTwo years later... I would still like this to move on. Getting the list of available MIME types or the list of file extensions mapped to a MIME type is not possible, and workarounds are not good.
In the ImageMagick module, at one point I was extending a class from the guesser, but this was forcing the alter hook to be called again. Lately I am using reflection to get the value of the $mapping array after it has been processed by the alter hook, but again this smells bad.
Relaunching here. This patch (sorry no interdiff):
file.mime_type.mapper
service, implemented through aDrupal\Core\File\MimeType\MimeTypeMapper
class (with a supporting Interface), that advocates the execution of thehook_file_mimetype_mapping_alter
hook and provides OO methods to retrieve and manipulate the MIME type to extension mapping array.ExtensionMimeTypeGuesser
class to use the new mapper; the::setMapping
method is deprecated, and deprecation tests are added.hook_file_mimetype_mapping_alter
hook to pass the mapper object in addition to the $mapping array. This for BC, in D9 the $mapping array should be removed. Hook implementations can, as of the release after which the changes are made, either manipulate the array directly or use the OO methods to alter the mapping.Comment #89
andypostthis means no BC, so not sure it is possible in minor versions
Comment #90
mondrakeOK, I agree it is confusing. Let me do sth else.
Comment #91
mondrakeInstead of changing the hook signature, this is now deprecating
hook_file_mimetype_mapping_alter
and replacing it with ahook_mimetype_alter
, where the mapper is passed as an argument.If we agree this is the sustainable way forward, I will add a test for the hook deprecation.
Comment #92
mondrakeUpdated IS.
Comment #93
mondrakeAdded deprecation tests for
hook_file_mimetype_mapping_alter
.Comment #95
mondrakeFTR, if anyone interested, I released today a beta1 version of the Sophron module:
Features
Comment #96
mondrakeComment #97
yogeshmpawarComment #98
yogeshmpawarRe-roll of #93.
Comment #100
yogeshmpawarSeems like these test fails are unrelated so setting back to Needs Review & Triggering bots.
Comment #102
apadernoComment #103
pcambraRe-rolled as patch was not applying on core.services.yml
Patch is smaller due to the similarity from ExtensionMimeTypeGuesser with MimeTypeMapper
Comment #111
quietone CreditAttribution: quietone at PreviousNext commentedThis was a bugsmash triage target. kim.pepper agreed it was a bug. So let's get a reroll here.
Comment #114
Bhanu951 CreditAttribution: Bhanu951 as a volunteer commentedComment #115
Bhanu951 CreditAttribution: Bhanu951 as a volunteer commentedOne last test failing.
ExtensionMimeTypeGuesserDeprecationTest::testConstructorDeprecation
test is failing due toExtensionMimeTypeGuesser::$mapping or ExtensionMimeTypeGuesser::$defaultMapping
are not being redirected to the mapper service.I am not sure if we should add setters or getters as added in #80 to maintain backwards compatibility.
Comment #116
needs-review-queue-bot CreditAttribution: needs-review-queue-bot as a volunteer 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.