MediaWiki master
MediaHandler.php
Go to the documentation of this file.
1<?php
29
43abstract class MediaHandler {
44 public const TRANSFORM_LATER = 1;
45 public const METADATA_GOOD = true;
46 public const METADATA_BAD = false;
47 public const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
51 private const MAX_ERR_LOG_SIZE = 65535;
52
59 public static function getHandler( $type ) {
60 return MediaWikiServices::getInstance()
61 ->getMediaHandlerFactory()->getHandler( $type );
62 }
63
69 abstract public function getParamMap();
70
80 abstract public function validateParam( $name, $value );
81
88 abstract public function makeParamString( $params );
89
96 abstract public function parseParamString( $str );
97
106 abstract public function normaliseParams( $image, &$params );
107
130 public function getImageSize( $image, $path ) {
131 return false;
132 }
133
159 public function getSizeAndMetadata( $state, $path ) {
160 return null;
161 }
162
172 public function getMetadata( $image, $path ) {
173 return '';
174 }
175
185 protected function useLegacyMetadata() {
186 return $this->hasMostDerivedMethod( 'getMetadata' )
187 || $this->hasMostDerivedMethod( 'getImageSize' );
188 }
189
197 protected function hasMostDerivedMethod( $name ) {
198 $rc = new ReflectionClass( $this );
199 $rm = new ReflectionMethod( $this, $name );
200 return $rm->getDeclaringClass()->getName() === $rc->getName();
201 }
202
223 final public function getSizeAndMetadataWithFallback( $file, $path ) {
224 if ( !$this->useLegacyMetadata() ) {
225 if ( $file instanceof MediaHandlerState ) {
226 $state = $file;
227 } else {
228 $state = new TrivialMediaHandlerState;
229 }
230 $info = $this->getSizeAndMetadata( $state, $path );
231 if ( $info === false ) {
232 return false;
233 }
234 if ( $info !== null ) {
235 $info += [ 'width' => 0, 'height' => 0, 'metadata' => [] ];
236 if ( !is_array( $info['metadata'] ) ) {
237 throw new InvalidArgumentException( 'Media handler ' .
238 static::class . ' returned ' . get_debug_type( $info['metadata'] ) .
239 ' for metadata, should be array' );
240 }
241 return $info;
242 }
243 }
244
245 $blob = $this->getMetadata( $file, $path );
246 // @phan-suppress-next-line PhanParamTooMany
247 $size = $this->getImageSize(
248 $file,
249 $path,
250 $blob // Secret TimedMediaHandler parameter
251 );
252 if ( $blob === false && $size === false ) {
253 return false;
254 }
255 if ( $size ) {
256 $info = [
257 'width' => $size[0] ?? 0,
258 'height' => $size[1] ?? 0
259 ];
260 if ( isset( $size['bits'] ) ) {
261 $info['bits'] = $size['bits'];
262 }
263 } else {
264 $info = [ 'width' => 0, 'height' => 0 ];
265 }
266 if ( $blob !== false ) {
267 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
268 $metadata = @unserialize( $blob );
269 if ( $metadata === false ) {
270 // Unserialize error
271 $metadata = [ '_error' => $blob ];
272 } elseif ( !is_array( $metadata ) ) {
273 $metadata = [];
274 }
275 $info['metadata'] = $metadata;
276 } else {
277 $info['metadata'] = [];
278 }
279 return $info;
280 }
281
299 public static function getMetadataVersion() {
300 $version = [ '2' ]; // core metadata version
301 ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )->onGetMetadataVersion( $version );
302
303 return implode( ';', $version );
304 }
305
317 public function convertMetadataVersion( $metadata, $version = 1 ) {
318 return $metadata;
319 }
320
329 public function getMetadataType( $image ) {
330 return false;
331 }
332
351 public function isMetadataValid( $image, $metadata ) {
352 return self::METADATA_GOOD;
353 }
354
380 public function isFileMetadataValid( $image ) {
381 return self::METADATA_GOOD;
382 }
383
418 public function getCommonMetaArray( File $file ) {
419 return false;
420 }
421
437 public function getScriptedTransform( $image, $script, $params ) {
438 return false;
439 }
440
453 final public function getTransform( $image, $dstPath, $dstUrl, $params ) {
454 return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
455 }
456
471 abstract public function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
472
483 public function getThumbType( $ext, $mime, $params = null ) {
484 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
485 if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
486 // The extension is not valid for this MIME type and we do
487 // recognize the MIME type
488 $knownExt = $magic->getExtensionFromMimeTypeOrNull( $mime );
489 if ( $knownExt !== null ) {
490 return [ $knownExt, $mime ];
491 }
492 }
493
494 // The extension is correct (true) or the MIME type is unknown to
495 // MediaWiki (null)
496 return [ $ext, $mime ];
497 }
498
507 public function canRender( $file ) {
508 return true;
509 }
510
520 public function mustRender( $file ) {
521 return false;
522 }
523
532 public function isMultiPage( $file ) {
533 return false;
534 }
535
544 public function pageCount( File $file ) {
545 return false;
546 }
547
556 public function isVectorized( $file ) {
557 return false;
558 }
559
570 public function isAnimatedImage( $file ) {
571 return false;
572 }
573
583 public function canAnimateThumbnail( $file ) {
584 return true;
585 }
586
593 public function isEnabled() {
594 return true;
595 }
596
614 public function getPageDimensions( File $image, $page ) {
615 return false;
616 }
617
628 public function getPageText( File $image, $page ) {
629 return false;
630 }
631
637 public function getEntireText( File $file ) {
638 $numPages = $file->pageCount();
639 if ( !$numPages ) {
640 // Not a multipage document
641 return $this->getPageText( $file, 1 );
642 }
643 $document = '';
644 for ( $i = 1; $i <= $numPages; $i++ ) {
645 $curPage = $this->getPageText( $file, $i );
646 if ( is_string( $curPage ) ) {
647 $document .= $curPage . "\n";
648 }
649 }
650 if ( $document !== '' ) {
651 return $document;
652 }
653 return false;
654 }
655
686 public function formatMetadata( $image, $context = false ) {
687 return false;
688 }
689
702 protected function formatMetadataHelper( $metadataArray, $context = false ) {
703 $result = [
704 'visible' => [],
705 'collapsed' => []
706 ];
707
708 // Allow this MediaHandler to override formatting on certain values
709 foreach ( $metadataArray as $tag => $vals ) {
710 $v = $this->formatTag( $tag, $vals, $context );
711 if ( $v === false ) {
712 // Use default formatting
713 continue;
714 }
715 if ( $v === null ) {
716 // Remove this tag, don't format it for display
717 unset( $metadataArray[$tag] );
718 } else {
719 // Allow subclass to override default formatting.
720 $metadataArray[$tag] = [ '_formatted' => $v ];
721 if ( isset( $v['_type'] ) ) {
722 $metadataArray[$tag]['_type'] = $v['_type'];
723 unset( $metadataArray[$tag]['_formatted']['_type'] );
724 }
725 }
726 }
727
728 $formatted = FormatMetadata::getFormattedData( $metadataArray, $context );
729 // Sort fields into visible and collapsed
730 $visibleFields = $this->visibleMetadataFields();
731 foreach ( $formatted as $name => $value ) {
732 $tag = strtolower( $name );
733 self::addMeta( $result,
734 in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
735 'exif',
736 $tag,
737 $value
738 );
739 }
740
741 return $result;
742 }
743
756 protected function formatTag( string $key, $vals, $context = false ) {
757 return false; // Use default formatting
758 }
759
768 protected function visibleMetadataFields() {
769 return FormatMetadata::getVisibleFields();
770 }
771
795 protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
796 $msg = wfMessage( "$type-$id", (string)$param );
797 if ( $msg->exists() ) {
798 $name = $msg->text();
799 } else {
800 // This is for future compatibility when using instant commons.
801 // So as to not display as ugly a name if a new metadata
802 // property is defined that we don't know about
803 // (not a major issue since such a property would be collapsed
804 // by default).
805 wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id );
806 $name = wfEscapeWikiText( $id );
807 }
808 $array[$visibility][] = [
809 'id' => "$type-$id",
810 'name' => $name,
811 'value' => $value
812 ];
813 }
814
823 public function getShortDesc( $file ) {
824 return self::getGeneralShortDesc( $file );
825 }
826
835 public function getLongDesc( $file ) {
836 return self::getGeneralLongDesc( $file );
837 }
838
845 public static function getGeneralShortDesc( $file ) {
846 global $wgLang;
847
848 return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
849 }
850
857 public static function getGeneralLongDesc( $file ) {
858 return wfMessage( 'file-info' )->sizeParams( $file->getSize() )
859 ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )->parse();
860 }
861
870 public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
871 $idealWidth = $boxWidth * $maxHeight / $boxHeight;
872 $roundedUp = ceil( $idealWidth );
873 if ( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) {
874 return (int)floor( $idealWidth );
875 }
876 return $roundedUp;
877 }
878
887 public function getDimensionsString( $file ) {
888 return '';
889 }
890
903 public function parserTransformHook( $parser, $file ) {
904 }
905
918 public function verifyUpload( $fileName ) {
919 return Status::newGood();
920 }
921
932 public function removeBadFile( $dstPath, $retval = 0 ) {
933 if ( file_exists( $dstPath ) ) {
934 $thumbstat = stat( $dstPath );
935 if ( $thumbstat['size'] == 0 || $retval != 0 ) {
936 $result = unlink( $dstPath );
937
938 if ( $result ) {
939 wfDebugLog( 'thumbnail',
940 sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded',
941 $thumbstat['size'], $dstPath ) );
942 } else {
943 wfDebugLog( 'thumbnail',
944 sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed',
945 $thumbstat['size'], $dstPath ) );
946 }
947
948 return true;
949 }
950 }
951
952 return false;
953 }
954
969 public function filterThumbnailPurgeList( &$files, $options ) {
970 // Do nothing
971 }
972
980 public function canRotate() {
981 return false;
982 }
983
1000 public function getRotation( $file ) {
1001 return 0;
1002 }
1003
1015 protected function logErrorForExternalProcess( $retval, $err, $cmd ) {
1016 # Keep error output limited (T59985)
1017 $errMessage = trim( substr( $err, 0, self::MAX_ERR_LOG_SIZE ) );
1018
1019 wfDebugLog( 'thumbnail',
1020 sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
1021 wfHostname(), $retval, $errMessage, $cmd ) );
1022 }
1023
1033 public function getAvailableLanguages( File $file ) {
1034 return [];
1035 }
1036
1048 public function getMatchedLanguage( $userPreferredLanguage, array $availableLanguages ) {
1049 return null;
1050 }
1051
1067 public function getDefaultRenderLanguage( File $file ) {
1068 return null;
1069 }
1070
1083 public function getLength( $file ) {
1084 return 0.0;
1085 }
1086
1094 public function isExpensiveToThumbnail( $file ) {
1095 return false;
1096 }
1097
1106 public function supportsBucketing() {
1107 return false;
1108 }
1109
1118 public function sanitizeParamsForBucketing( $params ) {
1119 return $params;
1120 }
1121
1148 public function getWarningConfig( $file ) {
1149 return null;
1150 }
1151
1159 public static function getPageRangesByDimensions( $pagesByDimensions ) {
1160 $pageRangesByDimensions = [];
1161
1162 foreach ( $pagesByDimensions as $dimensions => $pageList ) {
1163 $ranges = [];
1164 $firstPage = $pageList[0];
1165 $lastPage = $firstPage - 1;
1166
1167 foreach ( $pageList as $page ) {
1168 if ( $page > $lastPage + 1 ) {
1169 if ( $firstPage !== $lastPage ) {
1170 $ranges[] = "$firstPage-$lastPage";
1171 } else {
1172 $ranges[] = "$firstPage";
1173 }
1174
1175 $firstPage = $page;
1176 }
1177
1178 $lastPage = $page;
1179 }
1180
1181 if ( $firstPage != $lastPage ) {
1182 $ranges[] = "$firstPage-$lastPage";
1183 } else {
1184 $ranges[] = "$firstPage";
1185 }
1186
1187 $pageRangesByDimensions[ $dimensions ] = $ranges;
1188 }
1189
1190 $dimensionsString = [];
1191 foreach ( $pageRangesByDimensions as $dimensions => $pageRanges ) {
1192 $dimensionsString[] = "$dimensions:" . implode( ',', $pageRanges );
1193 }
1194
1195 return implode( '/', $dimensionsString );
1196 }
1197
1206 public function getContentHeaders( $metadata ) {
1207 return [ 'X-Content-Dimensions' => '' ]; // T175689
1208 }
1209
1218 public function useSplitMetadata() {
1219 return false;
1220 }
1221}
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:563
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.
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:93
pageCount()
Returns the number of pages of a multipage document, or false for documents which aren't multipage do...
Definition File.php:2262
Local file in the wiki's own database.
Definition LocalFile.php:93
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:147
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
Trivial implementation of MediaHandlerState.
Class representing a non-directory file on the file system.
Definition FSFile.php:34
An interface to support process-local caching of handler data associated with a given file.
Interface for objects which can provide a MediaWiki context on request.