X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=thumb.php;h=59bf8dcffdc8a7bac4d7b1f1feb4479b21cb3b8b;hb=3cca294d755b38ed3f7cc7264d59cf26ea0cd199;hp=bfaf9a02d087f268eab042695ddaf34290538237;hpb=f0d1e12ffa186654a2f262c2ac87944feec81eb5;p=lhc%2Fweb%2Fwiklou.git diff --git a/thumb.php b/thumb.php index bfaf9a02d0..59bf8dcffd 100644 --- a/thumb.php +++ b/thumb.php @@ -163,12 +163,6 @@ function wfStreamThumb( array $params ) { return; } - // Check if the file is hidden - if ( $img->isDeleted( File::DELETED_FILE ) ) { - wfThumbError( 404, "The source file '$fileName' does not exist." ); - return; - } - // Check permissions if there are read restrictions $varyHeader = array(); if ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) ) { @@ -181,6 +175,12 @@ function wfStreamThumb( array $params ) { $varyHeader[] = 'Cookie'; } + // Check if the file is hidden + if ( $img->isDeleted( File::DELETED_FILE ) ) { + wfThumbError( 404, "The source file '$fileName' does not exist." ); + return; + } + // Do rendering parameters extraction from thumbnail name. if ( isset( $params['thumbName'] ) ) { $params = wfExtractThumbParams( $img, $params ); @@ -324,31 +324,15 @@ function wfStreamThumb( array $params ) { } elseif ( $user->pingLimiter( 'renderfile' ) ) { wfThumbError( 500, wfMessage( 'actionthrottledtext' ) ); return; - } elseif ( wfThumbIsAttemptThrottled( $img, $thumbName, 4 ) ) { - wfThumbError( 500, wfMessage( 'thumbnail_image-failure-limit', 4 ) ); - return; } - // Thumbnail isn't already there, so create the new thumbnail... - $thumb = null; - try { - // Record failures on PHP fatals too - register_shutdown_function( function() use ( &$thumb, $img, $thumbName ) { - if ( $thumb === null ) { // transform() gave a fatal - wfThumbIncrAttemptFailures( $img, $thumbName ); - } - } ); - $thumb = $img->transform( $params, File::RENDER_NOW ); - } catch ( Exception $ex ) { - // Tried to select a page on a non-paged file? - $thumb = false; - } + // Actually generate a new thumbnail + list( $thumb, $errorMsg ) = wfGenerateThumbnail( $img, $params, $thumbName, $thumbPath ); // Check for thumbnail generation errors... - $errorMsg = false; $msg = wfMessage( 'thumbnail_error' ); if ( !$thumb ) { - $errorMsg = $msg->rawParams( 'File::transform() returned false' )->escaped(); + $errorMsg = $errorMsg ?: $msg->rawParams( 'File::transform() returned false' )->escaped(); } elseif ( $thumb->isError() ) { $errorMsg = $thumb->getHtmlMsg(); } elseif ( !$thumb->hasFile() ) { @@ -359,7 +343,6 @@ function wfStreamThumb( array $params ) { } if ( $errorMsg !== false ) { - wfThumbIncrAttemptFailures( $img, $thumbName ); wfThumbError( 500, $errorMsg ); } else { // Stream the file if there were no errors @@ -368,73 +351,138 @@ function wfStreamThumb( array $params ) { } /** - * Returns true if this thumbnail is one that MediaWiki generates - * links to on file description pages and possibly parser output. + * Actually try to generate a new thumbnail * - * $params is considered non-standard if they involve a non-standard - * width or any parameter aside from width and page number. The number - * of possible files with standard parameters is far less than that of all - * possible combinations; rate-limiting for them can thus be more generious. - * - * @param File $img + * @param File $file * @param array $params - * @return bool + * @param string $thumbName + * @param string $thumbPath + * @return array (MediaTransformOutput|bool, string|bool error message HTML) */ -function wfThumbIsStandard( File $img, array $params ) { - global $wgThumbLimits, $wgImageLimits; - // @TODO: use polymorphism with media handler here - if ( array_diff( array_keys( $params ), array( 'width', 'page' ) ) ) { - return false; // extra parameters present +function wfGenerateThumbnail( File $file, array $params, $thumbName, $thumbPath ) { + global $wgMemc, $wgAttemptFailureEpoch; + + $key = wfMemcKey( 'attempt-failures', $wgAttemptFailureEpoch, + $file->getRepo()->getName(), md5( $file->getName() ), md5( $thumbName ) ); + + // Check if this file keeps failing to render + if ( $wgMemc->get( $key ) >= 4 ) { + return array( false, wfMessage( 'thumbnail_image-failure-limit', 4 ) ); } - if ( isset( $params['width'] ) ) { - $widths = $wgThumbLimits; - foreach ( $wgImageLimits as $pair ) { - $widths[] = $pair[0]; + + $done = false; + // Record failures on PHP fatals in addition to caching exceptions + register_shutdown_function( function() use ( &$done, $key ) { + if ( !$done ) { // transform() gave a fatal + global $wgMemc; + // Randomize TTL to reduce stampedes + $wgMemc->incrWithInit( $key, 3600 + mt_rand( 0, 300 ) ); } - if ( !in_array( $params['width'], $widths ) ) { - return false; + } ); + + $thumb = false; + $errorHtml = false; + + // Thumbnail isn't already there, so create the new thumbnail... + try { + $work = new PoolCounterWorkViaCallback( 'FileRender', sha1( $file->getName() ), + array( + 'doWork' => function() use ( $file, $params ) { + return $file->transform( $params, File::RENDER_NOW ); + }, + 'getCachedWork' => function() use ( $file, $params, $thumbPath ) { + // If the worker that finished made this thumbnail then use it. + // Otherwise, it probably made a different thumbnail for this file. + return $file->getRepo()->fileExists( $thumbPath ) + ? $file->transform( $params, File::RENDER_NOW ) + : false; // retry once more in exclusive mode + }, + 'fallback' => function() { + return wfMessage( 'generic-pool-error' )->parse(); + }, + 'error' => function ( $status ) { + return $status->getHTML(); + } + ) + ); + $result = $work->execute(); + if ( $result instanceof MediaTransformOutput ) { + $thumb = $result; + } elseif ( is_string( $result ) ) { // error + $errorHtml = $result; } + } catch ( Exception $e ) { + // Tried to select a page on a non-paged file? } - return true; -} -/** - * @param File $img - * @param string $thumbName - * @param int $limit - * @return int|bool - */ -function wfThumbIsAttemptThrottled( File $img, $thumbName, $limit ) { - global $wgMemc; + $done = true; // no PHP fatal occured + + if ( !$thumb || $thumb->isError() ) { + // Randomize TTL to reduce stampedes + $wgMemc->incrWithInit( $key, 3600 + mt_rand( 0, 300 ) ); + } - return ( $wgMemc->get( wfThumbAttemptKey( $img, $thumbName ) ) >= $limit ); + return array( $thumb, $errorHtml ); } /** - * @param File $img - * @param string $thumbName + * Returns true if this thumbnail is one that MediaWiki generates + * links to on file description pages and possibly parser output. + * + * $params is considered non-standard if they involve a non-standard + * width or any non-default parameters aside from width and page number. + * The number of possible files with standard parameters is far less than + * that of all combinations; rate-limiting for them can thus be more generious. + * + * @param File $file + * @param array $params + * @return bool */ -function wfThumbIncrAttemptFailures( File $img, $thumbName ) { - global $wgMemc; +function wfThumbIsStandard( File $file, array $params ) { + global $wgThumbLimits, $wgImageLimits; - $key = wfThumbAttemptKey( $img, $thumbName ); - if ( !$wgMemc->incr( $key, 1 ) ) { - if ( !$wgMemc->add( $key, 1, 3600 ) ) { - $wgMemc->incr( $key, 1 ); + $handler = $file->getHandler(); + if ( !$handler || !isset( $params['width'] ) ) { + return false; + } + + $basicParams = array(); + if ( isset( $params['page'] ) ) { + $basicParams['page'] = $params['page']; + } + + // Check if the width matches one of $wgThumbLimits + if ( in_array( $params['width'], $wgThumbLimits ) ) { + $normalParams = $basicParams + array( 'width' => $params['width'] ); + // Append any default values to the map (e.g. "lossy", "lossless", ...) + $handler->normaliseParams( $file, $normalParams ); + } else { + // If not, then check if the width matchs one of $wgImageLimits + $match = false; + foreach ( $wgImageLimits as $pair ) { + $normalParams = $basicParams + array( 'width' => $pair[0], 'height' => $pair[1] ); + // Decide whether the thumbnail should be scaled on width or height. + // Also append any default values to the map (e.g. "lossy", "lossless", ...) + $handler->normaliseParams( $file, $normalParams ); + // Check if this standard thumbnail size maps to the given width + if ( $normalParams['width'] == $params['width'] ) { + $match = true; + break; + } + } + if ( !$match ) { + return false; // not standard for description pages } } -} -/** - * @param File $img - * @param string $thumbName - * @return string - */ -function wfThumbAttemptKey( File $img, $thumbName ) { - global $wgAttemptFailureEpoch; + // Check that the given values for non-page, non-width, params are just defaults + foreach ( $params as $key => $value ) { + if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) { + return false; + } + } - return wfMemcKey( 'attempt-failures', $wgAttemptFailureEpoch, - $img->getRepo()->getName(), md5( $img->getName() ), md5( $thumbName ) ); + return true; } /**