Revision: 31909
Initial Code
Initial URL
Initial Description
Initial Title
Initial Tags
Initial Language
at September 15, 2010 19:54 by dom111
Initial Code
<?php
/**
* Image
*
* Class for manipulating images as PHP objects
*
* @package default
* @author Dom Hastings
*/
class Image {
/**
* options
*
* @var array Contains the options for the functions
* @access private
*/
private $options = array(
// array Load options
'load' => array(
// integer Force the input type of image
'forceType' => false
),
// array Resizing specific options
'scale' => array(
// boolean Whether or not to force the resize (true) or preserve the ratio
'force' => false
),
// array Cutout specific options
'cutout' => array(
// mixed If null will default to taking the cutout from the absolute center of the image, otherwise uses the co-ordinates specified
'offsetX' => null,
'offsetY' => null,
// mixed If null defaults to the smallest possible size, otherwise resizes (forced) to the specified size
'sourceX' => null,
'sourceY' => null
),
// array Whitespace specific options
'whitespace' => array(
// string HTML hex code for the 'white' space
'color' => '#ffffff',
// integer Transparency value (see http://php.net/imagecolorallocatealpha)
'transparency' => 0,
// string Filename for applying as a background image
'image' => '',
// dimensions for scaling the image
'scaleX' => null,
'scaleY' => null,
// offsets for placing the image
'offsetX' => 0,
'offsetY' => 0
),
// array Watermarking options
'watermark' => array(
// mixed If null will default to taking the placing the watermark in the absolute center of the image, otherwise uses the co-ordinates specified
'offsetX' => null,
'offsetY' => null,
// boolean Repeats the image on the specified axis
'repeatX' => true,
'repeatY' => true
),
// array Text options
'text' => array(
// string The font file to use (TTF)
'font' => '',
// integer The font size in px (GD) or pt (GD2)
'size' => 10,
// integer The angle
'angle' => 0,
// string HTML colour code
'color' => '#000',
// integer Transparency value (see http://php.net/imagecolorallocatealpha)
'transparency' => 0
),
// array Line options
'line' => array(
// array The style of the line (see http://php.net/imagesetstyle)
'style' => array(),
// integer The line size in px
'size' => 1,
// string HTML colour code
'color' => '#000',
// integer Transparency value (see http://php.net/imagecolorallocatealpha)
'transparency' => 0
),
// array Line options
'box' => array(
// array The style of the line (see http://php.net/imagesetstyle)
'style' => array(),
// integer The line size in px
'size' => 1,
// string HTML colour code
'color' => '#000',
// integer Transparency value (see http://php.net/imagecolorallocatealpha)
'transparency' => 0,
// boolean If the box is filled or not
'filled' => true
),
// array Outputting options
'output' => array(
// integer Force the output type of image
'forceType' => false,
// array File options
'file' => array(
// boolean Whether to append the default extension
'extension' => false
),
// array JPEG options
'jpeg' => array(
// integer The quality parameter of imagejpeg() (http://php.net/imagejpeg)
'quality' => 85
),
// array PNG options
'png' => array(
// integer The quality parameter of imagepng() (http://php.net/imagepng)
'quality' => 1,
// integer The filters parameter...
'filters' => PNG_ALL_FILTERS
)
)
);
/**
* filename
*
* @var string The filename of the source image
* @access private
*/
private $filename = '';
/**
* source
*
* @var resource The GD image resource
* @access private
*/
private $source = null;
/**
* current
*
* @var resource The GD image resource
* @access private
*/
private $current = null;
/**
* info
*
* @var array The data from getimagesize() (http://php.net/function.getimagesize)
* @access private
*/
private $info = null;
/**
* __construct
*
* The constructor for the Image object
*
* @param string $f The filename of the source image
* @param array $o The options for the object
* @access public
* @author Dom Hastings
*/
public function __construct($f, $o = array()) {
if (file_exists($f)) {
$this->options = array_merge_recursive_distinct($this->options, is_array($o) ? $o : array());
// store the filename
$this->filename = $f;
// load the image
$this->load();
} else {
throw new Exception('Imgae::__construct: Unable to load image \''.$f.'\'.');
}
}
/**
* __get
*
* Magic method wrapper for specific properties
*
* @param string $p The property being retrieved
* @return mixed The return value of the function called
* @access public
* @author Dom Hastings
*/
public function __get($p) {
// only run this function if the image loaded successfully
if (!$this->source) {
throw new Exception('Image::__get: No image loaded.');
}
// switch the property
switch ($p) {
// return the image width
case 'x':
case 'width':
return $this->x();
break;
// return the image height
case 'y':
case 'height':
return $this->y();
break;
// return the image width
case 'currentX':
case 'currentWidth':
return $this->currentX();
break;
// return the image height
case 'currentY':
case 'currentHeight':
return $this->currentY();
break;
// return the image size ratio
case 'ratio':
return $this->x() / $this->y();
break;
// return the image size details
case 'size':
return array($this->x(), $this->y());
break;
// return the image information
case 'mimetype':
return $this->info[3];
break;
// return the image information
case 'extension':
return image_type_to_extension(
(!empty($this->options['forceWriteType']) ? $this->options['forceWriteType'] : $this->info[2])
);
break;
// return the image information
case 'imagetype':
return $this->info[2];
break;
// return the image information
case 'info':
return $this->info;
break;
// not caught
default:
throw new Exception('Image::__get: Undefined property');
break;
}
}
/**
* __set
*
* Magic method wrapper for setting values
*
* @param string $p The property being 'set'
* @param mixed $v The value to 'set' property to
* @return void
* @access public
* @author Dom Hastings
*/
public function __set($p, $v) {
switch ($p) {
case 'width':
case 'x':
$this->scale($v, 0);
break;
case 'height':
case 'y':
$this->scale(0, $v);
break;
case 'watermark':
$this->watermark($v);
break;
case 'type':
$this->options['output']['forceType'] = $v;
break;
default:
break;
}
}
/**
* load
*
* Loads the image and saves the details
*
* @return void
* @access private
* @author Dom Hastings
*/
private function load($options = array()) {
// merge in the options
$options = array_merge_recursive_distinct(
(is_array($this->options['load'])) ? $this->options['load'] : array(),
(is_array($options)) ? $options : array()
);
// get the image details stored
$this->info();
// if we're forcing a read type
if (!empty($options['forceType'])) {
// use it
$imageType = $options['forceType'];
} else {
// otherwise use the discovered type
$imageType = $this->info[2];
}
$this->source = $this->current = $this->loadFile($this->filename, $imageType);
// if the image loading failed
if (!$this->source) {
throw new Exception('Imgae::load: Unable to load image \''.$this->filename.'\'.');
}
}
/**
* loadFile
*
* Loads an image image from a file
*
* @param string f The filename
* @param string imageType The type of image
* @return resource The loaded image
* @access private
* @author Dom Hastings
*/
private function loadFile($f = null, $imageType = null) {
// switch the type and load using the correct function
switch ($imageType) {
case IMAGETYPE_GIF:
$resource = imagecreatefromgif($this->filename);
break;
case IMAGETYPE_JPEG:
case IMAGETYPE_JPEG2000:
case IMAGETYPE_JPC:
case IMAGETYPE_JP2:
case IMAGETYPE_JPX:
$resource = imagecreatefromjpeg($this->filename);
break;
case IMAGETYPE_PNG:
$resource = imagecreatefrompng($this->filename);
break;
case IMAGETYPE_BMP:
case IMAGETYPE_WBMP:
$resource = imagecreatefromwbmp($this->filename);
break;
case IMAGETYPE_XBM:
$resource = imagecreatefromxbm($this->filename);
break;
case IMAGETYPE_TIFF_II:
case IMAGETYPE_TIFF_MM:
case IMAGETYPE_IFF:
case IMAGETYPE_JB2:
case IMAGETYPE_SWF:
case IMAGETYPE_PSD:
case IMAGETYPE_SWC:
// case IMAGETYPE_ICO:
default:
$resource = null;
break;
}
return $resource;
}
/**
* output
*
* Output the image
*
* @param string $f (Optional) The filename to output to, if this is omitted the image is output to the browser
* @return void
* @access private
* @author Dom Hastings
*/
public function output($f = null, $options = array()) {
// merge in the options
$options = array_merge_recursive_distinct(
(is_array($this->options['output'])) ? $this->options['output'] : array(),
(is_array($options)) ? $options : array()
);
// if we're forcing an output type
if (!empty($options['forceType'])) {
$imageType = $options['forceType'];
} else {
$imageType = $this->info[2];
}
// use the correct output function
switch ($imageType) {
case IMAGETYPE_GIF:
header('Content-type: '.image_type_to_mime_type($imageType));
imagegif($this->current, $f);
break;
case IMAGETYPE_JPEG:
case IMAGETYPE_JPEG2000:
case IMAGETYPE_JPC:
case IMAGETYPE_JP2:
case IMAGETYPE_JPX:
header('Content-type: '.image_type_to_mime_type($imageType));
imagejpeg($this->current, $f, $options['jpeg']['quality']);
break;
case IMAGETYPE_PNG:
header('Content-type: '.image_type_to_mime_type($imageType));
imagepng($this->current, $f, $options['png']['quality'], $options['png']['filters']);
break;
case IMAGETYPE_BMP:
case IMAGETYPE_WBMP:
header('Content-type: '.image_type_to_mime_type($imageType));
imagewbmp($this->current, $f);
break;
case IMAGETYPE_XBM:
header('Content-type: '.image_type_to_mime_type($imageType));
imagexbm($this->current, $f);
break;
case IMAGETYPE_TIFF_II:
case IMAGETYPE_TIFF_MM:
case IMAGETYPE_IFF:
case IMAGETYPE_JB2:
case IMAGETYPE_SWF:
case IMAGETYPE_PSD:
case IMAGETYPE_SWC:
// case IMAGETYPE_ICO:
default:
break;
}
}
/**
* write
*
* Writes the output data to the specified filename
*
* @param string $f The filename
* @return string The filename written to
* @access public
* @author Dom Hastings
*/
public function write($f, $options = array()) {
// merge in the options
$options = array_merge_recursive_distinct(
(is_array($this->options['output']['file'])) ? $this->options['output']['file'] : array(),
(is_array($options)) ? $options : array()
);
if ($this->options['output']['forceType']) {
$imageType = $this->options['output']['forceType'];
} else {
$imageType = $this->info[2];
}
if ($options['extension'] || strpos($f, '.') === false) {
$f .= $this->extension;
}
$this->output($f);
return $f;
}
/**
* resource
*
* Returns the current image as a resource
*
* @return void
* @access public
* @author Dom Hastings
*/
public function resource() {
return $this->current;
}
/**
* info
*
* Gets information about the current image
*
* @return void
* @access private
* @author Dom Hastings
*/
private function info($f = null) {
// if the filename is empty
if (empty($f)) {
// stores the image information inside the object
$this->info = getimagesize($this->filename);
} else {
// it's not the main image so return it directly
return getimagesize($f);
}
}
/**
* x
*
* Returns the width of the image
*
* @param string $a Reads the image directly, otherwise uses the cached information form load
* @return integer The width of the image
* @access public
* @author Dom Hastings
*/
public function x($a = false) {
if ($a) {
return imagesx($this->source);
} else {
if (empty($this->info)) {
$this->info();
}
return $this->info[0];
}
}
/**
* currentX
*
* Returns the width of the thumb image
*
* @param string $a Reads the image directly, otherwise uses the cached information form load
* @return integer The width of the image
* @access public
* @author Dom Hastings
*/
public function currentX() {
if ($this->current) {
return imagesx($this->current);
}
}
/**
* y
*
* Returns the height of the image
*
* @param boolean $a Reads the image directly, otherwise uses the cached information form load
* @return integer The height of the image
* @access public
* @author Dom Hastings
*/
public function y($a = false) {
if ($a) {
return imagesy($this->source);
} else {
if (empty($this->info)) {
$this->info();
}
return $this->info[1];
}
}
/**
* currentY
*
* Returns the height of the current image
*
* @param boolean $a Reads the image directly, otherwise uses the cached information form load
* @return integer The height of the image
* @access public
* @author Dom Hastings
*/
public function currentY($a = false) {
if ($this->current) {
return imagesy($this->current);
}
}
/**
* scale
*
* Scales the current image to the dimensions specified, using the options specified
*
* @param integer $x The desired width
* @param integer $y The desired height
* @param array $options See main options block at top of file
* @return resource The new image
* @access public
* @author Dom Hastings
*/
public function scale($x, $y, $options = array()) {
// merge in the options
$options = array_merge_recursive_distinct(
(is_array($this->options['scale'])) ? $this->options['scale'] : array(),
(is_array($options)) ? $options : array()
);
// if we're not forcing the size
if (empty($options['force'])) {
// check we're not trying to enlarge the image
if ($x > $this->x) {
$x = $this->x;
}
if ($y > $this->y) {
$y = $this->y;
}
// if neither dimension is specified
if ($x == 0 && $y == 0) {
throw new Exception('Image::scale: At least one dimension must be spcified to scale an image.');
} elseif ($x > 0 && $y > 0) {
// maths!
$destX = $x;
$destY = intval($x / $this->ratio);
if ($destY > $y) {
$destX = intval($y * $this->ratio);
$destY = $y;
}
} elseif ($x == 0) {
$destX = intval($y * $this->ratio);
$destY = $y;
} elseif ($y == 0) {
$destX = $x;
$destY = intval($x / $this->ratio);
}
} else {
$destX = $x;
$destY = $y;
}
// create the destination
$dest = imagecreatetruecolor($destX, $destY);
// resample the image as specified
if (!imagecopyresampled($dest, $this->source, 0, 0, 0, 0, $destX, $destY, $this->x, $this->y)) {
throw new Exception('Image::scale: Error scaling image');
}
$this->current = $dest;
return $dest;
}
/**
* cutout
*
* Returns a selected portion of the image after optionally resizing it
*
* @param integer $x The desired width
* @param integer $y The desired height
* @param array $options
* @return resource The new image
* @access public
* @author Dom Hastings
*/
public function cutout($x, $y, $options = array()) {
// merge in the options
$options = array_merge_recursive_distinct(
(is_array($this->options['cutout'])) ? $this->options['cutout'] : array(),
(is_array($options)) ? $options : array()
);
// if the source image dimensions haven't been specified, work them out as best you can
if (empty($options['scaleX']) && empty($options['scaleY'])) {
// more maths!
if ($this->x >= $this->y) {
// landscape
$scaleX = intval($y * $this->ratio);
$scaleY = $y;
if ($scaleX < $x) {
$scaleX = $x;
$scaleY = intval($x / $this->ratio);
}
} else {
// portrait
$scaleX = $x;
$scaleY = intval($x / $this->ratio);
if ($scaleY < $y) {
$scaleX = intval($y * $this->ratio);
$scaleY = $y;
}
}
} else {
$scaleX = $options['scaleX'];
$scaleY = $options['scaleY'];
}
// scale the image
$source = $this->scale($scaleX, $scaleY, array('force' => true));
// if the offset hasn't been specified
if (!isset($options['offsetX']) || !isset($options['offsetY'])) {
// calculate the center
$offsetX = intval(($scaleX / 2) - ($x / 2));
$offsetY = intval(($scaleY / 2) - ($y / 2));
} else {
$offsetX = $options['offsetX'];
$offsetY = $options['offsetY'];
}
// create the destination
$dest = imagecreatetruecolor($x, $y);
// cut it out
if (!imagecopy($dest, $source, 0, 0, $offsetX, $offsetY, $scaleX, $scaleY)) {
throw new Exception('Image::scale: Error cutting out image');
}
$this->current = $dest;
return $dest;
}
/**
* whitespace
*
* Returns a scaled version of the image with any white space on the base filled with an image or a colour, depending on options specified
*
* @param string $x
* @param string $y
* @param string $options
* @return void
* @access public
* @author Dom Hastings
*/
public function whitespace($x, $y, $options = array()) {
// merge in the options
$options = array_merge_recursive_distinct(
(is_array($this->options['whitespace'])) ? $this->options['whitespace'] : array(),
(is_array($options)) ? $options : array()
);
// if we're using an image background
if (!empty($options['image'])) {
// load it
$orig = new Image($options['image']);
$orig->scale($x, $y, array('force' => true));
$dest = $orig->resource();
// else if it's just a colour
} elseif (!empty($options['color'])) {
// create the base image
$dest = imagecreatetruecolor($x, $y);
// extract the int values of the colour
list($r, $g, $b) = $this->hexToRGB($options['color']);
// allocate the colour
$color = imagecolorallocatealpha($dest, $r, $g, $b, $options['transparency']);
// fill it
imagefill($dest, 0, 0, $color);
// else, we aren't keeping any whitespace, so just scale it
} else {
return $this->scale($x, $y);
}
// if scaling options have been set
if (!empty($options['scaleX']) || !empty($options['scaleY'])) {
// use them
$scaleX = $options['scaleX'];
$scaleY = $options['scaleY'];
$options = array(
'force' => true
);
} else {
// otherwise assume the passed options
$scaleX = $x;
$scaleY = $y;
$options = array();
}
// scale the image
$source = $this->scale($scaleX, $scaleY, $options);
// extract the new height and width
$scaleX = $this->currentX;
$scaleY = $this->currentY;
// determine the offset
if (!isset($options['offsetX']) || !isset($options['offsetY'])) {
$offsetX = intval(($x / 2) - ($scaleX / 2));
$offsetY = intval(($y / 2) - ($scaleY / 2));
} else {
$offsetX = $options['offsetX'];
$offsetY = $options['offsetY'];
}
// overlay it
if (!imagecopy($dest, $source, $offsetX, $offsetY, 0, 0, $scaleX, $scaleY)) {
throw new Exception('Image::scale: Error whitespacing image');
}
$this->current = $dest;
return $dest;
}
/**
* watermark
*
* Watermarks the current image with the specified image
*
* @param string $i The image to use as a watermark
* @param array $options The options
* @return resource The watermarked image
* @access public
* @author Dom Hastings
*/
public function watermark($i, $options = array()) {
// merge in the options
$options = array_merge_recursive_distinct(
(is_array($this->options['watermark'])) ? $this->options['watermark'] : array(),
(is_array($options)) ? $options : array()
);
if (!file_exists($i)) {
throw new Exception('Image::watermark: Missing watermark image \''.$i.'\'.');
}
$dest = $this->current;
// load the watermark
$watermark = new Image($i);
// determine the offset
if (!isset($options['offsetX']) || !isset($options['offsetY'])) {
$offsetX = intval(($this->currentX / 2) - ($watermark->currentX / 2));
$offsetY = intval(($this->currentY / 2) - ($watermark->currentY / 2));
} else {
$offsetX = $options['offsetX'];
$offsetY = $options['offsetY'];
}
// overlay it
if (!empty($options['repeatX']) && !empty($options['repeatY'])) {
$offsetX = $offsetY = 0;
// rows
for ($i = $offsetY; $i < $this->currentY; $i += $watermark->y) {
// cols
for ($j = $offsetX; $j < $this->currentX; $j += $watermark->x) {
if (!imagecopy($dest, $watermark->resource(), $j, $i, 0, 0, $watermark->x, $watermark->y)) {
throw new Exception('Image::scale: Error watermarking image.');
}
}
}
} elseif (!empty($options['repeatX'])) {
$offsetX = 0;
for ($i = $offsetX; $i <= $this->currentX; $i += $watermark->x) {
if (!imagecopy($dest, $watermark->resource(), $i, $offsetY, 0, 0, $watermark->x, $watermark->y)) {
throw new Exception('Image::scale: Error watermarking image.');
}
}
} elseif (!empty($options['repeatY'])) {
$offsetY = 0;
for ($i = $offsetY; $i <= $this->currentY; $i += $watermark->y) {
if (!imagecopy($dest, $watermark->resource(), $offsetX, $i, 0, 0, $watermark->x, $watermark->y)) {
throw new Exception('Image::scale: Error watermarking image.');
}
}
} else {
if (!imagecopy($dest, $watermark->resource(), $offsetX, $offsetY, 0, 0, $watermark->x, $watermark->y)) {
throw new Exception('Image::scale: Error watermarking image.');
}
}
$this->current = $dest;
return $dest;
}
/**
* hexToRGB
*
* Returns the integer colour values from an HTML hex code
*
* @param string $h The HTML hex code
* @return array The integer colour values
* @access public
* @author Dom Hastings
*/
private function hexToRGB($h) {
// strip off the # if it's there
$h = trim($h, '#');
if (strlen($h) == 6) {
return array(
hexdec(substr($h, 0, 2)),
hexdec(substr($h, 2, 2)),
hexdec(substr($h, 4, 2))
);
} elseif (strlen($h) == 3) {
return array(
hexdec(substr($h, 0, 1).substr($h, 0, 1)),
hexdec(substr($h, 1, 1).substr($h, 1, 1)),
hexdec(substr($h, 2, 1).substr($h, 2, 1))
);
} else {
// default to white
return array(255, 255, 255);
}
}
/**
* addText
*
* Adds the specified text to the image at the specified location
*
* @param string $t The text to add to the image
* @param integer $x The x co-ordinate of the text
* @param integer $y The y co-ordinate of the text
* @param array $options The options
* @return array Results from imagettftext()
* @author Dom Hastings
*/
function addText($text, $x, $y, $options = array()) {
// merge in the options
$options = array_merge_recursive_distinct(
(is_array($this->options['text'])) ? $this->options['text'] : array(),
(is_array($options)) ? $options : array()
);
// check the font file exists
if (substr($options['font'], 0, 1) == '/') {
if (!file_exists($options['font'])) {
throw new Exception('Imge::addText: Unable to find font file \''.$options['font'].'\'');
}
} else {
if (!file_exists($options['font'].'.ttf')) {
throw new Exception('Imge::addText: Unable to find font file \''.$options['font'].'\'');
}
}
list($r, $g, $b) = $this->hexToRGB($options['color']);
$colour = imagecolorallocatealpha($this->current, $r, $g, $b, $options['transparency']);
return imagettftext($this->current, $options['size'], $options['angle'], $x, $y, $colour, $options['font'], $text);
}
/**
* drawLine
*
* Draws a line from the co-ordinates in array start to the co-ordinates in array finish using the GD library function
*
* @param array $start The start point index 0 should be the x co-ordinate, 1 the y
* @param array $finish The end point index 0 should be the x co-ordinate, 1 the y
* @param array $options The options
* @return mixed The result from imageline()
* @author Dom Hastings
*/
function drawLine($start, $finish, $options = array()) {
// merge in the options
$options = array_merge_recursive_distinct(
(is_array($this->options['line'])) ? $this->options['line'] : array(),
(is_array($options)) ? $options : array()
);
imagesetthickness($this->current, $options['size']);
if (!is_array($start) || !is_array($finish)) {
throw new Exception('Image::drawLine: Arguments 0 and 1 must be arrays.');
}
list($sX, $sY, $fX, $fY) = array_merge(array_values($start), array_values($finish));
list($r, $g, $b) = $this->hexToRGB($options['color']);
$colour = imagecolorallocatealpha($this->current, $r, $g, $b, $options['transparency']);
if (!empty($options['style'])) {
imagesetstyle($this->current, $options['style']);
}
return imageline($this->current, $sX, $sY, $fX, $fY, $colour);
}
/**
* drawBox
*
* Draws a box from the co-ordinates in array start to the co-ordinates in array finish using the GD library function
*
* @param array $start The start point index 0 should be the x co-ordinate, 1 the y
* @param array $finish The end point index 0 should be the x co-ordinate, 1 the y
* @param array $options The options
* @return mixed The result from imagerectangle()
* @author Dom Hastings
*/
function drawBox($start, $finish, $options = array()) {
// merge in the options
$options = array_merge_recursive_distinct(
(is_array($this->options['box'])) ? $this->options['box'] : array(),
(is_array($options)) ? $options : array()
);
imagesetthickness($this->current, $options['size']);
if (!is_array($start) || !is_array($finish)) {
throw new Exception('Image::drawLine: Arguments 0 and 1 must be arrays.');
}
list($sX, $sY, $fX, $fY) = array_merge(array_values($start), array_values($finish));
list($r, $g, $b) = $this->hexToRGB($options['color']);
$colour = imagecolorallocatealpha($this->current, $r, $g, $b, $options['transparency']);
if (empty($options['filled'])) {
if (!empty($options['style'])) {
imagesetstyle($this->current, $options['style']);
}
return imagerectangle($this->current, $sX, $sY, $fX, $fY, $colour);
} else {
return imagefilledrectangle($this->current, $sX, $sY, $fX, $fY, $colour);
}
}
}
/**
* array_merge_recursive_distinct
*
* Recursively process an array merge all child nodes together
*
* @return array
* @author Dom Hastings
*/
if (!function_exists('array_merge_recursive_distinct')) {
function array_merge_recursive_distinct() {
switch (func_num_args()) {
case 0:
return array();
break;
case 1:
return (array) func_get_arg(0);
break;
default:
$a = func_get_args();
$s = (array) array_shift($a);
foreach ($a as $i => $b) {
if (!is_array($b)) {
$b = (array) $b;
}
foreach ($b as $k => $v) {
if (is_numeric($k)) {
$s[] = $v;
} else {
if (isset($s[$k])) {
if (is_array($s[$k]) && is_array($v)) {
$s[$k] = array_merge_recursive_distinct($s[$k], $v);
} else {
$s[$k] = $v;
}
} else {
$s[$k] = $v;
}
}
}
}
break;
}
return $s;
}
}
Initial URL
http://www.dom111.co.uk/blog/coding/php-object-oriented-image-manipulation/156
Initial Description
I’ve been working on a CMS lately and having to create thumbnails for uploaded images is always a pain, lots of maths working out the correct sizes and such, so I’ve created a fairly small script to manipulate images in an object-oriented style.
Initial Title
Object Oriented Image Manipulation
Initial Tags
php, image
Initial Language
PHP