Commit 17977129 authored by alexpott's avatar alexpott

Issue #1921558 by ParisLiakos, xjm, Dave Reid: Convert file_get_mimetype() to...

Issue #1921558 by ParisLiakos, xjm, Dave Reid: Convert file_get_mimetype() to use Symfony MimeTypeGuessers.
parent 9b821cd3
......@@ -825,3 +825,12 @@ services:
element_info:
class: Drupal\Core\Render\ElementInfo
arguments: ['@module_handler']
file.mime_type.guesser:
class: Drupal\Core\File\MimeType\MimeTypeGuesser
tags:
- { name: service_collector, tag: mime_type_guesser, call: addGuesser }
file.mime_type.guesser.extension:
class: Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser
arguments: ['@module_handler']
tags:
- { name: mime_type_guesser }
......@@ -6,7 +6,6 @@
*/
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\StreamWrapper\LocalStream;
use Drupal\Component\PhpStorage\FileStorage;
use Drupal\Component\Utility\Bytes;
use Drupal\Component\Utility\String;
......@@ -1300,17 +1299,11 @@ function file_upload_max_size() {
* The internet media type registered for the extension or
* application/octet-stream for unknown extensions.
*
* @see file_default_mimetype_mapping()
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::service('file.mime_type.guesser')->guess($uri).
*/
function file_get_mimetype($uri, $mapping = NULL) {
if ($wrapper = file_stream_wrapper_get_instance_by_uri($uri)) {
return $wrapper->getMimeType($uri, $mapping);
}
else {
// getMimeType() is not implementation specific, so we can directly
// call it without an instance.
return LocalStream::getMimeType($uri, $mapping);
}
return \Drupal::service('file.mime_type.guesser')->guess($uri);
}
/**
......
......@@ -2,39 +2,26 @@
/**
* @file
* Provides mimetype mappings.
* Contains \Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser
*/
/**
* Return an array of MIME extension mappings.
*
* Returns the mapping after modules have altered the default mapping.
*
* @return
* Array of mimetypes correlated to the extensions that relate to them.
*
* @see file_get_mimetype()
*/
function file_mimetype_mapping() {
$mapping = &drupal_static(__FUNCTION__);
if (!isset($mapping)) {
$mapping = file_default_mimetype_mapping();
// Allow modules to alter the default mapping.
\Drupal::moduleHandler()->alter('file_mimetype_mapping', $mapping);
}
return $mapping;
}
namespace Drupal\Core\File\MimeType;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
* Default MIME extension mapping.
*
* @return
* Array of mimetypes correlated to the extensions that relate to them.
*
* @see file_get_mimetype()
* Makes possible to guess the MIME type of a file using its extension.
*/
function file_default_mimetype_mapping() {
return array(
class ExtensionMimeTypeGuesser implements MimeTypeGuesserInterface {
/**
* Default MIME extension mapping.
*
* @var array
* Array of mimetypes correlated to the extensions that relate to them.
*/
protected $defaultMapping = array(
'mimetypes' => array(
0 => 'application/andrew-inset',
1 => 'application/atom',
......@@ -876,4 +863,71 @@ function file_default_mimetype_mapping() {
'vtt' => 358,
),
);
/**
* The MIME types mapping array after going through the module handler.
*
* @var array
*/
protected $mapping;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a new ExtensionMimeTypeGuesser.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(ModuleHandlerInterface $module_handler) {
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public function guess($path) {
if ($this->mapping === NULL) {
$mapping = $this->defaultMapping;
// Allow modules to alter the default mapping.
$this->moduleHandler->alter('file_mimetype_mapping', $mapping);
$this->mapping = $mapping;
}
$extension = '';
$file_parts = explode('.', drupal_basename($path));
// Remove the first part: a full filename should not match an extension.
array_shift($file_parts);
// Iterate over the file parts, trying to find a match.
// For my.awesome.image.jpeg, we try:
// - jpeg
// - image.jpeg, and
// - awesome.image.jpeg
while ($additional_part = array_pop($file_parts)) {
$extension = strtolower($additional_part . ($extension ? '.' . $extension : ''));
if (isset($this->mapping['extensions'][$extension])) {
return $this->mapping['mimetypes'][$this->mapping['extensions'][$extension]];
}
}
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->mapping = $mapping;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\File\MimeType\MimeTypeGuesser.
*/
namespace Drupal\Core\File\MimeType;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
/**
* Defines a MIME type guesser that also supports stream wrapper paths.
*/
class MimeTypeGuesser implements MimeTypeGuesserInterface {
/**
* An array of arrays of registered guessers keyed by priority.
*
* @var array
*/
protected $guessers = array();
/**
* Holds the array of guessers sorted by priority.
*
* If this is NULL a rebuild will be triggered.
*
* @var \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface[]
*
* @see \Drupal\Core\File\MimeType\MimeTypeGuesser::addGuesser()
* @see \Drupal\Core\File\MimeType\MimeTypeGuesser::sortGuessers()
*/
protected $sortedGuessers = NULL;
/**
* {@inheritdoc}
*/
public function guess($path) {
if ($wrapper = file_stream_wrapper_get_instance_by_uri($path)) {
// Get the real path from the stream wrapper.
$path = $wrapper->realpath();
}
if ($this->sortedGuessers === NULL) {
// Sort is not trigerred yet.
$this->sortedGuessers = $this->sortGuessers();
}
foreach ($this->sortedGuessers as $guesser) {
$mime_type = $guesser->guess($path);
if ($mime_type !== NULL) {
return $mime_type;
}
}
}
/**
* Appends a MIME type guesser to the guessers chain.
*
* @param \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface $guesser
* The guesser to be appended.
* @param int $priority
* The priority of the guesser being added.
*
* @return self
* The called object.
*/
public function addGuesser(MimeTypeGuesserInterface $guesser, $priority = 0) {
$this->guessers[$priority][] = $guesser;
// Mark sorted guessers for rebuild.
$this->sortedGuessers = NULL;
return $this;
}
/**
* Sorts guessers according to priority.
*
* @return \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface[]
* A sorted array of MIME type guesser objects.
*/
protected function sortGuessers() {
$sorted = array();
krsort($this->guessers);
foreach ($this->guessers as $guesser) {
$sorted = array_merge($sorted, $guesser);
}
return $sorted;
}
}
......@@ -93,38 +93,6 @@ protected function getTarget($uri = NULL) {
return trim($target, '\/');
}
/**
* Implements Drupal\Core\StreamWrapper\StreamWrapperInterface::getMimeType().
*/
static function getMimeType($uri, $mapping = NULL) {
if (!isset($mapping)) {
// The default file map, defined in file.mimetypes.inc is quite big.
// We only load it when necessary.
include_once DRUPAL_ROOT . '/core/includes/file.mimetypes.inc';
$mapping = file_mimetype_mapping();
}
$extension = '';
$file_parts = explode('.', drupal_basename($uri));
// Remove the first part: a full filename should not match an extension.
array_shift($file_parts);
// Iterate over the file parts, trying to find a match.
// For my.awesome.image.jpeg, we try:
// - jpeg
// - image.jpeg, and
// - 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]];
}
}
return 'application/octet-stream';
}
/**
* Implements Drupal\Core\StreamWrapper\StreamWrapperInterface::realpath().
*/
......
......@@ -61,22 +61,6 @@ public function getUri();
*/
public function getExternalUrl();
/**
* Returns the MIME type of the resource.
*
* @param string $uri
* The URI, path, or filename.
* @param array $mapping
* An optional map of extensions to their mimetypes, in the form:
* - 'mimetypes': a list of mimetypes, keyed by an identifier,
* - 'extensions': the mapping itself, an associative array in which
* the key is the extension and the value is the mimetype identifier.
*
* @return string
* Returns a string containing the MIME type of the resource.
*/
public static function getMimeType($uri, $mapping = NULL);
/**
* Returns canonical, absolute path of the resource.
*
......
......@@ -50,18 +50,19 @@ public function testFileMimeTypeDetection() {
'test.ogg' => 'audio/ogg',
);
$guesser = $this->container->get('file.mime_type.guesser');
// Test using default mappings.
foreach ($test_case as $input => $expected) {
// Test stream [URI].
$output = file_get_mimetype($prefix . $input);
$output = $guesser->guess($prefix . $input);
$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 = file_get_mimetype($input);
$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 passing in the map.
// Now test the extension gusser by passing in a custom mapping.
$mapping = array(
'mimetypes' => array(
0 => 'application/java-archive',
......@@ -88,9 +89,11 @@ public function testFileMimeTypeDetection() {
'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 = file_get_mimetype($input, $mapping);
$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)));
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment