jenkins-bot has submitted this change and it was merged. Change subject: Improve MIME detection in FileBackend ......................................................................
Improve MIME detection in FileBackend The content type detector will now inspect the file contents to better handle extensionless files. Also dependency inject the callback and make the default one use FileInfo. Change-Id: Iad59bf6c6a416b706f976a4c425763fd30e2debb --- M includes/filebackend/FileBackendGroup.php M includes/filebackend/FileBackendStore.php M tests/phpunit/includes/filebackend/FileBackendTest.php 3 files changed, 74 insertions(+), 6 deletions(-) Approvals: Gilles: Looks good to me, approved jenkins-bot: Verified diff --git a/includes/filebackend/FileBackendGroup.php b/includes/filebackend/FileBackendGroup.php index b6ddbad..c043106 100644 --- a/includes/filebackend/FileBackendGroup.php +++ b/includes/filebackend/FileBackendGroup.php @@ -166,6 +166,7 @@ ? FileJournal::factory( $config['fileJournal'], $name ) : FileJournal::factory( array( 'class' => 'NullFileJournal' ), $name ); $config['wanCache'] = ObjectCache::getMainWANInstance(); + $config['mimeCallback'] = array( $this, 'guessMimeInternal' ); $this->backends[$name]['instance'] = new $class( $config ); } @@ -203,4 +204,27 @@ return null; } + + /** + * @param string $storagePath + * @param string|null $content + * @param string|null $fsPath + * @return string + * @since 1.27 + */ + public function guessMimeInternal( $storagePath, $content, $fsPath ) { + $magic = MimeMagic::singleton(); + // Trust the extension of the storage path (caller must validate) + $ext = FileBackend::extensionFromPath( $storagePath ); + $type = $magic->guessTypesForExtension( $ext ); + // For files without a valid extension (or one at all), inspect the contents + if ( !$type && $fsPath ) { + $type = $magic->guessMimeType( $fsPath, false ); + } elseif ( !$type && strlen( $content ) ) { + $tmpFile = TempFSFile::factory( 'mime_' ); + file_put_contents( $tmpFile->getPath(), $content ); + $type = $magic->guessMimeType( $tmpFile->getPath(), false ); + } + return $type ?: 'unknown/unknown'; + } } diff --git a/includes/filebackend/FileBackendStore.php b/includes/filebackend/FileBackendStore.php index e5ce968..8113ec2 100644 --- a/includes/filebackend/FileBackendStore.php +++ b/includes/filebackend/FileBackendStore.php @@ -58,7 +58,7 @@ /** * @see FileBackend::__construct() * Additional $config params include: - * - wanCache : WANOBjectCache object to use for persistent caching. + * - wanCache : WANObjectCache object to use for persistent caching. * - mimeCallback : Callback that takes (storage path, content, file system path) and * returns the MIME type of the file or 'unknown/unknown'. The file * system path parameter should be used if the content one is null. @@ -69,10 +69,7 @@ parent::__construct( $config ); $this->mimeCallback = isset( $config['mimeCallback'] ) ? $config['mimeCallback'] - : function ( $storagePath, $content, $fsPath ) { - // @todo handle the case of extension-less files using the contents - return StreamFile::contentTypeFromPath( $storagePath ) ?: 'unknown/unknown'; - }; + : null; $this->memCache = WANObjectCache::newEmpty(); // disabled by default $this->cheapCache = new ProcessCacheLRU( self::CACHE_CHEAP_SIZE ); $this->expensiveCache = new ProcessCacheLRU( self::CACHE_EXPENSIVE_SIZE ); @@ -1823,7 +1820,18 @@ * @return string MIME type */ protected function getContentType( $storagePath, $content, $fsPath ) { - return call_user_func_array( $this->mimeCallback, func_get_args() ); + if ( $this->mimeCallback ) { + return call_user_func_array( $this->mimeCallback, func_get_args() ); + } + + $mime = null; + if ( $fsPath !== null && function_exists( 'finfo_file' ) ) { + $finfo = finfo_open( FILEINFO_MIME_TYPE ); + $mime = finfo_file( $finfo, $fsPath ); + finfo_close( $finfo ); + } + + return is_string( $mime ) ? $mime : 'unknown/unknown'; } } diff --git a/tests/phpunit/includes/filebackend/FileBackendTest.php b/tests/phpunit/includes/filebackend/FileBackendTest.php index 0d15b75..09b3728 100644 --- a/tests/phpunit/includes/filebackend/FileBackendTest.php +++ b/tests/phpunit/includes/filebackend/FileBackendTest.php @@ -2397,6 +2397,42 @@ "Scoped unlocking of files succeeded with OK status ($backendName)." ); } + /** + * @dataProvider provider_testGetContentType + */ + public function testGetContentType( $mimeCallback, $mimeFromString ) { + global $IP; + + $be = TestingAccessWrapper::newFromObject( new MemoryFileBackend( + array( + 'name' => 'testing', + 'class' => 'MemoryFileBackend', + 'wikiId' => 'meow', + 'mimeCallback' => $mimeCallback + ) + ) ); + + $dst = 'mwstore://testing/container/path/to/file_no_ext'; + $src = "$IP/tests/phpunit/data/media/srgb.jpg"; + $this->assertEquals( 'image/jpeg', $be->getContentType( $dst, null, $src ) ); + $this->assertEquals( + $mimeFromString ? 'image/jpeg' : 'unknown/unknown', + $be->getContentType( $dst, file_get_contents( $src ), null ) ); + + $src = "$IP/tests/phpunit/data/media/Png-native-test.png"; + $this->assertEquals( 'image/png', $be->getContentType( $dst, null, $src ) ); + $this->assertEquals( + $mimeFromString ? 'image/png' : 'unknown/unknown', + $be->getContentType( $dst, file_get_contents( $src ), null ) ); + } + + public static function provider_testGetContentType() { + return array( + array( null, false ), + array( array( FileBackendGroup::singleton(), 'guessMimeInternal' ), true ) + ); + } + public function testReadAffinity() { $be = TestingAccessWrapper::newFromObject( new FileBackendMultiWrite( array( -- To view, visit https://gerrit.wikimedia.org/r/173241 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Iad59bf6c6a416b706f976a4c425763fd30e2debb Gerrit-PatchSet: 6 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Aaron Schulz <asch...@wikimedia.org> Gerrit-Reviewer: Aaron Schulz <asch...@wikimedia.org> Gerrit-Reviewer: Brian Wolff <bawolff...@gmail.com> Gerrit-Reviewer: Gilles <gdu...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits