MediaWiki master
MediaHandler.php
Go to the documentation of this file.
1<?php
15
29abstract class MediaHandler {
30 public const TRANSFORM_LATER = 1;
31 public const METADATA_GOOD = true;
32 public const METADATA_BAD = false;
33 public const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
37 private const MAX_ERR_LOG_SIZE = 65535;
38
45 public static function getHandler( $type ) {
46 return MediaWikiServices::getInstance()
47 ->getMediaHandlerFactory()->getHandler( $type );
48 }
49
55 abstract public function getParamMap();
56
66 abstract public function validateParam( $name, $value );
67
74 abstract public function makeParamString( $params );
75
82 abstract public function parseParamString( $str );
83
92 abstract public function normaliseParams( $image, &$params );
93
116 public function getImageSize( $image, $path ) {
117 return false;
118 }
119
145 public function getSizeAndMetadata( $state, $path ) {
146 return null;
147 }
148
158 public function getMetadata( $image, $path ) {
159 return '';
160 }
161
171 protected function useLegacyMetadata() {
172 return $this->hasMostDerivedMethod( 'getMetadata' )
173 || $this->hasMostDerivedMethod( 'getImageSize' );
174 }
175
183 protected function hasMostDerivedMethod( $name ) {
184 $rc = new ReflectionClass( $this );
185 $rm = new ReflectionMethod( $this, $name );
186 return $rm->getDeclaringClass()->getName() === $rc->getName();
187 }
188
209 final public function getSizeAndMetadataWithFallback( $file, $path ) {
210 if ( !$this->useLegacyMetadata() ) {
211 if ( $file instanceof MediaHandlerState ) {
212 $state = $file;
213 } else {
214 $state = new TrivialMediaHandlerState;
215 }
216 $info = $this->getSizeAndMetadata( $state, $path );
217 if ( $info === false ) {
218 return false;
219 }
220 if ( $info !== null ) {
221 $info += [ 'width' => 0, 'height' => 0, 'metadata' => [] ];
222 if ( !is_array( $info['metadata'] ) ) {
223 throw new InvalidArgumentException( 'Media handler ' .
224 static::class . ' returned ' . get_debug_type( $info['metadata'] ) .
225 ' for metadata, should be array' );
226 }
227 return $info;
228 }
229 }
230
231 $blob = $this->getMetadata( $file, $path );
232 // @phan-suppress-next-line PhanParamTooMany
233 $size = $this->getImageSize(
234 $file,
235 $path,
236 $blob // Secret TimedMediaHandler parameter
237 );
238 if ( $blob === false && $size === false ) {
239 return false;
240 }
241 if ( $size ) {
242 $info = [
243 'width' => $size[0] ?? 0,
244 'height' => $size[1] ?? 0
245 ];
246 if ( isset( $size['bits'] ) ) {
247 $info['bits'] = $size['bits'];
248 }
249 } else {
250 $info = [ 'width' => 0, 'height' => 0 ];
251 }
252 if ( $blob !== false ) {
253 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
254 $metadata = @unserialize( $blob );
255 if ( $metadata === false ) {
256 // Unserialize error
257 $metadata = [ '_error' => $blob ];
258 } elseif ( !is_array( $metadata ) ) {
259 $metadata = [];
260 }
261 $info['metadata'] = $metadata;
262 } else {
263 $info['metadata'] = [];
264 }
265 return $info;
266 }
267
285 public static function getMetadataVersion() {
286 $version = [ '2' ]; // core metadata version
287 ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) )->onGetMetadataVersion( $version );
288
289 return implode( ';', $version );
290 }
291
303 public function convertMetadataVersion( $metadata, $version = 1 ) {
304 return $metadata;
305 }
306
315 public function getMetadataType( $image ) {
316 return false;
317 }
318
337 public function isMetadataValid( $image, $metadata ) {
338 return self::METADATA_GOOD;
339 }
340
366 public function isFileMetadataValid( $image ) {
367 return self::METADATA_GOOD;
368 }
369
404 public function getCommonMetaArray( File $file ) {
405 return false;
406 }
407
423 public function getScriptedTransform( $image, $script, $params ) {
424 return false;
425 }
426
439 final public function getTransform( $image, $dstPath, $dstUrl, $params ) {
440 return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
441 }
442
457 abstract public function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
458
469 public function getThumbType( $ext, $mime, $params = null ) {
470 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
471 if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
472 // The extension is not valid for this MIME type and we do
473 // recognize the MIME type
474 $knownExt = $magic->getExtensionFromMimeTypeOrNull( $mime );
475 if ( $knownExt !== null ) {
476 return [ $knownExt, $mime ];
477 }
478 }
479
480 // The extension is correct (true) or the MIME type is unknown to
481 // MediaWiki (null)
482 return [ $ext, $mime ];
483 }
484
493 public function canRender( $file ) {
494 return true;
495 }
496
506 public function mustRender( $file ) {
507 return false;
508 }
509
518 public function isMultiPage( $file ) {
519 return false;
520 }
521
530 public function pageCount( File $file ) {
531 return false;
532 }
533
542 public function isVectorized( $file ) {
543 return false;
544 }
545
556 public function isAnimatedImage( $file ) {
557 return false;
558 }
559
569 public function canAnimateThumbnail( $file ) {
570 return true;
571 }
572
579 public function isEnabled() {
580 return true;
581 }
582
600 public function getPageDimensions( File $image, $page ) {
601 return false;
602 }
603
614 public function getPageText( File $image, $page ) {
615 return false;
616 }
617
623 public function getEntireText( File $file ) {
624 $numPages = $file->pageCount();
625 if ( !$numPages ) {
626 // Not a multipage document
627 return $this->getPageText( $file, 1 );
628 }
629 $document = '';
630 for ( $i = 1; $i <= $numPages; $i++ ) {
631 $curPage = $this->getPageText( $file, $i );
632 if ( is_string( $curPage ) ) {
633 $document .= $curPage . "\n";
634 }
635 }
636 if ( $document !== '' ) {
637 return $document;
638 }
639 return false;
640 }
641
672 public function formatMetadata( $image, $context = false ) {
673 return false;
674 }
675
688 protected function formatMetadataHelper( $metadataArray, $context = false ) {
689 $result = [
690 'visible' => [],
691 'collapsed' => []
692 ];
693
694 // Allow this MediaHandler to override formatting on certain values
695 foreach ( $metadataArray as $tag => $vals ) {
696 $v = $this->formatTag( $tag, $vals, $context );
697 if ( $v === false ) {
698 // Use default formatting
699 continue;
700 }
701 if ( $v === null ) {
702 // Remove this tag, don't format it for display
703 unset( $metadataArray[$tag] );
704 } else {
705 // Allow subclass to override default formatting.
706 $metadataArray[$tag] = [ '_formatted' => $v ];
707 if ( isset( $v['_type'] ) ) {
708 $metadataArray[$tag]['_type'] = $v['_type'];
709 unset( $metadataArray[$tag]['_formatted']['_type'] );
710 }
711 }
712 }
713
714 $formatted = FormatMetadata::getFormattedData( $metadataArray, $context );
715 // Sort fields into visible and collapsed
716 $visibleFields = $this->visibleMetadataFields();
717 foreach ( $formatted as $name => $value ) {
718 $tag = strtolower( $name );
719 self::addMeta( $result,
720 in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
721 'exif',
722 $tag,
723 $value
724 );
725 }
726
727 return $result;
728 }
729
742 protected function formatTag( string $key, $vals, $context = false ) {
743 return false; // Use default formatting
744 }
745
754 protected function visibleMetadataFields() {
755 return FormatMetadata::getVisibleFields();
756 }
757
781 protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
782 $msg = wfMessage( "$type-$id", (string)$param );
783 if ( $msg->exists() ) {
784 $name = $msg->text();
785 } else {
786 // This is for future compatibility when using instant commons.
787 // So as to not display as ugly a name if a new metadata
788 // property is defined that we don't know about
789 // (not a major issue since such a property would be collapsed
790 // by default).
791 wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id );
792 $name = wfEscapeWikiText( $id );
793 }
794 $array[$visibility][] = [
795 'id' => "$type-$id",
796 'name' => $name,
797 'value' => $value
798 ];
799 }
800
815 public function getShortDesc( $file ) {
816 return self::getGeneralShortDesc( $file );
817 }
818
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() ), ENT_QUOTES );
847 }
848
855 public static function getGeneralLongDesc( $file ) {
856 return wfMessage( 'file-info' )
857 ->sizeParams( $file->getSize() )
858 ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )
859 ->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(MW_ENTRY_POINT==='index') if(!defined( 'MW_NO_SESSION') &&MW_ENTRY_POINT !=='cli' $wgLang
Definition Setup.php:551
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:79
pageCount()
Returns the number of pages of a multipage document, or false for documents which aren't multipage do...
Definition File.php:2242
Local file in the wiki's own database.
Definition LocalFile.php:81
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:135
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Trivial implementation of MediaHandlerState.
Class representing a non-directory file on the file system.
Definition FSFile.php:21
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.