Issue #2388749 by alexpott, adci_contributor, ParisLiakos: Register symfony's mime guessers if they are supported

8.0.x
Nathaniel Catchpole 2015-01-27 11:09:27 +00:00
parent 5951f55ff3
commit 4dae1d994e
14 changed files with 157 additions and 219 deletions

View File

@ -1168,6 +1168,7 @@ services:
alias: plugin.manager.element_info
file.mime_type.guesser:
class: Drupal\Core\File\MimeType\MimeTypeGuesser
arguments: ['@stream_wrapper_manager']
tags:
- { name: service_collector, tag: mime_type_guesser, call: addGuesser }
file.mime_type.guesser.extension:
@ -1175,6 +1176,14 @@ services:
arguments: ['@module_handler']
tags:
- { name: mime_type_guesser }
file.mime_type.guesser.fileinfo:
class: Symfony\Component\HttpFoundation\File\MimeType\FileinfoMimeTypeGuesser
tags:
- { name: mime_type_guesser, priority: 64 }
file.mime_type.guesser.filebinary:
class: Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser
tags:
- { name: mime_type_guesser, priority: 32 }
renderer:
class: Drupal\Core\Render\Renderer
arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@request_stack', '@cache_factory', '@cache_contexts']

View File

@ -1150,32 +1150,6 @@ function file_upload_max_size() {
return $max_size;
}
/**
* Determines an Internet Media Type or MIME type from a filename.
*
* @param $uri
* A string containing the URI, path, or filename.
* @param $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 (lowercase) and the value is the mimetype identifier.
*
* @return
* The internet media type registered for the extension or
* application/octet-stream for unknown extensions.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. Calls are
* passed on to a new file.mime_type.guesser service, and the $mapping
* parameter is ignored. Use
* \Drupal::service('file.mime_type.guesser')->guess($uri).
*
* @see \Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser::guess()
*/
function file_get_mimetype($uri, $mapping = NULL) {
return \Drupal::service('file.mime_type.guesser')->guess($uri);
}
/**
* Sets the permissions on a file or directory.
*

View File

@ -16,12 +16,12 @@ use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
class ExtensionMimeTypeGuesser implements MimeTypeGuesserInterface {
/**
* Default MIME extension mapping.
* MIME extension mappings.
*
* @var array
* Array of mimetypes correlated to the extensions that relate to them.
*/
protected $defaultMapping = array(
protected $mapping = array(
'mimetypes' => array(
0 => 'application/andrew-inset',
1 => 'application/atom',
@ -864,41 +864,10 @@ class ExtensionMimeTypeGuesser implements MimeTypeGuesserInterface {
),
);
/**
* 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));
@ -920,14 +889,4 @@ class ExtensionMimeTypeGuesser implements MimeTypeGuesserInterface {
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;
}
}

View File

@ -7,6 +7,7 @@
namespace Drupal\Core\File\MimeType;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser as SymfonyMimeTypeGuesser;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
@ -16,6 +17,13 @@ use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
*/
class MimeTypeGuesser implements MimeTypeGuesserInterface {
/**
* The stream wrapper manager.
*
* @var \Drupal\Core\StreamWrapper\StreamWrapperManager
*/
protected $streamWrapperManager;
/**
* An array of arrays of registered guessers keyed by priority.
*
@ -35,11 +43,21 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface {
*/
protected $sortedGuessers = NULL;
/**
* Constructs the mime type guesser service.
*
* @param \Drupal\Core\StreamWrapper\StreamWrapperManager $stream_wrapper_manager
* The stream wrapper manager.
*/
public function __construct(StreamWrapperManager $stream_wrapper_manager) {
$this->streamWrapperManager = $stream_wrapper_manager;
}
/**
* {@inheritdoc}
*/
public function guess($path) {
if ($wrapper = file_stream_wrapper_get_instance_by_uri($path)) {
if ($wrapper = $this->streamWrapperManager->getViaUri($path)) {
// Get the real path from the stream wrapper.
$path = $wrapper->realpath();
}
@ -68,9 +86,15 @@ class MimeTypeGuesser implements MimeTypeGuesserInterface {
* @return $this
*/
public function addGuesser(MimeTypeGuesserInterface $guesser, $priority = 0) {
$this->guessers[$priority][] = $guesser;
// Mark sorted guessers for rebuild.
$this->sortedGuessers = NULL;
// Only add guessers which are supported.
// @see \Symfony\Component\HttpFoundation\File\MimeType\FileinfoMimeTypeGuesser
// @see \Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser
$supported = method_exists($guesser, 'isSupported') ? $guesser->isSupported() : TRUE;
if ($supported) {
$this->guessers[$priority][] = $guesser;
// Mark sorted guessers for rebuild.
$this->sortedGuessers = NULL;
}
return $this;
}

View File

@ -756,7 +756,7 @@ function file_save_upload($form_field_name, $validators = array(), $destination
'uri' => $file_info->getRealPath(),
'filesize' => $file_info->getSize(),
);
$values['filemime'] = file_get_mimetype($values['filename']);
$values['filemime'] = \Drupal::service('file.mime_type.guesser')->guess($values['uri']);
$file = entity_create('file', $values);
$extensions = '';

View File

@ -185,7 +185,7 @@ class File extends ContentEntityBase implements FileInterface {
// Automatically detect filemime if not set.
if (!isset($values['filemime']) && isset($values['uri'])) {
$values['filemime'] = file_get_mimetype($values['uri']);
$values['filemime'] = \Drupal::service('file.mime_type.guesser')->guess($values['uri']);
}
}

View File

@ -151,7 +151,8 @@ abstract class FileManagedUnitTestBase extends KernelTestBase {
*
* @param $filepath
* Optional string specifying the file path. If none is provided then a
* randomly named file will be created in the site's files directory.
* randomly named file with the extension .txt will be created in the site's
* files directory.
* @param $contents
* Optional contents to save into the file. If a NULL value is provided an
* arbitrary string will be used.
@ -182,7 +183,8 @@ abstract class FileManagedUnitTestBase extends KernelTestBase {
*
* @param string $filepath
* Optional string specifying the file path. If none is provided then a
* randomly named file will be created in the site's files directory.
* randomly named file with the extension .txt will be created in the site's
* files directory.
* @param string $contents
* Optional contents to save into the file. If a NULL value is provided an
* arbitrary string will be used.
@ -202,7 +204,7 @@ abstract class FileManagedUnitTestBase extends KernelTestBase {
if (!isset($scheme)) {
$scheme = file_default_scheme();
}
$filepath = $scheme . '://' . $filepath;
$filepath = $scheme . '://' . $filepath . '.txt';
if (!isset($contents)) {
$contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data.";

View File

@ -25,7 +25,7 @@ class SaveDataTest extends FileManagedUnitTestBase {
$this->assertEqual(file_default_scheme(), file_uri_scheme($result->getFileUri()), "File was placed in Drupal's files directory.");
$this->assertEqual($result->getFilename(), drupal_basename($result->getFileUri()), "Filename was set to the file's basename.");
$this->assertEqual($contents, file_get_contents($result->getFileUri()), 'Contents of the file are correct.');
$this->assertEqual($result->getMimeType(), 'application/octet-stream', 'A MIME type was set.');
$this->assertEqual($result->getMimeType(), 'text/plain', 'A MIME type was set.');
$this->assertTrue($result->isPermanent(), "The file's status was set to permanent.");
// Check that the correct hooks were called.
@ -74,7 +74,7 @@ class SaveDataTest extends FileManagedUnitTestBase {
$this->assertEqual('public', file_uri_scheme($result->getFileUri()), "File was placed in Drupal's files directory.");
$this->assertEqual($result->getFilename(), $existing->getFilename(), 'Filename was set to the basename of the source, rather than that of the renamed file.');
$this->assertEqual($contents, file_get_contents($result->getFileUri()), 'Contents of the file are correct.');
$this->assertEqual($result->getMimeType(), 'application/octet-stream', 'A MIME type was set.');
$this->assertEqual($result->getMimeType(), 'text/plain', 'A MIME type was set.');
$this->assertTrue($result->isPermanent(), "The file's status was set to permanent.");
// Check that the correct hooks were called.
@ -102,7 +102,7 @@ class SaveDataTest extends FileManagedUnitTestBase {
$this->assertEqual('public', file_uri_scheme($result->getFileUri()), "File was placed in Drupal's files directory.");
$this->assertEqual($result->getFilename(), $existing->getFilename(), 'Filename was set to the basename of the existing file, rather than preserving the original name.');
$this->assertEqual($contents, file_get_contents($result->getFileUri()), 'Contents of the file are correct.');
$this->assertEqual($result->getMimeType(), 'application/octet-stream', 'A MIME type was set.');
$this->assertEqual($result->getMimeType(), 'text/plain', 'A MIME type was set.');
$this->assertTrue($result->isPermanent(), "The file's status was set to permanent.");
// Check that the correct hooks were called.

View File

@ -283,21 +283,6 @@ function file_test_file_url_alter(&$uri) {
}
}
/**
* Implements hook_file_mimetype_mapping_alter().
*/
function file_test_file_mimetype_mapping_alter(&$mapping) {
// Add new mappings.
$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.
$mapping['extensions']['doc'] = 'file_test_mimetype_3';
}
/**
* Helper validator that returns the $errors parameter.
*/

View File

@ -99,30 +99,6 @@ function hook_file_url_alter(&$uri) {
}
}
/**
* Alter MIME type mappings used to determine MIME type from a file extension.
*
* Invoked by \Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser::guess(). It
* is used to allow modules to add to or modify the default mapping from
* \Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser::$defaultMapping.
*
* @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\ExtensionMimeTypeGuesser::guess()
* @see \Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser::$defaultMapping
*/
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.
$mapping['extensions']['ogg'] = 189;
}
/**
* Alter archiver information declared by other modules.
*

View File

@ -12,6 +12,7 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\StreamWrapper\PublicStream;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\ConfigFactoryInterface;
@ -44,6 +45,13 @@ class ThemeSettingsForm extends ConfigFormBase {
*/
protected $editableConfig = [];
/**
* The mime type guesser.
*
* @var \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface
*/
protected $mimeTypeGuesser;
/**
* Constructs a ThemeSettingsForm object.
*
@ -53,12 +61,15 @@ class ThemeSettingsForm extends ConfigFormBase {
* The module handler instance to use.
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler.
* @param \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface $mime_type_guesser
* The mime type guesser.
*/
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, MimeTypeGuesserInterface $mime_type_guesser) {
parent::__construct($config_factory);
$this->moduleHandler = $module_handler;
$this->themeHandler = $theme_handler;
$this->mimeTypeGuesser = $mime_type_guesser;
}
/**
@ -68,7 +79,8 @@ class ThemeSettingsForm extends ConfigFormBase {
return new static(
$container->get('config.factory'),
$container->get('module_handler'),
$container->get('theme_handler')
$container->get('theme_handler'),
$container->get('file.mime_type.guesser')
);
}
@ -446,7 +458,7 @@ class ThemeSettingsForm extends ConfigFormBase {
}
if (empty($values['default_favicon']) && !empty($values['favicon_path'])) {
$values['favicon_mimetype'] = file_get_mimetype($values['favicon_path']);
$values['favicon_mimetype'] = $this->mimeTypeGuesser->guess($values['favicon_path']);
}
}

View File

@ -1,94 +0,0 @@
<?php
/**
* @file
* Definition of Drupal\system\Tests\File\MimeTypeTest.
*/
namespace Drupal\system\Tests\File;
/**
* Tests filename mimetype detection.
*
* @group File
*/
class MimeTypeTest extends FileTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('file_test');
/**
* Test mapping of mimetypes from filenames.
*/
public function testFileMimeTypeDetection() {
$prefix = 'public://';
$test_case = array(
'test.jar' => 'application/java-archive',
'test.jpeg' => 'image/jpeg',
'test.JPEG' => 'image/jpeg',
'test.jpg' => 'image/jpeg',
'test.jar.jpg' => 'image/jpeg',
'test.jpg.jar' => 'application/java-archive',
'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',
);
$guesser = $this->container->get('file.mime_type.guesser');
// Test using default mappings.
foreach ($test_case as $input => $expected) {
// Test stream [URI].
$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 = $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 gusser by passing in a custom mapping.
$mapping = array(
'mimetypes' => array(
0 => 'application/java-archive',
1 => 'image/jpeg',
),
'extensions' => array(
'jar' => 0,
'jpg' => 1,
)
);
$test_case = array(
'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' => '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 = $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)));
}
}
}

View File

@ -0,0 +1,59 @@
<?php
/**
* @file
* Contains \Drupal\Tests\Core\File\ExtensionMimeTypeGuesserTest.
*/
namespace Drupal\Tests\Core\File {
use Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser
* @group File
*/
class ExtensionMimeTypeGuesserTest extends UnitTestCase {
/**
* @covers ::guess
* @dataProvider guesserDataProvider
*/
public function testGuesser($path, $mime_type) {
$guesser = new ExtensionMimeTypeGuesser();
$this->assertEquals($mime_type, $guesser->guess($path));
}
/**
* Provides data for ExtensionMimeTypeGuesserTest::testGuesser().
*/
public function guesserDataProvider() {
return [
['test.jar', 'application/java-archive'],
['test.jpeg', 'image/jpeg'],
['test.JPEG', 'image/jpeg'],
['test.jpg', 'image/jpeg'],
['test.jar.jpg', 'image/jpeg'],
['test.jpg.jar', 'application/java-archive'],
['test.pcf.Z', 'application/x-font'],
['pcf.z', 'application/octet-stream'],
['jar', 'application/octet-stream'],
['some.junk', 'application/octet-stream'],
['foo.file_test_1', 'application/octet-stream'],
['foo.file_test_2', 'application/octet-stream'],
['foo.doc', 'application/msword'],
['test.ogg', 'audio/ogg'],
];
}
}
}
namespace {
if (!function_exists('drupal_basename')) {
function drupal_basename($uri, $suffix = NULL) {
return basename($uri, $suffix);
}
}
}

View File

@ -14,10 +14,39 @@ use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser as SymfonyMim
/**
* @coversDefaultClass \Drupal\Core\File\MimeType\MimeTypeGuesser
* @group DrupalKernel
* @group File
*/
class MimeTypeGuesserTest extends UnitTestCase {
/**
* @covers ::guess
* @covers ::addGuesser
* @covers ::sortGuessers
*/
public function testGuess() {
$stream_wrapper_manager = $this->getMockBuilder('Drupal\Core\StreamWrapper\StreamWrapperManager')
->disableOriginalConstructor()
->getMock();
$stream_wrapper_manager->expects($this->any())
->method('getViaUri')
->willReturn(NULL);
$mime_guesser_service = new MimeTypeGuesser($stream_wrapper_manager);
$guesser_1 = $this->getMock('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface');
$guesser_1->expects($this->once())
->method('guess')
->with('file.txt')
->willReturn('text/plain');
$mime_guesser_service->addGuesser($guesser_1);
$this->assertEquals('text/plain', $mime_guesser_service->guess('file.txt'));
$guesser_2 = $this->getMock('Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface');
$guesser_2->expects($this->once())
->method('guess')
->with('file.txt')
->willReturn('text/x-diff');
$mime_guesser_service->addGuesser($guesser_2, 10);
$this->assertEquals('text/x-diff', $mime_guesser_service->guess('file.txt'));
}
/**
* @covers ::registerWithSymfonyGuesser
*
@ -33,8 +62,11 @@ class MimeTypeGuesserTest extends UnitTestCase {
if (count($guessers)) {
$this->assertNotInstanceOf('Drupal\Core\File\MimeType\MimeTypeGuesser', $guessers[0]);
}
$stream_wrapper_manager = $this->getMockBuilder('Drupal\Core\StreamWrapper\StreamWrapperManager')
->disableOriginalConstructor()
->getMock();
$container = new ContainerBuilder();
$container->set('file.mime_type.guesser', new MimeTypeGuesser());
$container->set('file.mime_type.guesser', new MimeTypeGuesser($stream_wrapper_manager));
MimeTypeGuesser::registerWithSymfonyGuesser($container);
$guessers = $this->readAttribute($symfony_guesser, 'guessers');
$this->assertInstanceOf('Drupal\Core\File\MimeType\MimeTypeGuesser', $guessers[0]);