1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259:
<?php
namespace Coast;
use Coast\Request;
use Coast\Response;
use Coast\File;
use Coast\Url;
use Intervention\Image\Image as InterventionImage;
use Intervention\Image\ImageManager;
class Image implements \Coast\App\Access, \Coast\App\Executable
{
use \Coast\App\Access\Implementation;
use \Coast\App\Executable\Implementation;
protected $_manager;
protected $_prefix = null;
protected $_driver;
protected $_baseDir;
protected $_outputDir;
protected $_resolver;
protected $_outputResolver;
protected $_quality = 80;
protected $_actions = [];
public function __construct(array $options = array())
{
$this->_driver = extension_loaded('imagick')
? 'imagick'
: 'gd';
foreach ($options as $name => $value) {
if ($name[0] == '_') {
throw new \Coast\Exception("Access to '{$name}' is prohibited");
}
$this->$name($value);
}
$this->_manager = new ImageManager([
'driver' => $this->_driver,
]);
}
public function prefix($prefix = null)
{
if (func_num_args() > 0) {
$this->_prefix = $prefix;
return $this;
}
return $this->_prefix;
}
public function driver($driver = null)
{
if (isset($driver)) {
$this->_driver = $driver;
return $this;
}
return $this->_driver;
}
public function baseDir(\Coast\Dir $baseDir = null)
{
if (isset($baseDir)) {
$this->_baseDir = $baseDir;
return $this;
}
return $this->_baseDir;
}
public function outputDir(\Coast\Dir $outputDir = null)
{
if (isset($outputDir)) {
$this->_outputDir = $outputDir;
return $this;
}
return $this->_outputDir;
}
public function resolver(\Coast\Resolver $resolver = null)
{
if (isset($resolver)) {
$this->_resolver = $resolver;
return $this;
}
return $this->_resolver;
}
public function outputResolver(\Coast\Resolver $outputResolver = null)
{
if (isset($outputResolver)) {
$this->_outputResolver = $outputResolver;
return $this;
}
return $this->_outputResolver;
}
public function quality($quality = null)
{
if (isset($quality)) {
$this->_quality = $quality;
return $this;
}
return $this->_quality;
}
public function action($name, $value = null)
{
if (isset($value)) {
$this->_actions[$name] = $value->bindTo($this);
return $this;
}
return isset($this->_actions[$name])
? $this->_actions[$name]
: null;
}
public function actions(array $actions = null)
{
if (isset($actions)) {
foreach ($actions as $name => $value) {
$this->action($name, $value);
}
return $this;
}
return $this->_actions;
}
public function url($file, $actions = array('default'), array $params = array())
{
if (\Coast\is_array_assoc($actions)) {
$params = $actions;
$actions = ['default'];
}
$file = !$file instanceof File
? new File("{$file}")
: $file;
$file = $file->isRelative()
? new File("{$this->_baseDir}/{$file}")
: $file;
$file = $file->toReal();
if (!$file->isWithin($this->_baseDir)) {
throw new Image\Exception("File '{$file}' is not within base directory '{$this->_baseDir}'");
} else if (!$file->isReadable() || !in_array(strtolower($file->extName()), ['jpg', 'jpeg', 'png', 'gif'])) {
return isset($this->_outputResolver)
? $this->_outputResolver->file($file)
: $this->_resolver->file($file);
}
if (!is_array($actions)) {
$actions = array_map('trim', explode(',', $actions));
}
foreach ($params as $name => $value) {
$params[(string) $name] = (string) $value;
}
$output = $this->_generateOutput($file, $actions, $params);
return $output->exists()
? (isset($this->_outputResolver)
? $this->_outputResolver->file($output)
: $this->_resolver->file($output))
: $this->_resolver->string($this->_prefix)->queryParams([
'file' => $file->toRelative($this->_baseDir)->name(),
'actions' => $actions,
'params' => $params,
]);
}
public function process($file, $actions = array('default'), array $params = array())
{
if (\Coast\is_array_assoc($actions)) {
$params = $actions;
$actions = ['default'];
}
$file = !$file instanceof File
? new File("{$file}")
: $file;
$file = $file->isRelative()
? new File("{$this->_baseDir}/{$file}")
: $file;
$file = $file->toReal();
if (!$file->isWithin($this->_baseDir)) {
throw new Image\Exception("File '{$file}' is not within base directory '{$this->_baseDir}'");
} else if (!$file->isReadable() || !in_array(strtolower($file->extName()), ['jpg', 'jpeg', 'png', 'gif'])) {
throw new Image\Exception("File '{$file}' is not readable or not in a supported format");
}
if (!is_array($actions)) {
$actions = array_map('trim', explode(',', $actions));
}
$output = $this->_generateOutput($file, $actions, $params);
$image = $this->_manager->make($file->name());
foreach ($actions as $name) {
$this->run($name, $image, $params);
}
$image->save($output->name(), isset($image->quality) ? $image->quality : $this->_quality);
return $output;
}
public function run($name, InterventionImage $image, array $params = array())
{
if (!isset($this->_actions[$name])) {
throw new Image\Exception("Transform '{$name}' is not defined");
}
$this->_actions[$name]($image, $params);
}
public function execute(Request $req, Response $res)
{
$parts = explode('/', $req->path());
if ($parts[0] != $this->_prefix) {
return;
}
$output = $this->process($req->file, $req->actions, isset($req->params) ? $req->params : []);
return isset($this->_outputResolver)
? $res->redirect($this->_outputResolver->file($output))
: $res->redirect($this->_resolver->file($output));
}
public function __invoke($file, $actions = array('default'), array $params = array())
{
return $this->url($file, $actions, $params);
}
protected function _generateOutput(File $file, $actions, array $params)
{
sort($actions);
ksort($params);
$id = md5(
$file->name() .
$file->modifyTime()->getTimestamp() .
serialize($actions) .
serialize($params)
);
return $this->_outputDir
->dir("{$id[0]}/{$id[1]}", true)
->file("{$id}.{$file->extName()}");
}
}