? 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 );
}
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';
+ }
}
/**
* @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.
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 );
* @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';
}
}
"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(