diff --git a/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php b/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php index c6f5eaf..9119048 100644 --- a/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php +++ b/core/lib/Drupal/Core/File/MimeType/ExtensionMimeTypeGuesser.php @@ -35,7 +35,6 @@ public function __construct(MimeTypeMapperInterface $mapper) { * {@inheritdoc} */ public function guess($path) { - $mapping = $this->mapper->getMapping(); $extension = ''; $file_parts = explode('.', drupal_basename($path)); @@ -49,8 +48,9 @@ public function guess($path) { // - awesome.image.jpeg while ($additional_part = array_pop($file_parts)) { $extension = strtolower($additional_part . ($extension ? '.' . $extension : '')); - if (isset($mapping['extensions'][$extension])) { - return $mapping['mimetypes'][$mapping['extensions'][$extension]]; + $mime_type = $this->mapper->getMimeTypeForExtension($extension); + if ($mime_type) { + return $mime_type; } } diff --git a/core/lib/Drupal/Core/File/MimeType/MimeTypeMapper.php b/core/lib/Drupal/Core/File/MimeType/MimeTypeMapper.php index 627b458..b5b64de 100644 --- a/core/lib/Drupal/Core/File/MimeType/MimeTypeMapper.php +++ b/core/lib/Drupal/Core/File/MimeType/MimeTypeMapper.php @@ -14,7 +14,7 @@ class MimeTypeMapper implements MimeTypeMapperInterface { /** - * Default MIME extension mapping. + * MIME extension mapping. * * @var array * Array of mimetypes correlated to the extensions that relate to them. @@ -889,7 +889,7 @@ public function __construct(ModuleHandlerInterface $module_handler) { /** * {@inheritdoc} */ - public function getMapping() { + protected function getMapping() { if ($this->mapping === NULL) { $mapping = $this->defaultMapping; // Allow modules to alter the default mapping. @@ -902,19 +902,43 @@ public function getMapping() { /** * {@inheritdoc} */ + public function addMapping($mimetype, $extension) { + if (!in_array($mimetype, $this->getMapping()['mimetypes'])) { + $this->mapping['mimetypes'][] = $mimetype; + } + $key = array_search($mimetype, $this->getMapping()['mimetypes']); + $this->mapping['extensions'][$extension] = $key; + + return TRUE; + } + + /** + * {@inheritdoc} + */ public function getMimeTypes() { - // Fire alter hooks. - list($mimetypes,) = $this->getMapping(); - return $mimetypes; + return array_values($this->getMapping()['mimetypes']); } /** * {@inheritdoc} */ public function getMimeTypeForExtension($extension) { - // Fire alter hooks. - list($mimetypes, $extensions) = $this->getMapping(); $extension = strtolower($extension); - return isset($extensions[$extension]) ? $mimetypes[$extensions[$extension]] : NULL; + $extensions = $this->getMapping()['extensions']; + return isset($extensions[$extension]) ? $this->getMapping()['mimetypes'][$extensions[$extension]] : NULL; } + + /** + * {@inheritdoc} + */ + public function getExtensionsForMimeType($mimetype) { + if (!in_array($mimetype, $this->getMapping()['mimetypes'])) { + return []; + } + $key = array_search($mimetype, $this->getMapping()['mimetypes']); + $extensions = array_keys($this->getMapping()['extensions'], $key, TRUE); + sort($extensions); + return $extensions; + } + } diff --git a/core/lib/Drupal/Core/File/MimeType/MimeTypeMapperInterface.php b/core/lib/Drupal/Core/File/MimeType/MimeTypeMapperInterface.php index c348193..1143aa5 100644 --- a/core/lib/Drupal/Core/File/MimeType/MimeTypeMapperInterface.php +++ b/core/lib/Drupal/Core/File/MimeType/MimeTypeMapperInterface.php @@ -10,25 +10,27 @@ * Provides a sensible mapping between filename extensions and MIME types. */ interface MimeTypeMapperInterface { + /** - * Returns known MIME types. + * Adds a mapping between a mimetype and a extension. * - * @return string[] - * An indexed array of MIME types. + * @param string $mimetype + * The mimetype the passed extension should map. + * @param string $extension + * The extension that should map to the passed mimetype. + * + * @return bool + * Boolean TRUE if the mapping was added successfully, FALSE otherwise. */ - public function getMimeTypes(); + public function addMapping($mimetype, $extension); /** - * Returns a mapping from file extensions to appropriate MIME types. + * Returns known MIME types. * - * @return array - * 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 string[] + * An array of MIME types. */ - public function getMapping(); + public function getMimeTypes(); /** * Returns the appropriate MIME type for a given file extension. @@ -40,4 +42,16 @@ public function getMapping(); * A matching MIME type, or NULL if no MIME type matches the extension. */ public function getMimeTypeForExtension($extension); + + /** + * Returns the appropriate extensions for a given MIME type. + * + * @param string $mimetype + * A MIME type. + * + * @return string[] + * An array of file extensions matching the MIME type, without leading dot. + */ + public function getExtensionsForMimeType($mimetype); + } diff --git a/core/modules/system/src/Tests/File/MimeTypeTest.php b/core/modules/system/src/Tests/File/MimeTypeTest.php index 3d21b16..aa7e554 100644 --- a/core/modules/system/src/Tests/File/MimeTypeTest.php +++ b/core/modules/system/src/Tests/File/MimeTypeTest.php @@ -7,6 +7,8 @@ namespace Drupal\system\Tests\File; +use Drupal\Component\Utility\String; + /** * Tests filename mimetype detection. * @@ -55,5 +57,53 @@ public function testFileMimeTypeDetection() { $output = $guesser->guess($input); $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', + ); + $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', + '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', + ); + + foreach ($test_case as $input => $expected) { + $output = $guesser->guess($input); + $this->assertIdentical($output, $expected, String::format('Mimetype (using altered mappings) for %input is %output (expected: %expected).', array('%input' => $input, '%output' => $output, '%expected' => $expected))); + } + + // Now test the extensions returned by the MIME type mapper given + // a MIME type. + $test_case = [ + 'bar/foo' => ['jar'], + 'fox/trot' => ['jpg'], + 'image/jpeg' => ['jpe', 'jpeg'], + 'madeup/file_test_1' => ['file_test_1'], + 'madeup/file_test_2' => ['file_test_2', 'file_test_3'], + 'madeup/doc' => ['doc'], + ]; + foreach ($test_case as $input => $expected) { + $extensions = $mime_type_mapper->getExtensionsForMimeType($input); + $this->assertIdentical($extensions, $expected, String::format('File extensions (using altered mappings) for %input are \'%output\' (expected: \'%expected\').', array('%input' => $input, '%output' => implode(', ', $extensions), '%expected' => implode(', ', $expected)))); + } } + }