diff --git a/core/core.services.yml b/core/core.services.yml index bee7b9f..1bceb3c 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1567,7 +1567,7 @@ services: lazy: true file.mime_type.guesser.extension: class: Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser - arguments: ['@file.mime_type.mapper'] + arguments: ['@module_handler'] tags: - { name: mime_type_guesser } lazy: true diff --git a/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php b/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php index ccc7c2b..dc92133 100644 --- a/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php +++ b/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php @@ -2,6 +2,7 @@ namespace Drupal\Core\File\MimeType; +use Drupal\Core\Extension\ModuleHandlerInterface; use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface; /** @@ -19,11 +20,11 @@ class ExtensionMimeTypeGuesser implements MimeTypeGuesserInterface { /** * Constructs a new ExtensionMimeTypeGuesser. * - * @param \Drupal\Core\File\MimeType\MimeTypeMapperInterface $mapper - * A MIME type mapper. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. */ - public function __construct(MimeTypeMapperInterface $mapper) { - $this->mapper = $mapper; + public function __construct(ModuleHandlerInterface $module_handler) { + $this->moduleHandler = $module_handler; } /** @@ -43,7 +44,7 @@ public function guess($path) { // - awesome.image.jpeg while ($additional_part = array_pop($file_parts)) { $extension = strtolower($additional_part . ($extension ? '.' . $extension : '')); - $mime_type = $this->mapper->getMimeTypeForExtension($extension); + $mime_type = $this->getMapper()->getMimeTypeForExtension($extension); if ($mime_type) { return $mime_type; } @@ -51,4 +52,28 @@ public function guess($path) { return 'application/octet-stream'; } + + /** + * Sets the mimetypes/extension mapping to use when guessing mimetype. + * + * @param array|null $mapping + * Passing a NULL mapping will cause guess() to use self::$defaultMapping. + */ + public function setMapping(array $mapping = NULL) { + $this->getMapper()->setMapping($mapping); + } + + /** + * Returns the MIME type mapper service. + * + * @return \Drupal\Core\File\MimeType\MimeTypeMapperInterface + * The MIME type mapper service. + */ + protected function getMapper() { + if (!$this->mapper) { + $this->mapper = \Drupal::service('file.mime_type.mapper'); + } + return $this->mapper; + } + } diff --git a/core/lib/Drupal/Core/File/MimeType/MimeTypeMapper.php b/core/lib/Drupal/Core/File/MimeType/MimeTypeMapper.php index ac68b2d..4ea3f2d 100644 --- a/core/lib/Drupal/Core/File/MimeType/MimeTypeMapper.php +++ b/core/lib/Drupal/Core/File/MimeType/MimeTypeMapper.php @@ -874,7 +874,7 @@ class MimeTypeMapper implements MimeTypeMapperInterface { */ public function alterMapping(ModuleHandlerInterface $module_handler) { if (!$this->isMappingAltered) { - $module_handler->alter('file_mimetype_mapping', $this); + $module_handler->alter('file_mimetype_mapping', $this->mapping); $this->isMappingAltered = TRUE; } return $this; @@ -897,6 +897,14 @@ protected function getMapping() { /** * {@inheritdoc} */ + protected function setMapping(array $mapping) { + $this->mapping = $mapping; + return $this; + } + + /** + * {@inheritdoc} + */ public function addMapping($mimetype, $extension) { $extension = strtolower($extension); if (!in_array($mimetype, $this->mapping['mimetypes'])) { diff --git a/core/lib/Drupal/Core/File/MimeType/MimeTypeMapperInterface.php b/core/lib/Drupal/Core/File/MimeType/MimeTypeMapperInterface.php index 1486c00..0c9d097 100644 --- a/core/lib/Drupal/Core/File/MimeType/MimeTypeMapperInterface.php +++ b/core/lib/Drupal/Core/File/MimeType/MimeTypeMapperInterface.php @@ -28,6 +28,20 @@ public function alterMapping(ModuleHandlerInterface $module_handler); /** + * Set the mapping array between MIME types and file extensions. + * + * @param array $mapping + * An array consisting of two arrays: + * - mimetypes: MIME types, keyed by a unique number. + * - extensions: an associative array with the MIME type key numbers as + * values. The keys are file extensions, in lower case and without any + * preceding dot. + * + * @return $this + */ + public function setMapping($mimetype, $extension); + + /** * Adds a mapping between a MIME type and an extension. * * @param string $mimetype diff --git a/core/lib/Drupal/Core/File/file.api.php b/core/lib/Drupal/Core/File/file.api.php index 290a3b0..315749c 100644 --- a/core/lib/Drupal/Core/File/file.api.php +++ b/core/lib/Drupal/Core/File/file.api.php @@ -100,45 +100,27 @@ function hook_file_url_alter(&$uri) { } /** - * Alter the mapping of MIME types to file extensions. - * - * Invoked by \Drupal\Core\File\MimeType\MimeTypeMapper::alterMapping(). - * - * It is used to allow modules to add to or modify the default mapping from - * \Drupal\Core\File\MimeType\MimeTypeMapper::$mapping. Implementations should - * use the \Drupal\Core\File\MimeType\MimeTypeMapperInterface::addMapping() - * method to add/override MIME type to file extension mapping, - * \Drupal\Core\File\MimeType\MimeTypeMapperInterface::removeMapping() to - * delete a file extension mapping to a MIME type, - * \Drupal\Core\File\MimeType\MimeTypeMapperInterface::removeMimeType() to - * entirely remove a MIME type and all file extensions mapping to it. - * The - * \Drupal\Core\File\MimeType\MimeTypeMapperInterface::getMimeTypeForExtension() - * and - * \Drupal\Core\File\MimeType\MimeTypeMapperInterface::getExtensionsForMimeType() - * methods can be used to get the current mapping respectively of the MIME - * type of a given extension, and of the extensions associated to a given - * MIME type. - * The - * \Drupal\Core\File\MimeType\MimeTypeMapperInterface::getMimeTypes() can be - * used to get the list of supported MIME types. - * - * @param \Drupal\Core\File\MimeType\MimeTypeMapperInterface $mime_type_mapper - * The MIME type mapper object. - * - * @see \Drupal\Core\File\MimeType\MimeTypeMapperInterface::addMapping() - * @see \Drupal\Core\File\MimeType\MimeTypeMapperInterface::removeMapping() - * @see \Drupal\Core\File\MimeType\MimeTypeMapperInterface::removeMimeType() - * @see \Drupal\Core\File\MimeType\MimeTypeMapperInterface::getMimeTypes() - * @see \Drupal\Core\File\MimeType\MimeTypeMapperInterface::getMimeTypeForExtension() - * @see \Drupal\Core\File\MimeType\MimeTypeMapperInterface::getExtensionsForMimeType() + * Alter MIME type mappings used to determine MIME type from a file extension. + * + * Invoked by \Drupal\Core\File\MimeType\MimeTypeMapper::alterMapping(). It is + * used to allow modules to add to or modify the default mapping from + * \Drupal\Core\File\MimeType\MimeTypeMapper::$mapping. + * + * @param $mapping + * An array of mimetypes correlated to the extensions that relate to them. + * The array has 'mimetypes' and 'extensions' elements, each of which is an + * array. + * + * @see \Drupal\Core\File\MimeType\MimeTypeMapper::alterMapping() + * @see \Drupal\Core\File\MimeType\MimeTypeMapper::$mapping */ -function hook_file_mimetype_mapping_alter(\Drupal\Core\File\MimeType\MimeTypeMapperInterface $mime_type_mapper) { - // Add new MIME type 'drupal/info', and map it to a new extension - // '.info.yml'. - $mime_type_mapper->addMapping('drupal/info', 'info.yml'); +function hook_file_mimetype_mapping_alter(&$mapping) { + // Add new MIME type 'drupal/info'. + $mapping['mimetypes']['example_info'] = 'drupal/info'; + // Add new extension '.info.yml' and map it to the 'drupal/info' MIME type. + $mapping['extensions']['info'] = 'example_info'; // Override existing extension mapping for '.ogg' files. - $mime_type_mapper->addMapping('audio/ogg', 'ogg'); + $mapping['extensions']['ogg'] = 189; } /** diff --git a/core/lib/Drupal/Core/ProxyClass/File/MimeType/ExtensionMimeTypeGuesser.php b/core/lib/Drupal/Core/ProxyClass/File/MimeType/ExtensionMimeTypeGuesser.php index c698afb..ccaa8cc 100644 --- a/core/lib/Drupal/Core/ProxyClass/File/MimeType/ExtensionMimeTypeGuesser.php +++ b/core/lib/Drupal/Core/ProxyClass/File/MimeType/ExtensionMimeTypeGuesser.php @@ -75,6 +75,14 @@ public function guess($path) return $this->lazyLoadItself()->guess($path); } + /** + * {@inheritdoc} + */ + public function setMapping(array $mapping = NULL) + { + return $this->lazyLoadItself()->setMapping($mapping); + } + } } diff --git a/core/modules/file/tests/file_test/file_test.module b/core/modules/file/tests/file_test/file_test.module index 19b6ae9..7b25af4 100644 --- a/core/modules/file/tests/file_test/file_test.module +++ b/core/modules/file/tests/file_test/file_test.module @@ -9,7 +9,6 @@ */ use Drupal\file\Entity\File; -use Drupal\Core\File\MimeType\MimeTypeMapperInterface; const FILE_URL_TEST_CDN_1 = 'http://cdn1.example.com'; const FILE_URL_TEST_CDN_2 = 'http://cdn2.example.com'; @@ -292,14 +291,16 @@ function file_test_file_url_alter(&$uri) { /** * Implements hook_file_mimetype_mapping_alter(). */ -function file_test_file_mimetype_mapping_alter(MimeTypeMapperInterface $mime_type_mapper) { +function file_test_file_mimetype_mapping_alter(&$mapping) { // Add new mappings. - $mime_type_mapper->addMapping('madeup/file_test_1', 'file_test_1'); - $mime_type_mapper->addMapping('madeup/file_test_2', 'file_test_2'); - $mime_type_mapper->addMapping('madeup/file_test_2', 'file_test_3'); - $mime_type_mapper->addMapping('drupal/info', 'info.yml'); + $mapping['mimetypes']['file_test_mimetype_1'] = 'madeup/file_test_1'; + $mapping['mimetypes']['file_test_mimetype_2'] = 'madeup/file_test_2'; + $mapping['mimetypes']['file_test_mimetype_3'] = 'madeup/doc'; + $mapping['extensions']['file_test_1'] = 'file_test_mimetype_1'; + $mapping['extensions']['file_test_2'] = 'file_test_mimetype_2'; + $mapping['extensions']['file_test_3'] = 'file_test_mimetype_2'; // Override existing mapping. - $mime_type_mapper->addMapping('madeup/doc', 'doc'); + $mapping['extensions']['doc'] = 'file_test_mimetype_3'; } /** diff --git a/core/tests/Drupal/KernelTests/Core/File/MimeTypeTest.php b/core/tests/Drupal/KernelTests/Core/File/MimeTypeTest.php index 336a51e..7c34554 100644 --- a/core/tests/Drupal/KernelTests/Core/File/MimeTypeTest.php +++ b/core/tests/Drupal/KernelTests/Core/File/MimeTypeTest.php @@ -2,8 +2,6 @@ namespace Drupal\KernelTests\Core\File; -use Drupal\Component\Utility\SafeMarkup; - /** * Tests filename mimetype detection. * @@ -39,7 +37,6 @@ public function testFileMimeTypeDetection() { 'foo.file_test_2' => 'madeup/file_test_2', 'foo.doc' => 'madeup/doc', 'test.ogg' => 'audio/ogg', - 'test.info.yml' => 'drupal/info', ); $guesser = $this->container->get('file.mime_type.guesser'); @@ -47,44 +44,46 @@ public function testFileMimeTypeDetection() { foreach ($test_case as $input => $expected) { // Test stream [URI]. $output = $guesser->guess($prefix . $input); - $this->assertIdentical($output, $expected, SafeMarkup::format('Mimetype for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); + $this->assertIdentical($output, $expected, format_string('Mimetype for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); // Test normal path equivalent $output = $guesser->guess($input); - $this->assertIdentical($output, $expected, SafeMarkup::format('Mimetype (using default mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); + $this->assertIdentical($output, $expected, format_string('Mimetype (using default mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); } - // Now test the extension guesser by altering the current mapping. - $mapping_overrides = array( - 'jar' => 'bar/foo', - 'jpg' => 'fox/trot', + // Now test the extension gusser by passing in a custom mapping. + $mapping = array( + 'mimetypes' => array( + 0 => 'application/java-archive', + 1 => 'image/jpeg', + ), + 'extensions' => array( + 'jar' => 0, + 'jpg' => 1, + ) ); - $mime_type_mapper = $this->container->get('file.mime_type.mapper'); - foreach ($mapping_overrides as $extension => $mimetype) { - $mime_type_mapper->addMapping($mimetype, $extension); - } $test_case = array( - 'test.jar' => 'bar/foo', - 'test.jpeg' => 'image/jpeg', - 'test.JPEG' => 'image/jpeg', - 'test.jpg' => 'fox/trot', - 'test.jar.jpg' => 'fox/trot', - 'test.jpg.jar' => 'bar/foo', - 'test.pcf.Z' => 'application/x-font', + 'test.jar' => 'application/java-archive', + 'test.jpeg' => 'application/octet-stream', + 'test.jpg' => 'image/jpeg', + 'test.jar.jpg' => 'image/jpeg', + 'test.jpg.jar' => 'application/java-archive', + 'test.pcf.z' => 'application/octet-stream', 'pcf.z' => 'application/octet-stream', 'jar' => 'application/octet-stream', 'some.junk' => 'application/octet-stream', - 'foo.file_test_1' => 'madeup/file_test_1', - 'foo.file_test_2' => 'madeup/file_test_2', - 'foo.doc' => 'madeup/doc', - 'test.ogg' => 'audio/ogg', - 'test.info.yml' => 'drupal/info', + 'foo.file_test_1' => 'application/octet-stream', + 'foo.file_test_2' => 'application/octet-stream', + 'foo.doc' => 'application/octet-stream', + 'test.ogg' => 'application/octet-stream', ); + $extension_guesser = $this->container->get('file.mime_type.guesser.extension'); + $extension_guesser->setMapping($mapping); foreach ($test_case as $input => $expected) { - $output = $guesser->guess($input); - $this->assertIdentical($output, $expected, SafeMarkup::format('Mimetype (using altered mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); + $output = $extension_guesser->guess($input); + $this->assertIdentical($output, $expected, format_string('Mimetype (using passed-in mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); } // Now test that MIME types added via alter hook or directly via @@ -100,13 +99,13 @@ public function testFileMimeTypeDetection() { 'drupal/info' => ['info.yml'], ]; $expected = array_keys($test_case); - $this->assertIdentical(array_intersect($expected, $mime_type_mapper->getMimeTypes()), $expected, 'Mime type mappings added via alter hook and directly are present.'); + $this->assertIdentical($expected, array_intersect($expected, $mime_type_mapper->getMimeTypes())); // Now test the extensions returned by the MIME type mapper given // a MIME type. foreach ($test_case as $input => $expected) { $extensions = $mime_type_mapper->getExtensionsForMimeType($input); - $this->assertIdentical($extensions, $expected, SafeMarkup::format('File extensions (using altered mappings) for %input are \'%output\' (expected: \'%expected\').', array('%input' => $input, '%output' => implode(', ', $extensions), '%expected' => implode(', ', $expected)))); + $this->assertIdentical($extensions, $expected); } // Now test mapping removals.