blob: afc37138f74e95e7de8b9dc2f15e0f6d6fb1aa5d [file] [log] [blame]
<?php
/**
* This file contains the ezcImageMethodcallHandler interface.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package ImageConversion
* @version //autogentag//
* @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
* @filesource
* @access private
*/
/**
* Special image handler which handles filters using method calls and keeps
* track of resources.
* This is a special abstract class which are extended by the ImageMagick and
* GD handlers and contains code which is common to both of them.
* It performs filter operations by keeping a filter object available and
* accesses the filter methods directly, this means the sub-classes only have
* to create the correct filter object.
*
* Implements all abstract methods of ezcImageHandler except load(), save()
* and close(). Instead it provides the {@link loadCommon()},
* {@link saveCommon()} and {@link closeCommon()} methods to simplify the code
* for sub-classes.
*
* @see ezcImageConverter
* @see ezcImageGdHandler
* @see ezcImageImagemagickHandler
* @see ezcImageFilters
*
* @package ImageConversion
* @version //autogentag//
* @access private
*/
abstract class ezcImageMethodcallHandler extends ezcImageHandler
{
/**
* Array of MIME types usable for input
*
* @var array
*/
protected $inputTypes;
/**
* Array of MIME types usable for output
*
* @var array
*/
protected $outputTypes;
/**
* Array of filter names cached from getFilterNames().
*
* @var array
*/
protected $filterNameCache;
/**
* Image references created through load().
* Format:
* array(
* 'id' => array(
* 'file' => <file name>,
* 'mime' => <MIME type>,
* 'resource' => <image resource>,
* )
* )
*
* @var array
*/
private $references = array();
/**
* Currently active image reference.
* This is used to determine by the filter, which image should be
* processed.
*
* @var string
*/
private $activeReference;
/**
* Create a new image handler.
* Creates an image handler. This should never be done directly,
* but only through the manager for configuration reasons. One can
* get a direct reference through manager afterwards. When overwriting
* the constructor.
*
* The contents of the $settings parameter may change from handler to
* handler. For detailed information take a look at the specific handler
* classes.
*
* @param ezcImageHandlerSettings $settings Settings for the handler.
*/
public function __construct( ezcImageHandlerSettings $settings )
{
parent::__construct( $settings );
}
/**
* Destroyes the handler and closes all open references correctly.
*
* @return void
*/
public function __destruct()
{
foreach ( $this->references as $id => $data )
{
$this->close( $id );
}
}
/**
* Check wether a specific MIME type is allowed as input for this handler.
*
* @param string $mime MIME type to check if it's allowed.
* @return bool
*/
public function allowsInput( $mime )
{
return ( in_array( strtolower( $mime ), $this->inputTypes ) );
}
/**
* Checks wether a specific MIME type is allowed as output for this handler.
*
* @param string $mime MIME type to check if it's allowed.
* @return bool
*/
public function allowsOutput( $mime )
{
return ( in_array( strtolower( $mime ), $this->outputTypes ) );
}
/**
* Checks if a given filter is available in this handler.
*
* @param string $name Name of the filter to check for.
* @return bool
*
*/
public function hasFilter( $name )
{
return method_exists( $this, $name );
}
/**
* Returns a list of filters this handler provides.
* The list returned is in format:
*
* <code>
* array(
* 0 => <string filtername>,
* 1 => <string filtername>,
* ...
* )
* </code>
*
* @return array(string)
*/
public function getFilterNames()
{
if ( !isset( $this->filterNameCache ) || !is_array( $this->filterNameCache || sizeof( $this->filterNameCache ) === 0 ) )
{
$this->filterNameCache = array();
$excludeMethods = array(
'__construct',
'__destruct',
'__get',
'__set',
'__call',
'allowsInput',
'allowsOutput',
'hasFilter',
'getFilterNames',
'applyFilter',
'convert',
'load',
'save',
'close',
'defaultSettings',
);
$refClass = new ReflectionClass( get_class( $this ) );
foreach ( $refClass->getMethods() as $method )
{
if ( $method->isPublic() && !in_array( $method->getName(), $excludeMethods ) )
{
$this->filterNameCache[] = $method->getName();
}
}
}
return $this->filterNameCache;
}
/**
* Applies a filter to a given image.
*
* @internal This method is the main one, which will dispatch the
* filter action to the specific function of the backend.
*
* @see ezcImageMethodcallHandler::load()
* @see ezcImageMethodcallHandler::save()
*
* @param string $image Image reference to apply the filter on.
* @param ezcImageFilter $filter Contains which filter operation to apply.
* @return void
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcImageInvalidReferenceException
* No loaded file could be found or an error destroyed a loaded reference.
* @throws ezcImageFilterNotAvailableException
* If the desired filter does not exist.
* @throws ezcImageFiltersMissingFilterParameterException
* If a parameter for the filter is missing.
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed.
* @throws ezcBaseValueException
* If a parameter was not within the expected range.
*/
public function applyFilter( $image, ezcImageFilter $filter )
{
if ( !$this->hasFilter( $filter->name ) )
{
throw new ezcImageFilterNotAvailableException( $filter->name );
}
$reflectClass = new ReflectionClass( get_class( $this ) );
$reflectParameters = $reflectClass->getMethod( $filter->name )->getParameters();
$parameters = array();
foreach ( $reflectParameters as $id => $parameter )
{
$paramName = $parameter->getName();
if ( isset( $filter->options[$paramName] ) )
{
$parameters[] = $filter->options[$paramName];
}
else if ( $parameter->isOptional() === false )
{
throw new ezcImageMissingFilterParameterException( $filter->name, $paramName );
}
}
// Backup last active reference
$oldRef = $this->getActiveReference();
// Perform actual filtering on given image
$this->setActiveReference( $image );
call_user_func_array( array( $this, $filter->name ), $parameters );
// Restore last active reference
$this->setActiveReference( $oldRef );
}
/**
* Converts an image to another MIME type.
*
* Use {@link ezcImageMethodcallHandler::allowsOutput()} to determine,
* if the output MIME type is supported by this handler!
*
* @see ezcImageMethodcallHandler::load()
* @see ezcImageMethodcallHandler::save()
*
* @param string $image Image reference to convert.
* @param string $mime MIME type to convert to.
* @return void
*
* @throws ezcImageMimeTypeUnsupportedException
* If the given MIME type is not supported by the filter.
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
*/
public function convert( $image, $mime )
{
$oldMime = $this->getReferenceData( $image, 'mime' );
if ( !$this->allowsOutput( $mime ) )
{
throw new ezcImageMimeTypeUnsupportedException( $mime, 'output' );
}
$this->setReferenceData( $image, $mime, 'mime' );
}
/**
* Receive the resource of the active image reference.
* This method is utilized by the ezcImageFilters* class to receive the
* currently active resource for manipulations.
*
* @return resource The currently active resource.
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
*/
protected function getActiveResource()
{
$ref = $this->getActiveReference();
if ( ( $resource = $this->getReferenceData( $ref, 'resource' ) ) === false )
{
throw new ezcImageInvalidReferenceException( "No resource found for the active reference '{$ref}'." );
}
return $resource;
}
/**
* Replace the resource of an image reference with a new one.
* After filtering the current image resource might have to be replaced
* with a new version. This can be done using this method.
*
* @param resource(GD) $resource
* @return void
*/
protected function setActiveResource( $resource )
{
$this->setReferenceData(
$this->getActiveReference(),
$resource,
'resource'
);
}
/**
* Returns the currently active reference.
* Returns the reference which is currently marked as active. This happens
* either by loading a new file or by using the setActiveReference()
* method.
*
* @see ezcImageMethodcallHandler::setActiveReference()
* @see ezcImageMethodcallHandler::load()
* @see ezcImageMethodcallHandler::$references
*
* @throws ezcImageInvalidReferenceException
* No loaded file could be found or an error destroyed a loaded reference.
*
* @return string The active reference.
*/
protected function getActiveReference()
{
if ( !isset( $this->activeReference ) )
{
throw new ezcImageInvalidReferenceException( 'No reference is defined as active. Either no file is loeaded, yet or an internal error destroyed the reference.' );
}
return $this->activeReference;
}
/**
* Mark the submitted image reference as active.
* The image reference submitted is marked as active. All following
* filter operations are performed on this reference.
*
* @param string $image The image reference.
* @return void
*
* @throws ezcImageInvalidReferenceException
* If the given reference is invalid.
*/
protected function setActiveReference( $image )
{
if ( !isset( $this->references[$image] ) )
{
throw new ezcImageInvalidReferenceException( 'Could not mark invalid reference as active.' );
}
$this->activeReference = $image;
}
/**
* Returns data about a reference.
* This gives you access to the data stored about a loaded image. You can
* either retrieve a certain detail (defined in the references array), with
* specifying it through the second parameter (the method then simply
* returns that detail) or retrieve all available details with leaving that
* parameter out.
*
* By default the following details are available:
* <code>
* 'file' => The file name of the image loaded.
* 'mime' => The mime type of the image loaded.
* 'resource' => A resource referencing it.
* </code>
*
* Of what type the resource is, may differ from handler to handler (e.g. a
* GD resource for the GD handler or a file path for the ImageMagick handler).
* You can simply store your own details be setting them and retreive them
* through this method.
*
* @param string $reference Reference string assigned.
* @param mixed $detail To receive a single detail, set to detail name.
* @return array Array of details if you specify $detail, else depending on
* the detail. If detail is not available, returns false.
*
* @throws ezcImageInvalidReferenceException
* If the given reference is invalid.
*
* @see ezcImageMethodcallHandler::setReferenceData()
*/
protected function getReferenceData( $reference, $detail = null )
{
if ( !isset( $this->references[$reference] ) )
{
throw new ezcImageInvalidReferenceException( "Inavlid image reference given: '{$reference}'." );
}
if ( isset( $detail ) )
{
return isset( $this->references[$reference][$detail] ) ? $this->references[$reference][$detail] : false;
}
return $this->references[$reference];
}
/**
* Set data for an image reference.
* This method allows you to set all data that can be retrieved through
* ezcImageMethodcallHandler::getReferenceData(). You can either set a single detail
* by providing the optional $detail parameter or submit an array containing
* all details at once as the value to set all details.
*
* @param string $reference Reference string of the image data.
* @param mixed $value The value to set.
* @param string $detail The name of the detail to set.
* @return void
*
* @throws ezcImageInvalidReferenceException
* If the given reference is invalid.
* @throws ezcBaseValueException
* If the given detail is invalid.
*/
protected function setReferenceData( $reference, $value, $detail = null )
{
if ( !isset( $this->references[$reference] ) )
{
throw new ezcImage( "Invalid image reference given: '{$reference}'." );
}
if ( isset( $detail ) )
{
$this->references[$reference][$detail] = $value;
}
else
{
if ( !is_array( $value ) )
{
throw new ezcBaseValueException( 'value', $value, 'array' );
}
if ( !isset( $value['file'] ) )
{
throw new ezcBaseValueException( 'file', null, 'string' );
}
if ( !isset( $value['mime'] ) )
{
throw new ezcBaseValueException( 'mime', null, 'string' );
}
if ( !isset( $value['resource'] ) )
{
throw new ezcBaseValueException( 'resource', null, 'string' );
}
$this->references[$reference] = $value;
}
}
/**
* Create a reference entry for this file.
* Performs common operations on a specific file, like checking if the file
* exists, if it is loadable, if it's already loaded. Beside of that, it
* creates the reference internally, so you don't need to handle this
* stuff manually with the internal data structure of
* ezcImageMethodcallHandler::$references. It also cares for determining the MIME-
* type of the image and sets the newly created reference to be active.
*
* @param string $file The file to load.
* @param string $mime The MIME type of the file.
* @return string reference The reference string for this file.
*
* @throws ezcBaseFileNotFoundException
* If the desired file does not exist.
* @throws ezcBaseFilePermissionException
* If the desired file is not readable.
* @throws ezcBaseValueException
* If the given detail is invalid.
* @throws ezcImageMimeTypeUnsupportedException
* If the desired file has a not recognized type.
*/
protected function loadCommon( $file, $mime = null )
{
if ( !is_file( $file ) )
{
throw new ezcBaseFileNotFoundException( $file );
}
if ( !is_readable( $file ) )
{
throw new ezcBaseFilePermissionException( $file, ezcBaseFileException::READ );
}
$file = realpath( $file );
$ref = md5( $file );
if ( !isset( $mime ) )
{
$mime = '';
try
{
$analyzer = new ezcImageAnalyzer( $file );
$mime = $analyzer->mime;
}
catch ( ezcImageAnalyzerException $e )
{
throw new ezcImageMimeTypeUnsupportedException( 'unknown/unknown', 'input' );
}
}
$this->references[$ref] = array();
$this->setReferenceData(
$ref,
array(
'file' => $file,
'mime' => $mime,
'resource' => false,
)
);
$this->setActiveReference( $ref );
return $ref;
}
/**
* Performs common operations before saving a file.
* This method should/can be used while implementing the save method of an
* ezcImageMethodcallHandler. It performs several tasks, like setting the new file name,
* if it has been submitted, and the new MIME type. Beside that, it checks
* if one can write to the new file and if the handler is able to process
* the new MIME type.
*
* @param string $reference The image reference.
* @param string $newFile The new filename.
* @param string $mime The new MIME type.
* @return void
*
* @throws ezcBaseFilePermissionException
* If the desired file is not writeable.
* @throws ezcImageMimeTypeUnsupportedException
* If the desired MIME type is not recognized.
*/
protected function saveCommon( $reference, $newFile = null, $mime = null )
{
if ( isset( $newFile ) )
{
$this->setReferenceData( $reference, $newFile, 'file' );
}
$file = $this->getReferenceData( $reference, 'file' );
if ( file_exists( $file ) && !is_writeable( $file ) )
{
throw new ezcBaseFilePermissionException( $file, ezcBaseFileException::WRITE );
}
if ( isset( $mime ) )
{
$this->setReferenceData( $reference, $mime, 'mime' );
}
$mime = $this->getReferenceData( $reference, 'mime' );
if ( $this->allowsOutput( $mime ) === false )
{
throw new ezcImageMimeTypeUnsupportedException( $mime, 'output' );
}
}
/**
* Unsets the reference data for the given reference.
* This method _must_ be called in the implementation of the close() method
* in every ezcImageMethodcallHandler to finally remove the reference.
*
* @param string $reference The reference to free.
* @return void
*/
protected function closeCommon( $reference )
{
$data = $this->getReferenceData( $reference );
unset( $this->references[$reference] );
}
}
?>