MediaWiki REL1_38
MediaHandler.php
Go to the documentation of this file.
1<?php
23
38abstract class MediaHandler {
39 public const TRANSFORM_LATER = 1;
40 public const METADATA_GOOD = true;
41 public const METADATA_BAD = false;
42 public const METADATA_COMPATIBLE = 2; // for old but backwards compatible.
46 private const MAX_ERR_LOG_SIZE = 65535;
47
54 public static function getHandler( $type ) {
55 return MediaWikiServices::getInstance()
56 ->getMediaHandlerFactory()->getHandler( $type );
57 }
58
63 abstract public function getParamMap();
64
73 abstract public function validateParam( $name, $value );
74
81 abstract public function makeParamString( $params );
82
89 abstract public function parseParamString( $str );
90
98 abstract public function normaliseParams( $image, &$params );
99
122 public function getImageSize( $image, $path ) {
123 return false;
124 }
125
151 public function getSizeAndMetadata( $state, $path ) {
152 return null;
153 }
154
164 public function getMetadata( $image, $path ) {
165 return '';
166 }
167
177 protected function useLegacyMetadata() {
178 return $this->hasMostDerivedMethod( 'getMetadata' )
179 || $this->hasMostDerivedMethod( 'getImageSize' );
180 }
181
189 protected function hasMostDerivedMethod( $name ) {
190 $rc = new ReflectionClass( $this );
191 $rm = new ReflectionMethod( $this, $name );
192 return $rm->getDeclaringClass()->getName() === $rc->getName();
193 }
194
215 final public function getSizeAndMetadataWithFallback( $file, $path ) {
216 if ( !$this->useLegacyMetadata() ) {
217 if ( $file instanceof MediaHandlerState ) {
218 $state = $file;
219 } else {
220 $state = new TrivialMediaHandlerState;
221 }
222 $info = $this->getSizeAndMetadata( $state, $path );
223 if ( $info === false ) {
224 return false;
225 }
226 if ( $info !== null ) {
227 $info += [ 'width' => 0, 'height' => 0, 'metadata' => [] ];
228 if ( !is_array( $info['metadata'] ) ) {
229 throw new InvalidArgumentException( 'Media handler ' .
230 static::class . ' returned ' . gettype( $info['metadata'] ) .
231 ' for metadata, should be array' );
232 }
233 return $info;
234 }
235 }
236
237 $blob = $this->getMetadata( $file, $path );
238 // @phan-suppress-next-line PhanParamTooMany
239 $size = $this->getImageSize(
240 $file,
241 $path,
242 $blob // Secret TimedMediaHandler parameter
243 );
244 if ( $blob === false && $size === false ) {
245 return false;
246 }
247 if ( $size ) {
248 $info = [
249 'width' => $size[0] ?? 0,
250 'height' => $size[1] ?? 0
251 ];
252 if ( isset( $size['bits'] ) ) {
253 $info['bits'] = $size['bits'];
254 }
255 } else {
256 $info = [ 'width' => 0, 'height' => 0 ];
257 }
258 if ( $blob !== false ) {
259 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
260 $metadata = @unserialize( $blob );
261 if ( $metadata === false ) {
262 // Unserialize error
263 $metadata = [ '_error' => $blob ];
264 } elseif ( !is_array( $metadata ) ) {
265 $metadata = [];
266 }
267 $info['metadata'] = $metadata;
268 } else {
269 $info['metadata'] = [];
270 }
271 return $info;
272 }
273
291 public static function getMetadataVersion() {
292 $version = [ '2' ]; // core metadata version
293 Hooks::runner()->onGetMetadataVersion( $version );
294
295 return implode( ';', $version );
296 }
297
309 public function convertMetadataVersion( $metadata, $version = 1 ) {
310 return $metadata;
311 }
312
321 public function getMetadataType( $image ) {
322 return false;
323 }
324
343 public function isMetadataValid( $image, $metadata ) {
344 return self::METADATA_GOOD;
345 }
346
372 public function isFileMetadataValid( $image ) {
373 return self::METADATA_GOOD;
374 }
375
410 public function getCommonMetaArray( File $file ) {
411 return false;
412 }
413
429 public function getScriptedTransform( $image, $script, $params ) {
430 return false;
431 }
432
445 final public function getTransform( $image, $dstPath, $dstUrl, $params ) {
446 return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
447 }
448
463 abstract public function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
464
475 public function getThumbType( $ext, $mime, $params = null ) {
476 $magic = MediaWikiServices::getInstance()->getMimeAnalyzer();
477 if ( !$ext || $magic->isMatchingExtension( $ext, $mime ) === false ) {
478 // The extension is not valid for this MIME type and we do
479 // recognize the MIME type
480 $knownExt = $magic->getExtensionFromMimeTypeOrNull( $mime );
481 if ( $knownExt !== null ) {
482 return [ $knownExt, $mime ];
483 }
484 }
485
486 // The extension is correct (true) or the MIME type is unknown to
487 // MediaWiki (null)
488 return [ $ext, $mime ];
489 }
490
499 public function canRender( $file ) {
500 return true;
501 }
502
512 public function mustRender( $file ) {
513 return false;
514 }
515
524 public function isMultiPage( $file ) {
525 return false;
526 }
527
536 public function pageCount( File $file ) {
537 return false;
538 }
539
548 public function isVectorized( $file ) {
549 return false;
550 }
551
562 public function isAnimatedImage( $file ) {
563 return false;
564 }
565
575 public function canAnimateThumbnail( $file ) {
576 return true;
577 }
578
585 public function isEnabled() {
586 return true;
587 }
588
606 public function getPageDimensions( File $image, $page ) {
607 return false;
608 }
609
620 public function getPageText( File $image, $page ) {
621 return false;
622 }
623
629 public function getEntireText( File $file ) {
630 $numPages = $file->pageCount();
631 if ( !$numPages ) {
632 // Not a multipage document
633 return $this->getPageText( $file, 1 );
634 }
635 $document = '';
636 for ( $i = 1; $i <= $numPages; $i++ ) {
637 $curPage = $this->getPageText( $file, $i );
638 if ( is_string( $curPage ) ) {
639 $document .= $curPage . "\n";
640 }
641 }
642 if ( $document !== '' ) {
643 return $document;
644 }
645 return false;
646 }
647
678 public function formatMetadata( $image, $context = false ) {
679 return false;
680 }
681
694 protected function formatMetadataHelper( $metadataArray, $context = false ) {
695 $result = [
696 'visible' => [],
697 'collapsed' => []
698 ];
699
700 // Allow this MediaHandler to override formatting on certain values
701 foreach ( $metadataArray as $tag => $vals ) {
702 $v = $this->formatTag( $tag, $vals, $context );
703 if ( $v === false ) {
704 // Use default formatting
705 continue;
706 }
707 if ( $v === null ) {
708 // Remove this tag, don't format it for display
709 unset( $metadataArray[$tag] );
710 } else {
711 // Allow subclass to override default formatting.
712 $metadataArray[$tag] = [ '_formatted' => $v ];
713 if ( isset( $v['_type'] ) ) {
714 $metadataArray[$tag]['_type'] = $v['_type'];
715 unset( $metadataArray[$tag]['_formatted']['_type'] );
716 }
717 }
718 }
719
720 $formatted = FormatMetadata::getFormattedData( $metadataArray, $context );
721 // Sort fields into visible and collapsed
722 $visibleFields = $this->visibleMetadataFields();
723 foreach ( $formatted as $name => $value ) {
724 $tag = strtolower( $name );
725 self::addMeta( $result,
726 in_array( $tag, $visibleFields ) ? 'visible' : 'collapsed',
727 'exif',
728 $tag,
729 $value
730 );
731 }
732
733 return $result;
734 }
735
748 protected function formatTag( string $key, $vals, $context = false ) {
749 return false; // Use default formatting
750 }
751
760 protected function visibleMetadataFields() {
762 }
763
787 protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
788 $msg = wfMessage( "$type-$id", $param );
789 if ( $msg->exists() ) {
790 $name = $msg->text();
791 } else {
792 // This is for future compatibility when using instant commons.
793 // So as to not display as ugly a name if a new metadata
794 // property is defined that we don't know about
795 // (not a major issue since such a property would be collapsed
796 // by default).
797 wfDebug( __METHOD__ . ' Unknown metadata name: ' . $id );
798 $name = wfEscapeWikiText( $id );
799 }
800 $array[$visibility][] = [
801 'id' => "$type-$id",
802 'name' => $name,
803 'value' => $value
804 ];
805 }
806
815 public function getShortDesc( $file ) {
817 }
818
827 public function getLongDesc( $file ) {
829 }
830
837 public static function getGeneralShortDesc( $file ) {
838 global $wgLang;
839
840 return htmlspecialchars( $wgLang->formatSize( $file->getSize() ) );
841 }
842
849 public static function getGeneralLongDesc( $file ) {
850 return wfMessage( 'file-info' )->sizeParams( $file->getSize() )
851 ->params( '<span class="mime-type">' . $file->getMimeType() . '</span>' )->parse();
852 }
853
862 public static function fitBoxWidth( $boxWidth, $boxHeight, $maxHeight ) {
863 $idealWidth = $boxWidth * $maxHeight / $boxHeight;
864 $roundedUp = ceil( $idealWidth );
865 if ( round( $roundedUp * $boxHeight / $boxWidth ) > $maxHeight ) {
866 return (int)floor( $idealWidth );
867 } else {
868 return $roundedUp;
869 }
870 }
871
880 public function getDimensionsString( $file ) {
881 return '';
882 }
883
896 public function parserTransformHook( $parser, $file ) {
897 }
898
911 public function verifyUpload( $fileName ) {
912 return Status::newGood();
913 }
914
925 public function removeBadFile( $dstPath, $retval = 0 ) {
926 if ( file_exists( $dstPath ) ) {
927 $thumbstat = stat( $dstPath );
928 if ( $thumbstat['size'] == 0 || $retval != 0 ) {
929 $result = unlink( $dstPath );
930
931 if ( $result ) {
932 wfDebugLog( 'thumbnail',
933 sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() succeeded',
934 $thumbstat['size'], $dstPath ) );
935 } else {
936 wfDebugLog( 'thumbnail',
937 sprintf( 'Removing bad %d-byte thumbnail "%s". unlink() failed',
938 $thumbstat['size'], $dstPath ) );
939 }
940
941 return true;
942 }
943 }
944
945 return false;
946 }
947
962 public function filterThumbnailPurgeList( &$files, $options ) {
963 // Do nothing
964 }
965
973 public function canRotate() {
974 return false;
975 }
976
993 public function getRotation( $file ) {
994 return 0;
995 }
996
1008 protected function logErrorForExternalProcess( $retval, $err, $cmd ) {
1009 # Keep error output limited (T59985)
1010 $errMessage = trim( substr( $err, 0, self::MAX_ERR_LOG_SIZE ) );
1011
1012 wfDebugLog( 'thumbnail',
1013 sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
1014 wfHostname(), $retval, $errMessage, $cmd ) );
1015 }
1016
1026 public function getAvailableLanguages( File $file ) {
1027 return [];
1028 }
1029
1041 public function getMatchedLanguage( $userPreferredLanguage, array $availableLanguages ) {
1042 return null;
1043 }
1044
1060 return null;
1061 }
1062
1075 public function getLength( $file ) {
1076 return 0.0;
1077 }
1078
1086 public function isExpensiveToThumbnail( $file ) {
1087 return false;
1088 }
1089
1098 public function supportsBucketing() {
1099 return false;
1100 }
1101
1110 public function sanitizeParamsForBucketing( $params ) {
1111 return $params;
1112 }
1113
1140 public function getWarningConfig( $file ) {
1141 return null;
1142 }
1143
1151 public static function getPageRangesByDimensions( $pagesByDimensions ) {
1152 $pageRangesByDimensions = [];
1153
1154 foreach ( $pagesByDimensions as $dimensions => $pageList ) {
1155 $ranges = [];
1156 $firstPage = $pageList[0];
1157 $lastPage = $firstPage - 1;
1158
1159 foreach ( $pageList as $page ) {
1160 if ( $page > $lastPage + 1 ) {
1161 if ( $firstPage != $lastPage ) {
1162 $ranges[] = "$firstPage-$lastPage";
1163 } else {
1164 $ranges[] = "$firstPage";
1165 }
1166
1167 $firstPage = $page;
1168 }
1169
1170 $lastPage = $page;
1171 }
1172
1173 if ( $firstPage != $lastPage ) {
1174 $ranges[] = "$firstPage-$lastPage";
1175 } else {
1176 $ranges[] = "$firstPage";
1177 }
1178
1179 $pageRangesByDimensions[ $dimensions ] = $ranges;
1180 }
1181
1182 $dimensionsString = [];
1183 foreach ( $pageRangesByDimensions as $dimensions => $pageRanges ) {
1184 $dimensionsString[] = "$dimensions:" . implode( ',', $pageRanges );
1185 }
1186
1187 return implode( '/', $dimensionsString );
1188 }
1189
1198 public function getContentHeaders( $metadata ) {
1199 return [ 'X-Content-Dimensions' => '' ]; // T175689
1200 }
1201
1210 public function useSplitMetadata() {
1211 return false;
1212 }
1213}
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,...
if(!defined( 'MW_NO_SESSION') &&! $wgCommandLineMode) $wgLang
Definition Setup.php:927
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:67
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