├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── composer.lock ├── lib └── Thumbor │ ├── Url.php │ └── Url │ ├── Builder.php │ ├── BuilderFactory.php │ └── CommandSet.php ├── phpunit.xml.dist └── tests └── Thumbor ├── Url ├── BuilderFactoryTest.php ├── BuilderTest.php └── CommandSetTest.php └── UrlTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.6 4 | - 5.5 5 | - 5.4 6 | - 5.3 7 | - hhvm 8 | before_script: composer install --dev 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2013 99designs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phumbor 2 | 3 | A minimal PHP client for generating [Thumbor][thumbor] URLs. 4 | 5 | [![Build Status](https://travis-ci.org/99designs/phumbor.png)](https://travis-ci.org/99designs/phumbor) 6 | 7 | 8 | ## Usage 9 | 10 | You construct a `Thumbor\Url` using a `Thumbor\Url\Builder`: 11 | 12 | ```php 13 | $server = 'http://thumbor.example.com:1234'; 14 | $secret = 'my-secret-key'; 15 | 16 | echo Thumbor\Url\Builder::construct($server, $secret, 'http://images.example.com/llamas.jpg') 17 | ->fitIn(640, 480) 18 | ->addFilter('fill', 'green'); 19 | 20 | // => http://thumbor.example.com:1234/OFDRoURwi9WVbZNfeOJVfIKr1Js=/fit-in/640x480/filters:fill(green)/http://images/example.com/llamas.jpg 21 | ``` 22 | 23 | To reuse your server/secret combination, create a `Thumbor\Url\BuilderFactory`: 24 | 25 | ```php 26 | $thumbnailUrlFactory = Thumbor\Url\BuilderFactory::construct($server, $secret); 27 | 28 | echo $thumbnailUrlFactory 29 | ->url('http://images.example.com/llamas.jpg') 30 | ->fitIn(640, 480) 31 | ->addFilter('fill', 'green'); 32 | 33 | echo $thumbnailUrlFactory 34 | ->url('http://images.example.com/butts.png') 35 | ->crop(20, 20, 300, 300) 36 | ->valign('middle'); 37 | 38 | // etc 39 | ``` 40 | 41 | 42 | ## Installation 43 | 44 | Add `99designs/phumbor` as a dependency in [`composer.json`][composer]. 45 | 46 | A [Laravel 4 package][laravel-phumbor] and a [Symfony2 Bundle][symfony-bundle] are also available. 47 | 48 | 49 | ## License 50 | 51 | MIT; see [`LICENSE`][license] 52 | 53 | 54 | [thumbor]: https://github.com/globocom/thumbor 55 | [license]: https://github.com/99designs/phumbor/blob/master/LICENSE 56 | [composer]: https://getcomposer.org/ 57 | [laravel-phumbor]: https://github.com/ceejayoz/laravel-phumbor 58 | [symfony-bundle]: https://github.com/jbouzekri/PhumborBundle 59 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "99designs/phumbor", 3 | "description": "A minimal Thumbor library for PHP", 4 | "homepage": "https://github.com/99designs/phumbor", 5 | "keywords": [ 6 | "php", "thumbor", "thumbnails", "99designs" 7 | ], 8 | "license": "MIT", 9 | "authors": [ 10 | { 11 | "name": "Stuart Campbell", 12 | "email": "stuart.campbell@99designs.com" 13 | } 14 | ], 15 | "require": { 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "~3.7.0" 19 | }, 20 | "autoload": { 21 | "psr-0": {"Thumbor": "lib"} 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" 5 | ], 6 | "hash": "87593850a79b42ddfa31bf7ab785f9d5", 7 | "packages": [ 8 | 9 | ], 10 | "packages-dev": [ 11 | { 12 | "name": "phpunit/php-code-coverage", 13 | "version": "1.2.10", 14 | "source": { 15 | "type": "git", 16 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 17 | "reference": "1.2.10" 18 | }, 19 | "dist": { 20 | "type": "zip", 21 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.10", 22 | "reference": "1.2.10", 23 | "shasum": "" 24 | }, 25 | "require": { 26 | "php": ">=5.3.3", 27 | "phpunit/php-file-iterator": ">=1.3.0@stable", 28 | "phpunit/php-text-template": ">=1.1.1@stable", 29 | "phpunit/php-token-stream": ">=1.1.3@stable" 30 | }, 31 | "suggest": { 32 | "ext-dom": "*", 33 | "ext-xdebug": ">=2.0.5" 34 | }, 35 | "type": "library", 36 | "autoload": { 37 | "classmap": [ 38 | "PHP/" 39 | ] 40 | }, 41 | "notification-url": "https://packagist.org/downloads/", 42 | "include-path": [ 43 | "" 44 | ], 45 | "license": [ 46 | "BSD-3-Clause" 47 | ], 48 | "authors": [ 49 | { 50 | "name": "Sebastian Bergmann", 51 | "email": "sb@sebastian-bergmann.de", 52 | "role": "lead" 53 | } 54 | ], 55 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 56 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 57 | "keywords": [ 58 | "coverage", 59 | "testing", 60 | "xunit" 61 | ], 62 | "time": "2013-05-13 05:49:10" 63 | }, 64 | { 65 | "name": "phpunit/php-file-iterator", 66 | "version": "1.3.3", 67 | "source": { 68 | "type": "git", 69 | "url": "git://github.com/sebastianbergmann/php-file-iterator.git", 70 | "reference": "1.3.3" 71 | }, 72 | "dist": { 73 | "type": "zip", 74 | "url": "https://github.com/sebastianbergmann/php-file-iterator/zipball/1.3.3", 75 | "reference": "1.3.3", 76 | "shasum": "" 77 | }, 78 | "require": { 79 | "php": ">=5.3.3" 80 | }, 81 | "type": "library", 82 | "autoload": { 83 | "classmap": [ 84 | "File/" 85 | ] 86 | }, 87 | "notification-url": "https://packagist.org/downloads/", 88 | "include-path": [ 89 | "" 90 | ], 91 | "license": [ 92 | "BSD-3-Clause" 93 | ], 94 | "authors": [ 95 | { 96 | "name": "Sebastian Bergmann", 97 | "email": "sb@sebastian-bergmann.de", 98 | "role": "lead" 99 | } 100 | ], 101 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 102 | "homepage": "http://www.phpunit.de/", 103 | "keywords": [ 104 | "filesystem", 105 | "iterator" 106 | ], 107 | "time": "2012-10-11 04:44:38" 108 | }, 109 | { 110 | "name": "phpunit/php-text-template", 111 | "version": "1.1.4", 112 | "source": { 113 | "type": "git", 114 | "url": "git://github.com/sebastianbergmann/php-text-template.git", 115 | "reference": "1.1.4" 116 | }, 117 | "dist": { 118 | "type": "zip", 119 | "url": "https://github.com/sebastianbergmann/php-text-template/zipball/1.1.4", 120 | "reference": "1.1.4", 121 | "shasum": "" 122 | }, 123 | "require": { 124 | "php": ">=5.3.3" 125 | }, 126 | "type": "library", 127 | "autoload": { 128 | "classmap": [ 129 | "Text/" 130 | ] 131 | }, 132 | "notification-url": "https://packagist.org/downloads/", 133 | "include-path": [ 134 | "" 135 | ], 136 | "license": [ 137 | "BSD-3-Clause" 138 | ], 139 | "authors": [ 140 | { 141 | "name": "Sebastian Bergmann", 142 | "email": "sb@sebastian-bergmann.de", 143 | "role": "lead" 144 | } 145 | ], 146 | "description": "Simple template engine.", 147 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 148 | "keywords": [ 149 | "template" 150 | ], 151 | "time": "2012-10-31 11:15:28" 152 | }, 153 | { 154 | "name": "phpunit/php-timer", 155 | "version": "1.0.4", 156 | "source": { 157 | "type": "git", 158 | "url": "git://github.com/sebastianbergmann/php-timer.git", 159 | "reference": "1.0.4" 160 | }, 161 | "dist": { 162 | "type": "zip", 163 | "url": "https://github.com/sebastianbergmann/php-timer/zipball/1.0.4", 164 | "reference": "1.0.4", 165 | "shasum": "" 166 | }, 167 | "require": { 168 | "php": ">=5.3.3" 169 | }, 170 | "type": "library", 171 | "autoload": { 172 | "classmap": [ 173 | "PHP/" 174 | ] 175 | }, 176 | "notification-url": "https://packagist.org/downloads/", 177 | "include-path": [ 178 | "" 179 | ], 180 | "license": [ 181 | "BSD-3-Clause" 182 | ], 183 | "authors": [ 184 | { 185 | "name": "Sebastian Bergmann", 186 | "email": "sb@sebastian-bergmann.de", 187 | "role": "lead" 188 | } 189 | ], 190 | "description": "Utility class for timing", 191 | "homepage": "http://www.phpunit.de/", 192 | "keywords": [ 193 | "timer" 194 | ], 195 | "time": "2012-10-11 04:45:58" 196 | }, 197 | { 198 | "name": "phpunit/php-token-stream", 199 | "version": "1.1.5", 200 | "source": { 201 | "type": "git", 202 | "url": "git://github.com/sebastianbergmann/php-token-stream.git", 203 | "reference": "1.1.5" 204 | }, 205 | "dist": { 206 | "type": "zip", 207 | "url": "https://github.com/sebastianbergmann/php-token-stream/zipball/1.1.5", 208 | "reference": "1.1.5", 209 | "shasum": "" 210 | }, 211 | "require": { 212 | "ext-tokenizer": "*", 213 | "php": ">=5.3.3" 214 | }, 215 | "type": "library", 216 | "autoload": { 217 | "classmap": [ 218 | "PHP/" 219 | ] 220 | }, 221 | "notification-url": "https://packagist.org/downloads/", 222 | "include-path": [ 223 | "" 224 | ], 225 | "license": [ 226 | "BSD-3-Clause" 227 | ], 228 | "authors": [ 229 | { 230 | "name": "Sebastian Bergmann", 231 | "email": "sb@sebastian-bergmann.de", 232 | "role": "lead" 233 | } 234 | ], 235 | "description": "Wrapper around PHP's tokenizer extension.", 236 | "homepage": "http://www.phpunit.de/", 237 | "keywords": [ 238 | "tokenizer" 239 | ], 240 | "time": "2012-10-11 04:47:14" 241 | }, 242 | { 243 | "name": "phpunit/phpunit", 244 | "version": "3.7.20", 245 | "source": { 246 | "type": "git", 247 | "url": "https://github.com/sebastianbergmann/phpunit.git", 248 | "reference": "3.7.20" 249 | }, 250 | "dist": { 251 | "type": "zip", 252 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.20", 253 | "reference": "3.7.20", 254 | "shasum": "" 255 | }, 256 | "require": { 257 | "ext-dom": "*", 258 | "ext-pcre": "*", 259 | "ext-reflection": "*", 260 | "ext-spl": "*", 261 | "php": ">=5.3.3", 262 | "phpunit/php-code-coverage": ">=1.2.1,<1.3.0", 263 | "phpunit/php-file-iterator": ">=1.3.1", 264 | "phpunit/php-text-template": ">=1.1.1", 265 | "phpunit/php-timer": ">=1.0.2,<1.1.0", 266 | "phpunit/phpunit-mock-objects": ">=1.2.0,<1.3.0", 267 | "symfony/yaml": ">=2.0,<3.0" 268 | }, 269 | "require-dev": { 270 | "pear-pear/pear": "1.9.4" 271 | }, 272 | "suggest": { 273 | "ext-json": "*", 274 | "ext-simplexml": "*", 275 | "ext-tokenizer": "*", 276 | "phpunit/php-invoker": ">=1.1.0,<1.2.0" 277 | }, 278 | "bin": [ 279 | "composer/bin/phpunit" 280 | ], 281 | "type": "library", 282 | "extra": { 283 | "branch-alias": { 284 | "dev-master": "3.7.x-dev" 285 | } 286 | }, 287 | "autoload": { 288 | "classmap": [ 289 | "PHPUnit/" 290 | ] 291 | }, 292 | "notification-url": "https://packagist.org/downloads/", 293 | "include-path": [ 294 | "", 295 | "../../symfony/yaml/" 296 | ], 297 | "license": [ 298 | "BSD-3-Clause" 299 | ], 300 | "authors": [ 301 | { 302 | "name": "Sebastian Bergmann", 303 | "email": "sebastian@phpunit.de", 304 | "role": "lead" 305 | } 306 | ], 307 | "description": "The PHP Unit Testing framework.", 308 | "homepage": "http://www.phpunit.de/", 309 | "keywords": [ 310 | "phpunit", 311 | "testing", 312 | "xunit" 313 | ], 314 | "time": "2013-05-13 06:06:41" 315 | }, 316 | { 317 | "name": "phpunit/phpunit-mock-objects", 318 | "version": "1.2.3", 319 | "source": { 320 | "type": "git", 321 | "url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git", 322 | "reference": "1.2.3" 323 | }, 324 | "dist": { 325 | "type": "zip", 326 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects/archive/1.2.3.zip", 327 | "reference": "1.2.3", 328 | "shasum": "" 329 | }, 330 | "require": { 331 | "php": ">=5.3.3", 332 | "phpunit/php-text-template": ">=1.1.1@stable" 333 | }, 334 | "suggest": { 335 | "ext-soap": "*" 336 | }, 337 | "type": "library", 338 | "autoload": { 339 | "classmap": [ 340 | "PHPUnit/" 341 | ] 342 | }, 343 | "notification-url": "https://packagist.org/downloads/", 344 | "include-path": [ 345 | "" 346 | ], 347 | "license": [ 348 | "BSD-3-Clause" 349 | ], 350 | "authors": [ 351 | { 352 | "name": "Sebastian Bergmann", 353 | "email": "sb@sebastian-bergmann.de", 354 | "role": "lead" 355 | } 356 | ], 357 | "description": "Mock Object library for PHPUnit", 358 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 359 | "keywords": [ 360 | "mock", 361 | "xunit" 362 | ], 363 | "time": "2013-01-13 10:24:48" 364 | }, 365 | { 366 | "name": "symfony/yaml", 367 | "version": "v2.2.1", 368 | "target-dir": "Symfony/Component/Yaml", 369 | "source": { 370 | "type": "git", 371 | "url": "https://github.com/symfony/Yaml.git", 372 | "reference": "v2.2.1" 373 | }, 374 | "dist": { 375 | "type": "zip", 376 | "url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.2.1", 377 | "reference": "v2.2.1", 378 | "shasum": "" 379 | }, 380 | "require": { 381 | "php": ">=5.3.3" 382 | }, 383 | "type": "library", 384 | "extra": { 385 | "branch-alias": { 386 | "dev-master": "2.2-dev" 387 | } 388 | }, 389 | "autoload": { 390 | "psr-0": { 391 | "Symfony\\Component\\Yaml\\": "" 392 | } 393 | }, 394 | "notification-url": "https://packagist.org/downloads/", 395 | "license": [ 396 | "MIT" 397 | ], 398 | "authors": [ 399 | { 400 | "name": "Fabien Potencier", 401 | "email": "fabien@symfony.com" 402 | }, 403 | { 404 | "name": "Symfony Community", 405 | "homepage": "http://symfony.com/contributors" 406 | } 407 | ], 408 | "description": "Symfony Yaml Component", 409 | "homepage": "http://symfony.com", 410 | "time": "2013-03-23 07:49:54" 411 | } 412 | ], 413 | "aliases": [ 414 | 415 | ], 416 | "minimum-stability": "stable", 417 | "stability-flags": [ 418 | 419 | ], 420 | "platform": [ 421 | 422 | ], 423 | "platform-dev": [ 424 | 425 | ] 426 | } 427 | -------------------------------------------------------------------------------- /lib/Thumbor/Url.php: -------------------------------------------------------------------------------- 1 | server = $server; 22 | $this->secret = $secret; 23 | $this->original = $original; 24 | $this->commands = $commands; 25 | } 26 | 27 | /** 28 | * Produce a URL to an image on a Thumbor server according to the specified 29 | * options. 30 | * 31 | * See https://github.com/globocom/thumbor/wiki/Usage for available $commands. 32 | * 33 | * @param string $server Thumbor server 34 | * @param string $secret shared secret key (may be blank/null) 35 | * @param string $original URL of original image 36 | * @param array $commands array of Thumbor commands 37 | */ 38 | public function stringify($server, $secret, $original, $commands) 39 | { 40 | if (count($commands) > 0) { 41 | $commandPath = implode('/', $commands); 42 | $imgPath = sprintf('%s/%s', $commandPath, $original); 43 | } else { 44 | $imgPath = $original; 45 | } 46 | 47 | $signature = $secret ? self::sign($imgPath, $secret) : 'unsafe'; 48 | 49 | return sprintf( 50 | '%s/%s/%s', 51 | $server, 52 | $signature, 53 | $imgPath 54 | ); 55 | } 56 | 57 | /** 58 | * Sign a message using a shared secret key, per 59 | * https://github.com/globocom/thumbor/wiki/Libraries 60 | * 61 | * @param string $msg 62 | * @param string $secret 63 | * @return string 64 | */ 65 | public static function sign($msg, $secret) 66 | { 67 | $signature = hash_hmac("sha1", $msg, $secret, true); 68 | return strtr( 69 | base64_encode($signature), 70 | '/+', '_-' 71 | ); 72 | } 73 | 74 | public function __toString() 75 | { 76 | return $this->stringify( 77 | $this->server, 78 | $this->secret, 79 | $this->original, 80 | $this->commands 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/Thumbor/Url/Builder.php: -------------------------------------------------------------------------------- 1 | fitIn(320, 240) 19 | * // Add filters 20 | * ->addFilter('brightness', 42) 21 | * // Construct and return Url 22 | * ->build(); 23 | * 24 | * If you coerce an instance of this class to String, you get the string 25 | * representation of the URL. 26 | * 27 | * See https://github.com/globocom/thumbor/wiki/Usage for all available options. 28 | * @method Builder trim($colourSource = null) 29 | * @method Builder crop($topLeftX, $topLeftY, $bottomRightX, $bottomRightY) 30 | * @method Builder fitIn($width, $height) 31 | * @method Builder resize($width, $height) 32 | * @method Builder halign($halign) 33 | * @method Builder valign($valign) 34 | * @method Builder smartCrop($smartCrop) 35 | * @method Builder addFilter($filter, $args, $_ = null) 36 | * @method Builder metadataOnly($metadataOnly) 37 | */ 38 | class Builder 39 | { 40 | private $server; 41 | private $secret; 42 | private $original; 43 | private $commands; 44 | 45 | public static function construct($server, $secret, $original) 46 | { 47 | return new self($server, $secret, $original); 48 | } 49 | 50 | public function __construct($server, $secret, $original) 51 | { 52 | $this->server = $server; 53 | $this->secret = $secret; 54 | $this->original = $original; 55 | $this->commands = new CommandSet(); 56 | } 57 | 58 | public function __clone() 59 | { 60 | $this->commands = clone $this->commands; 61 | } 62 | 63 | // Proxy remaining method calls to CommandSet 64 | public function __call($method, $args) 65 | { 66 | $proxied = array($this->commands, $method); 67 | if (!is_callable($proxied)) { 68 | throw new Exception(sprintf( 69 | 'Method "%s" not found for %s', 70 | $method, 71 | get_class($this->commands) 72 | )); 73 | } 74 | call_user_func_array($proxied, $args); 75 | return $this; 76 | } 77 | 78 | public function build() 79 | { 80 | return new Url( 81 | $this->server, 82 | $this->secret, 83 | $this->original, 84 | $this->commands->toArray() 85 | ); 86 | } 87 | 88 | public function __toString() 89 | { 90 | return (string) $this->build(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/Thumbor/Url/BuilderFactory.php: -------------------------------------------------------------------------------- 1 | urlFrom('http://example.com/llamas.jpg') 18 | * ->fitIn(320, 240) 19 | * // etc 20 | * ; 21 | */ 22 | class BuilderFactory 23 | { 24 | private $server; 25 | private $secret; 26 | 27 | /** 28 | * @param string $server 29 | * @param string|null $secret 30 | * @return BuilderFactory 31 | */ 32 | public static function construct($server, $secret=null) 33 | { 34 | return new self($server, $secret); 35 | } 36 | 37 | /** 38 | * @param string $server 39 | * @param string|null $secret 40 | */ 41 | public function __construct($server, $secret=null) 42 | { 43 | $this->server = $server; 44 | $this->secret = $secret; 45 | } 46 | 47 | /** 48 | * @param string $original 49 | * @return Builder 50 | */ 51 | public function url($original) 52 | { 53 | return Builder::construct($this->server, $this->secret, $original); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/Thumbor/Url/CommandSet.php: -------------------------------------------------------------------------------- 1 | trim = 'trim'; 32 | $this->trim .= $colourSource ? ":$colourSource" : ''; 33 | $this->trim .= $tolerance ? ":$tolerance" : ''; 34 | } 35 | 36 | /** 37 | * Manually specify crop window. 38 | */ 39 | public function crop($topLeftX, $topLeftY, $bottomRightX, $bottomRightY) 40 | { 41 | $this->crop = "{$topLeftX}x{$topLeftY}:{$bottomRightX}x{$bottomRightY}"; 42 | } 43 | 44 | /** 45 | * Resize the image to fit by smallest side in a box of the specified dimensions. 46 | * Overrides any previous call to `fullFitIn`, `fitIn` or `resize`. 47 | */ 48 | public function fullFitIn($width, $height) 49 | { 50 | $this->resize = "full-fit-in/{$width}x{$height}"; 51 | } 52 | 53 | /** 54 | * Resize the image to fit in a box of the specified dimensions. Overrides 55 | * any previous call to `fullFitIn`, `fitIn` or `resize`. 56 | */ 57 | public function fitIn($width, $height) 58 | { 59 | $this->resize = "fit-in/{$width}x{$height}"; 60 | } 61 | 62 | /** 63 | * Resize the image to the specified dimensions. Overrides any previous call 64 | * to `fullFitIn`, `fitIn` or `resize`. 65 | * 66 | * Use a value of 0 for proportional resizing. E.g. for a 640 x 480 image, 67 | * `->size(320, 0)` yields a 320 x 240 thumbnail. 68 | * 69 | * Use a value of 'orig' to use an original image dimension. E.g. for a 640 70 | * x 480 image, `->resize(320, 'orig')` yields a 320 x 480 thumbnail. 71 | */ 72 | public function resize($width, $height) 73 | { 74 | $this->resize = "{$width}x{$height}"; 75 | } 76 | 77 | /** 78 | * Specify horizontal alignment used if width is altered due to cropping 79 | * @param string $halign one of {'left', 'center', 'right'} 80 | */ 81 | public function halign($halign) 82 | { 83 | $this->halign = $halign; 84 | } 85 | 86 | /** 87 | * Specify vertical alignment used if height is altered due to cropping 88 | * @param string $valign one of {'top', 'middle', 'bottom'} 89 | */ 90 | public function valign($valign) 91 | { 92 | $this->valign = $valign; 93 | } 94 | 95 | /** 96 | * Specify that smart cropping should be used (overrides halign/valign). 97 | * @param bool $smartCrop 98 | */ 99 | public function smartCrop($smartCrop) 100 | { 101 | $this->smartCrop = $smartCrop; 102 | } 103 | 104 | /** 105 | * Append a filter, e.g. `->addFilter('brightness', 42)` 106 | */ 107 | public function addFilter(/*$filter, $args ...*/) 108 | { 109 | $args = func_get_args(); 110 | $filter = array_shift($args); 111 | $this->filters []= sprintf('%s(%s)', $filter, implode(',', $args)); 112 | } 113 | 114 | /** 115 | * Specify that JSON metadata should be returned instead of the thumbnailed 116 | * image. 117 | * @param bool $metadataOnly 118 | */ 119 | public function metadataOnly($metadataOnly) 120 | { 121 | $this->metadataOnly = $metadataOnly; 122 | } 123 | 124 | /** 125 | * Stringify and return commands as an array. 126 | */ 127 | public function toArray() 128 | { 129 | $commands = array(); 130 | 131 | $maybeAppend = function ($command) use (&$commands) { 132 | if ($command) $commands []= (string) $command; 133 | }; 134 | 135 | if ($this->metadataOnly) { 136 | $commands []= 'meta'; 137 | } 138 | 139 | $maybeAppend($this->trim); 140 | $maybeAppend($this->crop); 141 | $maybeAppend($this->resize); 142 | $maybeAppend($this->halign); 143 | $maybeAppend($this->valign); 144 | 145 | if ($this->smartCrop) { 146 | $commands []= 'smart'; 147 | } 148 | 149 | if (count($this->filters)) { 150 | $filters = 'filters:' . implode(':', $this->filters); 151 | $commands []= $filters; 152 | } 153 | 154 | return $commands; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/Thumbor/Url/BuilderFactoryTest.php: -------------------------------------------------------------------------------- 1 | url($original); 17 | 18 | $expected = Builder::construct($server, $secret, $original); 19 | 20 | $this->assertEquals($expected, $builder); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Thumbor/Url/BuilderTest.php: -------------------------------------------------------------------------------- 1 | fitIn(320, 240) 14 | ->smartCrop(true) 15 | ->addFilter('brightness', 42) 16 | ->build(); 17 | 18 | $expected = new Url( 19 | 'http://thumbor.example.com', 20 | 'butts', 21 | 'http://example.com/llamas.jpg', 22 | array( 23 | 'fit-in/320x240', 24 | 'smart', 25 | 'filters:brightness(42)' 26 | ) 27 | ); 28 | 29 | $this->assertEquals($expected, $url); 30 | } 31 | 32 | public function testToString() 33 | { 34 | $url = (string) Builder::construct('http://thumbor.example.com', 'butts', 'http://example.com/llamas.jpg') 35 | ->fitIn(320, 240) 36 | ->smartCrop(true) 37 | ->addFilter('brightness', 42); 38 | 39 | $expected = 'http://thumbor.example.com/dgzk7MVde2RUq5Hbq40FvfRdno0=/fit-in/320x240/smart/filters:brightness(42)/http://example.com/llamas.jpg'; 40 | 41 | $this->assertEquals($expected, $url); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Thumbor/Url/CommandSetTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 13 | array(), 14 | $commandSet->toArray() 15 | ); 16 | } 17 | 18 | public function testTrim() 19 | { 20 | $commandSet = new CommandSet(); 21 | $commandSet->trim(); 22 | $this->assertEquals( 23 | array('trim'), 24 | $commandSet->toArray() 25 | ); 26 | $commandSet->trim('bottom-right'); 27 | $this->assertEquals( 28 | array('trim:bottom-right'), 29 | $commandSet->toArray() 30 | ); 31 | $commandSet->trim('top-left', 50); 32 | $this->assertEquals( 33 | array('trim:top-left:50'), 34 | $commandSet->toArray() 35 | ); 36 | } 37 | 38 | public function testCrop() 39 | { 40 | $commandSet = new CommandSet(); 41 | $commandSet->crop(1, 2, 3, 4); 42 | $this->assertEquals( 43 | array('1x2:3x4'), 44 | $commandSet->toArray() 45 | ); 46 | } 47 | 48 | public function testFitIn() 49 | { 50 | $commandSet = new CommandSet(); 51 | $commandSet->fitIn(5, 6); 52 | $this->assertEquals( 53 | array('fit-in/5x6'), 54 | $commandSet->toArray() 55 | ); 56 | } 57 | 58 | public function testResize() 59 | { 60 | $commandSet = new CommandSet(); 61 | $commandSet->resize(5, 6); 62 | $this->assertEquals( 63 | array('5x6'), 64 | $commandSet->toArray() 65 | ); 66 | } 67 | 68 | public function testHalign() 69 | { 70 | $commandSet = new CommandSet(); 71 | $commandSet->halign('center'); 72 | $this->assertEquals( 73 | array('center'), 74 | $commandSet->toArray() 75 | ); 76 | } 77 | 78 | public function testValign() 79 | { 80 | $commandSet = new CommandSet(); 81 | $commandSet->valign('bottom'); 82 | $this->assertEquals( 83 | array('bottom'), 84 | $commandSet->toArray() 85 | ); 86 | } 87 | 88 | public function testSmartCrop() 89 | { 90 | $commandSet = new CommandSet(); 91 | $commandSet->smartCrop(true); 92 | $this->assertEquals( 93 | array('smart'), 94 | $commandSet->toArray() 95 | ); 96 | } 97 | 98 | public function testAddFilter() 99 | { 100 | $commandSet = new CommandSet(); 101 | $commandSet->addFilter('foo'); 102 | $commandSet->addFilter('bar', 'baz'); 103 | $commandSet->addFilter('bla', 'quux', 42); 104 | $this->assertEquals( 105 | array('filters:foo():bar(baz):bla(quux,42)'), 106 | $commandSet->toArray() 107 | ); 108 | } 109 | 110 | public function testMetadataOnly() 111 | { 112 | $commandSet = new CommandSet(); 113 | $commandSet->metadataOnly(true); 114 | $this->assertEquals( 115 | array('meta'), 116 | $commandSet->toArray() 117 | ); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tests/Thumbor/UrlTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 12 | 'bDv76lTvUdX6vORS96scx7P185c=', 13 | Url::sign( 14 | 'fit-in/560x420/filters:fill(green)/my/big/image.jpg', 15 | 'MY_SECURE_KEY' 16 | ) 17 | ); 18 | } 19 | 20 | public function testToString() 21 | { 22 | $url = new Url( 23 | 'http://thumbor-server:8888', 24 | 'MY_SECURE_KEY', 25 | 'my/big/image.jpg', 26 | array('fit-in', '560x420', 'filters:fill(green)') 27 | ); 28 | 29 | $this->assertEquals( 30 | 'http://thumbor-server:8888/bDv76lTvUdX6vORS96scx7P185c=/fit-in/560x420/filters:fill(green)/my/big/image.jpg', 31 | "$url" 32 | ); 33 | } 34 | 35 | public function testToStringWithoutCommand() 36 | { 37 | $url = new Url( 38 | 'http://thumbor-server:8888', 39 | 'MY_SECURE_KEY', 40 | 'my/big/image.jpg', 41 | array() 42 | ); 43 | 44 | $this->assertEquals( 45 | 'http://thumbor-server:8888/V2bYe7DAKqngbtv2GFxCcllDYWw=/my/big/image.jpg', 46 | "$url" 47 | ); 48 | } 49 | } 50 | --------------------------------------------------------------------------------