760 lines
22 KiB
PHP
760 lines
22 KiB
PHP
<?php
|
|
/**
|
|
* Image processing support for mla_viewer and thumbnail generation
|
|
*
|
|
* @package Media Library Assistant
|
|
* @since 2.13
|
|
*/
|
|
|
|
/**
|
|
* Class MLA (Media Library Assistant) Image Processor provides PDF thumbnails
|
|
* for the [mla_gallery] mla_viewer
|
|
* and Media/Assistant thumbnail generator.
|
|
*
|
|
* @package Media Library Assistant
|
|
* @since 2.10
|
|
*/
|
|
class MLAImageProcessor {
|
|
/**
|
|
* Log debug information if true
|
|
*
|
|
* @since 2.12
|
|
*
|
|
* @var boolean
|
|
*/
|
|
public static $mla_debug = false;
|
|
|
|
/**
|
|
* Generate a unique, writable file in the temporary directory
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @param string $extension File extension for the temporary file
|
|
*
|
|
* @return string Writable path and file name.
|
|
*/
|
|
private static function _get_temp_file( $extension = '.tmp' ) {
|
|
static $temp = NULL;
|
|
|
|
/*
|
|
* Find a temp directory
|
|
*/
|
|
if ( NULL == $temp ) {
|
|
if ( function_exists('sys_get_temp_dir') ) {
|
|
$temp = sys_get_temp_dir();
|
|
if ( @is_dir( $temp ) ) {
|
|
$temp = rtrim( $temp, '/\\' ) . '/';
|
|
}
|
|
} else {
|
|
$temp = ini_get('upload_tmp_dir');
|
|
if ( @is_dir( $temp ) ) {
|
|
$temp = rtrim( $temp, '/\\' ) . '/';
|
|
} else {
|
|
$temp = '/tmp/';
|
|
if ( false == @is_dir( $temp ) ) {
|
|
self::_mla_debug_add( 'MLAImageProcessor::_get_temp_file Temp directory failure' );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create a unique file
|
|
*/
|
|
$path = $temp . uniqid( mt_rand() ) . $extension;
|
|
$f = @fopen( $path, 'a' );
|
|
if ( $f === false ) {
|
|
self::_mla_debug_add( 'MLAImageProcessor::_get_temp_file Temp file failure' );
|
|
return false;
|
|
}
|
|
|
|
fclose( $f );
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* Imagick object for the image to be streamed
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @var Imagick
|
|
*/
|
|
protected static $image;
|
|
|
|
/**
|
|
* Direct Ghostscript file conversion
|
|
*
|
|
* @since 2.10
|
|
* @uses self::$image loads the converted file to this Imagick object
|
|
*
|
|
* @param string $file Input file, e.g., a PDF document
|
|
* @param string $frame Page/frame within the file, zero-based
|
|
* @param string $resolution Output file DPI. Default 72.
|
|
* @param string $output_type Output MIME type; 'image/jpeg' or 'image/png'.
|
|
* @param string $explicit_path Optional. Non-standard location to override default search, e.g., 'C:\Program Files (x86)\gs\gs9.15\bin\gswin32c.exe'
|
|
*
|
|
* @return boolean true if conversion succeeds else false
|
|
*/
|
|
private static function _ghostscript_convert( $file, $frame, $resolution, $output_type, $explicit_path = '' ) {
|
|
/*
|
|
* Look for exec() - from http://stackoverflow.com/a/12980534/866618
|
|
*/
|
|
$blacklist = preg_split( '/,\s*/', ini_get('disable_functions') . ',' . ini_get('suhosin.executor.func.blacklist') );
|
|
if ( in_array('exec', $blacklist) ) {
|
|
self::_mla_debug_add( 'MLAImageProcessor::_ghostscript_convert blacklist failure' );
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Look for the Ghostscript executable
|
|
*/
|
|
$ghostscript_path = NULL;
|
|
do {
|
|
|
|
if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3) ) ) {
|
|
if ( ! empty( $explicit_path ) ) {
|
|
$ghostscript_path = exec( 'dir /o:n/s/b "' . $explicit_path . '"' );
|
|
if ( ! empty( $ghostscript_path ) ) {
|
|
break;
|
|
} else {
|
|
$ghostscript_path = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( $ghostscript_path = getenv('GSC') ) {
|
|
break;
|
|
}
|
|
|
|
$ghostscript_path = exec('where gswin*c.exe');
|
|
if ( ! empty( $ghostscript_path ) ) {
|
|
break;
|
|
}
|
|
|
|
$ghostscript_path = exec('dir /o:n/s/b "C:\Program Files\gs\*gswin*c.exe"');
|
|
if ( ! empty( $ghostscript_path ) ) {
|
|
break;
|
|
}
|
|
|
|
$ghostscript_path = exec('dir /o:n/s/b "C:\Program Files (x86)\gs\*gswin32c.exe"');
|
|
if ( ! empty( $ghostscript_path ) ) {
|
|
break;
|
|
}
|
|
|
|
$ghostscript_path = NULL;
|
|
break;
|
|
} // Windows platform
|
|
|
|
if ( ! empty( $explicit_path ) ) {
|
|
exec( 'test -e ' . $explicit_path, $dummy, $ghostscript_path );
|
|
if ( $explicit_path !== $ghostscript_path ) {
|
|
$ghostscript_path = NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
$ghostscript_path = exec('which gs');
|
|
if ( ! empty( $ghostscript_path ) ) {
|
|
break;
|
|
}
|
|
|
|
$test_path = '/usr/bin/gs';
|
|
exec('test -e ' . $test_path, $dummy, $ghostscript_path);
|
|
|
|
if ( $test_path !== $ghostscript_path ) {
|
|
$ghostscript_path = NULL;
|
|
}
|
|
} while ( false );
|
|
self::_mla_debug_add( 'MLAImageProcessor::_ghostscript_convert ghostscript_path = ' . var_export( $ghostscript_path, true ) );
|
|
|
|
if ( isset( $ghostscript_path ) ) {
|
|
if ( 'image/jpeg' == $output_type ) {
|
|
$device = 'jpeg';
|
|
$extension = '.jpg';
|
|
} else {
|
|
$device = 'png16m';
|
|
$extension = '.png';
|
|
}
|
|
|
|
/*
|
|
* Generate a unique temporary file
|
|
*/
|
|
$output_file = self::_get_temp_file( $extension );
|
|
|
|
$cmd = escapeshellarg( $ghostscript_path ) . ' -sDEVICE=%1$s -r%2$dx%2$d -dFirstPage=%3$d -dLastPage=%3$d -dFitPage -o %4$s %5$s 2>&1';
|
|
$cmd = sprintf( $cmd, $device, $resolution, ( $frame + 1 ), escapeshellarg( $output_file ), escapeshellarg( $file ) );
|
|
exec( $cmd, $stdout, $return );
|
|
if ( 0 != $return ) {
|
|
self::_mla_debug_add( "ERROR: _ghostscript_convert exec returned '{$return}, cmd = " . var_export( $cmd, true ) );
|
|
self::_mla_debug_add( "ERROR: _ghostscript_convert exec returned '{$return}, details = " . var_export( $stdout, true ) );
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
self::$image->readImage( $output_file );
|
|
}
|
|
catch ( Exception $e ) {
|
|
self::_mla_debug_add( "ERROR: _ghostscript_convert readImage Exception = " . var_export( $e->getMessage(), true ) );
|
|
return false;
|
|
}
|
|
|
|
@unlink( $output_file );
|
|
return true;
|
|
} // found Ghostscript
|
|
|
|
self::_mla_debug_add( 'MLAImageProcessor::_ghostscript_convert Ghostscript detection failure' );
|
|
return false;
|
|
} // _ghostscript_convert
|
|
|
|
/**
|
|
* Prepare the image for output, scaling and flattening as required
|
|
*
|
|
* @since 2.10
|
|
* @uses self::$image updates the image in this Imagick object
|
|
*
|
|
* @param integer zero or new width
|
|
* @param integer zero or new height
|
|
* @param boolean proportional fit (true) or exact fit (false)
|
|
* @param string output MIME type
|
|
* @param integer compression quality; 1 - 100
|
|
*
|
|
* @return void
|
|
*/
|
|
private static function _prepare_image( $width, $height, $best_fit, $type, $quality ) {
|
|
//error_log( __LINE__ . " MLAImageProcessor::_prepare_image( {$width}, {$height}, {$best_fit}, {$type}, {$quality} )", 0 );
|
|
if ( 'WordPress' == $type ) {
|
|
$default_width = 0;
|
|
$type = 'image/jpeg';
|
|
} else {
|
|
$default_width = 150;
|
|
}
|
|
|
|
if ( is_callable( array( self::$image, 'scaleImage' ) ) ) {
|
|
if ( 0 < $width && 0 < $height ) {
|
|
// Both are set; use them as-is
|
|
self::$image->scaleImage( $width, $height, $best_fit );
|
|
} elseif ( 0 < $width || 0 < $height ) {
|
|
// One is set; scale the other one proportionally if reducing
|
|
$image_size = self::$image->getImageGeometry();
|
|
|
|
if ( $width && isset( $image_size['width'] ) && $width < $image_size['width'] ) {
|
|
self::$image->scaleImage( $width, 0 );
|
|
} elseif ( $height && isset( $image_size['height'] ) && $height < $image_size['height'] ) {
|
|
self::$image->scaleImage( 0, $height );
|
|
}
|
|
} else {
|
|
// Neither is specified, apply defaults; ( 0, 0 ) is invalid.
|
|
if ( $default_width ) {
|
|
self::$image->scaleImage( $default_width, 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( 0 < $quality && 101 > $quality ) {
|
|
if ( 'image/jpeg' == $type ) {
|
|
self::$image->setImageCompressionQuality( $quality );
|
|
self::$image->setImageCompression( imagick::COMPRESSION_JPEG );
|
|
}
|
|
else {
|
|
self::$image->setImageCompressionQuality( $quality );
|
|
}
|
|
}
|
|
|
|
if ( 'image/jpeg' == $type ) {
|
|
if ( is_callable( array( self::$image, 'setImageBackgroundColor' ) ) ) {
|
|
self::$image->setImageBackgroundColor('white');
|
|
}
|
|
|
|
if ( is_callable( array( self::$image, 'mergeImageLayers' ) ) ) {
|
|
self::$image = self::$image->mergeImageLayers( imagick::LAYERMETHOD_FLATTEN );
|
|
} elseif ( is_callable( array( self::$image, 'flattenImages' ) ) ) {
|
|
self::$image = self::$image->flattenImages();
|
|
}
|
|
}
|
|
} // _prepare_image
|
|
|
|
/**
|
|
* Log debug information
|
|
*
|
|
* @since 2.12
|
|
*
|
|
* @param string $message Error message.
|
|
*/
|
|
private static function _mla_debug_add( $message ) {
|
|
if ( self::$mla_debug ) {
|
|
if ( class_exists( 'MLACore' ) ) {
|
|
MLACore::mla_debug_add( $message );
|
|
} else {
|
|
error_log( $message, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Abort the operation and exit
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @param string $message Error message.
|
|
* @param string $title Optional. Error title. Default empty.
|
|
* @param integer $response Optional. HTML response code. Default 500.
|
|
|
|
* @return void echos page content and calls exit();
|
|
*/
|
|
private static function _mla_die( $message, $title = '', $response = 500 ) {
|
|
self::_mla_debug_add( __LINE__ . " _mla_die( '{$message}', '{$title}', '{$response}' )" );
|
|
exit();
|
|
}
|
|
|
|
/**
|
|
* Log the message and return error message array
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @param string $message Error message.
|
|
* @param string $line Optional. Line number in the caller.
|
|
*
|
|
* @return array( 'error' => message )
|
|
*/
|
|
private static function _mla_error_return( $message, $line = '' ) {
|
|
self::_mla_debug_add( $line . " MLAImageProcessor::_mla_error_return '{$message}'" );
|
|
return array( 'error' => $message );
|
|
}
|
|
|
|
/**
|
|
* Process Imagick thumbnail conversion request, e.g., for a PDF thumbnail
|
|
*
|
|
* Replaces download_url() in the Codex "Function Reference/wp handle sideload" example.
|
|
*
|
|
* @since 2.13
|
|
*
|
|
* @param string $input_file Path and name of the source file relative to upload directory
|
|
* @param array $args Generation parameters
|
|
*
|
|
* @return array file attributes ( 'file', 'url', 'type' ) on success, ( 'error' ) on failure
|
|
*/
|
|
public static function mla_handle_thumbnail_sideload( $input_file, $args ) {
|
|
MLACore::mla_debug_add( __LINE__ . " MLAImageProcessor::mla_handle_thumbnail_sideload( {$input_file} ) args = " . var_export( $args, true ), MLACore::MLA_DEBUG_CATEGORY_THUMBNAIL );
|
|
|
|
if ( ! class_exists( 'Imagick' ) ) {
|
|
return self::_mla_error_return( 'Imagick not installed', __LINE__ );
|
|
}
|
|
|
|
if( ini_get( 'zlib.output_compression' ) ) {
|
|
ini_set( 'zlib.output_compression', 'Off' );
|
|
}
|
|
|
|
if ( ! is_file( $input_file ) ) {
|
|
return self::_mla_error_return( 'File not found: ' . $input_file, __LINE__ );
|
|
}
|
|
|
|
// Process generation parameters and supply defaults
|
|
$width = isset( $args['width'] ) ? abs( intval( $args['width'] ) ) : 0;
|
|
$height = isset( $args['height'] ) ? abs( intval( $args['height'] ) ) : 0;
|
|
$type = isset( $args['type'] ) ? $args['type'] : 'image/jpeg';
|
|
$quality = isset( $args['quality'] ) ? abs( intval( $args['quality'] ) ) : 0;
|
|
$frame = isset( $args['frame'] ) ? abs( intval( $args['frame'] ) ) : 0;
|
|
$resolution = isset( $args['resolution'] ) ? abs( intval( $args['resolution'] ) ) : 72;
|
|
$best_fit = isset( $args['best_fit'] ) ? (boolean) $args['best_fit'] : false;
|
|
$ghostscript_path = isset( $args['ghostscript_path'] ) ? $args['ghostscript_path'] : '';
|
|
|
|
// Retain WordPress type for _prepare_image and adjust defaults
|
|
if ( 'WordPress' === $type ) {
|
|
$mime_type = 'image/jpeg';
|
|
$resolution = isset( $args['resolution'] ) ? abs( intval( $args['resolution'] ) ) : 128;
|
|
} else {
|
|
$mime_type = $type;
|
|
}
|
|
|
|
// Convert the file to an image format and load it
|
|
try {
|
|
$try_step = __LINE__ . ' new Imagick()';
|
|
self::$image = new Imagick();
|
|
|
|
/*
|
|
* this must be called before reading the image, otherwise has no effect -
|
|
* "-density {$x_resolution}x{$y_resolution}"
|
|
* this is important to give good quality output, otherwise text might be unclear
|
|
* default resolution is 72,72 or 128,128 for WordPress thumbnails
|
|
*/
|
|
$try_step = __LINE__ . ' setResolution';
|
|
self::$image->setResolution( $resolution, $resolution );
|
|
|
|
$try_step = __LINE__ . ' _ghostscript_convert';
|
|
$result = self::_ghostscript_convert( $input_file, $frame, $resolution, $mime_type, $ghostscript_path );
|
|
|
|
if ( false === $result ) {
|
|
try {
|
|
$try_step = __LINE__ . " readImage [{$frame}]";
|
|
self::$image->readImage( $input_file . '[' . $frame . ']' );
|
|
}
|
|
catch ( Exception $e ) {
|
|
$try_step = __LINE__ . ' readImage [0]';
|
|
self::$image->readImage( $input_file . '[0]' );
|
|
}
|
|
|
|
if ( 'image/jpeg' == $mime_type ) {
|
|
$extension = 'JPG';
|
|
} else {
|
|
$extension = 'PNG';
|
|
}
|
|
|
|
$try_step = __LINE__ . " setImageFormat( {$extension} )";
|
|
self::$image->setImageFormat( $extension );
|
|
}
|
|
|
|
if ( ! self::$image->valid() ) {
|
|
self::_mla_die( 'File not loaded', __LINE__, 404 );
|
|
}
|
|
} catch ( Throwable $e ) { // PHP 7
|
|
return self::_mla_error_return( 'Image load Throwable: ' . $e->getMessage() . ' from step ' . $try_step, __LINE__ );
|
|
} catch ( Exception $e ) { // PHP 5
|
|
return self::_mla_error_return( 'Image load Exception: ' . $e->getMessage() . ' from step ' . $try_step, __LINE__ );
|
|
}
|
|
|
|
/*
|
|
* Prepare the output image; resize and flatten, if necessary.
|
|
* $type retains "WordPress" selection
|
|
*/
|
|
try {
|
|
self::_prepare_image( $width, $height, $best_fit, $type, $quality );
|
|
} catch ( Throwable $e ) { // PHP 7
|
|
return self::_mla_error_return( '_prepare_image Throwable: ' . $e->getMessage(), __LINE__ );
|
|
} catch ( Exception $e ) { // PHP 5
|
|
return self::_mla_error_return( '_prepare_image Exception: ' . $e->getMessage(), __LINE__ );
|
|
}
|
|
|
|
// Write the image to an appropriately-named file
|
|
try {
|
|
$output_file = wp_tempnam( $input_file );
|
|
self::$image->writeImage( $output_file );
|
|
$dimensions = self::$image->getImageGeometry();
|
|
} catch ( Throwable $e ) { // PHP 7
|
|
return self::_mla_error_return( 'Image write Throwable: ' . $e->getMessage(), __LINE__ );
|
|
} catch ( Exception $e ) { // PHP 5
|
|
@unlink( $output_file );
|
|
return self::_mla_error_return( 'Image write Exception: ' . $e->getMessage(), __LINE__ );
|
|
}
|
|
|
|
// array based on $_FILE as seen in PHP file uploads
|
|
$results = array(
|
|
'name' => basename( $input_file ),
|
|
'type' => $mime_type,
|
|
'tmp_name' => $output_file,
|
|
'error' => 0,
|
|
'size' => filesize( $output_file ),
|
|
'width' => $dimensions['width'],
|
|
'height' => $dimensions['height'],
|
|
);
|
|
|
|
MLACore::mla_debug_add( __LINE__ . " MLAImageProcessor::mla_handle_thumbnail_sideload( {$input_file} ) results = " . var_export( $results, true ), MLACore::MLA_DEBUG_CATEGORY_THUMBNAIL );
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Process Imagick image stream request, e.g., for a PDF thumbnail
|
|
*
|
|
* Requires mla_stream_file (relative to wp_upload_dir ) in $_REQUEST;
|
|
* optional $_REQUEST parameters are:
|
|
* mla_stream_width, mla_stream_height, mla_stream_frame, mla_stream_resolution,
|
|
* mla_stream_quality, mla_stream_type, mla_stream_fit, mla_ghostscript_path
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @return void echos image content and calls exit();
|
|
*/
|
|
public static function mla_process_stream_image() {
|
|
self::_mla_debug_add( 'MLAImageProcessor::mla_process_stream_image REQUEST = ' . var_export( $_REQUEST, true ) );
|
|
if ( ! class_exists( 'Imagick' ) ) {
|
|
self::_mla_die( 'Imagick not installed', __LINE__, 500 );
|
|
}
|
|
|
|
if( ini_get( 'zlib.output_compression' ) ) {
|
|
ini_set( 'zlib.output_compression', 'Off' );
|
|
}
|
|
|
|
$file = $_REQUEST['mla_stream_file'];
|
|
if ( ! is_file( $file ) ) {
|
|
self::_mla_die( 'File not found', __LINE__, 404 );
|
|
}
|
|
|
|
$use_mutex = isset( $_REQUEST['mla_single_thread'] );
|
|
$width = isset( $_REQUEST['mla_stream_width'] ) ? abs( intval( $_REQUEST['mla_stream_width'] ) ) : 0;
|
|
$height = isset( $_REQUEST['mla_stream_height'] ) ? abs( intval( $_REQUEST['mla_stream_height'] ) ) : 0;
|
|
$type = isset( $_REQUEST['mla_stream_type'] ) ? $_REQUEST['mla_stream_type'] : 'image/jpeg';
|
|
$quality = isset( $_REQUEST['mla_stream_quality'] ) ? abs( intval( $_REQUEST['mla_stream_quality'] ) ) : 0;
|
|
$frame = isset( $_REQUEST['mla_stream_frame'] ) ? abs( intval( $_REQUEST['mla_stream_frame'] ) ) : 0;
|
|
$resolution = isset( $_REQUEST['mla_stream_resolution'] ) ? abs( intval( $_REQUEST['mla_stream_resolution'] ) ) : 72;
|
|
/*
|
|
* If mla_ghostscript_path is present, a non-standard GS location can be found in a file written by
|
|
* the [mla_gallery] shortcode processor.
|
|
*/
|
|
$ghostscript_path = isset( $_REQUEST['mla_ghostscript_path'] ) ? $_REQUEST['mla_ghostscript_path'] : '';
|
|
if ( ! empty( $ghostscript_path ) ) {
|
|
$ghostscript_path = @file_get_contents( dirname( __FILE__ ) . '/' . 'mla-ghostscript-path.txt' );
|
|
}
|
|
|
|
if ( $use_mutex ) {
|
|
$temp_file = self::_get_temp_file();
|
|
@unlink( $temp_file );
|
|
$temp_file = pathinfo( $temp_file, PATHINFO_DIRNAME ) . '/mla-mutex.txt';
|
|
|
|
$mutex = new MLAMutex();
|
|
$mutex->init( 1, $temp_file );
|
|
$mutex->acquire();
|
|
self::_mla_debug_add( 'MLAImageProcessor::mla_process_stream_image begin file = ' . var_export( $file, true ) );
|
|
}
|
|
|
|
/*
|
|
* Convert the file to an image format and load it
|
|
*/
|
|
try {
|
|
self::$image = new Imagick();
|
|
|
|
/*
|
|
* this must be called before reading the image, otherwise has no effect -
|
|
* "-density {$x_resolution}x{$y_resolution}"
|
|
* this is important to give good quality output, otherwise text might be unclear
|
|
* default resolution is 72,72
|
|
*/
|
|
self::$image->setResolution( $resolution, $resolution );
|
|
|
|
//$result = false;
|
|
$result = self::_ghostscript_convert( $file, $frame, $resolution, $type, $ghostscript_path );
|
|
|
|
if ( false === $result ) {
|
|
try {
|
|
self::$image->readImage( $file . '[' . $frame . ']' );
|
|
}
|
|
catch ( Exception $e ) {
|
|
self::$image->readImage( $file . '[0]' );
|
|
}
|
|
|
|
if ( 'image/jpeg' == $type ) {
|
|
$extension = 'JPG';
|
|
} else {
|
|
$extension = 'PNG';
|
|
}
|
|
|
|
self::$image->setImageFormat( $extension );
|
|
}
|
|
|
|
if ( ! self::$image->valid() ) {
|
|
self::_mla_die( 'File not loaded', __LINE__, 404 );
|
|
}
|
|
}
|
|
catch ( Exception $e ) {
|
|
self::_mla_die( 'Image load exception: ' . $e->getMessage(), __LINE__, 404 );
|
|
}
|
|
|
|
/*
|
|
* Prepare the output image; resize and flatten, if necessary
|
|
*/
|
|
try {
|
|
if ( isset( $_REQUEST['mla_stream_fit'] ) ) {
|
|
$best_fit = ( '1' == $_REQUEST['mla_stream_fit'] );
|
|
} else {
|
|
$best_fit = false;
|
|
}
|
|
|
|
self::_prepare_image( $width, $height, $best_fit, $type, $quality );
|
|
}
|
|
catch ( Exception $e ) {
|
|
self::_mla_die( '_prepare_image exception: ' . $e->getMessage(), __LINE__, 500 );
|
|
}
|
|
|
|
/*
|
|
* Stream the image back to the requestor
|
|
*/
|
|
try {
|
|
header( "Content-Type: $type" );
|
|
echo self::$image->getImageBlob();
|
|
}
|
|
catch ( Exception $e ) {
|
|
self::_mla_die( 'Image stream exception: ' . $e->getMessage(), __LINE__, 500 );
|
|
}
|
|
|
|
if ( $use_mutex ) {
|
|
$mutex->release();
|
|
}
|
|
|
|
exit();
|
|
} // mla_process_stream_image
|
|
} // Class MLAImageProcessor
|
|
|
|
/**
|
|
* Class MLA (Media Library Assistant) Mutex provides a simple "mutual exclusion" semaphore
|
|
* for the [mla_gallery] mla_viewer=single option
|
|
*
|
|
* Adapted from the example by mr.smaon@gmail.com in the PHP Manual "Semaphore Functions" page.
|
|
*
|
|
* @package Media Library Assistant
|
|
* @since 2.10
|
|
*/
|
|
class MLAMutex {
|
|
/**
|
|
* Semaphore identifier returned by sem_get()
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @var resource
|
|
*/
|
|
private $sem_id;
|
|
|
|
/**
|
|
* True if the semaphore has been acquired
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @var boolean
|
|
*/
|
|
private $is_acquired = false;
|
|
|
|
/**
|
|
* True if using a file lock instead of a semaphore
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @var boolean
|
|
*/
|
|
private $use_file_lock = false;
|
|
|
|
/**
|
|
* Name of the (locked) file used as a semaphore
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @var string
|
|
*/
|
|
private $filename = '';
|
|
|
|
/**
|
|
* File system pointer resource of the (locked) file used as a semaphore
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @var resource
|
|
*/
|
|
private $filepointer;
|
|
|
|
/**
|
|
* Initializes the choice of semaphore Vs file lock
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @param boolean $use_lock True to force use of file locking
|
|
*
|
|
* @return void
|
|
*/
|
|
function __construct( $use_lock = false ) {
|
|
/*
|
|
* If sem_ functions are not available require file locking
|
|
*/
|
|
if ( ! is_callable( 'sem_get' ) ) {
|
|
$use_lock = true;
|
|
}
|
|
|
|
if ( $use_lock || 'WIN' == substr( PHP_OS, 0, 3 ) ) {
|
|
$this->use_file_lock = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates the semaphore or sets the (lock) file name
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @param integer $id Key to identify the semaphore
|
|
* @param string $filename Absolute path and name of the file for locking
|
|
*
|
|
* @return boolean True if the initialization succeeded
|
|
*/
|
|
public function init( $id, $filename = '' ) {
|
|
|
|
if( $this->use_file_lock ) {
|
|
if( empty( $filename ) ) {
|
|
return false;
|
|
} else {
|
|
$this->filename = $filename;
|
|
}
|
|
} else {
|
|
if( ! ( $this->sem_id = sem_get( $id, 1) ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Acquires the semaphore or opens and locks the file
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @return boolean True if the acquisition succeeded
|
|
*/
|
|
public function acquire() {
|
|
if( $this->use_file_lock ) {
|
|
if ( empty( $this->filename ) ) {
|
|
return true;
|
|
}
|
|
|
|
if( false == ( $this->filepointer = @fopen( $this->filename, "w+" ) ) ) {
|
|
return false;
|
|
}
|
|
|
|
if( false == flock( $this->filepointer, LOCK_EX ) ) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if ( ! sem_acquire( $this->sem_id ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
$this->is_acquired = true;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Releases the semaphore or unlocks and closes (but does not unlink) the file
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @return boolean True if the release succeeded
|
|
*/
|
|
public function release() {
|
|
if( ! $this->is_acquired ) {
|
|
return true;
|
|
}
|
|
|
|
if( $this->use_file_lock ) {
|
|
if( false == flock( $this->filepointer, LOCK_UN ) ) {
|
|
return false;
|
|
}
|
|
|
|
fclose( $this->filepointer );
|
|
} else {
|
|
if ( ! sem_release( $this->sem_id ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
$this->is_acquired = false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns the semaphore identifier, if it exists, else NULL
|
|
*
|
|
* @since 2.10
|
|
*
|
|
* @return resource Semaphore identifier or NULL
|
|
*/
|
|
public function getId() {
|
|
return $this->sem_id;
|
|
}
|
|
} // MLAMutex
|
|
?>
|