First commit

This commit is contained in:
Pierre Hubert
2016-11-19 12:08:12 +01:00
commit 990540b2b9
4706 changed files with 931207 additions and 0 deletions

View File

@ -0,0 +1,320 @@
<?php
namespace Gregwar\Cache;
/**
* A cache system based on files
*
* @author Gregwar <g.passault@gmail.com>
*/
class Cache
{
/**
* Cache directory
*/
protected $cacheDirectory;
/**
* Use a different directory as actual cache
*/
protected $actualCacheDirectory = null;
/**
* Prefix directories size
*
* For instance, if the file is helloworld.txt and the prefix size is
* 5, the cache file will be: h/e/l/l/o/helloworld.txt
*
* This is useful to avoid reaching a too large number of files into the
* cache system directories
*/
protected $prefixSize = 5;
/**
* Directory mode
*
* Allows setting of the access mode for the directories created.
*/
protected $directoryMode = 0755;
/**
* Constructs the cache system
*/
public function __construct($cacheDirectory = 'cache')
{
$this->cacheDirectory = $cacheDirectory;
}
/**
* Sets the cache directory
*
* @param $cacheDirectory the cache directory
*/
public function setCacheDirectory($cacheDirectory)
{
$this->cacheDirectory = $cacheDirectory;
return $this;
}
/**
* Gets the cache directory
*
* @return string the cache directory
*/
public function getCacheDirectory()
{
return $this->cacheDirectory;
}
/**
* Sets the actual cache directory
*/
public function setActualCacheDirectory($actualCacheDirectory = null)
{
$this->actualCacheDirectory = $actualCacheDirectory;
return $this;
}
/**
* Returns the actual cache directory
*/
public function getActualCacheDirectory()
{
return $this->actualCacheDirectory ?: $this->cacheDirectory;
}
/**
* Change the prefix size
*
* @param $prefixSize the size of the prefix directories
*/
public function setPrefixSize($prefixSize)
{
$this->prefixSize = $prefixSize;
return $this;
}
/**
* Change the directory mode
*
* @param $directoryMode the directory mode to use
*/
public function setDirectoryMode($directoryMode)
{
if (!$directoryMode) {
$directoryMode = 0755;
}
$this->directoryMode = $directoryMode;
return $this;
}
/**
* Creates a directory
*
* @param $directory, the target directory
*/
protected function mkdir($directory)
{
if (!is_dir($directory)) {
@mkdir($directory, $this->directoryMode, true);
}
}
/**
* Gets the cache file name
*
* @param $filename, the name of the cache file
* @param $actual get the actual file or the public file
* @param $mkdir, a boolean to enable/disable the construction of the
* cache file directory
*/
public function getCacheFile($filename, $actual = false, $mkdir = false)
{
$path = array();
// Getting the length of the filename before the extension
$parts = explode('.', $filename);
$len = strlen($parts[0]);
for ($i=0; $i<min($len, $this->prefixSize); $i++) {
$path[] = $filename[$i];
}
$path = implode('/', $path);
$actualDir = $this->getActualCacheDirectory() . '/' . $path;
if ($mkdir && !is_dir($actualDir)) {
mkdir($actualDir, $this->directoryMode, true);
}
$path .= '/' . $filename;
if ($actual) {
return $this->getActualCacheDirectory() . '/' . $path;
} else {
return $this->getCacheDirectory() . '/' . $path;
}
}
/**
* Checks that the cache conditions are respected
*
* @param $cacheFile the cache file
* @param $conditions an array of conditions to check
*/
protected function checkConditions($cacheFile, array $conditions = array())
{
// Implicit condition: the cache file should exist
if (!file_exists($cacheFile)) {
return false;
}
foreach ($conditions as $type => $value) {
switch ($type) {
case 'maxage':
case 'max-age':
// Return false if the file is older than $value
$age = time() - filectime($cacheFile);
if ($age > $value) {
return false;
}
break;
case 'younger-than':
case 'youngerthan':
// Return false if the file is older than the file $value, or the files $value
$check = function($filename) use ($cacheFile) {
return !file_exists($filename) || filectime($cacheFile) < filectime($filename);
};
if (!is_array($value)) {
if (!$this->isRemote($value) && $check($value)) {
return false;
}
} else {
foreach ($value as $file) {
if (!$this->isRemote($file) && $check($file)) {
return false;
}
}
}
break;
default:
throw new \Exception('Cache condition '.$type.' not supported');
}
}
return true;
}
/**
* Checks if the targt filename exists in the cache and if the conditions
* are respected
*
* @param $filename the filename
* @param $conditions the conditions to respect
*/
public function exists($filename, array $conditions = array())
{
$cacheFile = $this->getCacheFile($filename, true);
return $this->checkConditions($cacheFile, $conditions);
}
/**
* Alias for exists
*/
public function check($filename, array $conditions = array())
{
return $this->exists($filename, $conditions);
}
/**
* Write data in the cache
*/
public function set($filename, $contents = '')
{
$cacheFile = $this->getCacheFile($filename, true, true);
file_put_contents($cacheFile, $contents);
return $this;
}
/**
* Alias for set()
*/
public function write($filename, $contents = '')
{
return $this->set($filename, $contents);
}
/**
* Get data from the cache
*/
public function get($filename, array $conditions = array())
{
if ($this->exists($filename, $conditions)) {
return file_get_contents($this->getCacheFile($filename, true));
} else {
return null;
}
}
/**
* Is this URL remote?
*/
protected function isRemote($file)
{
return preg_match('/^http(s{0,1}):\/\//', $file);
}
/**
* Get or create the cache entry
*
* @param $filename the cache file name
* @param $conditions an array of conditions about expiration
* @param $function the closure to call if the file does not exists
* @param $file returns the cache file or the file contents
* @param $actual returns the actual cache file
*/
public function getOrCreate($filename, array $conditions = array(), $function, $file = false, $actual = false)
{
if (!is_callable($function)) {
throw new InvalidArgumentException('The argument $function should be callable');
}
$cacheFile = $this->getCacheFile($filename, true, true);
$data = null;
if ($this->check($filename, $conditions)) {
$data = file_get_contents($cacheFile);
} else {
if(file_exists($cacheFile)) {
unlink($cacheFile);
}
$data = call_user_func($function, $cacheFile);
// Test if the closure wrote the file or if it returned the data
if (!file_exists($cacheFile)) {
$this->set($filename, $data);
} else {
$data = file_get_contents($cacheFile);
}
}
return $file ? $this->getCacheFile($filename, $actual) : $data;
}
/**
* Alias to getOrCreate with $file = true
*/
public function getOrCreateFile($filename, array $conditions = array(), $function, $actual = false)
{
return $this->getOrCreate($filename, $conditions, $function, true, $actual);
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace Gregwar\Cache;
/**
* Garbage collect a directory, this will crawl a directory, lookng
* for files older than X days and destroy them
*
* @author Gregwar <g.passault@gmail.com>
*/
class GarbageCollect
{
/**
* Drops old files of a directory
*
* @param string $directory the name of the target directory
* @param int $days the number of days to consider a file old
* @param bool $verbose enable verbose output
*
* @return true if all the files/directories of a directory was wiped
*/
public static function dropOldFiles($directory, $days = 30, $verbose = false)
{
$allDropped = true;
$now = time();
$dir = opendir($directory);
if (!$dir) {
if ($verbose) {
echo "! Unable to open $directory\n";
}
return false;
}
while ($file = readdir($dir)) {
if ($file == '.' || $file == '..') {
continue;
}
$fullName = $directory.'/'.$file;
$old = $now-filemtime($fullName);
if (is_dir($fullName)) {
// Directories are recursively crawled
if (static::dropOldFiles($fullName, $days, $verbose)) {
self::drop($fullName, $verbose);
} else {
$allDropped = false;
}
} else {
if ($old > (24*60*60*$days)) {
self::drop($fullName, $verbose);
} else {
$allDropped = false;
}
}
}
closedir($dir);
return $allDropped;
}
/**
* Drops a file or an empty directory
*
* @param $file the file to be removed
* @param $verbose the verbosity
*/
public static function drop($file, $verbose = false)
{
if (is_dir($file)) {
@rmdir($file);
} else {
@unlink($file);
}
if ($verbose) {
echo "> Dropping $file...\n";
}
}
}

View File

@ -0,0 +1,19 @@
Copyright (c) <2013> Grégoire Passault
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,166 @@
Cache
=====
![Build status](https://travis-ci.org/Gregwar/Cache.svg?branch=master)
This is a lightweight cache system based on file and directories.
Usage
=====
Step 1: Install it
------------------
Via composer:
```json
{
"require": {
"gregwar/cache": "1.0.*"
}
}
```
Or with a clone of the repository:
```bash
git clone https://github.com/Gregwar/Cache.git
```
Or downloading it:
* [Download .zip](https://github.com/Gregwar/Cache/archive/master.zip)
* [Download .tar.gz](https://github.com/Gregwar/Cache/archive/master.tar.gz)
Step 2: Setup the rights
------------------------
You need your PHP script to have access to the cache directory, you can for instance
create a `cache` directory with mode 777:
```
mkdir cache
chmod 777 cache
```
Step 3: Access the cache
------------------------
To access the cache, you can do like this:
```php
<?php
include('vendor/autoload.php'); // If using composer
use Gregwar\Cache\Cache;
$cache = new Cache;
$cache->setCacheDirectory('cache'); // This is the default
// If the cache exists, this will return it, else, the closure will be called
// to create this image
$data = $cache->getOrCreate('red-square.png', array(), function($filename) {
$i = imagecreatetruecolor(100, 100);
imagefill($i, 0, 0, 0xff0000);
imagepng($i, $filename);
});
header('Content-type: image/png');
echo $data;
```
This will render a red square. If the cache file (which will look like `cache/r/e/d/-/s/red-square.png')
exists, it will be read, else, the closure will be called in order to create the cache file.
API
===
You can use the following methods:
* `setCacheDirectory($directory)`: sets the cache directory (see below).
* `setActualCacheDirectory($directory)`: sets the actual cache directory (see below).
* `exists($filename, $conditions = array())`: check that the $filename file exists in the cache, checking
the conditions (see below).
* `check($filename, $conditions = array())`: alias for `exists`.
* `getCacheFile($filename, $actual = false, $mkdir = false)`: gets the cache file. If the `$actual` flag
is true, the actual cache file name will be returned (see below), if the `$mkdir` flag is true, the
cache file directories tree will be created.
* `set($filename, $contents)`: write contents to `$filename` cache file.
* `write($filename, $contents)`: alias for `set()`
* `get($filename, $conditions = array())`: if the cache file for `$filename` exists, contents will be
returned, else, `NULL` will be returned.
* `setPrefixSize($prefixSize)`: sets the prefix size for directories, default is 5. For instance, the
cache file for `helloworld.txt`, will be `'h/e/l/l/o/helloworld.txt`.
* `setDirectoryMode($directoryMode)`: sets the directory mode when creating direcotries, default is `0755`.
Does not affect any directories previously created.
* `getOrCreate($filename, $conditions = array(), $function, $file = false)`: this will check if the `$filename`
cache file exists and verifies `$conditions` (see below). If the cache file is OK, it will return its
contents. Else, it will call the `$function`, passing it the target file, this function can write the
file given in parameter or just return data. Then, cache data will be returned. If `$file` flag is set,
the cache file name will be returned instead of file data.
Note: consider using an hash for the `$filename` cache file, to avoid special characters.
Conditions
==========
You can use conditions to manage file expirations on the cache, there is two way of expiring:
* Using `max-age`, in seconds, to set the maximum age of the file
* Using `younger-than`, by passing another file, this will compare the modification date
and regenerate the cache if the given file is younger.
For instance, if you want to uppercase a file:
```php
<?php
use Gregwar\Cache\Cache;
$cache = new Cache;
$data = $cache->getOrCreate('uppercase.txt',
array(
'younger-than' => 'original.txt'
),
function() {
echo "Generating file...\n";
return strtoupper(file_get_contents('original.txt'));
});
echo $data;
```
This will be create the `uppercase.txt` cache file by uppercasing the `original.txt` if the cache file
does not exists or if the `original.txt` file is more recent than the cache file.
For instance:
```
php uppercase.php # Will generate the cache file
php uppercase.php # Will not generate the cache file
touch original.txt # Sets the last modification time to now
php uppercase.php # Will re-generate the cache file
```
Cache directory and actual cache directory
==========================================
In some cases, you'll want to get the cache file name. For instance, if you're caching
images, you'll want to give a string like `cache/s/o/m/e/i/someimage.png` to put it into
an `<img>` tag. This can be done by passing the `$file` argument to the `getOrCreate` to true,
or directly using `getCacheFile` method (see above).
However, the visible `cache` directory of your users is not the same as the absolute path
you want to access. To do that, you can set both the cache directory and the actual cache directory.
The cache directory is the prefix visible by the users (for instance: `cache/s/o/m/e/i/someimage.png`),
and the actual cache directory is the prefix to use to actually access to the image (for instance:
`/var/www/somesite/cache/s/o/m/e/i/someimage.png`). This way, the file will be accessed using absolute
path and the cache file returned will directly be usable for your user's browsers.
License
=======
This repository is under the MIT license, have a look at the `LICENCE` file.

View File

@ -0,0 +1,16 @@
<?php
/**
* Registers an autoload for all the classes in Gregwar\Cache
*/
spl_autoload_register(function ($className) {
$namespace = 'Gregwar\\Cache';
if (strpos($className, $namespace) === 0) {
$className = str_replace($namespace, '', $className);
$fileName = __DIR__ . '/' . str_replace('\\', '/', $className) . '.php';
if (file_exists($fileName)) {
require($fileName);
}
}
});

View File

@ -0,0 +1,58 @@
<?php
namespace Gregwar\Image\Adapter;
use Gregwar\Image\Source\Source;
/**
* Base Adapter Implementation to handle Image information
*/
abstract class Adapter implements AdapterInterface
{
/**
* @var Source
*/
protected $source;
/**
* The image resource handler
*/
protected $resource;
public function __construct(){
}
/**
* @inheritdoc
*/
public function setSource(Source $source)
{
$this->source = $source;
return $this;
}
/**
* @inheritdoc
*/
public function getResource()
{
return $this->resource;
}
/**
* Does this adapter supports the given type ?
*/
protected function supports($type)
{
return false;
}
/**
* Converts the image to true color
*/
protected function convertToTrueColor()
{
}
}

View File

@ -0,0 +1,388 @@
<?php
namespace Gregwar\Image\Adapter;
use Gregwar\Image\Image;
use Gregwar\Image\Source\Source;
/**
* all the functions / methods to work on images
*
* if changing anything please also add it to \Gregwar\Image\Image
*
* @author wodka <michael.schramm@gmail.com>
*/
interface AdapterInterface{
/**
* set the image source for the adapter
*
* @param Source $source
* @return $this
*/
public function setSource(Source $source);
/**
* get the raw resource
*
* @return resource
*/
public function getResource();
/**
* Gets the name of the adapter
*
* @return string
*/
public function getName();
/**
* Image width
*
* @return int
*/
public function width();
/**
* Image height
*
* @return int
*/
public function height();
/**
* Init the resource
*
* @return $this
*/
public function init();
/**
* Save the image as a gif
*
* @return $this
*/
public function saveGif($file);
/**
* Save the image as a png
*
* @return $this
*/
public function savePng($file);
/**
* Save the image as a jpeg
*
* @return $this
*/
public function saveJpeg($file, $quality);
/**
* Works as resize() excepts that the layout will be cropped
*
* @param int $width the width
* @param int $height the height
* @param int $background the background
*
* @return $this
*/
public function cropResize($width = null, $height = null, $background=0xffffff);
/**
* Resize the image preserving scale. Can enlarge it.
*
* @param int $width the width
* @param int $height the height
* @param int $background the background
* @param boolean $crop
*
* @return $this
*/
public function scaleResize($width = null, $height = null, $background=0xffffff, $crop = false);
/**
* Resizes the image. It will never be enlarged.
*
* @param int $width the width
* @param int $height the height
* @param int $background the background
* @param boolean $force
* @param boolean $rescale
* @param boolean $crop
*
* @return $this
*/
public function resize($width = null, $height = null, $background = 0xffffff, $force = false, $rescale = false, $crop = false);
/**
* Crops the image
*
* @param int $x the top-left x position of the crop box
* @param int $y the top-left y position of the crop box
* @param int $width the width of the crop box
* @param int $height the height of the crop box
*
* @return $this
*/
public function crop($x, $y, $width, $height);
/**
* enable progressive image loading
*
* @return $this
*/
public function enableProgressive();
/**
* Resizes the image forcing the destination to have exactly the
* given width and the height
*
* @param int $width the width
* @param int $height the height
* @param int $background the background
*
* @return $this
*/
public function forceResize($width = null, $height = null, $background = 0xffffff);
/**
* Perform a zoom crop of the image to desired width and height
*
* @param integer $width Desired width
* @param integer $height Desired height
* @param int $background
*
* @return $this
*/
public function zoomCrop($width, $height, $background = 0xffffff);
/**
* Fills the image background to $bg if the image is transparent
*
* @param int $background background color
*
* @return $this
*/
public function fillBackground($background = 0xffffff);
/**
* Negates the image
*
* @return $this
*/
public function negate();
/**
* Changes the brightness of the image
*
* @param int $brightness the brightness
*
* @return $this
*/
public function brightness($brightness);
/**
* Contrasts the image
*
* @param int $contrast the contrast [-100, 100]
*
* @return $this
*/
public function contrast($contrast);
/**
* Apply a grayscale level effect on the image
*
* @return $this
*/
public function grayscale();
/**
* Emboss the image
*
* @return $this
*/
public function emboss();
/**
* Smooth the image
*
* @param int $p value between [-10,10]
*
* @return $this
*/
public function smooth($p);
/**
* Sharps the image
*
* @return $this
*/
public function sharp();
/**
* Edges the image
*
* @return $this
*/
public function edge();
/**
* Colorize the image
*
* @param int $red value in range [-255, 255]
* @param int $green value in range [-255, 255]
* @param int $blue value in range [-255, 255]
*
* @return $this
*/
public function colorize($red, $green, $blue);
/**
* apply sepia to the image
*
* @return $this
*/
public function sepia();
/**
* Merge with another image
*
* @param Image $other
* @param int $x
* @param int $y
* @param int $width
* @param int $height
*
* @return $this
*/
public function merge(Image $other, $x = 0, $y = 0, $width = null, $height = null);
/**
* Rotate the image
*
* @param float $angle
* @param int $background
*
* @return $this
*/
public function rotate($angle, $background = 0xffffff);
/**
* Fills the image
*
* @param int $color
* @param int $x
* @param int $y
*
* @return $this
*/
public function fill($color = 0xffffff, $x = 0, $y = 0);
/**
* write text to the image
*
* @param string $font
* @param string $text
* @param int $x
* @param int $y
* @param int $size
* @param int $angle
* @param int $color
* @param string $align
*/
public function write($font, $text, $x = 0, $y = 0, $size = 12, $angle = 0, $color = 0x000000, $align = 'left');
/**
* Draws a rectangle
*
* @param int $x1
* @param int $y1
* @param int $x2
* @param int $y2
* @param int $color
* @param bool $filled
*
* @return $this
*/
public function rectangle($x1, $y1, $x2, $y2, $color, $filled = false);
/**
* Draws a rounded rectangle
*
* @param int $x1
* @param int $y1
* @param int $x2
* @param int $y2
* @param int $radius
* @param int $color
* @param bool $filled
*
* @return $this
*/
public function roundedRectangle($x1, $y1, $x2, $y2, $radius, $color, $filled = false);
/**
* Draws a line
*
* @param int $x1
* @param int $y1
* @param int $x2
* @param int $y2
* @param int $color
*
* @return $this
*/
public function line($x1, $y1, $x2, $y2, $color = 0x000000);
/**
* Draws an ellipse
*
* @param int $cx
* @param int $cy
* @param int $width
* @param int $height
* @param int $color
* @param bool $filled
*
* @return $this
*/
public function ellipse($cx, $cy, $width, $height, $color = 0x000000, $filled = false);
/**
* Draws a circle
*
* @param int $cx
* @param int $cy
* @param int $r
* @param int $color
* @param bool $filled
*
* @return $this
*/
public function circle($cx, $cy, $r, $color = 0x000000, $filled = false);
/**
* Draws a polygon
*
* @param array $points
* @param int $color
* @param bool $filled
*
* @return $this
*/
public function polygon(array $points, $color, $filled = false);
/**
* Flips the image
*
* @param int $flipVertical
* @param int $flipHorizontal
*
* @return $this
*/
public function flip($flipVertical, $flipHorizontal);
}

View File

@ -0,0 +1,351 @@
<?php
namespace Gregwar\Image\Adapter;
abstract class Common extends Adapter
{
/**
* @inheritdoc
*/
public function zoomCrop($width, $height, $background = 'transparent', $xPosLetter = 'center', $yPosLetter = 'center')
{
// Calculate the different ratios
$originalRatio = $this->width() / $this->height();
$newRatio = $width / $height;
// Compare ratios
if ($originalRatio > $newRatio) {
// Original image is wider
$newHeight = $height;
$newWidth = (int) $height * $originalRatio;
} else {
// Equal width or smaller
$newHeight = (int) $width / $originalRatio;
$newWidth = $width;
}
// Perform resize
$this->resize($newWidth, $newHeight, $background, true);
// Define x position
switch($xPosLetter) {
case 'L':
case 'left':
$xPos = 0;
break;
case 'R':
case 'right':
$xPos = (int) $newWidth - $width;
break;
default:
$xPos = (int) ($newWidth - $width) / 2;
}
// Define y position
switch($yPosLetter) {
case 'T':
case 'top':
$yPos = 0;
break;
case 'B':
case 'bottom':
$yPos = (int) $newHeight - $height;
break;
default:
$yPos = (int) ($newHeight - $height) / 2;
}
// Crop image to reach desired size
$this->crop($xPos, $yPos, $width, $height);
return $this;
}
/**
* Resizes the image forcing the destination to have exactly the
* given width and the height
*
* @param int $w the width
* @param int $h the height
* @param int $bg the background
*/
public function forceResize($width = null, $height = null, $background = 'transparent')
{
return $this->resize($width, $height, $background, true);
}
/**
* @inheritdoc
*/
public function scaleResize($width = null, $height = null, $background='transparent', $crop = false)
{
return $this->resize($width, $height, $background, false, true, $crop);
}
/**
* @inheritdoc
*/
public function cropResize($width = null, $height = null, $background='transparent')
{
return $this->resize($width, $height, $background, false, false, true);
}
/**
* Fix orientation using Exif informations
*/
public function fixOrientation()
{
if (!extension_loaded('exif')) {
throw new \RuntimeException('You need to EXIF PHP Extension to use this function');
}
$exif = exif_read_data($this->source->getInfos());
if(!array_key_exists('Orientation', $exif)) {
return $this;
}
switch($exif['Orientation']) {
case 1:
break;
case 2:
$this->flip(false, true);
break;
case 3: // 180 rotate left
$this->rotate(180);
break;
case 4: // vertical flip
$this->flip(true, false);
break;
case 5: // vertical flip + 90 rotate right
$this->flip(true, false);
$this->rotate(-90);
break;
case 6: // 90 rotate right
$this->rotate(-90);
break;
case 7: // horizontal flip + 90 rotate right
$this->flip(false, true);
$this->rotate(-90);
break;
case 8: // 90 rotate left
$this->rotate(90);
break;
}
return $this;
}
/**
* Opens the image
*/
abstract protected function openGif($file);
abstract protected function openJpeg($file);
abstract protected function openPng($file);
/**
* Creates an image
*/
abstract protected function createImage($width, $height);
/**
* Creating an image using $data
*/
abstract protected function createImageFromData($data);
/**
* Loading image from $resource
*/
protected function loadResource($resource)
{
$this->resource = $resource;
}
protected function loadFile($file, $type)
{
if (!$this->supports($type)) {
throw new \RuntimeException('Type '.$type.' is not supported by GD');
}
if ($type == 'jpeg') {
$this->openJpeg($file);
}
if ($type == 'gif') {
$this->openGif($file);
}
if ($type == 'png') {
$this->openPng($file);
}
if (false === $this->resource) {
throw new \UnexpectedValueException('Unable to open file ('.$file.')');
} else {
$this->convertToTrueColor();
}
}
/**
* @inheritdoc
*/
public function init()
{
$source = $this->source;
if ($source instanceof \Gregwar\Image\Source\File) {
$this->loadFile($source->getFile(), $source->guessType());
} else if ($source instanceof \Gregwar\Image\Source\Create) {
$this->createImage($source->getWidth(), $source->getHeight());
} else if ($source instanceof \Gregwar\Image\Source\Data) {
$this->createImageFromData($source->getData());
} else if ($source instanceof \Gregwar\Image\Source\Resource) {
$this->loadResource($source->getResource());
} else {
throw new \Exception('Unsupported image source type '.get_class($source));
}
return $this;
}
/**
* @inheritdoc
*/
public function resize($width = null, $height = null, $background = 'transparent', $force = false, $rescale = false, $crop = false)
{
$current_width = $this->width();
$current_height = $this->height();
$new_width = 0;
$new_height = 0;
$scale = 1.0;
if ($height === null && preg_match('#^(.+)%$#mUsi', $width, $matches)) {
$width = round($current_width * ((float)$matches[1]/100.0));
$height = round($current_height * ((float)$matches[1]/100.0));
}
if (!$rescale && (!$force || $crop)) {
if ($width!=null && $current_width>$width) {
$scale = $current_width/$width;
}
if ($height!=null && $current_height>$height) {
if ($current_height/$height > $scale)
$scale = $current_height/$height;
}
} else {
if ($width!=null) {
$scale = $current_width/$width;
$new_width = $width;
}
if ($height!=null) {
if ($width!=null && $rescale) {
$scale = max($scale,$current_height/$height);
} else {
$scale = $current_height/$height;
}
$new_height = $height;
}
}
if (!$force || $width==null || $rescale) {
$new_width = round($current_width/$scale);
}
if (!$force || $height==null || $rescale) {
$new_height = round($current_height/$scale);
}
if ($width == null || $crop) {
$width = $new_width;
}
if ($height == null || $crop) {
$height = $new_height;
}
$this->doResize($background, $width, $height, $new_width, $new_height);
}
/**
* Trim background color arround the image
*
* @param int $bg the background
*/
protected function _trimColor($background='transparent')
{
$width = $this->width();
$height = $this->height();
$b_top = 0;
$b_lft = 0;
$b_btm = $height - 1;
$b_rt = $width - 1;
//top
for(; $b_top < $height; ++$b_top) {
for($x = 0; $x < $width; ++$x) {
if ($this->getColor($x, $b_top) != $background) {
break 2;
}
}
}
// bottom
for(; $b_btm >= 0; --$b_btm) {
for($x = 0; $x < $width; ++$x) {
if ($this->getColor($x, $b_btm) != $background) {
break 2;
}
}
}
// left
for(; $b_lft < $width; ++$b_lft) {
for($y = $b_top; $y <= $b_btm; ++$y) {
if ($this->getColor($b_lft, $y) != $background) {
break 2;
}
}
}
// right
for(; $b_rt >= 0; --$b_rt) {
for($y = $b_top; $y <= $b_btm; ++$y) {
if ($this->getColor($b_rt, $y) != $background) {
break 2;
}
}
}
$b_btm++;
$b_rt++;
$this->crop($b_lft, $b_top, $b_rt - $b_lft, $b_btm - $b_top);
}
/**
* Resizes the image to an image having size of $target_width, $target_height, using
* $new_width and $new_height and padding with $bg color
*/
abstract protected function doResize($bg, $target_width, $target_height, $new_width, $new_height);
/**
* Gets the color of the $x, $y pixel
*/
abstract protected function getColor($x, $y);
/**
* @inheritdoc
*/
public function enableProgressive(){
throw new \Exception('The Adapter '.$this->getName().' does not support Progressive Image loading');
}
}

View File

@ -0,0 +1,567 @@
<?php
namespace Gregwar\Image\Adapter;
use Gregwar\Image\ImageColor;
use Gregwar\Image\Image;
class GD extends Common
{
public static $gdTypes = array(
'jpeg' => \IMG_JPG,
'gif' => \IMG_GIF,
'png' => \IMG_PNG,
);
protected function loadResource($resource)
{
parent::loadResource($resource);
imagesavealpha($this->resource, true);
}
/**
* Gets the width and the height for writing some text
*/
public static function TTFBox($font, $text, $size, $angle = 0)
{
$box = imagettfbbox($size, $angle, $font, $text);
return array(
'width' => abs($box[2] - $box[0]),
'height' => abs($box[3] - $box[5])
);
}
public function __construct()
{
parent::__construct();
if (!(extension_loaded('gd') && function_exists('gd_info'))) {
throw new \RuntimeException('You need to install GD PHP Extension to use this library');
}
}
/**
* @inheritdoc
*/
public function getName()
{
return 'GD';
}
/**
* @inheritdoc
*/
public function fillBackground($background = 0xffffff)
{
$w = $this->width();
$h = $this->height();
$n = imagecreatetruecolor($w, $h);
imagefill($n, 0, 0, ImageColor::gdAllocate($this->resource, $background));
imagecopyresampled($n, $this->resource, 0, 0, 0, 0, $w, $h, $w, $h);
imagedestroy($this->resource);
$this->resource = $n;
return $this;
}
/**
* Do the image resize
*
* @return $this
*/
protected function doResize($bg, $target_width, $target_height, $new_width, $new_height)
{
$width = $this->width();
$height = $this->height();
$n = imagecreatetruecolor($target_width, $target_height);
if ($bg != 'transparent') {
imagefill($n, 0, 0, ImageColor::gdAllocate($this->resource, $bg));
} else {
imagealphablending($n, false);
$color = ImageColor::gdAllocate($this->resource, 'transparent');
imagefill($n, 0, 0, $color);
imagesavealpha($n, true);
}
imagecopyresampled($n, $this->resource, ($target_width-$new_width)/2, ($target_height-$new_height)/2, 0, 0, $new_width, $new_height, $width, $height);
imagedestroy($this->resource);
$this->resource = $n;
return $this;
}
/**
* @inheritdoc
*/
public function crop($x, $y, $width, $height)
{
$destination = imagecreatetruecolor($width, $height);
imagealphablending($destination, false);
imagesavealpha($destination, true);
imagecopy($destination, $this->resource, 0, 0, $x, $y, $this->width(), $this->height());
imagedestroy($this->resource);
$this->resource = $destination;
return $this;
}
/**
* @inheritdoc
*/
public function negate()
{
imagefilter($this->resource, IMG_FILTER_NEGATE);
return $this;
}
/**
* @inheritdoc
*/
public function brightness($brightness)
{
imagefilter($this->resource, IMG_FILTER_BRIGHTNESS, $brightness);
return $this;
}
/**
* @inheritdoc
*/
public function contrast($contrast)
{
imagefilter($this->resource, IMG_FILTER_CONTRAST, $contrast);
return $this;
}
/**
* @inheritdoc
*/
public function grayscale()
{
imagefilter($this->resource, IMG_FILTER_GRAYSCALE);
return $this;
}
/**
* @inheritdoc
*/
public function emboss()
{
imagefilter($this->resource, IMG_FILTER_EMBOSS);
return $this;
}
/**
* @inheritdoc
*/
public function smooth($p)
{
imagefilter($this->resource, IMG_FILTER_SMOOTH, $p);
return $this;
}
/**
* @inheritdoc
*/
public function sharp()
{
imagefilter($this->resource, IMG_FILTER_MEAN_REMOVAL);
return $this;
}
/**
* @inheritdoc
*/
public function edge()
{
imagefilter($this->resource, IMG_FILTER_EDGEDETECT);
return $this;
}
/**
* @inheritdoc
*/
public function colorize($red, $green, $blue)
{
imagefilter($this->resource, IMG_FILTER_COLORIZE, $red, $green, $blue);
return $this;
}
/**
* @inheritdoc
*/
public function sepia()
{
imagefilter($this->resource, IMG_FILTER_GRAYSCALE);
imagefilter($this->resource, IMG_FILTER_COLORIZE, 100, 50, 0);
return $this;
}
/**
* @inheritdoc
*/
public function merge(Image $other, $x = 0, $y = 0, $width = null, $height = null)
{
$other = clone $other;
$other->init();
$other->applyOperations();
imagealphablending($this->resource, true);
if (null == $width) {
$width = $other->width();
}
if (null == $height) {
$height = $other->height();
}
imagecopyresampled($this->resource, $other->getAdapter()->getResource(), $x, $y, 0, 0, $width, $height, $width, $height);
return $this;
}
/**
* @inheritdoc
*/
public function rotate($angle, $background = 0xffffff)
{
$this->resource = imagerotate($this->resource, $angle, ImageColor::gdAllocate($this->resource, $background));
imagealphablending($this->resource, true);
imagesavealpha($this->resource, true);
return $this;
}
/**
* @inheritdoc
*/
public function fill($color = 0xffffff, $x = 0, $y = 0)
{
imagealphablending($this->resource, false);
imagefill($this->resource, $x, $y, ImageColor::gdAllocate($this->resource, $color));
return $this;
}
/**
* @inheritdoc
*/
public function write($font, $text, $x = 0, $y = 0, $size = 12, $angle = 0, $color = 0x000000, $align = 'left')
{
imagealphablending($this->resource, true);
if ($align != 'left') {
$sim_size = self::TTFBox($font, $text, $size, $angle);
if ($align == 'center') {
$x -= $sim_size['width'] / 2;
}
if ($align == 'right') {
$x -= $sim_size['width'];
}
}
imagettftext($this->resource, $size, $angle, $x, $y, ImageColor::gdAllocate($this->resource, $color), $font, $text);
return $this;
}
/**
* @inheritdoc
*/
public function rectangle($x1, $y1, $x2, $y2, $color, $filled = false)
{
if ($filled) {
imagefilledrectangle($this->resource, $x1, $y1, $x2, $y2, ImageColor::gdAllocate($this->resource, $color));
} else {
imagerectangle($this->resource, $x1, $y1, $x2, $y2, ImageColor::gdAllocate($this->resource, $color));
}
return $this;
}
/**
* @inheritdoc
*/
public function roundedRectangle($x1, $y1, $x2, $y2, $radius, $color, $filled = false) {
if ($color) {
$color = ImageColor::gdAllocate($this->resource, $color);
}
if ($filled == true) {
imagefilledrectangle($this->resource, $x1+$radius, $y1, $x2-$radius, $y2, $color);
imagefilledrectangle($this->resource, $x1, $y1+$radius, $x1+$radius-1, $y2-$radius, $color);
imagefilledrectangle($this->resource, $x2-$radius+1, $y1+$radius, $x2, $y2-$radius, $color);
imagefilledarc($this->resource,$x1+$radius, $y1+$radius, $radius*2, $radius*2, 180 , 270, $color, IMG_ARC_PIE);
imagefilledarc($this->resource,$x2-$radius, $y1+$radius, $radius*2, $radius*2, 270 , 360, $color, IMG_ARC_PIE);
imagefilledarc($this->resource,$x1+$radius, $y2-$radius, $radius*2, $radius*2, 90 , 180, $color, IMG_ARC_PIE);
imagefilledarc($this->resource,$x2-$radius, $y2-$radius, $radius*2, $radius*2, 360 , 90, $color, IMG_ARC_PIE);
} else {
imageline($this->resource, $x1+$radius, $y1, $x2-$radius, $y1, $color);
imageline($this->resource, $x1+$radius, $y2, $x2-$radius, $y2, $color);
imageline($this->resource, $x1, $y1+$radius, $x1, $y2-$radius, $color);
imageline($this->resource, $x2, $y1+$radius, $x2, $y2-$radius, $color);
imagearc($this->resource,$x1+$radius, $y1+$radius, $radius*2, $radius*2, 180 , 270, $color);
imagearc($this->resource,$x2-$radius, $y1+$radius, $radius*2, $radius*2, 270 , 360, $color);
imagearc($this->resource,$x1+$radius, $y2-$radius, $radius*2, $radius*2, 90 , 180, $color);
imagearc($this->resource,$x2-$radius, $y2-$radius, $radius*2, $radius*2, 360 , 90, $color);
}
return $this;
}
/**
* @inheritdoc
*/
public function line($x1, $y1, $x2, $y2, $color = 0x000000)
{
imageline($this->resource, $x1, $y1, $x2, $y2, ImageColor::gdAllocate($this->resource, $color));
return $this;
}
/**
* @inheritdoc
*/
public function ellipse($cx, $cy, $width, $height, $color = 0x000000, $filled = false)
{
if ($filled) {
imagefilledellipse($this->resource, $cx, $cy, $width, $height, ImageColor::gdAllocate($this->resource, $color));
} else {
imageellipse($this->resource, $cx, $cy, $width, $height, ImageColor::gdAllocate($this->resource, $color));
}
return $this;
}
/**
* @inheritdoc
*/
public function circle($cx, $cy, $r, $color = 0x000000, $filled = false)
{
return $this->ellipse($cx, $cy, $r, $r, ImageColor::gdAllocate($this->resource, $color), $filled);
}
/**
* @inheritdoc
*/
public function polygon(array $points, $color, $filled = false)
{
if ($filled) {
imagefilledpolygon($this->resource, $points, count($points)/2, ImageColor::gdAllocate($this->resource, $color));
} else {
imagepolygon($this->resource, $points, count($points)/2, ImageColor::gdAllocate($this->resource, $color));
}
return $this;
}
/**
* @inheritdoc
*/
public function flip($flipVertical, $flipHorizontal) {
if (!$flipVertical && !$flipHorizontal) {
return $this;
}
if (function_exists('imageflip')) {
if ($flipVertical && $flipHorizontal) {
$flipMode = \IMG_FLIP_BOTH;
} else if ($flipVertical && !$flipHorizontal) {
$flipMode = \IMG_FLIP_VERTICAL;
} else if (!$flipVertical && $flipHorizontal) {
$flipMode = \IMG_FLIP_HORIZONTAL;
}
imageflip($this->resource, $flipMode);
} else {
$width = $this->width();
$height = $this->height();
$src_x = 0;
$src_y = 0;
$src_width = $width;
$src_height = $height;
if ($flipVertical) {
$src_y = $height -1;
$src_height = -$height;
}
if ($flipHorizontal) {
$src_x = $width -1;
$src_width = -$width;
}
$imgdest = imagecreatetruecolor ($width, $height);
imagealphablending($imgdest, false);
imagesavealpha($imgdest, true);
if (imagecopyresampled($imgdest, $this->resource, 0, 0, $src_x, $src_y , $width, $height, $src_width, $src_height)) {
imagedestroy($this->resource);
$this->resource = $imgdest;
}
}
return $this;
}
/**
* @inheritdoc
*/
public function width()
{
if (null === $this->resource) {
$this->init();
}
return imagesx($this->resource);
}
/**
* @inheritdoc
*/
public function height()
{
if (null === $this->resource) {
$this->init();
}
return imagesy($this->resource);
}
protected function createImage($width, $height)
{
$this->resource = imagecreatetruecolor($width, $height);
}
protected function createImageFromData($data)
{
$this->resource = @imagecreatefromstring($data);
}
/**
* Converts the image to true color
*/
protected function convertToTrueColor()
{
if (!imageistruecolor($this->resource)) {
if (function_exists('imagepalettetotruecolor')) {
// Available in PHP 5.5
imagepalettetotruecolor($this->resource);
} else {
$transparentIndex = imagecolortransparent($this->resource);
$w = $this->width();
$h = $this->height();
$img = imagecreatetruecolor($w, $h);
imagecopy($img, $this->resource, 0, 0, 0, 0, $w, $h);
if ($transparentIndex != -1) {
$width = $this->width();
$height = $this->height();
imagealphablending($img, false);
imagesavealpha($img, true);
for ($x=0; $x<$width; $x++) {
for ($y=0; $y<$height; $y++) {
if (imagecolorat($this->resource, $x, $y) == $transparentIndex) {
imagesetpixel($img, $x, $y, 127 << 24);
}
}
}
}
$this->resource = $img;
}
}
imagesavealpha($this->resource, true);
}
/**
* @inheritdoc
*/
public function saveGif($file)
{
$transColor = imagecolorallocatealpha($this->resource, 255, 255, 255, 127);
imagecolortransparent($this->resource, $transColor);
imagegif($this->resource, $file);
return $this;
}
/**
* @inheritdoc
*/
public function savePng($file)
{
imagepng($this->resource, $file);
return $this;
}
/**
* @inheritdoc
*/
public function saveJpeg($file, $quality)
{
imagejpeg($this->resource, $file, $quality);
return $this;
}
/**
* Try to open the file using jpeg
*
*/
protected function openJpeg($file)
{
$this->resource = @imagecreatefromjpeg($file);
}
/**
* Try to open the file using gif
*/
protected function openGif($file)
{
$this->resource = @imagecreatefromgif($file);
}
/**
* Try to open the file using PNG
*/
protected function openPng($file)
{
$this->resource = @imagecreatefrompng($file);
}
/**
* Does this adapter supports type ?
*/
protected function supports($type)
{
return (imagetypes() & self::$gdTypes[$type]);
}
protected function getColor($x, $y)
{
return imagecolorat($this->resource, $x, $y);
}
/**
* @inheritdoc
*/
public function enableProgressive(){
imageinterlace($this->resource, 1);
return $this;
}
}

View File

@ -0,0 +1,389 @@
<?php
namespace Gregwar\Image\Adapter;
use Gregwar\Image\Image;
class Imagick extends Common{
public function __construct(){
throw new \Exception('Imagick is not supported right now');
}
/**
* Gets the name of the adapter
*
* @return string
*/
public function getName(){
return 'ImageMagick';
}
/**
* Image width
*
* @return int
*/
public function width(){
// TODO: Implement width() method.
}
/**
* Image height
*
* @return int
*/
public function height(){
// TODO: Implement height() method.
}
/**
* Save the image as a gif
*
* @return $this
*/
public function saveGif($file){
// TODO: Implement saveGif() method.
}
/**
* Save the image as a png
*
* @return $this
*/
public function savePng($file){
// TODO: Implement savePng() method.
}
/**
* Save the image as a jpeg
*
* @return $this
*/
public function saveJpeg($file, $quality){
// TODO: Implement saveJpeg() method.
}
/**
* Crops the image
*
* @param int $x the top-left x position of the crop box
* @param int $y the top-left y position of the crop box
* @param int $width the width of the crop box
* @param int $height the height of the crop box
*
* @return $this
*/
public function crop($x, $y, $width, $height){
// TODO: Implement crop() method.
}
/**
* Fills the image background to $bg if the image is transparent
*
* @param int $background background color
*
* @return $this
*/
public function fillBackground($background = 0xffffff){
// TODO: Implement fillBackground() method.
}
/**
* Negates the image
*
* @return $this
*/
public function negate(){
// TODO: Implement negate() method.
}
/**
* Changes the brightness of the image
*
* @param int $brightness the brightness
*
* @return $this
*/
public function brightness($brightness){
// TODO: Implement brightness() method.
}
/**
* Contrasts the image
*
* @param int $contrast the contrast [-100, 100]
*
* @return $this
*/
public function contrast($contrast){
// TODO: Implement contrast() method.
}
/**
* Apply a grayscale level effect on the image
*
* @return $this
*/
public function grayscale(){
// TODO: Implement grayscale() method.
}
/**
* Emboss the image
*
* @return $this
*/
public function emboss(){
// TODO: Implement emboss() method.
}
/**
* Smooth the image
*
* @param int $p value between [-10,10]
*
* @return $this
*/
public function smooth($p){
// TODO: Implement smooth() method.
}
/**
* Sharps the image
*
* @return $this
*/
public function sharp(){
// TODO: Implement sharp() method.
}
/**
* Edges the image
*
* @return $this
*/
public function edge(){
// TODO: Implement edge() method.
}
/**
* Colorize the image
*
* @param int $red value in range [-255, 255]
* @param int $green value in range [-255, 255]
* @param int $blue value in range [-255, 255]
*
* @return $this
*/
public function colorize($red, $green, $blue){
// TODO: Implement colorize() method.
}
/**
* apply sepia to the image
*
* @return $this
*/
public function sepia(){
// TODO: Implement sepia() method.
}
/**
* Merge with another image
*
* @param Image $other
* @param int $x
* @param int $y
* @param int $width
* @param int $height
*
* @return $this
*/
public function merge(Image $other, $x = 0, $y = 0, $width = null, $height = null){
// TODO: Implement merge() method.
}
/**
* Rotate the image
*
* @param float $angle
* @param int $background
*
* @return $this
*/
public function rotate($angle, $background = 0xffffff){
// TODO: Implement rotate() method.
}
/**
* Fills the image
*
* @param int $color
* @param int $x
* @param int $y
*
* @return $this
*/
public function fill($color = 0xffffff, $x = 0, $y = 0){
// TODO: Implement fill() method.
}
/**
* write text to the image
*
* @param string $font
* @param string $text
* @param int $x
* @param int $y
* @param int $size
* @param int $angle
* @param int $color
* @param string $align
*/
public function write($font, $text, $x = 0, $y = 0, $size = 12, $angle = 0, $color = 0x000000, $align = 'left'){
// TODO: Implement write() method.
}
/**
* Draws a rectangle
*
* @param int $x1
* @param int $y1
* @param int $x2
* @param int $y2
* @param int $color
* @param bool $filled
*
* @return $this
*/
public function rectangle($x1, $y1, $x2, $y2, $color, $filled = false){
// TODO: Implement rectangle() method.
}
/**
* Draws a rounded rectangle
*
* @param int $x1
* @param int $y1
* @param int $x2
* @param int $y2
* @param int $radius
* @param int $color
* @param bool $filled
*
* @return $this
*/
public function roundedRectangle($x1, $y1, $x2, $y2, $radius, $color, $filled = false){
// TODO: Implement roundedRectangle() method.
}
/**
* Draws a line
*
* @param int $x1
* @param int $y1
* @param int $x2
* @param int $y2
* @param int $color
*
* @return $this
*/
public function line($x1, $y1, $x2, $y2, $color = 0x000000){
// TODO: Implement line() method.
}
/**
* Draws an ellipse
*
* @param int $cx
* @param int $cy
* @param int $width
* @param int $height
* @param int $color
* @param bool $filled
*
* @return $this
*/
public function ellipse($cx, $cy, $width, $height, $color = 0x000000, $filled = false){
// TODO: Implement ellipse() method.
}
/**
* Draws a circle
*
* @param int $cx
* @param int $cy
* @param int $r
* @param int $color
* @param bool $filled
*
* @return $this
*/
public function circle($cx, $cy, $r, $color = 0x000000, $filled = false){
// TODO: Implement circle() method.
}
/**
* Draws a polygon
*
* @param array $points
* @param int $color
* @param bool $filled
*
* @return $this
*/
public function polygon(array $points, $color, $filled = false){
// TODO: Implement polygon() method.
}
/**
* @inheritdoc
*/
public function flip($flipVertical, $flipHorizontal) {
// TODO: Implement flip method
}
/**
* Opens the image
*/
protected function openGif($file){
// TODO: Implement openGif() method.
}
protected function openJpeg($file){
// TODO: Implement openJpeg() method.
}
protected function openPng($file){
// TODO: Implement openPng() method.
}
/**
* Creates an image
*/
protected function createImage($width, $height){
// TODO: Implement createImage() method.
}
/**
* Creating an image using $data
*/
protected function createImageFromData($data){
// TODO: Implement createImageFromData() method.
}
/**
* Resizes the image to an image having size of $target_width, $target_height, using
* $new_width and $new_height and padding with $bg color
*/
protected function doResize($bg, $target_width, $target_height, $new_width, $new_height){
// TODO: Implement doResize() method.
}
/**
* Gets the color of the $x, $y pixel
*/
protected function getColor($x, $y){
// TODO: Implement getColor() method.
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Gregwar\Image\Exceptions;
class GenerationError extends \Exception
{
public function __construct($newNewFile)
{
$this->newNewFile = $newNewFile;
}
public function getNewFile()
{
return $this->newNewFile;
}
}

View File

@ -0,0 +1,83 @@
<?php
namespace Gregwar\Image;
/**
* Garbage collect a directory, this will crawl a directory, lookng
* for files older than X days and destroy them
*
* @author Gregwar <g.passault@gmail.com>
*/
class GarbageCollect
{
/**
* Drops old files of a directory
*
* @param string $directory the name of the target directory
* @param int $days the number of days to consider a file old
* @param bool $verbose enable verbose output
*
* @return true if all the files/directories of a directory was wiped
*/
public static function dropOldFiles($directory, $days = 30, $verbose = false)
{
$allDropped = true;
$now = time();
$dir = opendir($directory);
if (!$dir) {
if ($verbose) {
echo "! Unable to open $directory\n";
}
return false;
}
while ($file = readdir($dir)) {
if ($file == '.' || $file == '..') {
continue;
}
$fullName = $directory.'/'.$file;
$old = $now-filemtime($fullName);
if (is_dir($fullName)) {
// Directories are recursively crawled
if (static::dropOldFiles($fullName, $days, $verbose)) {
self::drop($fullName, $verbose);
} else {
$allDropped = false;
}
} else {
if ($old > (24*60*60*$days)) {
self::drop($fullName, $verbose);
} else {
$allDropped = false;
}
}
}
closedir($dir);
return $allDropped;
}
/**
* Drops a file or an empty directory
*/
public static function drop($file, $verbose = false)
{
if (is_dir($file)) {
@rmdir($file);
} else {
@unlink($file);
}
if ($verbose) {
echo "> Dropping $file...\n";
}
}
}

View File

@ -0,0 +1,752 @@
<?php
namespace Gregwar\Image;
use Gregwar\Image\Adapter\AdapterInterface;
use Gregwar\Image\Exceptions\GenerationError;
/**
* Images handling class
*
* @author Gregwar <g.passault@gmail.com>
*
* @method Image saveGif($file)
* @method Image savePng($file)
* @method Image saveJpeg($file, $quality)
* @method Image cropResize($width = null, $height = null, $background=0xffffff)
* @method Image scale($width = null, $height = null, $background=0xffffff, $crop = false)
* @method Image ($width = null, $height = null, $background = 0xffffff, $force = false, $rescale = false, $crop = false)
* @method Image crop($x, $y, $width, $height)
* @method Image enableProgressive()
* @method Image force($width = null, $height = null, $background = 0xffffff)
* @method Image zoomCrop($width, $height, $background = 0xffffff)
* @method Image fillBackground($background = 0xffffff)
* @method Image negate()
* @method Image brightness($brightness)
* @method Image contrast($contrast)
* @method Image grayscale()
* @method Image emboss()
* @method Image smooth($p)
* @method Image sharp()
* @method Image edge()
* @method Image colorize($red, $green, $blue)
* @method Image sepia()
* @method Image merge(Image $other, $x = 0, $y = 0, $width = null, $height = null)
* @method Image rotate($angle, $background = 0xffffff)
* @method Image fill($color = 0xffffff, $x = 0, $y = 0)
* @method Image write($font, $text, $x = 0, $y = 0, $size = 12, $angle = 0, $color = 0x000000, $align = 'left')
* @method Image rectangle($x1, $y1, $x2, $y2, $color, $filled = false)
* @method Image roundedRectangle($x1, $y1, $x2, $y2, $radius, $color, $filled = false)
* @method Image line($x1, $y1, $x2, $y2, $color = 0x000000)
* @method Image ellipse($cx, $cy, $width, $height, $color = 0x000000, $filled = false)
* @method Image circle($cx, $cy, $r, $color = 0x000000, $filled = false)
* @method Image polygon(array $points, $color, $filled = false)
* @method Image flip($flipVertical, $flipHorizontal)
*/
class Image
{
/**
* Directory to use for file caching
*/
protected $cacheDir = 'cache/images';
/**
* Directory cache mode
*/
protected $cacheMode = null;
/**
* Internal adapter
*
* @var AdapterInterface
*/
protected $adapter = null;
/**
* Pretty name for the image
*/
protected $prettyName = '';
protected $prettyPrefix;
/**
* Transformations hash
*/
protected $hash = null;
/**
* The image source
*/
protected $source = null;
/**
* Force image caching, even if there is no operation applied
*/
protected $forceCache = true;
/**
* Supported types
*/
public static $types = array(
'jpg' => 'jpeg',
'jpeg' => 'jpeg',
'png' => 'png',
'gif' => 'gif',
);
/**
* Fallback image
*/
protected $fallback;
/**
* Use fallback image
*/
protected $useFallbackImage = true;
/**
* Cache system
*/
protected $cache;
/**
* Change the caching directory
*/
public function setCacheDir($cacheDir)
{
$this->cache->setCacheDirectory($cacheDir);
return $this;
}
/**
* @param int $dirMode
*/
public function setCacheDirMode($dirMode)
{
$this->cache->setDirectoryMode($dirMode);
}
/**
* Enable or disable to force cache even if the file is unchanged
*/
public function setForceCache($forceCache = true)
{
$this->forceCache = $forceCache;
return $this;
}
/**
* The actual cache dir
*/
public function setActualCacheDir($actualCacheDir)
{
$this->cache->setActualCacheDirectory($actualCacheDir);
return $this;
}
/**
* Sets the pretty name of the image
*/
public function setPrettyName($name, $prefix = true)
{
if (empty($name)) {
return $this;
}
$this->prettyName = $this->urlize($name);
$this->prettyPrefix = $prefix;
return $this;
}
/**
* Urlizes the prettyName
*/
protected function urlize($name)
{
$transliterator = '\Behat\Transliterator\Transliterator';
if (class_exists($transliterator)) {
$name = $transliterator::transliterate($name);
$name = $transliterator::urlize($name);
} else {
$name = strtolower($name);
$name = str_replace(' ', '-', $name);
$name = preg_replace('/([^a-z0-9\-]+)/m', '', $name);
}
return $name;
}
/**
* Operations array
*/
protected $operations = array();
public function __construct($originalFile = null, $width = null, $height = null)
{
$this->cache = new \Gregwar\Cache\Cache;
$this->cache->setCacheDirectory($this->cacheDir);
$this->setFallback(null);
if ($originalFile) {
$this->source = new Source\File($originalFile);
} else {
$this->source = new Source\Create($width, $height);
}
}
/**
* Sets the image data
*/
public function setData($data)
{
$this->source = new Source\Data($data);
}
/**
* Sets the resource
*/
public function setResource($resource)
{
$this->source = new Source\Resource($resource);
}
/**
* Use the fallback image or not
*/
public function useFallback($useFallbackImage = true)
{
$this->useFallbackImage = $useFallbackImage;
return $this;
}
/**
* Sets the fallback image to use
*/
public function setFallback($fallback = null)
{
if ($fallback === null) {
$this->fallback = __DIR__ . '/images/error.jpg';
} else {
$this->fallback = $fallback;
}
return $this;
}
/**
* Gets the fallack image path
*/
public function getFallback()
{
return $this->fallback;
}
/**
* Gets the fallback into the cache dir
*/
public function getCacheFallback()
{
$fallback = $this->fallback;
return $this->cache->getOrCreateFile('fallback.jpg', array(), function($target) use ($fallback) {
copy($fallback, $target);
});
}
/**
* @return AdapterInterface
*/
public function getAdapter()
{
if (null === $this->adapter) {
// Defaults to GD
$this->setAdapter('gd');
}
return $this->adapter;
}
public function setAdapter($adapter)
{
if ($adapter instanceof Adapter\Adapter) {
$this->adapter = $adapter;
} else {
if (is_string($adapter)) {
$adapter = strtolower($adapter);
switch ($adapter) {
case 'gd':
$this->adapter = new Adapter\GD();
break;
case 'imagemagick':
case 'imagick':
$this->adapter = new Adapter\Imagick();
break;
default:
throw new \Exception('Unknown adapter: '.$adapter);
break;
}
} else {
throw new \Exception('Unable to load the given adapter (not string or Adapter)');
}
}
$this->adapter->setSource($this->source);
}
/**
* Get the file path
*
* @return mixed a string with the filen name, null if the image
* does not depends on a file
*/
public function getFilePath()
{
if ($this->source instanceof Source\File) {
return $this->source->getFile();
} else {
return null;
}
}
/**
* Defines the file only after instantiation
*
* @param string $originalFile the file path
*/
public function fromFile($originalFile)
{
$this->source = new Source\File($originalFile);
return $this;
}
/**
* Tells if the image is correct
*/
public function correct()
{
return $this->source->correct();
}
/**
* Guess the file type
*/
public function guessType()
{
return $this->source->guessType();
}
/**
* Adds an operation
*/
protected function addOperation($method, $args)
{
$this->operations[] = array($method, $args);
}
/**
* Generic function
*/
public function __call($methodName, $args)
{
$adapter = $this->getAdapter();
$reflection = new \ReflectionClass(get_class($adapter));
if ($reflection->hasMethod($methodName)) {
$method = $reflection->getMethod($methodName);
if ($method->getNumberOfRequiredParameters() > count($args)) {
throw new \InvalidArgumentException('Not enough arguments given for '.$methodName);
}
$this->addOperation($methodName, $args);
return $this;
}
throw new \BadFunctionCallException('Invalid method: '.$methodName);
}
/**
* Serialization of operations
*/
public function serializeOperations()
{
$datas = array();
foreach ($this->operations as $operation) {
$method = $operation[0];
$args = $operation[1];
foreach ($args as &$arg) {
if ($arg instanceof self) {
$arg = $arg->getHash();
}
}
$datas[] = array($method, $args);
}
return serialize($datas);
}
/**
* Generates the hash
*/
public function generateHash($type = 'guess', $quality = 80)
{
$inputInfos = $this->source->getInfos();
$datas = array(
$inputInfos,
$this->serializeOperations(),
$type,
$quality
);
$this->hash = sha1(serialize($datas));
}
/**
* Gets the hash
*/
public function getHash($type = 'guess', $quality = 80)
{
if (null === $this->hash) {
$this->generateHash($type, $quality);
}
return $this->hash;
}
/**
* Gets the cache file name and generate it if it does not exists.
* Note that if it exists, all the image computation process will
* not be done.
*
* @param string $type the image type
* @param int $quality the quality (for JPEG)
*/
public function cacheFile($type = 'jpg', $quality = 80, $actual = false)
{
if ($type == 'guess') {
$type = $this->guessType();
}
if (!count($this->operations) && $type == $this->guessType() && !$this->forceCache) {
return $this->getFilename($this->getFilePath());
}
// Computes the hash
$this->hash = $this->getHash($type, $quality);
// Generates the cache file
$cacheFile = '';
if (!$this->prettyName || $this->prettyPrefix) {
$cacheFile .= $this->hash;
}
if ($this->prettyPrefix) {
$cacheFile .= '-';
}
if ($this->prettyName) {
$cacheFile .= $this->prettyName;
}
$cacheFile .= '.'.$type;
// If the files does not exists, save it
$image = $this;
// Target file should be younger than all the current image
// dependencies
$conditions = array(
'younger-than' => $this->getDependencies()
);
// The generating function
$generate = function($target) use ($image, $type, $quality) {
$result = $image->save($target, $type, $quality);
if ($result != $target) {
throw new GenerationError($result);
}
};
// Asking the cache for the cacheFile
try {
$file = $this->cache->getOrCreateFile($cacheFile, $conditions, $generate, $actual);
} catch (GenerationError $e) {
$file = $e->getNewFile();
}
if ($actual) {
return $file;
} else {
return $this->getFilename($file);
}
}
/**
* Get cache data (to render the image)
*
* @param string $type the image type
* @param int $quality the quality (for JPEG)
*/
public function cacheData($type = 'jpg', $quality = 80)
{
return file_get_contents($this->cacheFile($type, $quality));
}
/**
* Hook to helps to extends and enhance this class
*/
protected function getFilename($filename)
{
return $filename;
}
/**
* Generates and output a jpeg cached file
*/
public function jpeg($quality = 80)
{
return $this->cacheFile('jpg', $quality);
}
/**
* Generates and output a gif cached file
*/
public function gif()
{
return $this->cacheFile('gif');
}
/**
* Generates and output a png cached file
*/
public function png()
{
return $this->cacheFile('png');
}
/**
* Generates and output an image using the same type as input
*/
public function guess($quality = 80)
{
return $this->cacheFile('guess', $quality);
}
/**
* Get all the files that this image depends on
*
* @return string[] this is an array of strings containing all the files that the
* current Image depends on
*/
public function getDependencies()
{
$dependencies = array();
$file = $this->getFilePath();
if ($file) {
$dependencies[] = $file;
}
foreach ($this->operations as $operation) {
foreach ($operation[1] as $argument) {
if ($argument instanceof self) {
$dependencies = array_merge($dependencies, $argument->getDependencies());
}
}
}
return $dependencies;
}
/**
* Applies the operations
*/
public function applyOperations()
{
// Renders the effects
foreach ($this->operations as $operation) {
call_user_func_array(array($this->adapter, $operation[0]), $operation[1]);
}
}
/**
* Initialize the adapter
*/
public function init()
{
$this->getAdapter()->init();
}
/**
* Save the file to a given output
*/
public function save($file, $type = 'guess', $quality = 80)
{
if ($file) {
$directory = dirname($file);
if (!is_dir($directory)) {
@mkdir($directory, 0777, true);
}
}
if (is_int($type)) {
$quality = $type;
$type = 'jpeg';
}
if ($type == 'guess') {
$type = $this->guessType();
}
if (!isset(self::$types[$type])) {
throw new \InvalidArgumentException('Given type ('.$type.') is not valid');
}
$type = self::$types[$type];
try {
$this->init();
$this->applyOperations();
$success = false;
if (null == $file) {
ob_start();
}
if ($type == 'jpeg') {
$success = $this->getAdapter()->saveJpeg($file, $quality);
}
if ($type == 'gif') {
$success = $this->getAdapter()->saveGif($file);
}
if ($type == 'png') {
$success = $this->getAdapter()->savePng($file);
}
if (!$success) {
return false;
}
return (null === $file ? ob_get_clean() : $file);
} catch (\Exception $e) {
if ($this->useFallbackImage) {
return (null === $file ? file_get_contents($this->fallback) : $this->getCacheFallback());
} else {
throw $e;
}
}
}
/**
* Get the contents of the image
*/
public function get($type = 'guess', $quality = 80)
{
return $this->save(null, $type, $quality);
}
/* Image API */
/**
* Image width
*/
public function width()
{
return $this->getAdapter()->width();
}
/**
* Image height
*/
public function height()
{
return $this->getAdapter()->height();
}
/**
* Tostring defaults to jpeg
*/
public function __toString()
{
return $this->guess();
}
/**
* Returning basic html code for this image
*/
public function html($title = '', $type = 'jpg', $quality = 80)
{
return '<img title="' . $title . '" src="' . $this->cacheFile($type, $quality) . '" />';
}
/**
* Returns the Base64 inlinable representation
*/
public function inline($type = 'jpg', $quality = 80)
{
$mime = $type;
if ($mime == 'jpg') {
$mime = 'jpeg';
}
return 'data:image/'.$mime.';base64,'.base64_encode(file_get_contents($this->cacheFile($type, $quality, true)));
}
/**
* Creates an instance, usefull for one-line chaining
*/
public static function open($file = '')
{
return new static($file);
}
/**
* Creates an instance of a new resource
*/
public static function create($width, $height)
{
return new static(null, $width, $height);
}
/**
* Creates an instance of image from its data
*/
public static function fromData($data)
{
$image = new static();
$image->setData($data);
return $image;
}
/**
* Creates an instance of image from resource
*/
public static function fromResource($resource)
{
$image = new static();
$image->setResource($resource);
return $image;
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace Gregwar\Image;
/**
* Color manipulation class
*/
class ImageColor
{
private static $colors = array(
'black' => 0x000000,
'silver' => 0xc0c0c0,
'gray' => 0x808080,
'teal' => 0x008080,
'aqua' => 0x00ffff,
'blue' => 0x0000ff,
'navy' => 0x000080,
'green' => 0x008000,
'lime' => 0x00ff00,
'white' => 0xffffff,
'fuschia' => 0xff00ff,
'purple' => 0x800080,
'olive' => 0x808000,
'yellow' => 0xffff00,
'orange' => 0xffA500,
'red' => 0xff0000,
'maroon' => 0x800000,
'transparent' => 0x7fffffff
);
public static function gdAllocate($image, $color)
{
$colorRGBA = self::parse($color);
$b = ($colorRGBA)&0xff;
$colorRGBA >>= 8;
$g = ($colorRGBA)&0xff;
$colorRGBA >>= 8;
$r = ($colorRGBA)&0xff;
$colorRGBA >>= 8;
$a = ($colorRGBA)&0xff;
$c = imagecolorallocatealpha($image, $r, $g, $b, $a);
if ($color == 'transparent') {
imagecolortransparent($image, $c);
}
return $c;
}
public static function parse($color)
{
// Direct color representation (ex: 0xff0000)
if (!is_string($color) && is_numeric($color))
return $color;
// Color name (ex: "red")
if (isset(self::$colors[$color]))
return self::$colors[$color];
if (is_string($color)) {
$color_string = str_replace(' ', '', $color);
// Color string (ex: "ff0000", "#ff0000" or "0xfff")
if (preg_match('/^(#|0x|)([0-9a-f]{3,6})/i', $color_string, $matches)) {
$col = $matches[2];
if (strlen($col) == 6)
return hexdec($col);
if (strlen($col) == 3) {
$r = '';
for ($i=0; $i<3; $i++)
$r.= $col[$i].$col[$i];
return hexdec($r);
}
}
// Colors like "rgb(255, 0, 0)"
if (preg_match('/^rgb\(([0-9]+),([0-9]+),([0-9]+)\)/i', $color_string, $matches)) {
$r = $matches[1];
$g = $matches[2];
$b = $matches[3];
if ($r>=0 && $r<=0xff && $g>=0 && $g<=0xff && $b>=0 && $b<=0xff) {
return ($r << 16) | ($g << 8) | ($b);
}
}
}
throw new \InvalidArgumentException('Invalid color: '.$color);
}
}

View File

@ -0,0 +1,19 @@
Copyright (c) <2012-2013> Grégoire Passault
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,275 @@
# Gregwar's Image class
[![Build status](https://travis-ci.org/Gregwar/Image.svg?branch=master)](https://travis-ci.org/Gregwar/Image)
The `Gregwar\Image` class purpose is to provide a simple object-oriented images handling and caching API.
# Installation
With composer :
``` json
{
...
"require": {
"gregwar/image": "2.*"
}
}
```
# Usage
## Basic handling
Using methods chaining, you can open, transform and save a file in a single line:
```php
<?php use Gregwar\Image\Image;
Image::open('in.png')
->resize(100, 100)
->negate()
->save('out.jpg');
```
Here are the resize methods:
* `resize($width, $height, $background)`: resizes the image, will preserve scale and never
enlarge it (background is `red` in order to understand what happens):
![resize()](doc/resize.jpg)
* `scaleResize($width, $height, $background)`: resizes the image, will preserve scale, can enlarge
it (background is `red` in order to understand what happens):
![scaleResize()](doc/scaleResize.jpg)
* `forceResize($width, $height, $background)`: resizes the image forcing it to
be exactly `$width` by `$height`
![forceResize()](doc/forceResize.jpg)
* `cropResize($width, $height, $background)`: resizes the image preserving scale (just like `resize()`)
and croping the whitespaces:
![cropResize()](doc/cropResize.jpg)
* `zoomCrop($width, $height, $background, $xPos, $yPos)`: resize and crop the image to fit to given dimensions:
![zoomCrop()](doc/zoomCrop.jpg)
* In `zoomCrop()`, You can change the position of the resized image using the `$xPos` (center, left or right) and `$yPos` (center,
top or bottom):
![zoomCrop() with yPos=top](doc/zoomCropTop.jpg)
The other methods available are:
* `crop($x, $y, $w, $h)`: crops the image to a box located on coordinates $x,y and
which size is $w by $h
* `negate()`: negates the image colors
* `brighness($b)`: applies a brightness effect to the image (from -255 to +255)
* `contrast($c)`: applies a contrast effect to the image (from -100 to +100)
* `grayscale()`: converts the image to grayscale
* `emboss()`: emboss the image
* `smooth($p)`: smooth the image
* `sharp()`: applies a mean removal filter on the image
* `edge()`: applies an edge effect on the image
* `colorize($red, $green, $blue)`: colorize the image (from -255 to +255 for each color)
* `sepia()`: applies a sepia effect
* `merge($image, $x, $y, $width, $height)`: merges two images
* `fill($color, $x, $y)`: fills the image with the given color
* `write($font, $text, $x, $y, $size, $angle, $color, $position)`: writes text over image, $position can be any of 'left', 'right', or 'center'
* `rectangle($x1, $y1, $x2, $y2, $color, $filled=false)`: draws a rectangle
* `rotate($angle, $background = 0xffffff)` : rotate the image to given angle
* `roundedRectangle($x1, $y1, $x2, $y2, $radius, $color, $filled=false)`: draws a rounded rectangle ($radius can be anything from 0)
* `line($x1, $y1, $x2, $y2, $color)`: draws a line
* `ellipse($cx, $cy, $width, $height, $color, $filled=false)`: draws an ellipse
* `circle($cx, $cy, $r, $color, $filled=false)`: draws a circle
* `fillBackground($bg=0xffffff)`: fills the background of a transparent image to the 'bg' color
* `fixOrientation()`: return the image rotated and flipped using image exif information
* `html($title = '', $type = 'jpg')`: return the `<img ... />` tag with the cache image
* `flip($flipVertical, $flipHorizontal)`: flips the image in the given directions. Both params are boolean and at least one must be true.
* `inline($type = 'jpg')`: returns the HTML inlinable base64 string (see `demo/inline.php`)
You can also create image from scratch using:
```php
<?php
Image::create(200, 100);
```
Where 200 is the width and 100 the height
## Saving the image
You can save the image to an explicit file using `save($file, $type = 'jpg', $quality = 80)`:
```php
<?php
// ...
$image->save('output.jpg', 'jpg', 85);
```
You can also get the contents of the image using `get($type = 'jpg', $quality = 80)`, which will return the binary contents of the image
## Using cache
Each operation above is not actually applied on the opened image, but added in an operations
array. This operation array, the name, type and modification time of file are hashed using
`sha1()` and the hash is used to look up for a cache file.
Once the cache directory configured, you can call the following methods:
* `jpeg($quality = 80)`: lookup or create a jpeg cache file on-the-fly
* `gif()`: lookup or create a gif cache file on-the-fly
* `png()`: lookup or create a png cache file on-the-fly
* `guess($quality = 80)`: guesses the type (use the same as input) and lookup or create a
cache file on-the-fly
* `setPrettyName($prettyName, $prefix = true)`: sets a "pretty" name suffix for the file, if you want it to be more SEO-friendly.
for instance, if you call it "Fancy Image", the cache will look like something/something-fancy-image.jpg.
If `$prefix` is passed to `false` (default `true`), the pretty name won't have any hash prefix.
If you want to use non-latin1 pretty names, **behat/transliterator** package must be installed.
For instance:
```php
<?php use Gregwar\Image\Image;
echo Image::open('test.png')
->sepia()
->jpeg();
//Outputs: cache/images/1/8/6/9/c/86e4532dbd9c073075ef08e9751fc9bc0f4.jpg
```
If the original file and operations do not change, the hashed value will be the same and the
cache will not be generated again.
You can use this directly in an HTML document:
```php
<?php use Gregwar\Image\Image;
// ...
<img src="<?php echo Image::open('image.jpg')->resize(150, 150)->jpeg(); ?>" />
// ...
```
This is powerful since if you change the original image or any of your code the cached hash
will change and the file will be regenerated.
Writing image
-------------
You can also create your own image on-the-fly using drawing functions:
```php
<?php
$img_src = Image::create(300, 300)
->fill(0xffaaaa) // Filling with a light red
->rectangle(0xff3333, 0, 100, 300, 200, true) // Drawing a red rectangle
// Writing "Hello $username !" on the picture using a custom TTF font file
->write('./fonts/CaviarDreams.ttf', 'Hello '.$username.'!', 150, 150, 20, 0, 'white', 'center')
->jpeg();
?>
<img src="<?= $img_src ?>" />
```
## Using fallback image
If the image file doesn't exists, you can configurate a fallback image that will be used
by the class (note that this require the cache directory to be available).
A default "error" image which is used is in `images/error.jpg`, you can change it with:
```php
<?php
$img->setFallback('/path/to/my/fallback.jpg');
```
## Garbage Collect
To prevent the cache from growing forever, you can use the provided GarbageCollect class as below:
```php
<?php use Gregwar\Image\GarbageCollect;
// This could be a cron called each day @3:00AM for instance
// Removes all the files from ../cache that are more than 30 days
// old. A verbose output will explain which files are deleted
GarbageCollect::dropOldFiles(__DIR__.'/../cache', 30, true);
```
# Development
`Gregwar\Image` is using PHP metaprogramming paradigms that makes it easy to enhance.
Each function that handles the image is implemented in an *Adapter*, this is where
all the specific actions take place.
The `Common` adapter is design to contain common abstract actions, while the
specific adatpers (like `GD`) are designed to contain actions specific to the low
level layer.
You can add your own methods by adding it in the corresponding adapter.
```php
<?php
// In the adapter
private function myFilter()
{
$this->negate();
$this->sepia();
}
```
Which could be used on the Image
```php
<?php
$image->myFilter();
```
You can also write your own adapter which could extend one of this repository and use it by calling `setAdapter()`:
```php
<?php
$image->setAdapter(new MyCustomAdapter);
```
# License
`Gregwar\Image` is under MIT License, please read the LICENSE file for further details.
Do not hesitate to fork this repository and customize it !

View File

@ -0,0 +1,38 @@
<?php
namespace Gregwar\Image\Source;
/**
* Creates a new image from scratch
*/
class Create extends Source
{
protected $width;
protected $height;
public function __construct($width, $height)
{
$this->width = $width;
$this->height = $height;
}
public function getWidth()
{
return $this->width;
}
public function getHeight()
{
return $this->height;
}
public function getInfos()
{
return array($this->width, $this->height);
}
public function correct()
{
return $this->width > 0 && $this->height > 0;
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace Gregwar\Image\Source;
/**
* Having image in some string
*/
class Data extends Source
{
protected $data;
public function __construct($data)
{
$this->data = $data;
}
public function getData()
{
return $this->data;
}
public function getInfos()
{
return sha1($this->data);
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace Gregwar\Image\Source;
use Gregwar\Image\Image;
/**
* Open an image from a file
*/
class File extends Source
{
protected $file;
public function __construct($file)
{
$this->file = $file;
}
public function getFile()
{
return $this->file;
}
public function correct()
{
return false !== @exif_imagetype($this->file);
}
public function guessType()
{
if (function_exists('exif_imagetype')) {
$type = @exif_imagetype($this->file);
if (false !== $type) {
if ($type == IMAGETYPE_JPEG) {
return 'jpeg';
}
if ($type == IMAGETYPE_GIF) {
return 'gif';
}
if ($type == IMAGETYPE_PNG) {
return 'png';
}
}
}
$parts = explode('.', $this->file);
$ext = strtolower($parts[count($parts)-1]);
if (isset(Image::$types[$ext])) {
return Image::$types[$ext];
}
return 'jpeg';
}
public function getInfos()
{
return $this->file;
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Gregwar\Image\Source;
/**
* Have the image directly in a specific resource
*/
class Resource extends Source
{
protected $resource;
public function __construct($resource)
{
$this->resource = $resource;
}
public function getResource()
{
return $this->resource;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Gregwar\Image\Source;
/**
* An Image source
*/
class Source
{
/**
* Guess the type of the image
*/
public function guessType()
{
return 'jpeg';
}
/**
* Is this image correct ?
*/
public function correct()
{
return true;
}
/**
* Returns information about images, these informations should
* change only if the original image changed
*/
public function getInfos()
{
return null;
}
}

View File

@ -0,0 +1,21 @@
<?php
$vendors = __DIR__.'/vendor/autoload.php';
if (file_exists($vendors)) {
require($vendors);
}
/**
* Registers an autoload for all the classes in Gregwar\Image
*/
spl_autoload_register(function ($className) {
$namespace = 'Gregwar\\Image';
if (strpos($className, $namespace) === 0) {
$className = str_replace($namespace, '', $className);
$fileName = __DIR__ . '/' . str_replace('\\', '/', $className) . '.php';
if (file_exists($fileName)) {
require($fileName);
}
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB