<commit_before><?php
/**
 * Represents an image on the filesystem, also provides image manipulation functionality
 * 
 * @copyright  Copyright (c) 2007-2010 <NAME>, others
 * @author     <NAME> [wb] <<EMAIL>>
 * @license    http://flourishlib.com/license
 * 
 * @package    Flourish
 * @link       http://flourishlib.com/fImage
 * 
 * @version    1.0.0b22
 * @changes    1.0.0b22  Fixed ::isImageCompatible() to handle certain JPGs created with Photoshop [wb, 2010-04-03]
 * @changes    1.0.0b21  Fixed ::resize() to allow dimensions to be numeric strings instead of just integers [wb, 2010-04-09]
 * @changes    1.0.0b20  Added ::append() [wb, 2010-03-15]
 * @changes    1.0.0b19  Updated for the new fFile API [wb-imarc, 2010-03-05]
 * @changes    1.0.0b18  Fixed a bug in ::saveChanges() that would incorrectly cause new filenames to be created, added the $overwrite parameter to ::saveChanges(), added the $allow_upsizing parameter to ::resize() [wb, 2010-03-03]
 * @changes    1.0.0b17  Fixed a couple of bug with using ImageMagick on Windows and BSD machines [wb, 2010-03-02]
 * @changes    1.0.0b16  Fixed some bugs with GD not properly handling transparent backgrounds and desaturation of .gif files [wb, 2009-10-27]
 * @changes    1.0.0b15  Added ::getDimensions() [wb, 2009-08-07]
 * @changes    1.0.0b14  Performance updates for checking image type and compatiblity [wb, 2009-07-31]
 * @changes    1.0.0b13  Updated class to work even if the file extension is wrong or not present, ::saveChanges() detects files that aren't writable [wb, 2009-07-29]
 * @changes    1.0.0b12  Fixed a bug where calling ::saveChanges() after unserializing would throw an exception related to the image processor [wb, 2009-05-27]
 * @changes    1.0.0b11  Added a ::crop() method [wb, 2009-05-27]
 * @changes    1.0.0b10  Fixed a bug with GD not saving changes to files ending in .jpeg [wb, 2009-03-18]
 * @changes    1.0.0b9   Changed ::processWithGD() to explicitly free the image resource [wb, 2009-03-18]
 * @changes    1.0.0b8   Updated for new fCore API [wb, 2009-02-16]
 * @changes    1.0.0b7   Changed @ error suppression operator to `error_reporting()` calls [wb, 2009-01-26]
 * @changes    1.0.0b6   Fixed ::cropToRatio() and ::resize() to always return the object even if nothing is to be done [wb, 2009-01-05]
 * @changes    1.0.0b5   Added check to see if exec() is disabled, which causes ImageMagick to not work [wb, 2009-01-03]
 * @changes    1.0.0b4   Fixed ::saveChanges() to not delete the image if no changes have been made [wb, 2008-12-18]
 * @changes    1.0.0b3   Fixed a bug with $jpeg_quality in ::saveChanges() from 1.0.0b2 [wb, 2008-12-16]
 * @changes    1.0.0b2   Changed some int casts to round() to fix ::resize() dimension issues [wb, 2008-12-11]
 * @changes    1.0.0b    The initial implementation [wb, 2007-12-19]
 */
class fImage extends fFile
{
	// The following constants allow for nice looking callbacks to static methods
	const create                  = 'fImage::create';
	const getCompatibleMimetypes  = 'fImage::getCompatibleMimetypes';
	const isImageCompatible       = 'fImage::isImageCompatible';
	const reset                   = 'fImage::reset';
	const setImageMagickDirectory = 'fImage::setImageMagickDirectory';
	const setImageMagickTempDir   = 'fImage::setImageMagickTempDir';
	
	
	/**
	 * If we are using the ImageMagick processor, this stores the path to the binaries
	 * 
	 * @var string
	 */
	static private $imagemagick_dir = NULL;
	
	/**
	 * A custom tmp path to use for ImageMagick
	 * 
	 * @var string
	 */
	static private $imagemagick_temp_dir = NULL;
	
	/**
	 * The processor to use for the image manipulation
	 * 
	 * @var string
	 */
	static private $processor = NULL;
	
	
	/**
	 * Checks to make sure we can get to and execute the ImageMagick convert binary
	 * 
	 * @param  string $path  The path to ImageMagick on the filesystem
	 * @return void
	 */
	static private function checkImageMagickBinary($path)
	{
		// Make sure we can execute the convert binary
		if (self::isSafeModeExecDirRestricted($path)) {
			throw new fEnvironmentException(
				'Safe mode is turned on and the ImageMagick convert binary is not in the directory defined by the safe_mode_exec_dir ini setting or safe_mode_exec_dir is not set - safe_mode_exec_dir is currently %s.',
				ini_get('safe_mode_exec_dir')
			);
		}
		
		if (self::isOpenBaseDirRestricted($path)) {
			exec($path . 'convert -version', $executable);
		} else {
			$executable = is_executable($path . (fCore::checkOS('windows') ? 'convert.exe' : 'convert'));
		}
		
		if (!$executable) {
			throw new fEnvironmentException(
				'The ImageMagick convert binary located in the directory %s does not exist or is not executable',
				$path
			);
		}
	}
	
	
	/**
	 * Creates an image on the filesystem and returns an object representing it
	 * 
	 * This operation will be reverted by a filesystem transaction being rolled
	 * back.
	 * 
	 * @throws fValidationException  When no image was specified or when the image already exists
	 * 
	 * @param  string $file_path  The path to the new image
	 * @param  string $contents   The contents to write to the image
	 * @return fImage
	 */
	static public function create($file_path, $contents)
	{
		if (empty($file_path)) {
			throw new fValidationException('No filename was specified');
		}
		
		if (file_exists($file_path)) {
			throw new fValidationException(
				'The image specified, %s, already exists',
				$file_path
			);
		}
		
		$directory = fFilesystem::getPathInfo($file_path, 'dirname');
		if (!is_writable($directory)) {
			throw new fEnvironmentException(
				'The file path specified, %s, is inside of a directory that is not writable',
				$file_path
			);
		}
		
		file_put_contents($file_path, $contents);
		
		$image = new fImage($file_path);
		
		fFilesystem::recordCreate($image);
		
		return $image;
	}
	
	
	/**
	 * Determines what processor to use for image manipulation
	 * 
	 * @return void
	 */
	static private function determineProcessor()
	{
		// Determine what processor to use
		if (self::$processor === NULL) {
			
			// Look for imagemagick first since it can handle more than GD
			try {
				
				// If exec is disabled we can't use imagemagick
				if (in_array('exec', explode(',', ini_get('disable_functions')))) {
					throw new Exception();	
				}
				
				if (fCore::checkOS('windows')) {
					
						$win_search = 'dir /B "C:\Program Files\ImageMagick*" 2> NUL';
						exec($win_search, $win_output);
						$win_output = trim(join("\n", $win_output));
						 
						if (!$win_output || stripos($win_output, 'File not found') !== FALSE) {
							throw new Exception();
						}
						 
						$path = 'C:\\Program Files\\' . $win_output . '\\';
						
				} elseif (fCore::checkOS('linux', 'bsd', 'solaris', 'osx')) {
					
					$found = FALSE;
					
					if (fCore::checkOS('solaris')) {
						$locations = array(
							'/opt/local/bin/',
							'/opt/bin/',<commit_msg>Completed ticket #471 - fixed fImage::determineProcessor() to find exec() when spaces are included in the disabled_functions ini setting<commit_after><?php
/**
 * Represents an image on the filesystem, also provides image manipulation functionality
 * 
 * @copyright  Copyright (c) 2007-2010 <NAME>, others
 * @author     <NAME> [wb] <<EMAIL>>
 * @license    http://flourishlib.com/license
 * 
 * @package    Flourish
 * @link       http://flourishlib.com/fImage
 * 
 * @version    1.0.0b23
 * @changes    1.0.0b23  Fixed the class to detect when exec() is disabled and the function has a space before or after in the list [wb, 2010-07-21]
 * @changes    1.0.0b22  Fixed ::isImageCompatible() to handle certain JPGs created with Photoshop [wb, 2010-04-03]
 * @changes    1.0.0b21  Fixed ::resize() to allow dimensions to be numeric strings instead of just integers [wb, 2010-04-09]
 * @changes    1.0.0b20  Added ::append() [wb, 2010-03-15]
 * @changes    1.0.0b19  Updated for the new fFile API [wb-imarc, 2010-03-05]
 * @changes    1.0.0b18  Fixed a bug in ::saveChanges() that would incorrectly cause new filenames to be created, added the $overwrite parameter to ::saveChanges(), added the $allow_upsizing parameter to ::resize() [wb, 2010-03-03]
 * @changes    1.0.0b17  Fixed a couple of bug with using ImageMagick on Windows and BSD machines [wb, 2010-03-02]
 * @changes    1.0.0b16  Fixed some bugs with GD not properly handling transparent backgrounds and desaturation of .gif files [wb, 2009-10-27]
 * @changes    1.0.0b15  Added ::getDimensions() [wb, 2009-08-07]
 * @changes    1.0.0b14  Performance updates for checking image type and compatiblity [wb, 2009-07-31]
 * @changes    1.0.0b13  Updated class to work even if the file extension is wrong or not present, ::saveChanges() detects files that aren't writable [wb, 2009-07-29]
 * @changes    1.0.0b12  Fixed a bug where calling ::saveChanges() after unserializing would throw an exception related to the image processor [wb, 2009-05-27]
 * @changes    1.0.0b11  Added a ::crop() method [wb, 2009-05-27]
 * @changes    1.0.0b10  Fixed a bug with GD not saving changes to files ending in .jpeg [wb, 2009-03-18]
 * @changes    1.0.0b9   Changed ::processWithGD() to explicitly free the image resource [wb, 2009-03-18]
 * @changes    1.0.0b8   Updated for new fCore API [wb, 2009-02-16]
 * @changes    1.0.0b7   Changed @ error suppression operator to `error_reporting()` calls [wb, 2009-01-26]
 * @changes    1.0.0b6   Fixed ::cropToRatio() and ::resize() to always return the object even if nothing is to be done [wb, 2009-01-05]
 * @changes    1.0.0b5   Added check to see if exec() is disabled, which causes ImageMagick to not work [wb, 2009-01-03]
 * @changes    1.0.0b4   Fixed ::saveChanges() to not delete the image if no changes have been made [wb, 2008-12-18]
 * @changes    1.0.0b3   Fixed a bug with $jpeg_quality in ::saveChanges() from 1.0.0b2 [wb, 2008-12-16]
 * @changes    1.0.0b2   Changed some int casts to round() to fix ::resize() dimension issues [wb, 2008-12-11]
 * @changes    1.0.0b    The initial implementation [wb, 2007-12-19]
 */
class fImage extends fFile
{
	// The following constants allow for nice looking callbacks to static methods
	const create                  = 'fImage::create';
	const getCompatibleMimetypes  = 'fImage::getCompatibleMimetypes';
	const isImageCompatible       = 'fImage::isImageCompatible';
	const reset                   = 'fImage::reset';
	const setImageMagickDirectory = 'fImage::setImageMagickDirectory';
	const setImageMagickTempDir   = 'fImage::setImageMagickTempDir';
	
	
	/**
	 * If we are using the ImageMagick processor, this stores the path to the binaries
	 * 
	 * @var string
	 */
	static private $imagemagick_dir = NULL;
	
	/**
	 * A custom tmp path to use for ImageMagick
	 * 
	 * @var string
	 */
	static private $imagemagick_temp_dir = NULL;
	
	/**
	 * The processor to use for the image manipulation
	 * 
	 * @var string
	 */
	static private $processor = NULL;
	
	
	/**
	 * Checks to make sure we can get to and execute the ImageMagick convert binary
	 * 
	 * @param  string $path  The path to ImageMagick on the filesystem
	 * @return void
	 */
	static private function checkImageMagickBinary($path)
	{
		// Make sure we can execute the convert binary
		if (self::isSafeModeExecDirRestricted($path)) {
			throw new fEnvironmentException(
				'Safe mode is turned on and the ImageMagick convert binary is not in the directory defined by the safe_mode_exec_dir ini setting or safe_mode_exec_dir is not set - safe_mode_exec_dir is currently %s.',
				ini_get('safe_mode_exec_dir')
			);
		}
		
		if (self::isOpenBaseDirRestricted($path)) {
			exec($path . 'convert -version', $executable);
		} else {
			$executable = is_executable($path . (fCore::checkOS('windows') ? 'convert.exe' : 'convert'));
		}
		
		if (!$executable) {
			throw new fEnvironmentException(
				'The ImageMagick convert binary located in the directory %s does not exist or is not executable',
				$path
			);
		}
	}
	
	
	/**
	 * Creates an image on the filesystem and returns an object representing it
	 * 
	 * This operation will be reverted by a filesystem transaction being rolled
	 * back.
	 * 
	 * @throws fValidationException  When no image was specified or when the image already exists
	 * 
	 * @param  string $file_path  The path to the new image
	 * @param  string $contents   The contents to write to the image
	 * @return fImage
	 */
	static public function create($file_path, $contents)
	{
		if (empty($file_path)) {
			throw new fValidationException('No filename was specified');
		}
		
		if (file_exists($file_path)) {
			throw new fValidationException(
				'The image specified, %s, already exists',
				$file_path
			);
		}
		
		$directory = fFilesystem::getPathInfo($file_path, 'dirname');
		if (!is_writable($directory)) {
			throw new fEnvironmentException(
				'The file path specified, %s, is inside of a directory that is not writable',
				$file_path
			);
		}
		
		file_put_contents($file_path, $contents);
		
		$image = new fImage($file_path);
		
		fFilesystem::recordCreate($image);
		
		return $image;
	}
	
	
	/**
	 * Determines what processor to use for image manipulation
	 * 
	 * @return void
	 */
	static private function determineProcessor()
	{
		// Determine what processor to use
		if (self::$processor === NULL) {
			
			// Look for imagemagick first since it can handle more than GD
			try {
				
				// If exec is disabled we can't use imagemagick
				if (in_array('exec', array_map('trim', explode(',', ini_get('disable_functions'))))) {
					throw new Exception();
				}
				
				if (fCore::checkOS('windows')) {
					
						$win_search = 'dir /B "C:\Program Files\ImageMagick*" 2> NUL';
						exec($win_search, $win_output);
						$win_output = trim(join("\n", $win_output));
						 
						if (!$win_output || stripos($win_output, 'File not found') !== FALSE) {
							throw new Exception();
						}
						 
						$path = 'C:\\Program Files\\' . $win_output . '\\';
						
				} elseif (fCore::checkOS('linux', 'bsd', 'solaris', 'osx')) {
					
					$found = FALSE;
					
					if (fCore::checkOS('solaris')) {
						$locations = array(
							'/opt/local/bin/',
							'/opt/bin/',
{'fraction_non_alphanumeric': 0.11362032462949895, 'fraction_numerical': 0.04262526464361327, 'mean_word_length': 3.2225864123957093, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2835747', 'n_tokens_mistral': 5312, 'n_tokens_neox': 4338, 'n_words': 1531}