MediaWiki master
MediaHandler.php
Go to the documentation of this file.
1<?php
25
39abstract class MediaHandler {
40 public const TRANSFORM_LATER = 1;
41 public const METADATA_GOOD = true;
42 public const METADATA_BAD = false;
43 public const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
47 private const MAX_ERR_LOG_SIZE = 65535;
48
55 public static function getHandler( $type ) {
56 return MediaWikiServices::getInstance()
57 ->getMediaHandlerFactory()->getHandler( $type );
58 }
59
65 abstract public function getParamMap();
66
76 abstract public function validateParam( $name, $value );
77
84 abstract public function makeParamString( $params );
85
92 abstract public function parseParamString( $str );
93
102 abstract public function normaliseParams( $image, &$params );
103
126 public function getImageSize( $image, $path ) {
127 return false;
128 }
129
155 public function getSizeAndMetadata( $state, $path ) {
156 return null;
157 }
158
168 public function getMetadata( $image, $path ) {
169 return '';
170 }
171
181 protected function useLegacyMetadata() {
182 return $this->hasMostDerivedMethod( 'getMetadata' )
183 || $this->hasMostDerivedMethod( 'getImageSize' );
184 }
185
193 protected function hasMostDerivedMethod( $name ) {
194 $rc = new ReflectionClass( $this );
195 $rm = new ReflectionMethod( $this, $name );
196 return $rm->getDeclaringClass()->getName() === $rc->getName();
197 }
198
219 final public function getSizeAndMetadataWithFallback( $file, $path ) {
220 if ( !$this->useLegacyMetadata() ) {
221 if ( $file instanceof MediaHandlerState ) {
222 $state = $file;
223 } else {
224 $state = new TrivialMediaHandlerState;
225 }
226 $info = $this->getSizeAndMetadata( $state, $path );
227 if ( $info === false ) {
228 return false;
229 }
230 if ( $info !== null ) {
231 $info += [ 'width' => 0, 'height' => 0, 'metadata' => [] ];
232 if ( !is_array( $info['metadata'] ) ) {
233 throw new InvalidArgumentException( 'Media handler ' .
234 static::class . ' returned ' . gettype( $info['metadata'] ) .
235 ' for metadata, should be array' );
236 }
237 return $info;
238 }
239 }
240
241 $blob = $this->getMetadata( $file, $path );
242 // @phan-suppress-next-line PhanParamTooMany
243 $size = $this->getImageSize(
244 $file,
245 $path,
246 $blob // Secret TimedMediaHandler parameter
247 );
248 if ( $blob === false && $size === false ) {
249 return false;
250 }
251 if ( $size ) {
252 $info = [
253 'width' => $size[0] ?? 0,
254 'height' => $size[1] ?? 0
255 ];
256 if ( isset( $size['bits'] ) ) {
257 $info['bits'] = $size['bits'];
258 }
259 } else {
260 $info = [ 'width' => 0, 'height' => 0 ];
261 }
262 if ( $blob !== false ) {
263 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
264 $metadata = @unserialize( $blob );
265 if ( $metadata === false ) {
266 // Unserialize error
267 $metadata = [ '_error' => $blob ];
268 } elseif ( !is_array( $metadata ) ) {
269 $metadata = [];
270 }
271 $info['metadata'] = $metadata;
272 } else {
273 $info['metadata'] = [];
274 }
275 return $info;
276 }
277
295 public static function getMetadataVersion() {
296 $version = [ '2' ]; // core metadata version
297 ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )->onGetMetadataVersion( $version );
298
299 return implode( ';', $version );
300 }
301
313 public function convertMetadataVersion( $metadata, $version = 1 ) {
314 return $metadata;
315 }
316
325 public function getMetadataType( $image ) {
326 return false;
327 }
328
347 public function isMetadataValid( $image, $metadata ) {
348 return self::METADATA_GOOD;
349 }
350
376 public function isFileMetadataValid( $image ) {
377 return self::METADATA_GOOD;
378 }
379
414 public function getCommonMetaArray( File $file ) {
415 return false;
416 }
417
433 public function getScriptedTransform( $image, $script, $params ) {
434 return false;
435 }
436
449 final public function getTransform( $image, $dstPath, $dstUrl, $params ) {
450 return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
451 }
452
467 abstract public function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
468
479 public function getThumbType( $ext, $mime, $params = null ) {
480 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
481 if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
482 // The extension is not valid for this MIME type and we do
483 // recognize the MIME type
484 $knownExt = $magic->getExtensionFromMimeTypeOrNull( $mime );
485 if ( $knownExt !== null ) {
486 return [ $knownExt, $mime ];
487 }
488 }
489
490 // The extension is correct (true) or the MIME type is unknown to
491 // MediaWiki (null)
492 return [ $ext, $mime ];
493 }
494
503 public function canRender( $file ) {
504 return true;
505 }
506
516 public function mustRender( $file ) {
517 return false;
518 }
519
528 public function isMultiPage( $file ) {
529 return false;
530 }
531
540 public function pageCount( File $file ) {
541 return false;
542 }
543
552 public function isVectorized( $file ) {
553 return false;
554 }
555
566 public function isAnimatedImage( $file ) {
567 return false;
568 }
569
579 public function canAnimateThumbnail( $file ) {
580 return true;
581 }
582
589 public function isEnabled() {
590 return true;
591 }
592
610 public function getPageDimensions( File $image, $page ) {
611 return false;
612 }
613
624 public function getPageText( File $image, $page ) {
625 return false;
626 }
627
633 public function getEntireText( File $file ) {
634 $numPages = $file->pageCount();
635 if ( !$numPages ) {
636 // Not a multipage document
637 return $this->getPageText( $file, 1 );
638 }
639 $document = '';
640 for ( $i = 1; $i <= $numPages; $i++ ) {
641 $curPage = $this->getPageText( $file, $i );
642 if ( is_string( $curPage ) ) {
643 $document .= $curPage . "\n";
644 }
645 }
646 if ( $document !== '' ) {
647 return $document;
648 }
649 return false;
650 }
651
682 public function formatMetadata( $image, $context = false ) {
683 return false;
684 }
685
698 protected function formatMetadataHelper( $metadataArray, $context = false ) {
699 $result = [
700 'visible' => [],
701 'collapsed' => []
702 ];
703
704 // Allow this MediaHandler to override formatting on certain values
705 foreach ( $metadataArray as $tag => $vals ) {
706 $v = $this->formatTag( $tag, $vals, $context );
707 if ( $v === false ) {
708 // Use default formatting
709 continue;
710 }
711 if ( $v === null ) {
712 // Remove this tag, don't format it for display
713 unset( $metadataArray[$tag] );
714 } else {
715 // Allow subclass to override default formatting.
716 $metadataArray[$tag] = [ '_formatted' => $v ];
717 if ( isset( $v['_type'] ) ) {
718 $metadataArray[$tag]['_type'] = $v['_type'];
719 unset( $metadataArray[$tag]['_formatted']['_type'] );
720 }
721 }
722 }
723
724 $formatted = FormatMetadata::getFormattedData( $metadataArray, $context );
725 // Sort fields into visible and collapsed
726 $visibleFields = $this->visibleMetadataFields();
727 foreach ( $formatted as $name => $value ) {
728 $tag = strtolower( $name );
729 self::addMeta( $result,
730 in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
731 'exif',
732 $tag,
733 $value
734 );
735 }
736
737 return $result;
738 }
739
752 protected function formatTag( string $key, $vals, $context = false ) {
753 return false; // Use default formatting
754 }
755
764 protected function visibleMetadataFields() {
766 }
767
791 protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
792 $msg = wfMessage( "$type-$id", $param );
793 if ( $msg->exists() ) {
794 $name = $msg->text();
795 } else {
796 // This is for future compatibility when using instant commons.
797 // So as to not display as ugly a name if a new metadata
798 // property is defined that we don't know about
799 // (not a major issue since such a property would be collapsed
800 // by default).
801 wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id );
802 $name = wfEscapeWikiText( $id );
803 }
804 $array[$visibility][] = [
805 'id' => "$type-$id",
806 'name' => $name,
807 'value' => $value
808 ];
809 }
810
819 public function getShortDesc( $file ) {
820 return self::getGeneralShortDesc( $file );
821 }
822
831 public function getLongDesc( $file ) {
832 return self::getGeneralLongDesc( $file );
833 }
834
841 public static function getGeneralShortDesc( $file ) {
842 global $wgLang;
843
844 return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
845 }
846
853 public static function getGeneralLongDesc( $file ) {
854 return wfMessage( 'file-info' )->sizeParams( $file->getSize() )
855 ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )->parse();
856 }
857
866 public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
867 $idealWidth = $boxWidth * $maxHeight / $boxHeight;
868 $roundedUp = ceil( $idealWidth );
869 if ( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) {
870 return (int)floor( $idealWidth );
871 }
872 return $roundedUp;
873 }
874
883 public function getDimensionsString( $file ) {
884 return '';
885 }
886
899 public function parserTransformHook( $parser, $file ) {
900 }
901
914 public function verifyUpload( $fileName ) {
915 return Status::newGood();
916 }
917
928 public function removeBadFile( $dstPath, $retval = 0 ) {
929 if ( file_exists( $dstPath ) ) {
930 $thumbstat = stat( $dstPath );
931 if ( $thumbstat['size'] == 0 || $retval != 0 ) {
932 $result = unlink( $dstPath );
933
934 if ( $result ) {
935 wfDebugLog( 'thumbnail',
936 sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded',
937 $thumbstat['size'], $dstPath ) );
938 } else {
939 wfDebugLog( 'thumbnail',
940 sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed',
941 $thumbstat['size'], $dstPath ) );
942 }
943
944 return true;
945 }
946 }
947
948 return false;
949 }
950
965 public function filterThumbnailPurgeList( &$files, $options ) {
966 // Do nothing
967 }
968
976 public function canRotate() {
977 return false;
978 }
979
996 public function getRotation( $file ) {
997 return 0;
998 }
999
1011 protected function logErrorForExternalProcess( $retval, $err, $cmd ) {
1012 # Keep error output limited (T59985)
1013 $errMessage = trim( substr( $err, 0, self::MAX_ERR_LOG_SIZE ) );
1014
1015 wfDebugLog( 'thumbnail',
1016 sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
1017 wfHostname(), $retval, $errMessage, $cmd ) );
1018 }
1019
1029 public function getAvailableLanguages( File $file ) {
1030 return [];
1031 }
1032
1044 public function getMatchedLanguage( $userPreferredLanguage, array $availableLanguages ) {
1045 return null;
1046 }
1047
1063 public function getDefaultRenderLanguage( File $file ) {
1064 return null;
1065 }
1066
1079 public function getLength( $file ) {
1080 return 0.0;
1081 }
1082
1090 public function isExpensiveToThumbnail( $file ) {
1091 return false;
1092 }
1093
1102 public function supportsBucketing() {
1103 return false;
1104 }
1105
1115 return $params;
1116 }
1117
1144 public function getWarningConfig( $file ) {
1145 return null;
1146 }
1147
1155 public static function getPageRangesByDimensions( $pagesByDimensions ) {
1156 $pageRangesByDimensions = [];
1157
1158 foreach ( $pagesByDimensions as $dimensions => $pageList ) {
1159 $ranges = [];
1160 $firstPage = $pageList[0];
1161 $lastPage = $firstPage - 1;
1162
1163 foreach ( $pageList as $page ) {
1164 if ( $page > $lastPage + 1 ) {
1165 if ( $firstPage !== $lastPage ) {
1166 $ranges[] = "$firstPage-$lastPage";
1167 } else {
1168 $ranges[] = "$firstPage";
1169 }
1170
1171 $firstPage = $page;
1172 }
1173
1174 $lastPage = $page;
1175 }
1176
1177 if ( $firstPage != $lastPage ) {
1178 $ranges[] = "$firstPage-$lastPage";
1179 } else {
1180 $ranges[] = "$firstPage";
1181 }
1182
1183 $pageRangesByDimensions[ $dimensions ] = $ranges;
1184 }
1185
1186 $dimensionsString = [];
1187 foreach ( $pageRangesByDimensions as $dimensions => $pageRanges ) {
1188 $dimensionsString[] = "$dimensions:" . implode( ',', $pageRanges );
1189 }
1190
1191 return implode( '/', $dimensionsString );
1192 }
1193
1202 public function getContentHeaders( $metadata ) {
1203 return [ 'X-Content-Dimensions' => '' ]; // T175689
1204 }
1205
1214 public function useSplitMetadata() {
1215 return false;
1216 }
1217}
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
array $params
The job parameters.
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:156
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.