MediaWiki REL1_37
MediaHandler.php
Go to the documentation of this file.
1<?php
22
37abstract class MediaHandler {
38 public const TRANSFORM_LATER = 1;
39 public const METADATA_GOOD = true;
40 public const METADATA_BAD = false;
41 public const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
45 private const MAX_ERR_LOG_SIZE = 65535;
46
53 public static function getHandler( $type ) {
54 return MediaWikiServices::getInstance()
55 ->getMediaHandlerFactory()->getHandler( $type );
56 }
57
62 abstract public function getParamMap();
63
72 abstract public function validateParam( $name, $value );
73
80 abstract public function makeParamString( $params );
81
88 abstract public function parseParamString( $str );
89
97 abstract public function normaliseParams( $image, &$params );
98
121 public function getImageSize( $image, $path ) {
122 return false;
123 }
124
150 public function getSizeAndMetadata( $state, $path ) {
151 return null;
152 }
153
163 public function getMetadata( $image, $path ) {
164 return '';
165 }
166
176 protected function useLegacyMetadata() {
177 return $this->hasMostDerivedMethod( 'getMetadata' )
178 || $this->hasMostDerivedMethod( 'getImageSize' );
179 }
180
188 protected function hasMostDerivedMethod( $name ) {
189 $rc = new ReflectionClass( $this );
190 $rm = new ReflectionMethod( $this, $name );
191 return $rm->getDeclaringClass()->getName() === $rc->getName();
192 }
193
214 final public function getSizeAndMetadataWithFallback( $file, $path ) {
215 if ( !$this->useLegacyMetadata() ) {
216 if ( $file instanceof MediaHandlerState ) {
217 $state = $file;
218 } else {
219 $state = new TrivialMediaHandlerState;
220 }
221 $info = $this->getSizeAndMetadata( $state, $path );
222 if ( $info === false ) {
223 return false;
224 }
225 if ( $info !== null ) {
226 $info += [ 'width' => 0, 'height' => 0, 'metadata' => [] ];
227 if ( !is_array( $info['metadata'] ) ) {
228 throw new InvalidArgumentException( 'Media handler ' .
229 static::class . ' returned ' . gettype( $info['metadata'] ) .
230 ' for metadata, should be array' );
231 }
232 return $info;
233 }
234 }
235
236 $blob = $this->getMetadata( $file, $path );
237 // @phan-suppress-next-line PhanParamTooMany
238 $size = $this->getImageSize(
239 $file,
240 $path,
241 $blob // Secret TimedMediaHandler parameter
242 );
243 if ( $blob === false && $size === false ) {
244 return false;
245 }
246 if ( $size ) {
247 $info = [
248 'width' => $size[0] ?? 0,
249 'height' => $size[1] ?? 0
250 ];
251 if ( isset( $size['bits'] ) ) {
252 $info['bits'] = $size['bits'];
253 }
254 } else {
255 $info = [ 'width' => 0, 'height' => 0 ];
256 }
257 if ( $blob !== false ) {
258 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
259 $metadata = @unserialize( $blob );
260 if ( $metadata === false ) {
261 // Unserialize error
262 $metadata = [ '_error' => $blob ];
263 } elseif ( !is_array( $metadata ) ) {
264 $metadata = [];
265 }
266 $info['metadata'] = $metadata;
267 } else {
268 $info['metadata'] = [];
269 }
270 return $info;
271 }
272
290 public static function getMetadataVersion() {
291 $version = [ '2' ]; // core metadata version
292 Hooks::runner()->onGetMetadataVersion( $version );
293
294 return implode( ';', $version );
295 }
296
308 public function convertMetadataVersion( $metadata, $version = 1 ) {
309 return $metadata;
310 }
311
320 public function getMetadataType( $image ) {
321 return false;
322 }
323
342 public function isMetadataValid( $image, $metadata ) {
343 return self::METADATA_GOOD;
344 }
345
371 public function isFileMetadataValid( $image ) {
372 return self::METADATA_GOOD;
373 }
374
409 public function getCommonMetaArray( File $file ) {
410 return false;
411 }
412
428 public function getScriptedTransform( $image, $script, $params ) {
429 return false;
430 }
431
444 final public function getTransform( $image, $dstPath, $dstUrl, $params ) {
445 return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
446 }
447
462 abstract public function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
463
474 public function getThumbType( $ext, $mime, $params = null ) {
475 $magic = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
476 if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
477 // The extension is not valid for this MIME type and we do
478 // recognize the MIME type
479 $knownExt = $magic->getExtensionFromMimeTypeOrNull( $mime );
480 if ( $knownExt !== null ) {
481 return [ $knownExt, $mime ];
482 }
483 }
484
485 // The extension is correct (true) or the MIME type is unknown to
486 // MediaWiki (null)
487 return [ $ext, $mime ];
488 }
489
498 public function canRender( $file ) {
499 return true;
500 }
501
511 public function mustRender( $file ) {
512 return false;
513 }
514
523 public function isMultiPage( $file ) {
524 return false;
525 }
526
535 public function pageCount( File $file ) {
536 return false;
537 }
538
547 public function isVectorized( $file ) {
548 return false;
549 }
550
561 public function isAnimatedImage( $file ) {
562 return false;
563 }
564
574 public function canAnimateThumbnail( $file ) {
575 return true;
576 }
577
584 public function isEnabled() {
585 return true;
586 }
587
605 public function getPageDimensions( File $image, $page ) {
606 return false;
607 }
608
619 public function getPageText( File $image, $page ) {
620 return false;
621 }
622
628 public function getEntireText( File $file ) {
629 $numPages = $file->pageCount();
630 if ( !$numPages ) {
631 // Not a multipage document
632 return $this->getPageText( $file, 1 );
633 }
634 $document = '';
635 for ( $i = 1; $i <= $numPages; $i++ ) {
636 $curPage = $this->getPageText( $file, $i );
637 if ( is_string( $curPage ) ) {
638 $document .= $curPage . "\n";
639 }
640 }
641 if ( $document !== '' ) {
642 return $document;
643 }
644 return false;
645 }
646
677 public function formatMetadata( $image, $context = false ) {
678 return false;
679 }
680
693 protected function formatMetadataHelper( $metadataArray, $context = false ) {
694 $result = [
695 'visible' => [],
696 'collapsed' => []
697 ];
698
699 // Allow this MediaHandler to override formatting on certain values
700 foreach ( $metadataArray as $tag => $vals ) {
701 $v = $this->formatTag( $tag, $vals, $context );
702 if ( $v === false ) {
703 // Use default formatting
704 continue;
705 }
706 if ( $v === null ) {
707 // Remove this tag, don't format it for display
708 unset( $metadataArray[$tag] );
709 } else {
710 // Allow subclass to override default formatting.
711 $metadataArray[$tag] = [ '_formatted' => $v ];
712 if ( isset( $v['_type'] ) ) {
713 $metadataArray[$tag]['_type'] = $v['_type'];
714 unset( $metadataArray[$tag]['_formatted']['_type'] );
715 }
716 }
717 }
718
719 $formatted = FormatMetadata::getFormattedData( $metadataArray, $context );
720 // Sort fields into visible and collapsed
721 $visibleFields = $this->visibleMetadataFields();
722 foreach ( $formatted as $name => $value ) {
723 $tag = strtolower( $name );
724 self::addMeta( $result,
725 in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
726 'exif',
727 $tag,
728 $value
729 );
730 }
731
732 return $result;
733 }
734
747 protected function formatTag( string $key, $vals, $context = false ) {
748 return false; // Use default formatting
749 }
750
759 protected function visibleMetadataFields() {
761 }
762
786 protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
787 $msg = wfMessage( "$type-$id", $param );
788 if ( $msg->exists() ) {
789 $name = $msg->text();
790 } else {
791 // This is for future compatibility when using instant commons.
792 // So as to not display as ugly a name if a new metadata
793 // property is defined that we don't know about
794 // (not a major issue since such a property would be collapsed
795 // by default).
796 wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id );
797 $name = wfEscapeWikiText( $id );
798 }
799 $array[$visibility][] = [
800 'id' => "$type-$id",
801 'name' => $name,
802 'value' => $value
803 ];
804 }
805
814 public function getShortDesc( $file ) {
816 }
817
826 public function getLongDesc( $file ) {
828 }
829
836 public static function getGeneralShortDesc( $file ) {
837 global $wgLang;
838
839 return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
840 }
841
848 public static function getGeneralLongDesc( $file ) {
849 return wfMessage( 'file-info' )->sizeParams( $file->getSize() )
850 ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )->parse();
851 }
852
861 public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
862 $idealWidth = $boxWidth * $maxHeight / $boxHeight;
863 $roundedUp = ceil( $idealWidth );
864 if ( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) {
865 return floor( $idealWidth );
866 } else {
867 return $roundedUp;
868 }
869 }
870
879 public function getDimensionsString( $file ) {
880 return '';
881 }
882
895 public function parserTransformHook( $parser, $file ) {
896 }
897
910 public function verifyUpload( $fileName ) {
911 return Status::newGood();
912 }
913
924 public function removeBadFile( $dstPath, $retval = 0 ) {
925 if ( file_exists( $dstPath ) ) {
926 $thumbstat = stat( $dstPath );
927 if ( $thumbstat['size'] == 0 || $retval != 0 ) {
928 $result = unlink( $dstPath );
929
930 if ( $result ) {
931 wfDebugLog( 'thumbnail',
932 sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded',
933 $thumbstat['size'], $dstPath ) );
934 } else {
935 wfDebugLog( 'thumbnail',
936 sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed',
937 $thumbstat['size'], $dstPath ) );
938 }
939
940 return true;
941 }
942 }
943
944 return false;
945 }
946
961 public function filterThumbnailPurgeList( &$files, $options ) {
962 // Do nothing
963 }
964
972 public function canRotate() {
973 return false;
974 }
975
992 public function getRotation( $file ) {
993 return 0;
994 }
995
1007 protected function logErrorForExternalProcess( $retval, $err, $cmd ) {
1008 # Keep error output limited (T59985)
1009 $errMessage = trim( substr( $err, 0, self::MAX_ERR_LOG_SIZE ) );
1010
1011 wfDebugLog( 'thumbnail',
1012 sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
1013 wfHostname(), $retval, $errMessage, $cmd ) );
1014 }
1015
1025 public function getAvailableLanguages( File $file ) {
1026 return [];
1027 }
1028
1040 public function getMatchedLanguage( $userPreferredLanguage, array $availableLanguages ) {
1041 return null;
1042 }
1043
1059 return null;
1060 }
1061
1074 public function getLength( $file ) {
1075 return 0.0;
1076 }
1077
1085 public function isExpensiveToThumbnail( $file ) {
1086 return false;
1087 }
1088
1097 public function supportsBucketing() {
1098 return false;
1099 }
1100
1109 public function sanitizeParamsForBucketing( $params ) {
1110 return $params;
1111 }
1112
1139 public function getWarningConfig( $file ) {
1140 return null;
1141 }
1142
1150 public static function getPageRangesByDimensions( $pagesByDimensions ) {
1151 $pageRangesByDimensions = [];
1152
1153 foreach ( $pagesByDimensions as $dimensions => $pageList ) {
1154 $ranges = [];
1155 $firstPage = $pageList[0];
1156 $lastPage = $firstPage - 1;
1157
1158 foreach ( $pageList as $page ) {
1159 if ( $page > $lastPage + 1 ) {
1160 if ( $firstPage != $lastPage ) {
1161 $ranges[] = "$firstPage-$lastPage";
1162 } else {
1163 $ranges[] = "$firstPage";
1164 }
1165
1166 $firstPage = $page;
1167 }
1168
1169 $lastPage = $page;
1170 }
1171
1172 if ( $firstPage != $lastPage ) {
1173 $ranges[] = "$firstPage-$lastPage";
1174 } else {
1175 $ranges[] = "$firstPage";
1176 }
1177
1178 $pageRangesByDimensions[ $dimensions ] = $ranges;
1179 }
1180
1181 $dimensionsString = [];
1182 foreach ( $pageRangesByDimensions as $dimensions => $pageRanges ) {
1183 $dimensionsString[] = "$dimensions:" . implode( ',', $pageRanges );
1184 }
1185
1186 return implode( '/', $dimensionsString );
1187 }
1188
1197 public function getContentHeaders( $metadata ) {
1198 return [ 'X-Content-Dimensions' => '' ]; // T175689
1199 }
1200
1209 public function useSplitMetadata() {
1210 return false;
1211 }
1212}
unserialize( $serialized)
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfHostname()
Get host name of the current machine, for use in error reporting.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
$wgLang
Definition Setup.php:831
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:66
static getVisibleFields()
Get a list of fields that are visible by default.
static getFormattedData( $tags, $context=false)
Numbers given by Exif user agents are often magical, that is they should be replaced by a detailed ex...
Base media handler class.
normaliseParams( $image, &$params)
Changes the parameter array as necessary, ready for transformation.
getRotation( $file)
On supporting image formats, try to read out the low-level orientation of the file and return the ang...
const METADATA_COMPATIBLE
canRender( $file)
True if the handled types can be transformed.
getSizeAndMetadata( $state, $path)
Get image size information and metadata array.
canAnimateThumbnail( $file)
If the material is animated, we can animate the thumbnail.
useLegacyMetadata()
If this returns true, the new method getSizeAndMetadata() will not be called.
verifyUpload( $fileName)
File validation hook called on upload.
visibleMetadataFields()
Get a list of metadata items which should be displayed when the metadata table is collapsed.
static addMeta(&$array, $visibility, $type, $id, $value, $param=false)
This is used to generate an array element for each metadata value That array is then used to generate...
supportsBucketing()
Returns whether or not this handler supports the chained generation of thumbnails according to bucket...
useSplitMetadata()
If this returns true, LocalFile may split metadata up and store its constituent items separately.
parserTransformHook( $parser, $file)
Modify the parser object post-transform.
static getGeneralLongDesc( $file)
Used instead of getLongDesc if there is no handler registered for file.
formatTag(string $key, $vals, $context=false)
Override default formatting for the given metadata field.
canRotate()
True if the handler can rotate the media.
sanitizeParamsForBucketing( $params)
Returns a normalised params array for which parameters have been cleaned up for bucketing purposes.
getDefaultRenderLanguage(File $file)
On file types that support renderings in multiple languages, which language is used by default if uns...
getLength( $file)
If its an audio file, return the length of the file.
getTransform( $image, $dstPath, $dstUrl, $params)
Get a MediaTransformOutput object representing the transformed output.
getDimensionsString( $file)
Shown in file history box on image description page.
static getHandler( $type)
Get a MediaHandler for a given MIME type from the instance cache.
isFileMetadataValid( $image)
Check if the metadata is valid for this handler.
getCommonMetaArray(File $file)
Get an array of standard (FormatMetadata type) metadata values.
doTransform( $image, $dstPath, $dstUrl, $params, $flags=0)
Get a MediaTransformOutput object representing the transformed output.
getMetadata( $image, $path)
Get handler-specific metadata which will be saved in the img_metadata field.
getPageText(File $image, $page)
Generic getter for text layer.
getSizeAndMetadataWithFallback( $file, $path)
Get the metadata array and the image size, with b/c fallback.
isAnimatedImage( $file)
The material is an image, and is animated.
isVectorized( $file)
The material is vectorized and thus scaling is lossless.
getImageSize( $image, $path)
Get an image size array like that returned by getimagesize(), or false if it can't be determined.
convertMetadataVersion( $metadata, $version=1)
Convert metadata version.
formatMetadata( $image, $context=false)
Get an array structure that looks like this:
getWarningConfig( $file)
Gets configuration for the file warning message.
logErrorForExternalProcess( $retval, $err, $cmd)
Log an error that occurred in an external process.
isEnabled()
False if the handler is disabled for all files.
parseParamString( $str)
Parse a param string made with makeParamString back into an array.
static getGeneralShortDesc( $file)
Used instead of getShortDesc if there is no handler registered for file.
isMultiPage( $file)
True if the type has multi-page capabilities.
getLongDesc( $file)
Long description.
static fitBoxWidth( $boxWidth, $boxHeight, $maxHeight)
Calculate the largest thumbnail width for a given original file size such that the thumbnail's height...
mustRender( $file)
True if handled types cannot be displayed directly in a browser but can be rendered.
getScriptedTransform( $image, $script, $params)
Get a MediaTransformOutput object representing an alternate of the transformed output which will call...
getMetadataType( $image)
Get a string describing the type of metadata, for display purposes.
isExpensiveToThumbnail( $file)
True if creating thumbnails from the file is large or otherwise resource-intensive.
getEntireText(File $file)
Get the text of the entire document.
getAvailableLanguages(File $file)
Get list of languages file can be viewed in.
filterThumbnailPurgeList(&$files, $options)
Remove files from the purge list.
getShortDesc( $file)
Short description.
makeParamString( $params)
Merge a parameter array into a string appropriate for inclusion in filenames.
formatMetadataHelper( $metadataArray, $context=false)
sorts the visible/invisible field.
validateParam( $name, $value)
Validate a thumbnail parameter at parse time.
static getPageRangesByDimensions( $pagesByDimensions)
Converts a dimensions array about a potentially multipage document from an exhaustive list of ordered...
getPageDimensions(File $image, $page)
Get an associative array of page dimensions Currently "width" and "height" are understood,...
getContentHeaders( $metadata)
Get useful response headers for GET/HEAD requests for a file with the given metadata.
const TRANSFORM_LATER
getParamMap()
Get an associative array mapping magic word IDs to parameter names.
const MAX_ERR_LOG_SIZE
Max length of error logged by logErrorForExternalProcess()
const METADATA_GOOD
getThumbType( $ext, $mime, $params=null)
Get the thumbnail extension and MIME type for a given source MIME type.
removeBadFile( $dstPath, $retval=0)
Check for zero-sized thumbnails.
hasMostDerivedMethod( $name)
Check whether a method is implemented in the most derived class.
isMetadataValid( $image, $metadata)
Check if the metadata string is valid for this handler.
getMatchedLanguage( $userPreferredLanguage, array $availableLanguages)
When overridden in a descendant class, returns a language code most suiting.
static getMetadataVersion()
Get metadata version.
pageCount(File $file)
Page count for a multi-page document, false if unsupported or unknown.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Trivial implementation of MediaHandlerState.
An interface to support process-local caching of handler data associated with a given file.
$mime
Definition router.php:60
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition router.php:42
if(!is_readable( $file)) $ext
Definition router.php:48