MediaWiki master
MediaHandler.php
Go to the documentation of this file.
1<?php
26
41abstract class MediaHandler {
42 public const TRANSFORM_LATER = 1;
43 public const METADATA_GOOD = true;
44 public const METADATA_BAD = false;
45 public const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
49 private const MAX_ERR_LOG_SIZE = 65535;
50
57 public static function getHandler( $type ) {
58 return MediaWikiServices::getInstance()
59 ->getMediaHandlerFactory()->getHandler( $type );
60 }
61
67 abstract public function getParamMap();
68
78 abstract public function validateParam( $name, $value );
79
86 abstract public function makeParamString( $params );
87
94 abstract public function parseParamString( $str );
95
104 abstract public function normaliseParams( $image, &$params );
105
128 public function getImageSize( $image, $path ) {
129 return false;
130 }
131
157 public function getSizeAndMetadata( $state, $path ) {
158 return null;
159 }
160
170 public function getMetadata( $image, $path ) {
171 return '';
172 }
173
183 protected function useLegacyMetadata() {
184 return $this->hasMostDerivedMethod( 'getMetadata' )
185 || $this->hasMostDerivedMethod( 'getImageSize' );
186 }
187
195 protected function hasMostDerivedMethod( $name ) {
196 $rc = new ReflectionClass( $this );
197 $rm = new ReflectionMethod( $this, $name );
198 return $rm->getDeclaringClass()->getName() === $rc->getName();
199 }
200
221 final public function getSizeAndMetadataWithFallback( $file, $path ) {
222 if ( !$this->useLegacyMetadata() ) {
223 if ( $file instanceof MediaHandlerState ) {
224 $state = $file;
225 } else {
226 $state = new TrivialMediaHandlerState;
227 }
228 $info = $this->getSizeAndMetadata( $state, $path );
229 if ( $info === false ) {
230 return false;
231 }
232 if ( $info !== null ) {
233 $info += [ 'width' => 0, 'height' => 0, 'metadata' => [] ];
234 if ( !is_array( $info['metadata'] ) ) {
235 throw new InvalidArgumentException( 'Media handler ' .
236 static::class . ' returned ' . gettype( $info['metadata'] ) .
237 ' for metadata, should be array' );
238 }
239 return $info;
240 }
241 }
242
243 $blob = $this->getMetadata( $file, $path );
244 // @phan-suppress-next-line PhanParamTooMany
245 $size = $this->getImageSize(
246 $file,
247 $path,
248 $blob // Secret TimedMediaHandler parameter
249 );
250 if ( $blob === false && $size === false ) {
251 return false;
252 }
253 if ( $size ) {
254 $info = [
255 'width' => $size[0] ?? 0,
256 'height' => $size[1] ?? 0
257 ];
258 if ( isset( $size['bits'] ) ) {
259 $info['bits'] = $size['bits'];
260 }
261 } else {
262 $info = [ 'width' => 0, 'height' => 0 ];
263 }
264 if ( $blob !== false ) {
265 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
266 $metadata = @unserialize( $blob );
267 if ( $metadata === false ) {
268 // Unserialize error
269 $metadata = [ '_error' => $blob ];
270 } elseif ( !is_array( $metadata ) ) {
271 $metadata = [];
272 }
273 $info['metadata'] = $metadata;
274 } else {
275 $info['metadata'] = [];
276 }
277 return $info;
278 }
279
297 public static function getMetadataVersion() {
298 $version = [ '2' ]; // core metadata version
299 ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )->onGetMetadataVersion( $version );
300
301 return implode( ';', $version );
302 }
303
315 public function convertMetadataVersion( $metadata, $version = 1 ) {
316 return $metadata;
317 }
318
327 public function getMetadataType( $image ) {
328 return false;
329 }
330
349 public function isMetadataValid( $image, $metadata ) {
350 return self::METADATA_GOOD;
351 }
352
378 public function isFileMetadataValid( $image ) {
379 return self::METADATA_GOOD;
380 }
381
416 public function getCommonMetaArray( File $file ) {
417 return false;
418 }
419
435 public function getScriptedTransform( $image, $script, $params ) {
436 return false;
437 }
438
451 final public function getTransform( $image, $dstPath, $dstUrl, $params ) {
452 return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
453 }
454
469 abstract public function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
470
481 public function getThumbType( $ext, $mime, $params = null ) {
482 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
483 if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
484 // The extension is not valid for this MIME type and we do
485 // recognize the MIME type
486 $knownExt = $magic->getExtensionFromMimeTypeOrNull( $mime );
487 if ( $knownExt !== null ) {
488 return [ $knownExt, $mime ];
489 }
490 }
491
492 // The extension is correct (true) or the MIME type is unknown to
493 // MediaWiki (null)
494 return [ $ext, $mime ];
495 }
496
505 public function canRender( $file ) {
506 return true;
507 }
508
518 public function mustRender( $file ) {
519 return false;
520 }
521
530 public function isMultiPage( $file ) {
531 return false;
532 }
533
542 public function pageCount( File $file ) {
543 return false;
544 }
545
554 public function isVectorized( $file ) {
555 return false;
556 }
557
568 public function isAnimatedImage( $file ) {
569 return false;
570 }
571
581 public function canAnimateThumbnail( $file ) {
582 return true;
583 }
584
591 public function isEnabled() {
592 return true;
593 }
594
612 public function getPageDimensions( File $image, $page ) {
613 return false;
614 }
615
626 public function getPageText( File $image, $page ) {
627 return false;
628 }
629
635 public function getEntireText( File $file ) {
636 $numPages = $file->pageCount();
637 if ( !$numPages ) {
638 // Not a multipage document
639 return $this->getPageText( $file, 1 );
640 }
641 $document = '';
642 for ( $i = 1; $i <= $numPages; $i++ ) {
643 $curPage = $this->getPageText( $file, $i );
644 if ( is_string( $curPage ) ) {
645 $document .= $curPage . "\n";
646 }
647 }
648 if ( $document !== '' ) {
649 return $document;
650 }
651 return false;
652 }
653
684 public function formatMetadata( $image, $context = false ) {
685 return false;
686 }
687
700 protected function formatMetadataHelper( $metadataArray, $context = false ) {
701 $result = [
702 'visible' => [],
703 'collapsed' => []
704 ];
705
706 // Allow this MediaHandler to override formatting on certain values
707 foreach ( $metadataArray as $tag => $vals ) {
708 $v = $this->formatTag( $tag, $vals, $context );
709 if ( $v === false ) {
710 // Use default formatting
711 continue;
712 }
713 if ( $v === null ) {
714 // Remove this tag, don't format it for display
715 unset( $metadataArray[$tag] );
716 } else {
717 // Allow subclass to override default formatting.
718 $metadataArray[$tag] = [ '_formatted' => $v ];
719 if ( isset( $v['_type'] ) ) {
720 $metadataArray[$tag]['_type'] = $v['_type'];
721 unset( $metadataArray[$tag]['_formatted']['_type'] );
722 }
723 }
724 }
725
726 $formatted = FormatMetadata::getFormattedData( $metadataArray, $context );
727 // Sort fields into visible and collapsed
728 $visibleFields = $this->visibleMetadataFields();
729 foreach ( $formatted as $name => $value ) {
730 $tag = strtolower( $name );
731 self::addMeta( $result,
732 in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
733 'exif',
734 $tag,
735 $value
736 );
737 }
738
739 return $result;
740 }
741
754 protected function formatTag( string $key, $vals, $context = false ) {
755 return false; // Use default formatting
756 }
757
766 protected function visibleMetadataFields() {
768 }
769
793 protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
794 $msg = wfMessage( "$type-$id", $param );
795 if ( $msg->exists() ) {
796 $name = $msg->text();
797 } else {
798 // This is for future compatibility when using instant commons.
799 // So as to not display as ugly a name if a new metadata
800 // property is defined that we don't know about
801 // (not a major issue since such a property would be collapsed
802 // by default).
803 wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id );
804 $name = wfEscapeWikiText( $id );
805 }
806 $array[$visibility][] = [
807 'id' => "$type-$id",
808 'name' => $name,
809 'value' => $value
810 ];
811 }
812
821 public function getShortDesc( $file ) {
822 return self::getGeneralShortDesc( $file );
823 }
824
833 public function getLongDesc( $file ) {
834 return self::getGeneralLongDesc( $file );
835 }
836
843 public static function getGeneralShortDesc( $file ) {
844 global $wgLang;
845
846 return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
847 }
848
855 public static function getGeneralLongDesc( $file ) {
856 return wfMessage( 'file-info' )->sizeParams( $file->getSize() )
857 ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )->parse();
858 }
859
868 public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
869 $idealWidth = $boxWidth * $maxHeight / $boxHeight;
870 $roundedUp = ceil( $idealWidth );
871 if ( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) {
872 return (int)floor( $idealWidth );
873 }
874 return $roundedUp;
875 }
876
885 public function getDimensionsString( $file ) {
886 return '';
887 }
888
901 public function parserTransformHook( $parser, $file ) {
902 }
903
916 public function verifyUpload( $fileName ) {
917 return Status::newGood();
918 }
919
930 public function removeBadFile( $dstPath, $retval = 0 ) {
931 if ( file_exists( $dstPath ) ) {
932 $thumbstat = stat( $dstPath );
933 if ( $thumbstat['size'] == 0 || $retval != 0 ) {
934 $result = unlink( $dstPath );
935
936 if ( $result ) {
937 wfDebugLog( 'thumbnail',
938 sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded',
939 $thumbstat['size'], $dstPath ) );
940 } else {
941 wfDebugLog( 'thumbnail',
942 sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed',
943 $thumbstat['size'], $dstPath ) );
944 }
945
946 return true;
947 }
948 }
949
950 return false;
951 }
952
967 public function filterThumbnailPurgeList( &$files, $options ) {
968 // Do nothing
969 }
970
978 public function canRotate() {
979 return false;
980 }
981
998 public function getRotation( $file ) {
999 return 0;
1000 }
1001
1013 protected function logErrorForExternalProcess( $retval, $err, $cmd ) {
1014 # Keep error output limited (T59985)
1015 $errMessage = trim( substr( $err, 0, self::MAX_ERR_LOG_SIZE ) );
1016
1017 wfDebugLog( 'thumbnail',
1018 sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
1019 wfHostname(), $retval, $errMessage, $cmd ) );
1020 }
1021
1031 public function getAvailableLanguages( File $file ) {
1032 return [];
1033 }
1034
1046 public function getMatchedLanguage( $userPreferredLanguage, array $availableLanguages ) {
1047 return null;
1048 }
1049
1065 public function getDefaultRenderLanguage( File $file ) {
1066 return null;
1067 }
1068
1081 public function getLength( $file ) {
1082 return 0.0;
1083 }
1084
1092 public function isExpensiveToThumbnail( $file ) {
1093 return false;
1094 }
1095
1104 public function supportsBucketing() {
1105 return false;
1106 }
1107
1116 public function sanitizeParamsForBucketing( $params ) {
1117 return $params;
1118 }
1119
1146 public function getWarningConfig( $file ) {
1147 return null;
1148 }
1149
1157 public static function getPageRangesByDimensions( $pagesByDimensions ) {
1158 $pageRangesByDimensions = [];
1159
1160 foreach ( $pagesByDimensions as $dimensions => $pageList ) {
1161 $ranges = [];
1162 $firstPage = $pageList[0];
1163 $lastPage = $firstPage - 1;
1164
1165 foreach ( $pageList as $page ) {
1166 if ( $page > $lastPage + 1 ) {
1167 if ( $firstPage !== $lastPage ) {
1168 $ranges[] = "$firstPage-$lastPage";
1169 } else {
1170 $ranges[] = "$firstPage";
1171 }
1172
1173 $firstPage = $page;
1174 }
1175
1176 $lastPage = $page;
1177 }
1178
1179 if ( $firstPage != $lastPage ) {
1180 $ranges[] = "$firstPage-$lastPage";
1181 } else {
1182 $ranges[] = "$firstPage";
1183 }
1184
1185 $pageRangesByDimensions[ $dimensions ] = $ranges;
1186 }
1187
1188 $dimensionsString = [];
1189 foreach ( $pageRangesByDimensions as $dimensions => $pageRanges ) {
1190 $dimensionsString[] = "$dimensions:" . implode( ',', $pageRanges );
1191 }
1192
1193 return implode( '/', $dimensionsString );
1194 }
1195
1204 public function getContentHeaders( $metadata ) {
1205 return [ 'X-Content-Dimensions' => '' ]; // T175689
1206 }
1207
1216 public function useSplitMetadata() {
1217 return false;
1218 }
1219}
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
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.
if(!defined( 'MW_NO_SESSION') &&MW_ENTRY_POINT !=='cli') $wgLang
Definition Setup.php:536
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:73
pageCount()
Returns the number of pages of a multipage document, or false for documents which aren't multipage do...
Definition File.php:2174
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 it's 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 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.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Service locator for MediaWiki core services.
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition Parser.php:155
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
Trivial implementation of MediaHandlerState.
An interface to support process-local caching of handler data associated with a given file.