├── .gitignore
├── README.md
├── composer.json
├── composer.lock
├── phpunit.xml.dist
├── src
└── Bravo3
│ └── ImageManager
│ ├── Encoders
│ ├── AbstractEncoder.php
│ ├── AbstractFilesystemEncoder.php
│ ├── EncoderInterface.php
│ ├── ImagickEncoder.php
│ └── InterventionEncoder.php
│ ├── Entities
│ ├── Image.php
│ ├── ImageCropDimensions.php
│ ├── ImageDimensions.php
│ ├── ImageMetadata.php
│ ├── ImageVariation.php
│ └── Interfaces
│ │ └── SerialisableInterface.php
│ ├── Enum
│ ├── ImageFormat.php
│ └── ImageOrientation.php
│ ├── Exceptions
│ ├── BadImageException.php
│ ├── ImageManagerException.php
│ ├── InvalidImageMetadataException.php
│ ├── IoException.php
│ ├── NoSupportedEncoderException.php
│ ├── NotExistsException.php
│ ├── NotPermittedException.php
│ └── ObjectAlreadyExistsException.php
│ ├── Services
│ ├── DataInspector.php
│ ├── ImageInspector.php
│ └── ImageManager.php
│ └── Traits
│ └── FriendTrait.php
└── tests
├── Bravo3
└── ImageManager
│ └── Tests
│ ├── Entities
│ └── ImageTest.php
│ ├── Resources
│ ├── FriendlyClass.php
│ ├── UnfriendlyClass.php
│ ├── actually_a_png.jpg
│ ├── animated.gif
│ ├── image.jpg
│ ├── image.png
│ ├── not_an_image.png
│ ├── sample_pdf.pdf
│ ├── sample_pdf2.pdf
│ └── transparent.png
│ ├── Services
│ └── ImageManagerTest.php
│ └── Traits
│ └── FriendTraitTest.php
└── bootstrap.php
/.gitignore:
--------------------------------------------------------------------------------
1 | !.gitkeep
2 | /vendor/
3 | /bin/
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Image Manager
2 | =============
3 | A PHP 5.4 image manager intended for cloud use. This image manager is designed to be low-level and work with 'keys' -
4 | not directly attach to an entity.
5 |
6 | Features
7 | --------
8 | * Easily push and pull images to any remote filesystem (eg Amazon S3)
9 | * Request an image with specific dimensions - allow the manager to transparently create & store this variation
10 | * Request that an image dimension exists (will be created if it doesn't), allowing for the storage device to be used as a CDN end-point
11 | * Use a caching service to maintain a knowledge base of image dimensions available to improve performance
12 | * Load & save images from memory or a file
13 | * Convert image format & quality with ease
14 | * Customisable encoders
15 | * GD and Imagick support
16 | * PDF support (via Imagick)
17 |
18 | Examples
19 | --------
20 | ### Storing an image
21 |
22 | // Use the local filesystem as a fake remote (replace with S3, etc)
23 | $im = new ImageManager(new Filesystem(new LocalAdapter('/tmp/images')));
24 |
25 | // Load local "image.png" and give it a key of 'content_123_image_1' (using the filename is suitable too)
26 | $image = $im->loadFromFile('image.png', 'content_123_image_1');
27 |
28 | // Save it on the remote
29 | $im->push($image);
30 |
31 | ### Retrieving an image
32 |
33 | $image = new Image('content_123_image_1');
34 | $im->pull($image);
35 |
36 | // Save to local filesystem
37 | $im->save($image, '/tmp/image.png');
38 |
39 | // Output to client
40 | echo $image->getData();
41 |
42 | ### Retrieving an automatic variation
43 |
44 | // Define a dimension that the image will fit in a height of 200px
45 | $dimensions = new ImageDimensions(null, 200);
46 |
47 | // Create a specification for a JPEG format, quality 75% and use the above dimensions
48 | $image = new ImageVariation('content_123_image_1', ImageFormat::JPEG(), 75, $dimensions);
49 |
50 | // Automatically create the variation when we pull
51 | $im->pull($image);
52 |
53 | if (!$im->isPersistent()) {
54 | // Make sure our new variation exists on the remote
55 | $im->push($image);
56 | }
57 |
58 | echo $image->getData();
59 |
60 | ### Create a variation manually
61 |
62 | $source = $im->loadFromFile('image.png', 'content_123_image_1');
63 | $resized = $im->createVariation($source, ImageFormat::JPEG(), 90, new ImageDimensions(100, 100));
64 | $im->push($resized);
65 |
66 | ### Check if a variation exists
67 |
68 | $image = new ImageVariation('content_123_image_1', ImageFormat::JPEG(), 75, $dimensions);
69 |
70 | if (!$im->exists($image)) {
71 | $im->pull($image); // Creates the variation
72 | $im->push($image); // Save it on the remote
73 | }
74 |
75 | echo '
';
76 |
77 | Caching
78 | -------
79 | Because remote storage services have a moderate degree of lag while talking to, it's probably not appropriate to do
80 | "exists" checks on every image variation during page generation. To avoid this you can either pre-render the image and
81 | assume it will exist, or using a quick caching mechanic to store an inventory of all images available.
82 |
83 | Your caching mechanic MUST be persistent - if a cache key is lost the image manager will assume the remote file does
84 | not exist. Using a database or disk backed key/value storage is recommended (eg Redis).
85 |
86 | To use caching, just include a \Bravo3\Cache\PoolInterface implementation in the ImageManager's constructor.
87 |
88 | Encoders
89 | --------
90 | By default the image manager will use the InterventionEncoder (@see http://image.intervention.io/), which supports
91 | base image formats. You can switch this for the ImagickEncoder which also allows for PDF documents to be used as an
92 | input.
93 |
94 | To *add* support for native Imagick, but use Intervention where possible (thus giving you PDF support):
95 |
96 | $im = new ImageManager(...);
97 | $im->addEncoder(new ImagickEncoder());
98 |
99 | To use only the Imagick encoder:
100 |
101 | $im = new ImageManager($filesystem, $cache_pool, [new ImagickEncoder()]);
102 |
103 | Future Considerations
104 | ---------------------
105 | * Allow for image manipulations (eg add text, rotate, etc)
106 | * Allow for customisable variation naming schemes
107 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bravo3/image-manager",
3 | "type": "library",
4 | "description": "A PHP 5.4 library to control dynamic image assets in a cloud environment",
5 | "keywords": ["image", "aws", "google cloud", "azure"],
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Jordon Scott",
10 | "email": "jordon@novatek.com.au"
11 | }
12 | ],
13 | "autoload": {
14 | "psr-0": {
15 | "": ["src/", "tests/"]
16 | }
17 | },
18 | "require": {
19 | "php": ">=5.4.0",
20 | "psr/log": "~1.0",
21 | "bravo3/cache": "~0.1.1",
22 | "eloquent/enumeration": "~5.1",
23 | "intervention/image": "~1.6.2",
24 | "knplabs/gaufrette": "~0.1.7",
25 | "symfony/filesystem": "~2.4|^3.0"
26 | },
27 | "require-dev": {
28 | "phpunit/phpunit": ">=4.0.0"
29 | },
30 | "minimum-stability": "beta",
31 | "scripts": {
32 | "post-install-cmd": [
33 | ],
34 | "post-update-cmd": [
35 | ]
36 | },
37 | "config": {
38 | "bin-dir": "bin"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "hash": "864a4d4e4bf9112cd8de2f247f061071",
8 | "content-hash": "ef1708351fb385fb90cb52156a812882",
9 | "packages": [
10 | {
11 | "name": "bravo3/cache",
12 | "version": "0.1.4",
13 | "source": {
14 | "type": "git",
15 | "url": "https://github.com/bravo3/cache.git",
16 | "reference": "2d78cb0e19bf3a659bd3d98caeae7eec9bce84a6"
17 | },
18 | "dist": {
19 | "type": "zip",
20 | "url": "https://api.github.com/repos/bravo3/cache/zipball/2d78cb0e19bf3a659bd3d98caeae7eec9bce84a6",
21 | "reference": "2d78cb0e19bf3a659bd3d98caeae7eec9bce84a6",
22 | "shasum": ""
23 | },
24 | "require": {
25 | "php": ">=5.4.0",
26 | "psr/log": "~1.0"
27 | },
28 | "require-dev": {
29 | "bravo3/orm": "^0.5.1",
30 | "phpunit/phpunit": ">=4.0.0",
31 | "predis/predis": "^1.0.3"
32 | },
33 | "suggest": {
34 | "bravo3/orm": "For Bravo3/ORM support",
35 | "predis/predis": "For Redis support"
36 | },
37 | "type": "library",
38 | "autoload": {
39 | "psr-0": {
40 | "": "src/"
41 | }
42 | },
43 | "notification-url": "https://packagist.org/downloads/",
44 | "license": [
45 | "MIT"
46 | ],
47 | "authors": [
48 | {
49 | "name": "Jordon Scott",
50 | "email": "jordon@novatek.com.au"
51 | }
52 | ],
53 | "description": "A PHP 5.4 cache interface and various implementations including Redis and Bravo3/ORM",
54 | "keywords": [
55 | "cache",
56 | "orm",
57 | "predis",
58 | "redis"
59 | ],
60 | "time": "2015-11-10 04:54:37"
61 | },
62 | {
63 | "name": "eloquent/enumeration",
64 | "version": "5.1.1",
65 | "source": {
66 | "type": "git",
67 | "url": "https://github.com/eloquent/enumeration.git",
68 | "reference": "0242859435d9b135939816858348556d3cde9e3c"
69 | },
70 | "dist": {
71 | "type": "zip",
72 | "url": "https://api.github.com/repos/eloquent/enumeration/zipball/0242859435d9b135939816858348556d3cde9e3c",
73 | "reference": "0242859435d9b135939816858348556d3cde9e3c",
74 | "shasum": ""
75 | },
76 | "require": {
77 | "php": ">=5.3"
78 | },
79 | "require-dev": {
80 | "icecave/archer": "dev-develop",
81 | "phpunit/phpunit": "^4",
82 | "sami/sami": "^3"
83 | },
84 | "type": "library",
85 | "autoload": {
86 | "psr-4": {
87 | "Eloquent\\Enumeration\\": "src"
88 | }
89 | },
90 | "notification-url": "https://packagist.org/downloads/",
91 | "license": [
92 | "MIT"
93 | ],
94 | "authors": [
95 | {
96 | "name": "Erin Millard",
97 | "email": "ezzatron@gmail.com",
98 | "homepage": "http://ezzatron.com/"
99 | }
100 | ],
101 | "description": "An enumeration implementation for PHP.",
102 | "homepage": "https://github.com/eloquent/enumeration",
103 | "keywords": [
104 | "class",
105 | "enum",
106 | "enumeration",
107 | "multiton",
108 | "set",
109 | "type"
110 | ],
111 | "time": "2015-11-03 22:21:38"
112 | },
113 | {
114 | "name": "illuminate/support",
115 | "version": "v4.2.17",
116 | "target-dir": "Illuminate/Support",
117 | "source": {
118 | "type": "git",
119 | "url": "https://github.com/illuminate/support.git",
120 | "reference": "db61f3f6d507ce417ca993e1f93585db7efd8b12"
121 | },
122 | "dist": {
123 | "type": "zip",
124 | "url": "https://api.github.com/repos/illuminate/support/zipball/db61f3f6d507ce417ca993e1f93585db7efd8b12",
125 | "reference": "db61f3f6d507ce417ca993e1f93585db7efd8b12",
126 | "shasum": ""
127 | },
128 | "require": {
129 | "php": ">=5.4.0"
130 | },
131 | "require-dev": {
132 | "jeremeamia/superclosure": "~1.0.1",
133 | "patchwork/utf8": "~1.1"
134 | },
135 | "type": "library",
136 | "extra": {
137 | "branch-alias": {
138 | "dev-master": "4.2-dev"
139 | }
140 | },
141 | "autoload": {
142 | "psr-0": {
143 | "Illuminate\\Support": ""
144 | },
145 | "files": [
146 | "Illuminate/Support/helpers.php"
147 | ]
148 | },
149 | "notification-url": "https://packagist.org/downloads/",
150 | "license": [
151 | "MIT"
152 | ],
153 | "authors": [
154 | {
155 | "name": "Taylor Otwell",
156 | "email": "taylorotwell@gmail.com"
157 | }
158 | ],
159 | "time": "2015-01-27 20:51:43"
160 | },
161 | {
162 | "name": "intervention/image",
163 | "version": "1.6.5",
164 | "source": {
165 | "type": "git",
166 | "url": "https://github.com/Intervention/image.git",
167 | "reference": "432f3b28ea08455146b157c6bfdebf4ef5bc661e"
168 | },
169 | "dist": {
170 | "type": "zip",
171 | "url": "https://api.github.com/repos/Intervention/image/zipball/432f3b28ea08455146b157c6bfdebf4ef5bc661e",
172 | "reference": "432f3b28ea08455146b157c6bfdebf4ef5bc661e",
173 | "shasum": ""
174 | },
175 | "require": {
176 | "ext-gd": "*",
177 | "illuminate/support": "4.*",
178 | "php": ">=5.3.0"
179 | },
180 | "require-dev": {
181 | "phpunit/phpunit": "4.1.*"
182 | },
183 | "suggest": {
184 | "intervention/imagecache": "Caching extension for the Intervention Image library"
185 | },
186 | "type": "library",
187 | "autoload": {
188 | "psr-0": {
189 | "Intervention\\Image": "src/"
190 | }
191 | },
192 | "notification-url": "https://packagist.org/downloads/",
193 | "license": [
194 | "MIT"
195 | ],
196 | "authors": [
197 | {
198 | "name": "Oliver Vogel",
199 | "email": "oliver@olivervogel.net",
200 | "homepage": "http://olivervogel.net/"
201 | }
202 | ],
203 | "description": "Image handling and manipulation library with support for Laravel 4 integration",
204 | "homepage": "http://image.intervention.io/",
205 | "keywords": [
206 | "gd",
207 | "image",
208 | "laravel",
209 | "thumbnail",
210 | "watermark"
211 | ],
212 | "time": "2014-06-07 16:25:27"
213 | },
214 | {
215 | "name": "knplabs/gaufrette",
216 | "version": "v0.1.9",
217 | "source": {
218 | "type": "git",
219 | "url": "https://github.com/KnpLabs/Gaufrette.git",
220 | "reference": "4c73bb66ff41d7c9beb57372a82047cf5dcc6d1c"
221 | },
222 | "dist": {
223 | "type": "zip",
224 | "url": "https://api.github.com/repos/KnpLabs/Gaufrette/zipball/4c73bb66ff41d7c9beb57372a82047cf5dcc6d1c",
225 | "reference": "4c73bb66ff41d7c9beb57372a82047cf5dcc6d1c",
226 | "shasum": ""
227 | },
228 | "require": {
229 | "php": ">=5.3.2"
230 | },
231 | "require-dev": {
232 | "amazonwebservices/aws-sdk-for-php": "1.5.*",
233 | "aws/aws-sdk-php": "~2",
234 | "doctrine/dbal": ">=2.3",
235 | "dropbox-php/dropbox-php": "*",
236 | "google/apiclient": "~1.1",
237 | "herzult/php-ssh": "*",
238 | "microsoft/windowsazure": "dev-master",
239 | "mikey179/vfsstream": "~1.2.0",
240 | "phpseclib/phpseclib": "dev-master",
241 | "phpspec/phpspec": "2.0.*",
242 | "phpunit/phpunit": "3.7.*",
243 | "rackspace/php-opencloud": "1.9.*"
244 | },
245 | "suggest": {
246 | "amazonwebservices/aws-sdk-for-php": "to use the legacy Amazon S3 adapters",
247 | "aws/aws-sdk-php": "to use the Amazon S3 adapter",
248 | "doctrine/dbal": "to use the Doctrine DBAL adapter",
249 | "dropbox-php/dropbox-php": "to use the Dropbox adapter",
250 | "ext-apc": "to use the APC adapter",
251 | "ext-curl": "*",
252 | "ext-fileinfo": "This extension is used to automatically detect the content-type of a file in the AwsS3, OpenCloud, AzureBlogStorage and GoogleCloudStorage adapters",
253 | "ext-mbstring": "*",
254 | "ext-mongo": "*",
255 | "ext-zip": "to use the Zip adapter",
256 | "google/apiclient": "to use GoogleCloudStorage adapter",
257 | "herzult/php-ssh": "to use SFtp adapter",
258 | "knplabs/knp-gaufrette-bundle": "to use with Symfony2",
259 | "microsoft/windowsazure": "to use Microsoft Azure Blob Storage adapter",
260 | "phpseclib/phpseclib": "to use PhpseclibSftp adapter",
261 | "rackspace/php-opencloud": "to use Opencloud adapter"
262 | },
263 | "type": "library",
264 | "extra": {
265 | "branch-alias": {
266 | "dev-master": "0.2.x-dev"
267 | }
268 | },
269 | "autoload": {
270 | "psr-0": {
271 | "Gaufrette": "src/"
272 | }
273 | },
274 | "notification-url": "https://packagist.org/downloads/",
275 | "license": [
276 | "MIT"
277 | ],
278 | "authors": [
279 | {
280 | "name": "The contributors",
281 | "homepage": "http://github.com/knplabs/Gaufrette/contributors"
282 | },
283 | {
284 | "name": "KnpLabs Team",
285 | "homepage": "http://knplabs.com"
286 | }
287 | ],
288 | "description": "PHP5 library that provides a filesystem abstraction layer",
289 | "homepage": "http://knplabs.com",
290 | "keywords": [
291 | "abstraction",
292 | "file",
293 | "filesystem",
294 | "media"
295 | ],
296 | "time": "2015-03-09 08:06:57"
297 | },
298 | {
299 | "name": "psr/log",
300 | "version": "1.0.0",
301 | "source": {
302 | "type": "git",
303 | "url": "https://github.com/php-fig/log.git",
304 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
305 | },
306 | "dist": {
307 | "type": "zip",
308 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
309 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b",
310 | "shasum": ""
311 | },
312 | "type": "library",
313 | "autoload": {
314 | "psr-0": {
315 | "Psr\\Log\\": ""
316 | }
317 | },
318 | "notification-url": "https://packagist.org/downloads/",
319 | "license": [
320 | "MIT"
321 | ],
322 | "authors": [
323 | {
324 | "name": "PHP-FIG",
325 | "homepage": "http://www.php-fig.org/"
326 | }
327 | ],
328 | "description": "Common interface for logging libraries",
329 | "keywords": [
330 | "log",
331 | "psr",
332 | "psr-3"
333 | ],
334 | "time": "2012-12-21 11:40:51"
335 | },
336 | {
337 | "name": "symfony/filesystem",
338 | "version": "v3.0.0",
339 | "source": {
340 | "type": "git",
341 | "url": "https://github.com/symfony/filesystem.git",
342 | "reference": "692d98d813e4ef314b9c22775c86ddbeb0f44884"
343 | },
344 | "dist": {
345 | "type": "zip",
346 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/692d98d813e4ef314b9c22775c86ddbeb0f44884",
347 | "reference": "692d98d813e4ef314b9c22775c86ddbeb0f44884",
348 | "shasum": ""
349 | },
350 | "require": {
351 | "php": ">=5.5.9"
352 | },
353 | "type": "library",
354 | "extra": {
355 | "branch-alias": {
356 | "dev-master": "3.0-dev"
357 | }
358 | },
359 | "autoload": {
360 | "psr-4": {
361 | "Symfony\\Component\\Filesystem\\": ""
362 | },
363 | "exclude-from-classmap": [
364 | "/Tests/"
365 | ]
366 | },
367 | "notification-url": "https://packagist.org/downloads/",
368 | "license": [
369 | "MIT"
370 | ],
371 | "authors": [
372 | {
373 | "name": "Fabien Potencier",
374 | "email": "fabien@symfony.com"
375 | },
376 | {
377 | "name": "Symfony Community",
378 | "homepage": "https://symfony.com/contributors"
379 | }
380 | ],
381 | "description": "Symfony Filesystem Component",
382 | "homepage": "https://symfony.com",
383 | "time": "2015-11-23 10:41:47"
384 | }
385 | ],
386 | "packages-dev": [
387 | {
388 | "name": "doctrine/instantiator",
389 | "version": "1.0.5",
390 | "source": {
391 | "type": "git",
392 | "url": "https://github.com/doctrine/instantiator.git",
393 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
394 | },
395 | "dist": {
396 | "type": "zip",
397 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
398 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
399 | "shasum": ""
400 | },
401 | "require": {
402 | "php": ">=5.3,<8.0-DEV"
403 | },
404 | "require-dev": {
405 | "athletic/athletic": "~0.1.8",
406 | "ext-pdo": "*",
407 | "ext-phar": "*",
408 | "phpunit/phpunit": "~4.0",
409 | "squizlabs/php_codesniffer": "~2.0"
410 | },
411 | "type": "library",
412 | "extra": {
413 | "branch-alias": {
414 | "dev-master": "1.0.x-dev"
415 | }
416 | },
417 | "autoload": {
418 | "psr-4": {
419 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
420 | }
421 | },
422 | "notification-url": "https://packagist.org/downloads/",
423 | "license": [
424 | "MIT"
425 | ],
426 | "authors": [
427 | {
428 | "name": "Marco Pivetta",
429 | "email": "ocramius@gmail.com",
430 | "homepage": "http://ocramius.github.com/"
431 | }
432 | ],
433 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
434 | "homepage": "https://github.com/doctrine/instantiator",
435 | "keywords": [
436 | "constructor",
437 | "instantiate"
438 | ],
439 | "time": "2015-06-14 21:17:01"
440 | },
441 | {
442 | "name": "myclabs/deep-copy",
443 | "version": "1.5.0",
444 | "source": {
445 | "type": "git",
446 | "url": "https://github.com/myclabs/DeepCopy.git",
447 | "reference": "e3abefcd7f106677fd352cd7c187d6c969aa9ddc"
448 | },
449 | "dist": {
450 | "type": "zip",
451 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e3abefcd7f106677fd352cd7c187d6c969aa9ddc",
452 | "reference": "e3abefcd7f106677fd352cd7c187d6c969aa9ddc",
453 | "shasum": ""
454 | },
455 | "require": {
456 | "php": ">=5.4.0"
457 | },
458 | "require-dev": {
459 | "doctrine/collections": "1.*",
460 | "phpunit/phpunit": "~4.1"
461 | },
462 | "type": "library",
463 | "autoload": {
464 | "psr-4": {
465 | "DeepCopy\\": "src/DeepCopy/"
466 | }
467 | },
468 | "notification-url": "https://packagist.org/downloads/",
469 | "license": [
470 | "MIT"
471 | ],
472 | "description": "Create deep copies (clones) of your objects",
473 | "homepage": "https://github.com/myclabs/DeepCopy",
474 | "keywords": [
475 | "clone",
476 | "copy",
477 | "duplicate",
478 | "object",
479 | "object graph"
480 | ],
481 | "time": "2015-11-07 22:20:37"
482 | },
483 | {
484 | "name": "phpdocumentor/reflection-docblock",
485 | "version": "2.0.4",
486 | "source": {
487 | "type": "git",
488 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
489 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8"
490 | },
491 | "dist": {
492 | "type": "zip",
493 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8",
494 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8",
495 | "shasum": ""
496 | },
497 | "require": {
498 | "php": ">=5.3.3"
499 | },
500 | "require-dev": {
501 | "phpunit/phpunit": "~4.0"
502 | },
503 | "suggest": {
504 | "dflydev/markdown": "~1.0",
505 | "erusev/parsedown": "~1.0"
506 | },
507 | "type": "library",
508 | "extra": {
509 | "branch-alias": {
510 | "dev-master": "2.0.x-dev"
511 | }
512 | },
513 | "autoload": {
514 | "psr-0": {
515 | "phpDocumentor": [
516 | "src/"
517 | ]
518 | }
519 | },
520 | "notification-url": "https://packagist.org/downloads/",
521 | "license": [
522 | "MIT"
523 | ],
524 | "authors": [
525 | {
526 | "name": "Mike van Riel",
527 | "email": "mike.vanriel@naenius.com"
528 | }
529 | ],
530 | "time": "2015-02-03 12:10:50"
531 | },
532 | {
533 | "name": "phpspec/prophecy",
534 | "version": "v1.5.0",
535 | "source": {
536 | "type": "git",
537 | "url": "https://github.com/phpspec/prophecy.git",
538 | "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7"
539 | },
540 | "dist": {
541 | "type": "zip",
542 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7",
543 | "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7",
544 | "shasum": ""
545 | },
546 | "require": {
547 | "doctrine/instantiator": "^1.0.2",
548 | "phpdocumentor/reflection-docblock": "~2.0",
549 | "sebastian/comparator": "~1.1"
550 | },
551 | "require-dev": {
552 | "phpspec/phpspec": "~2.0"
553 | },
554 | "type": "library",
555 | "extra": {
556 | "branch-alias": {
557 | "dev-master": "1.4.x-dev"
558 | }
559 | },
560 | "autoload": {
561 | "psr-0": {
562 | "Prophecy\\": "src/"
563 | }
564 | },
565 | "notification-url": "https://packagist.org/downloads/",
566 | "license": [
567 | "MIT"
568 | ],
569 | "authors": [
570 | {
571 | "name": "Konstantin Kudryashov",
572 | "email": "ever.zet@gmail.com",
573 | "homepage": "http://everzet.com"
574 | },
575 | {
576 | "name": "Marcello Duarte",
577 | "email": "marcello.duarte@gmail.com"
578 | }
579 | ],
580 | "description": "Highly opinionated mocking framework for PHP 5.3+",
581 | "homepage": "https://github.com/phpspec/prophecy",
582 | "keywords": [
583 | "Double",
584 | "Dummy",
585 | "fake",
586 | "mock",
587 | "spy",
588 | "stub"
589 | ],
590 | "time": "2015-08-13 10:07:40"
591 | },
592 | {
593 | "name": "phpunit/php-code-coverage",
594 | "version": "3.0.2",
595 | "source": {
596 | "type": "git",
597 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
598 | "reference": "f7bb5cddf4ffe113eeb737b05241adb947b43f9d"
599 | },
600 | "dist": {
601 | "type": "zip",
602 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f7bb5cddf4ffe113eeb737b05241adb947b43f9d",
603 | "reference": "f7bb5cddf4ffe113eeb737b05241adb947b43f9d",
604 | "shasum": ""
605 | },
606 | "require": {
607 | "php": ">=5.6",
608 | "phpunit/php-file-iterator": "~1.3",
609 | "phpunit/php-text-template": "~1.2",
610 | "phpunit/php-token-stream": "~1.3",
611 | "sebastian/environment": "^1.3.2",
612 | "sebastian/version": "~1.0"
613 | },
614 | "require-dev": {
615 | "ext-xdebug": ">=2.1.4",
616 | "phpunit/phpunit": "~5"
617 | },
618 | "suggest": {
619 | "ext-dom": "*",
620 | "ext-xdebug": ">=2.2.1",
621 | "ext-xmlwriter": "*"
622 | },
623 | "type": "library",
624 | "extra": {
625 | "branch-alias": {
626 | "dev-master": "3.0.x-dev"
627 | }
628 | },
629 | "autoload": {
630 | "classmap": [
631 | "src/"
632 | ]
633 | },
634 | "notification-url": "https://packagist.org/downloads/",
635 | "license": [
636 | "BSD-3-Clause"
637 | ],
638 | "authors": [
639 | {
640 | "name": "Sebastian Bergmann",
641 | "email": "sb@sebastian-bergmann.de",
642 | "role": "lead"
643 | }
644 | ],
645 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
646 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
647 | "keywords": [
648 | "coverage",
649 | "testing",
650 | "xunit"
651 | ],
652 | "time": "2015-11-12 21:08:20"
653 | },
654 | {
655 | "name": "phpunit/php-file-iterator",
656 | "version": "1.4.1",
657 | "source": {
658 | "type": "git",
659 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
660 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0"
661 | },
662 | "dist": {
663 | "type": "zip",
664 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
665 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
666 | "shasum": ""
667 | },
668 | "require": {
669 | "php": ">=5.3.3"
670 | },
671 | "type": "library",
672 | "extra": {
673 | "branch-alias": {
674 | "dev-master": "1.4.x-dev"
675 | }
676 | },
677 | "autoload": {
678 | "classmap": [
679 | "src/"
680 | ]
681 | },
682 | "notification-url": "https://packagist.org/downloads/",
683 | "license": [
684 | "BSD-3-Clause"
685 | ],
686 | "authors": [
687 | {
688 | "name": "Sebastian Bergmann",
689 | "email": "sb@sebastian-bergmann.de",
690 | "role": "lead"
691 | }
692 | ],
693 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
694 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
695 | "keywords": [
696 | "filesystem",
697 | "iterator"
698 | ],
699 | "time": "2015-06-21 13:08:43"
700 | },
701 | {
702 | "name": "phpunit/php-text-template",
703 | "version": "1.2.1",
704 | "source": {
705 | "type": "git",
706 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
707 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
708 | },
709 | "dist": {
710 | "type": "zip",
711 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
712 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
713 | "shasum": ""
714 | },
715 | "require": {
716 | "php": ">=5.3.3"
717 | },
718 | "type": "library",
719 | "autoload": {
720 | "classmap": [
721 | "src/"
722 | ]
723 | },
724 | "notification-url": "https://packagist.org/downloads/",
725 | "license": [
726 | "BSD-3-Clause"
727 | ],
728 | "authors": [
729 | {
730 | "name": "Sebastian Bergmann",
731 | "email": "sebastian@phpunit.de",
732 | "role": "lead"
733 | }
734 | ],
735 | "description": "Simple template engine.",
736 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
737 | "keywords": [
738 | "template"
739 | ],
740 | "time": "2015-06-21 13:50:34"
741 | },
742 | {
743 | "name": "phpunit/php-timer",
744 | "version": "1.0.7",
745 | "source": {
746 | "type": "git",
747 | "url": "https://github.com/sebastianbergmann/php-timer.git",
748 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b"
749 | },
750 | "dist": {
751 | "type": "zip",
752 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
753 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
754 | "shasum": ""
755 | },
756 | "require": {
757 | "php": ">=5.3.3"
758 | },
759 | "type": "library",
760 | "autoload": {
761 | "classmap": [
762 | "src/"
763 | ]
764 | },
765 | "notification-url": "https://packagist.org/downloads/",
766 | "license": [
767 | "BSD-3-Clause"
768 | ],
769 | "authors": [
770 | {
771 | "name": "Sebastian Bergmann",
772 | "email": "sb@sebastian-bergmann.de",
773 | "role": "lead"
774 | }
775 | ],
776 | "description": "Utility class for timing",
777 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
778 | "keywords": [
779 | "timer"
780 | ],
781 | "time": "2015-06-21 08:01:12"
782 | },
783 | {
784 | "name": "phpunit/php-token-stream",
785 | "version": "1.4.8",
786 | "source": {
787 | "type": "git",
788 | "url": "https://github.com/sebastianbergmann/php-token-stream.git",
789 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da"
790 | },
791 | "dist": {
792 | "type": "zip",
793 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
794 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
795 | "shasum": ""
796 | },
797 | "require": {
798 | "ext-tokenizer": "*",
799 | "php": ">=5.3.3"
800 | },
801 | "require-dev": {
802 | "phpunit/phpunit": "~4.2"
803 | },
804 | "type": "library",
805 | "extra": {
806 | "branch-alias": {
807 | "dev-master": "1.4-dev"
808 | }
809 | },
810 | "autoload": {
811 | "classmap": [
812 | "src/"
813 | ]
814 | },
815 | "notification-url": "https://packagist.org/downloads/",
816 | "license": [
817 | "BSD-3-Clause"
818 | ],
819 | "authors": [
820 | {
821 | "name": "Sebastian Bergmann",
822 | "email": "sebastian@phpunit.de"
823 | }
824 | ],
825 | "description": "Wrapper around PHP's tokenizer extension.",
826 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
827 | "keywords": [
828 | "tokenizer"
829 | ],
830 | "time": "2015-09-15 10:49:45"
831 | },
832 | {
833 | "name": "phpunit/phpunit",
834 | "version": "5.0.10",
835 | "source": {
836 | "type": "git",
837 | "url": "https://github.com/sebastianbergmann/phpunit.git",
838 | "reference": "9104a4e2f6a3ebdc4eb036624949a1a2849373dd"
839 | },
840 | "dist": {
841 | "type": "zip",
842 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9104a4e2f6a3ebdc4eb036624949a1a2849373dd",
843 | "reference": "9104a4e2f6a3ebdc4eb036624949a1a2849373dd",
844 | "shasum": ""
845 | },
846 | "require": {
847 | "ext-dom": "*",
848 | "ext-json": "*",
849 | "ext-pcre": "*",
850 | "ext-reflection": "*",
851 | "ext-spl": "*",
852 | "myclabs/deep-copy": "~1.3",
853 | "php": ">=5.6",
854 | "phpspec/prophecy": "^1.3.1",
855 | "phpunit/php-code-coverage": "~3.0",
856 | "phpunit/php-file-iterator": "~1.4",
857 | "phpunit/php-text-template": "~1.2",
858 | "phpunit/php-timer": ">=1.0.6",
859 | "phpunit/phpunit-mock-objects": ">=3.0",
860 | "sebastian/comparator": "~1.1",
861 | "sebastian/diff": "~1.2",
862 | "sebastian/environment": "~1.3",
863 | "sebastian/exporter": "~1.2",
864 | "sebastian/global-state": "~1.0",
865 | "sebastian/resource-operations": "~1.0",
866 | "sebastian/version": "~1.0",
867 | "symfony/yaml": "~2.1|~3.0"
868 | },
869 | "suggest": {
870 | "phpunit/php-invoker": "~1.1"
871 | },
872 | "bin": [
873 | "phpunit"
874 | ],
875 | "type": "library",
876 | "extra": {
877 | "branch-alias": {
878 | "dev-master": "5.0.x-dev"
879 | }
880 | },
881 | "autoload": {
882 | "classmap": [
883 | "src/"
884 | ]
885 | },
886 | "notification-url": "https://packagist.org/downloads/",
887 | "license": [
888 | "BSD-3-Clause"
889 | ],
890 | "authors": [
891 | {
892 | "name": "Sebastian Bergmann",
893 | "email": "sebastian@phpunit.de",
894 | "role": "lead"
895 | }
896 | ],
897 | "description": "The PHP Unit Testing framework.",
898 | "homepage": "https://phpunit.de/",
899 | "keywords": [
900 | "phpunit",
901 | "testing",
902 | "xunit"
903 | ],
904 | "time": "2015-11-30 08:33:35"
905 | },
906 | {
907 | "name": "phpunit/phpunit-mock-objects",
908 | "version": "3.0.4",
909 | "source": {
910 | "type": "git",
911 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
912 | "reference": "b28b029356e65091dfbf8c2bc4ef106ffece509e"
913 | },
914 | "dist": {
915 | "type": "zip",
916 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/b28b029356e65091dfbf8c2bc4ef106ffece509e",
917 | "reference": "b28b029356e65091dfbf8c2bc4ef106ffece509e",
918 | "shasum": ""
919 | },
920 | "require": {
921 | "doctrine/instantiator": "^1.0.2",
922 | "php": ">=5.6",
923 | "phpunit/php-text-template": "~1.2",
924 | "sebastian/exporter": "~1.2"
925 | },
926 | "require-dev": {
927 | "phpunit/phpunit": "~5"
928 | },
929 | "suggest": {
930 | "ext-soap": "*"
931 | },
932 | "type": "library",
933 | "extra": {
934 | "branch-alias": {
935 | "dev-master": "3.0.x-dev"
936 | }
937 | },
938 | "autoload": {
939 | "classmap": [
940 | "src/"
941 | ]
942 | },
943 | "notification-url": "https://packagist.org/downloads/",
944 | "license": [
945 | "BSD-3-Clause"
946 | ],
947 | "authors": [
948 | {
949 | "name": "Sebastian Bergmann",
950 | "email": "sb@sebastian-bergmann.de",
951 | "role": "lead"
952 | }
953 | ],
954 | "description": "Mock Object library for PHPUnit",
955 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
956 | "keywords": [
957 | "mock",
958 | "xunit"
959 | ],
960 | "time": "2015-11-09 15:37:17"
961 | },
962 | {
963 | "name": "sebastian/comparator",
964 | "version": "1.2.0",
965 | "source": {
966 | "type": "git",
967 | "url": "https://github.com/sebastianbergmann/comparator.git",
968 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22"
969 | },
970 | "dist": {
971 | "type": "zip",
972 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22",
973 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22",
974 | "shasum": ""
975 | },
976 | "require": {
977 | "php": ">=5.3.3",
978 | "sebastian/diff": "~1.2",
979 | "sebastian/exporter": "~1.2"
980 | },
981 | "require-dev": {
982 | "phpunit/phpunit": "~4.4"
983 | },
984 | "type": "library",
985 | "extra": {
986 | "branch-alias": {
987 | "dev-master": "1.2.x-dev"
988 | }
989 | },
990 | "autoload": {
991 | "classmap": [
992 | "src/"
993 | ]
994 | },
995 | "notification-url": "https://packagist.org/downloads/",
996 | "license": [
997 | "BSD-3-Clause"
998 | ],
999 | "authors": [
1000 | {
1001 | "name": "Jeff Welch",
1002 | "email": "whatthejeff@gmail.com"
1003 | },
1004 | {
1005 | "name": "Volker Dusch",
1006 | "email": "github@wallbash.com"
1007 | },
1008 | {
1009 | "name": "Bernhard Schussek",
1010 | "email": "bschussek@2bepublished.at"
1011 | },
1012 | {
1013 | "name": "Sebastian Bergmann",
1014 | "email": "sebastian@phpunit.de"
1015 | }
1016 | ],
1017 | "description": "Provides the functionality to compare PHP values for equality",
1018 | "homepage": "http://www.github.com/sebastianbergmann/comparator",
1019 | "keywords": [
1020 | "comparator",
1021 | "compare",
1022 | "equality"
1023 | ],
1024 | "time": "2015-07-26 15:48:44"
1025 | },
1026 | {
1027 | "name": "sebastian/diff",
1028 | "version": "1.3.0",
1029 | "source": {
1030 | "type": "git",
1031 | "url": "https://github.com/sebastianbergmann/diff.git",
1032 | "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3"
1033 | },
1034 | "dist": {
1035 | "type": "zip",
1036 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3",
1037 | "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3",
1038 | "shasum": ""
1039 | },
1040 | "require": {
1041 | "php": ">=5.3.3"
1042 | },
1043 | "require-dev": {
1044 | "phpunit/phpunit": "~4.2"
1045 | },
1046 | "type": "library",
1047 | "extra": {
1048 | "branch-alias": {
1049 | "dev-master": "1.3-dev"
1050 | }
1051 | },
1052 | "autoload": {
1053 | "classmap": [
1054 | "src/"
1055 | ]
1056 | },
1057 | "notification-url": "https://packagist.org/downloads/",
1058 | "license": [
1059 | "BSD-3-Clause"
1060 | ],
1061 | "authors": [
1062 | {
1063 | "name": "Kore Nordmann",
1064 | "email": "mail@kore-nordmann.de"
1065 | },
1066 | {
1067 | "name": "Sebastian Bergmann",
1068 | "email": "sebastian@phpunit.de"
1069 | }
1070 | ],
1071 | "description": "Diff implementation",
1072 | "homepage": "http://www.github.com/sebastianbergmann/diff",
1073 | "keywords": [
1074 | "diff"
1075 | ],
1076 | "time": "2015-02-22 15:13:53"
1077 | },
1078 | {
1079 | "name": "sebastian/environment",
1080 | "version": "1.3.2",
1081 | "source": {
1082 | "type": "git",
1083 | "url": "https://github.com/sebastianbergmann/environment.git",
1084 | "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44"
1085 | },
1086 | "dist": {
1087 | "type": "zip",
1088 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44",
1089 | "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44",
1090 | "shasum": ""
1091 | },
1092 | "require": {
1093 | "php": ">=5.3.3"
1094 | },
1095 | "require-dev": {
1096 | "phpunit/phpunit": "~4.4"
1097 | },
1098 | "type": "library",
1099 | "extra": {
1100 | "branch-alias": {
1101 | "dev-master": "1.3.x-dev"
1102 | }
1103 | },
1104 | "autoload": {
1105 | "classmap": [
1106 | "src/"
1107 | ]
1108 | },
1109 | "notification-url": "https://packagist.org/downloads/",
1110 | "license": [
1111 | "BSD-3-Clause"
1112 | ],
1113 | "authors": [
1114 | {
1115 | "name": "Sebastian Bergmann",
1116 | "email": "sebastian@phpunit.de"
1117 | }
1118 | ],
1119 | "description": "Provides functionality to handle HHVM/PHP environments",
1120 | "homepage": "http://www.github.com/sebastianbergmann/environment",
1121 | "keywords": [
1122 | "Xdebug",
1123 | "environment",
1124 | "hhvm"
1125 | ],
1126 | "time": "2015-08-03 06:14:51"
1127 | },
1128 | {
1129 | "name": "sebastian/exporter",
1130 | "version": "1.2.1",
1131 | "source": {
1132 | "type": "git",
1133 | "url": "https://github.com/sebastianbergmann/exporter.git",
1134 | "reference": "7ae5513327cb536431847bcc0c10edba2701064e"
1135 | },
1136 | "dist": {
1137 | "type": "zip",
1138 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e",
1139 | "reference": "7ae5513327cb536431847bcc0c10edba2701064e",
1140 | "shasum": ""
1141 | },
1142 | "require": {
1143 | "php": ">=5.3.3",
1144 | "sebastian/recursion-context": "~1.0"
1145 | },
1146 | "require-dev": {
1147 | "phpunit/phpunit": "~4.4"
1148 | },
1149 | "type": "library",
1150 | "extra": {
1151 | "branch-alias": {
1152 | "dev-master": "1.2.x-dev"
1153 | }
1154 | },
1155 | "autoload": {
1156 | "classmap": [
1157 | "src/"
1158 | ]
1159 | },
1160 | "notification-url": "https://packagist.org/downloads/",
1161 | "license": [
1162 | "BSD-3-Clause"
1163 | ],
1164 | "authors": [
1165 | {
1166 | "name": "Jeff Welch",
1167 | "email": "whatthejeff@gmail.com"
1168 | },
1169 | {
1170 | "name": "Volker Dusch",
1171 | "email": "github@wallbash.com"
1172 | },
1173 | {
1174 | "name": "Bernhard Schussek",
1175 | "email": "bschussek@2bepublished.at"
1176 | },
1177 | {
1178 | "name": "Sebastian Bergmann",
1179 | "email": "sebastian@phpunit.de"
1180 | },
1181 | {
1182 | "name": "Adam Harvey",
1183 | "email": "aharvey@php.net"
1184 | }
1185 | ],
1186 | "description": "Provides the functionality to export PHP variables for visualization",
1187 | "homepage": "http://www.github.com/sebastianbergmann/exporter",
1188 | "keywords": [
1189 | "export",
1190 | "exporter"
1191 | ],
1192 | "time": "2015-06-21 07:55:53"
1193 | },
1194 | {
1195 | "name": "sebastian/global-state",
1196 | "version": "1.1.1",
1197 | "source": {
1198 | "type": "git",
1199 | "url": "https://github.com/sebastianbergmann/global-state.git",
1200 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
1201 | },
1202 | "dist": {
1203 | "type": "zip",
1204 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
1205 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
1206 | "shasum": ""
1207 | },
1208 | "require": {
1209 | "php": ">=5.3.3"
1210 | },
1211 | "require-dev": {
1212 | "phpunit/phpunit": "~4.2"
1213 | },
1214 | "suggest": {
1215 | "ext-uopz": "*"
1216 | },
1217 | "type": "library",
1218 | "extra": {
1219 | "branch-alias": {
1220 | "dev-master": "1.0-dev"
1221 | }
1222 | },
1223 | "autoload": {
1224 | "classmap": [
1225 | "src/"
1226 | ]
1227 | },
1228 | "notification-url": "https://packagist.org/downloads/",
1229 | "license": [
1230 | "BSD-3-Clause"
1231 | ],
1232 | "authors": [
1233 | {
1234 | "name": "Sebastian Bergmann",
1235 | "email": "sebastian@phpunit.de"
1236 | }
1237 | ],
1238 | "description": "Snapshotting of global state",
1239 | "homepage": "http://www.github.com/sebastianbergmann/global-state",
1240 | "keywords": [
1241 | "global state"
1242 | ],
1243 | "time": "2015-10-12 03:26:01"
1244 | },
1245 | {
1246 | "name": "sebastian/recursion-context",
1247 | "version": "1.0.1",
1248 | "source": {
1249 | "type": "git",
1250 | "url": "https://github.com/sebastianbergmann/recursion-context.git",
1251 | "reference": "994d4a811bafe801fb06dccbee797863ba2792ba"
1252 | },
1253 | "dist": {
1254 | "type": "zip",
1255 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/994d4a811bafe801fb06dccbee797863ba2792ba",
1256 | "reference": "994d4a811bafe801fb06dccbee797863ba2792ba",
1257 | "shasum": ""
1258 | },
1259 | "require": {
1260 | "php": ">=5.3.3"
1261 | },
1262 | "require-dev": {
1263 | "phpunit/phpunit": "~4.4"
1264 | },
1265 | "type": "library",
1266 | "extra": {
1267 | "branch-alias": {
1268 | "dev-master": "1.0.x-dev"
1269 | }
1270 | },
1271 | "autoload": {
1272 | "classmap": [
1273 | "src/"
1274 | ]
1275 | },
1276 | "notification-url": "https://packagist.org/downloads/",
1277 | "license": [
1278 | "BSD-3-Clause"
1279 | ],
1280 | "authors": [
1281 | {
1282 | "name": "Jeff Welch",
1283 | "email": "whatthejeff@gmail.com"
1284 | },
1285 | {
1286 | "name": "Sebastian Bergmann",
1287 | "email": "sebastian@phpunit.de"
1288 | },
1289 | {
1290 | "name": "Adam Harvey",
1291 | "email": "aharvey@php.net"
1292 | }
1293 | ],
1294 | "description": "Provides functionality to recursively process PHP variables",
1295 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
1296 | "time": "2015-06-21 08:04:50"
1297 | },
1298 | {
1299 | "name": "sebastian/resource-operations",
1300 | "version": "1.0.0",
1301 | "source": {
1302 | "type": "git",
1303 | "url": "https://github.com/sebastianbergmann/resource-operations.git",
1304 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
1305 | },
1306 | "dist": {
1307 | "type": "zip",
1308 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
1309 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
1310 | "shasum": ""
1311 | },
1312 | "require": {
1313 | "php": ">=5.6.0"
1314 | },
1315 | "type": "library",
1316 | "extra": {
1317 | "branch-alias": {
1318 | "dev-master": "1.0.x-dev"
1319 | }
1320 | },
1321 | "autoload": {
1322 | "classmap": [
1323 | "src/"
1324 | ]
1325 | },
1326 | "notification-url": "https://packagist.org/downloads/",
1327 | "license": [
1328 | "BSD-3-Clause"
1329 | ],
1330 | "authors": [
1331 | {
1332 | "name": "Sebastian Bergmann",
1333 | "email": "sebastian@phpunit.de"
1334 | }
1335 | ],
1336 | "description": "Provides a list of PHP built-in functions that operate on resources",
1337 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
1338 | "time": "2015-07-28 20:34:47"
1339 | },
1340 | {
1341 | "name": "sebastian/version",
1342 | "version": "1.0.6",
1343 | "source": {
1344 | "type": "git",
1345 | "url": "https://github.com/sebastianbergmann/version.git",
1346 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
1347 | },
1348 | "dist": {
1349 | "type": "zip",
1350 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
1351 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
1352 | "shasum": ""
1353 | },
1354 | "type": "library",
1355 | "autoload": {
1356 | "classmap": [
1357 | "src/"
1358 | ]
1359 | },
1360 | "notification-url": "https://packagist.org/downloads/",
1361 | "license": [
1362 | "BSD-3-Clause"
1363 | ],
1364 | "authors": [
1365 | {
1366 | "name": "Sebastian Bergmann",
1367 | "email": "sebastian@phpunit.de",
1368 | "role": "lead"
1369 | }
1370 | ],
1371 | "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1372 | "homepage": "https://github.com/sebastianbergmann/version",
1373 | "time": "2015-06-21 13:59:46"
1374 | },
1375 | {
1376 | "name": "symfony/yaml",
1377 | "version": "v3.0.0",
1378 | "source": {
1379 | "type": "git",
1380 | "url": "https://github.com/symfony/yaml.git",
1381 | "reference": "177a015cb0e19ff4a49e0e2e2c5fc1c1bee07002"
1382 | },
1383 | "dist": {
1384 | "type": "zip",
1385 | "url": "https://api.github.com/repos/symfony/yaml/zipball/177a015cb0e19ff4a49e0e2e2c5fc1c1bee07002",
1386 | "reference": "177a015cb0e19ff4a49e0e2e2c5fc1c1bee07002",
1387 | "shasum": ""
1388 | },
1389 | "require": {
1390 | "php": ">=5.5.9"
1391 | },
1392 | "type": "library",
1393 | "extra": {
1394 | "branch-alias": {
1395 | "dev-master": "3.0-dev"
1396 | }
1397 | },
1398 | "autoload": {
1399 | "psr-4": {
1400 | "Symfony\\Component\\Yaml\\": ""
1401 | },
1402 | "exclude-from-classmap": [
1403 | "/Tests/"
1404 | ]
1405 | },
1406 | "notification-url": "https://packagist.org/downloads/",
1407 | "license": [
1408 | "MIT"
1409 | ],
1410 | "authors": [
1411 | {
1412 | "name": "Fabien Potencier",
1413 | "email": "fabien@symfony.com"
1414 | },
1415 | {
1416 | "name": "Symfony Community",
1417 | "homepage": "https://symfony.com/contributors"
1418 | }
1419 | ],
1420 | "description": "Symfony Yaml Component",
1421 | "homepage": "https://symfony.com",
1422 | "time": "2015-11-30 12:36:17"
1423 | }
1424 | ],
1425 | "aliases": [],
1426 | "minimum-stability": "beta",
1427 | "stability-flags": [],
1428 | "prefer-stable": false,
1429 | "prefer-lowest": false,
1430 | "platform": {
1431 | "php": ">=5.4.0"
1432 | },
1433 | "platform-dev": []
1434 | }
1435 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
17 |
18 | tests
19 |
20 |
21 |
22 |
23 |
24 | src
25 |
26 | src/**/Resources
27 | src/**/Exceptions
28 |
29 |
30 |
31 |
32 |
33 |
34 | integration
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Encoders/AbstractEncoder.php:
--------------------------------------------------------------------------------
1 | data = $data;
19 | }
20 | }
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Encoders/AbstractFilesystemEncoder.php:
--------------------------------------------------------------------------------
1 | filesystem = new Filesystem();
21 | }
22 |
23 | public function __destruct()
24 | {
25 | $this->cleanup();
26 | }
27 |
28 | /**
29 | * Get a temporary file and populate its data
30 | *
31 | * Any files created with this function will be destroyed on cleanup/destruction.
32 | *
33 | * @param string $data Data to populate file with
34 | * @param string $prefix Optional file prefix
35 | * @return string
36 | */
37 | protected function getTempFile($data = null, $prefix = 'img-mngr-')
38 | {
39 | $file = tempnam(sys_get_temp_dir(), $prefix);
40 | if ($data) {
41 | file_put_contents($file, $data);
42 | } else {
43 | $this->filesystem->touch($file);
44 | }
45 |
46 | $this->temp_files[] = $file;
47 | return $file;
48 | }
49 |
50 | /**
51 | * Remove all temp files
52 | */
53 | protected function cleanup()
54 | {
55 | foreach ($this->temp_files as $file) {
56 | $this->filesystem->remove($file);
57 | }
58 | $this->temp_files = [];
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Encoders/EncoderInterface.php:
--------------------------------------------------------------------------------
1 | filter = $filter;
31 | $this->resolution = $resolution;
32 | }
33 |
34 | /**
35 | * Check if we support this data-type.
36 | *
37 | * @param string $data
38 | *
39 | * @return bool
40 | */
41 | public function supports(&$data)
42 | {
43 | $inspector = new DataInspector();
44 |
45 | // Normal image formats supported
46 | if ($inspector->getImageFormat($data) !== null) {
47 | return true;
48 | }
49 |
50 | // PDF supported
51 | if ($inspector->isPdf($data)) {
52 | return true;
53 | }
54 |
55 | return false;
56 | }
57 |
58 | /**
59 | * {@inheritdoc}
60 | */
61 | public function createVariation(ImageFormat $output_format, $quality,
62 | ImageDimensions $dimensions = null, ImageCropDimensions $crop_dimensions = null)
63 | {
64 | $src = $this->getTempFile($this->data);
65 | $img = new \Imagick();
66 | $img->setResolution($this->resolution, $this->resolution);
67 | $img->readImage($src);
68 | $img->setIteratorIndex(0);
69 |
70 | // Flatten images here helps the encoder to get rid of the black background
71 | // that appears on encoded image files.
72 | $img = $img->flattenImages();
73 | $img->setImageFormat((string) $output_format->value());
74 | $img->setImageCompressionQuality($quality);
75 |
76 | if (null !== $crop_dimensions) {
77 | $img->cropImage(
78 | $crop_dimensions->getWidth(),
79 | $crop_dimensions->getHeight(),
80 | $crop_dimensions->getX(),
81 | $crop_dimensions->getY()
82 | );
83 | }
84 |
85 | if (null !== $dimensions) {
86 | $img->resizeImage(
87 | $dimensions->getWidth() ?: 0,
88 | $dimensions->getHeight() ?: 0,
89 | $this->filter,
90 | 1,
91 | false
92 | );
93 | }
94 |
95 | return $img->getImageBlob();
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Encoders/InterventionEncoder.php:
--------------------------------------------------------------------------------
1 | getImageFormat($data) !== null;
26 | }
27 |
28 | /**
29 | * {@inheritdoc}
30 | */
31 | public function createVariation(ImageFormat $output_format, $quality,
32 | ImageDimensions $dimensions = null, ImageCropDimensions $crop_dimensions = null)
33 | {
34 | try {
35 | $img = new InterventionImage($this->data);
36 | } catch (\Intervention\Image\Exception\InvalidImageDataStringException $e) {
37 | throw new BadImageException('Bad image data', 0, $e);
38 | } catch (\Intervention\Image\Exception\ImageNotFoundException $e) {
39 | throw new BadImageException('Not an image', 0, $e);
40 | }
41 |
42 | if ($dimensions) {
43 | if ($dimensions->getGrab()) {
44 | $img->grab($dimensions->getWidth(), $dimensions->getHeight());
45 | } else {
46 | $img->resize(
47 | $dimensions->getWidth(),
48 | $dimensions->getHeight(),
49 | $dimensions->getMaintainRatio(),
50 | $dimensions->canUpscale()
51 | );
52 | }
53 | }
54 |
55 | return $img->encode($output_format->key(), $quality);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Entities/Image.php:
--------------------------------------------------------------------------------
1 | key = $key;
47 | }
48 |
49 | /**
50 | * Flush image data from memory.
51 | *
52 | * @param bool $collect_garbage
53 | */
54 | public function flush($collect_garbage = true)
55 | {
56 | $this->data = null;
57 |
58 | if ($collect_garbage) {
59 | gc_collect_cycles();
60 | }
61 | }
62 |
63 | /**
64 | * Check the data data for the image type.
65 | *
66 | * If unknown or no data data is present, null will be returned
67 | *
68 | * @deprecated since 1.1.0 Use DataInspector::getImageFormat() instead
69 | *
70 | * @return ImageFormat|null
71 | */
72 | public function getDataFormat()
73 | {
74 | $inspector = new DataInspector();
75 |
76 | return $inspector->getImageFormat($this->data);
77 | }
78 |
79 | /**
80 | * Set the remote key.
81 | *
82 | * @param string $key
83 | *
84 | * @return $this
85 | */
86 | public function setKey($key)
87 | {
88 | $this->key = $key;
89 | $this->persistent = false;
90 |
91 | return $this;
92 | }
93 |
94 | /**
95 | * Get the remote key.
96 | *
97 | * @return string
98 | */
99 | public function getKey()
100 | {
101 | return $this->key;
102 | }
103 |
104 | /**
105 | * Check if the image is known to exist on the remote.
106 | *
107 | * @return bool
108 | */
109 | public function isPersistent()
110 | {
111 | return $this->persistent;
112 | }
113 |
114 | /**
115 | * Check if the image data has been loaded.
116 | *
117 | * @return bool
118 | */
119 | public function isHydrated()
120 | {
121 | return !empty($this->data) && !is_null($this->data);
122 | }
123 |
124 | /**
125 | * Set image data.
126 | *
127 | * @param string $data
128 | *
129 | * @return $this
130 | */
131 | public function setData($data)
132 | {
133 | $this->data = $data;
134 | $this->persistent = false;
135 |
136 | // Guess MIME-type
137 | $inspector = new DataInspector();
138 | $this->raw_content_type = $inspector->guessMimeType($this->data);
139 |
140 | return $this;
141 | }
142 |
143 | /**
144 | * Return the content-type of data specified.
145 | *
146 | * @return string
147 | */
148 | public function getMimeType()
149 | {
150 | return $this->raw_content_type;
151 | }
152 |
153 | /**
154 | * Get image data.
155 | *
156 | * @return string
157 | */
158 | public function getData()
159 | {
160 | return $this->data;
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Entities/ImageCropDimensions.php:
--------------------------------------------------------------------------------
1 | width = $width;
39 | $this->height = $height;
40 | $this->x = $x;
41 | $this->y = $y;
42 | }
43 |
44 | /**
45 | * Creates a signature containing the crop-dimension specification.
46 | *
47 | * @return string
48 | */
49 | public function __toString()
50 | {
51 | return 'x'.($this->getX() ?: '-').
52 | 'y'.($this->getY() ?: '-').
53 | 'w'.($this->getWidth() ?: '-').
54 | 'h'.($this->getHeight() ?: '-');
55 | }
56 |
57 | /**
58 | * Set the start pixel in the x-axis for the horizontal crop of the image.
59 | *
60 | * @param int $x
61 | *
62 | * @return $this
63 | */
64 | public function setX($x)
65 | {
66 | $this->x = $x;
67 |
68 | return $this;
69 | }
70 |
71 | /**
72 | * Set the start pixel in the y-axis for the vertical crop of the image.
73 | *
74 | * @param int $y
75 | *
76 | * @return $this
77 | */
78 | public function setY($y)
79 | {
80 | $this->y = $y;
81 |
82 | return $this;
83 | }
84 |
85 | /**
86 | * Sets the horizontal boundary of the cropping dimensions taking only an
87 | * integer relative to the left of the image in pixels, starting from a zero-indexed row.
88 | *
89 | * @param int $width
90 | *
91 | * @return $this
92 | */
93 | public function setWidth($width)
94 | {
95 | $this->width = $width;
96 |
97 | return $this;
98 | }
99 |
100 | /**
101 | * Sets the vertical boundary of the cropping dimensions taking only an
102 | * integer relative to the top of the image in pixels, starting from a zero-indexed row.
103 | *
104 | * @param int $height
105 | *
106 | * @return $this
107 | */
108 | public function setHeight($height)
109 | {
110 | $this->height = $height;
111 |
112 | return $this;
113 | }
114 |
115 | /**
116 | * Get $x crop start pixel in the x-axis of the image.
117 | *
118 | * @return int
119 | */
120 | public function getX()
121 | {
122 | return $this->x;
123 | }
124 |
125 | /**
126 | * Get $y crop start pixel in the x-axis of the image.
127 | *
128 | * @return int
129 | */
130 | public function getY()
131 | {
132 | return $this->y;
133 | }
134 |
135 | /**
136 | * Get crop width.
137 | *
138 | * @return int
139 | */
140 | public function getWidth()
141 | {
142 | return $this->width;
143 | }
144 |
145 | /**
146 | * Get crop height.
147 | *
148 | * @return int
149 | */
150 | public function getHeight()
151 | {
152 | return $this->height;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Entities/ImageDimensions.php:
--------------------------------------------------------------------------------
1 | width = $width;
48 | $this->height = $height;
49 | $this->upscale = $upscale;
50 | $this->maintain_ratio = $maintain_ratio;
51 | $this->grab = $grab;
52 | }
53 |
54 | /**
55 | * Creates a signature containing the dimension specification.
56 | *
57 | * @return string
58 | */
59 | public function __toString()
60 | {
61 | return 'x'.($this->getWidth() ?: '-').
62 | 'y'.($this->getHeight() ?: '-').
63 | 'u'.($this->canUpscale() ? '1' : '0').
64 | 'r'.($this->getMaintainRatio() ? '1' : '0').
65 | 'g'.($this->getGrab() ? 'g' : '0');
66 | }
67 |
68 | /**
69 | * Get proposed width.
70 | *
71 | * @return int
72 | */
73 | public function getWidth()
74 | {
75 | return $this->width;
76 | }
77 |
78 | /**
79 | * Get proposed height.
80 | *
81 | * @return int
82 | */
83 | public function getHeight()
84 | {
85 | return $this->height;
86 | }
87 |
88 | /**
89 | * Get image aspect ratio based on the width and height
90 | * provided.
91 | *
92 | * Function uses binary calculator division to 3 decimal places.
93 | *
94 | * @return string
95 | */
96 | public function getAspectRatio()
97 | {
98 | return bcdiv($this->width, $this->height, 3);
99 | }
100 |
101 | /**
102 | * Check if the image can be upscaled.
103 | *
104 | * @return bool
105 | */
106 | public function canUpscale()
107 | {
108 | return $this->upscale;
109 | }
110 |
111 | /**
112 | * Check if we should maintain the image ratio.
113 | *
114 | * @return bool
115 | */
116 | public function getMaintainRatio()
117 | {
118 | return $this->maintain_ratio;
119 | }
120 |
121 | /**
122 | * Check if also crop as well as resize.
123 | *
124 | * @return bool
125 | */
126 | public function getGrab()
127 | {
128 | return $this->grab;
129 | }
130 |
131 | /**
132 | * {@inheritdoc}
133 | */
134 | public function serialise()
135 | {
136 | return json_encode([
137 | 'width' => $this->getWidth(),
138 | 'height' => $this->getHeight(),
139 | 'upscale' => (bool) $this->canUpscale(),
140 | 'grab' => (bool) $this->getGrab(),
141 | 'maintain-ratio' => (bool) $this->getMaintainRatio(),
142 | ]);
143 | }
144 |
145 | /**
146 | * {@inheritdoc}
147 | */
148 | public static function deserialise($json)
149 | {
150 | if (empty($json)) {
151 | throw \InvalidArgumentException('Json string is empty');
152 | }
153 |
154 | $object_data = json_decode($json, true);
155 |
156 | $instance = new static(
157 | isset($object_data['width']) ? $object_data['width'] : null,
158 | isset($object_data['height']) ? $object_data['height'] : null,
159 | isset($object_data['maintain-ratio']) ? $object_data['maintain-ratio'] : null,
160 | isset($object_data['upscale']) ? $object_data['upscale'] : null,
161 | isset($object_data['grab']) ? $object_data['grab'] : null
162 | );
163 |
164 | return $instance;
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Entities/ImageMetadata.php:
--------------------------------------------------------------------------------
1 | mimetype;
60 | }
61 |
62 | /**
63 | * Sets the Internet media type.
64 | *
65 | * @param string $mimetype the mimetype
66 | *
67 | * @return self
68 | */
69 | public function setMimetype($mimetype)
70 | {
71 | $this->mimetype = $mimetype;
72 |
73 | return $this;
74 | }
75 |
76 | /**
77 | * Gets the ImageManager format.
78 | *
79 | * @return ImageFormat
80 | */
81 | public function getFormat()
82 | {
83 | return $this->format;
84 | }
85 |
86 | /**
87 | * Sets the ImageManager format.
88 | *
89 | * @param ImageFormat $format the format
90 | *
91 | * @return self
92 | */
93 | public function setFormat(ImageFormat $format)
94 | {
95 | $this->format = $format;
96 |
97 | return $this;
98 | }
99 |
100 | /**
101 | * Gets the Image resolution.
102 | *
103 | * @return ImageDimensions
104 | */
105 | public function getResolution()
106 | {
107 | return $this->resolution;
108 | }
109 |
110 | /**
111 | * Sets the Image resolution.
112 | *
113 | * @param ImageDimensions
114 | *
115 | * @return self
116 | */
117 | public function setResolution(ImageDimensions $resolution)
118 | {
119 | $this->resolution = $resolution;
120 |
121 | return $this;
122 | }
123 |
124 | /**
125 | * Gets the Orientation of the image.
126 | *
127 | * @return ImageOrientation
128 | */
129 | public function getOrientation()
130 | {
131 | return $this->orientation;
132 | }
133 |
134 | /**
135 | * Sets the Orientation of the image.
136 | *
137 | * @param ImageOrientation $orientation the orientation
138 | *
139 | * @return self
140 | */
141 | public function setOrientation(ImageOrientation $orientation)
142 | {
143 | $this->orientation = $orientation;
144 |
145 | return $this;
146 | }
147 |
148 | /**
149 | * Gets the Source image dimensions.
150 | *
151 | * @return ImageDimensions
152 | */
153 | public function getDimensions()
154 | {
155 | return $this->dimensions;
156 | }
157 |
158 | /**
159 | * Sets the Source image dimensions.
160 | *
161 | * @param ImageDimensions $dimensions the dimensions
162 | *
163 | * @return self
164 | */
165 | public function setDimensions(ImageDimensions $dimensions)
166 | {
167 | $this->dimensions = $dimensions;
168 |
169 | return $this;
170 | }
171 |
172 | /**
173 | * {@inheritdoc}
174 | */
175 | public function serialise()
176 | {
177 | return json_encode([
178 | 'mimetype' => $this->getMimetype(),
179 | 'format' => $this->getFormat()->value(),
180 | 'resolution' => $this->getResolution()->serialise(),
181 | 'orientation' => $this->getOrientation()->value(),
182 | 'dimensions' => $this->getDimensions()->serialise(),
183 | ]);
184 | }
185 |
186 | /**
187 | * {@inheritdoc}
188 | */
189 | public static function deserialise($json)
190 | {
191 | if (empty($json)) {
192 | throw new InvalidImageMetadataException();
193 | }
194 |
195 | $object_data = json_decode($json, true);
196 |
197 | // MIME-type is an minimum requirement for metadata
198 | if (!isset($object_data['mimetype'])) {
199 | throw new InvalidImageMetadataException();
200 | }
201 |
202 | $instance = new static();
203 | $instance
204 | ->setMimeType($object_data['mimetype'])
205 | ->setFormat(isset($object_data['format']) ? ImageFormat::memberByValue($object_data['format']) : null)
206 | ->setResolution(isset($object_data['resolution']) ? ImageDimensions::deserialise($object_data['resolution']) : null)
207 | ->setOrientation(isset($object_data['orientation']) ? ImageOrientation::memberByValue($object_data['orientation']) : null)
208 | ->setDimensions(isset($object_data['dimensions']) ? ImageDimensions::deserialise($object_data['dimensions']) : null)
209 | ;
210 |
211 | return $instance;
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Entities/ImageVariation.php:
--------------------------------------------------------------------------------
1 | dimensions = $dimensions;
37 | $this->format = $format;
38 | $this->quality = $quality;
39 | $this->crop_dimensions = $crop_dimensions;
40 | }
41 |
42 | /**
43 | * Get variation or image key.
44 | *
45 | * @param bool $parent
46 | *
47 | * @return string
48 | */
49 | public function getKey($parent = false)
50 | {
51 | if ($parent) {
52 | return parent::getKey();
53 | } else {
54 | // TODO: add a configurable naming scheme here
55 | return parent::getKey().'~'.$this;
56 | }
57 | }
58 |
59 | /**
60 | * Get Dimensions.
61 | *
62 | * @return ImageDimensions
63 | */
64 | public function getDimensions()
65 | {
66 | return $this->dimensions;
67 | }
68 |
69 | /**
70 | * Get Crop Dimensions.
71 | *
72 | * @return ImageCropDimensions
73 | */
74 | public function getCropDimensions()
75 | {
76 | return $this->crop_dimensions;
77 | }
78 |
79 | /**
80 | * Get Format.
81 | *
82 | * @return ImageFormat
83 | */
84 | public function getFormat()
85 | {
86 | return $this->format;
87 | }
88 |
89 | /**
90 | * Get Quality.
91 | *
92 | * @return int
93 | */
94 | public function getQuality()
95 | {
96 | return $this->quality;
97 | }
98 |
99 | /**
100 | * Creates a signature based on the variations applied.
101 | *
102 | * @return string
103 | */
104 | public function __toString()
105 | {
106 | $out = 'q'.($this->getQuality() ?: self::DEFAULT_QUALITY).
107 | ',d'.($this->getDimensions() ?: '--').
108 | ',c'.($this->getCropDimensions() ?: '--').
109 | '.'.(string) $this->getFormat()->value();
110 |
111 | return $out;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Entities/Interfaces/SerialisableInterface.php:
--------------------------------------------------------------------------------
1 | buffer($data);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Services/ImageInspector.php:
--------------------------------------------------------------------------------
1 | isHydrated()) {
31 | throw new ImageManagerException(ImageManager::ERR_NOT_HYDRATED);
32 | }
33 |
34 | $img = new \Imagick();
35 | $img->readImageBlob($image->getData());
36 | $width = $img->getImageWidth();
37 | $height = $img->getImageHeight();
38 |
39 | if ($width >= $height) {
40 | return ImageOrientation::LANDSCAPE();
41 | }
42 |
43 | return ImageOrientation::PORTRAIT();
44 | }
45 |
46 | /**
47 | * Return image dimensions based on the Image object provided
48 | *
49 | * @param Image $image
50 | * @return ImageDimensions
51 | */
52 | protected function getImageDimensions(Image $image)
53 | {
54 | if (!$image->isHydrated()) {
55 | throw new ImageManagerException(ImageManager::ERR_NOT_HYDRATED);
56 | }
57 |
58 | $img = new \Imagick();
59 | $img->readImageBlob($image->getData());
60 |
61 | $dimensions = new ImageDimensions(
62 | $img->getImageWidth(),
63 | $img->getImageHeight()
64 | );
65 |
66 | return $dimensions;
67 | }
68 |
69 | /**
70 | * Return image resolution based on the Image object provided
71 | *
72 | * NB: This function may return the image density or DPI, not it's output resolution.
73 | *
74 | * @param Image $image
75 | * @return ImageDimensions
76 | * @throws ImageManagerException
77 | */
78 | protected function getImageResolution(Image $image)
79 | {
80 | if (!$image->isHydrated()) {
81 | throw new ImageManagerException(ImageManager::ERR_NOT_HYDRATED);
82 | }
83 |
84 | $img = new \Imagick();
85 | $img->readImageBlob($image->getData());
86 | $d = $img->getImageResolution();
87 |
88 | $dimensions = new ImageDimensions($d['x'], $d['y']);
89 |
90 | return $dimensions;
91 | }
92 |
93 | /**
94 | * @param Image $image
95 | * @return ImageMetadata
96 | * @throws ImageManagerException
97 | */
98 | public function getImageMetadata(Image $image)
99 | {
100 | if (!$image->isHydrated()) {
101 | throw new ImageManagerException(ImageManager::ERR_NOT_HYDRATED);
102 | }
103 |
104 | if ($image instanceof ImageVariation) {
105 | throw new ImageManagerException(self::ERR_SOURCE_IMAGE);
106 | }
107 |
108 | $metadata = new ImageMetadata();
109 | $data_inspector = new DataInspector();
110 | $data = $image->getData();
111 |
112 | if ($data_inspector->isPdf($data)) {
113 | $format = ImageFormat::PDF();
114 | } else {
115 | $format = $data_inspector->getImageFormat($data);
116 | }
117 |
118 | $metadata->setMimetype($data_inspector->guessMimeType($data))
119 | ->setFormat($format)
120 | ->setResolution($this->getImageResolution($image))
121 | ->setOrientation($this->getImageOrientation($image))
122 | ->setDimensions($this->getImageDimensions($image));
123 |
124 | return $metadata;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Services/ImageManager.php:
--------------------------------------------------------------------------------
1 | filesystem = $filesystem;
97 | $this->cache_pool = $cache_pool;
98 | $this->encoders = $encoders;
99 | $this->validate_tags = $validate_tags;
100 |
101 | if (!$this->encoders) {
102 | $this->addEncoder(new InterventionEncoder());
103 | }
104 | }
105 |
106 | /**
107 | * Push a local image/variation to the remote.
108 | *
109 | * If it is not hydrated this function will throw an exception
110 | *
111 | * @param Image $image
112 | * @param bool $overwrite
113 | *
114 | * @return $this
115 | *
116 | * @throws ImageManagerException
117 | * @throws ObjectAlreadyExistsException
118 | * @throws \Exception
119 | */
120 | public function push(Image $image, $overwrite = true)
121 | {
122 | if (!$image->isHydrated() && ($image instanceof ImageVariation)) {
123 | // A pull on a variation will check if the variation exists, if not create it
124 | $this->pull($image);
125 | }
126 |
127 | if (!$image->isHydrated()) {
128 | throw new ImageManagerException(self::ERR_NOT_HYDRATED);
129 | }
130 |
131 | if (!$overwrite && $this->tagExists($image->getKey()) === true) {
132 | throw new ObjectAlreadyExistsException(self::ERR_ALREADY_EXISTS);
133 | }
134 |
135 | $adapter = $this->filesystem->getAdapter();
136 | if ($adapter instanceof MetadataSupporter) {
137 | $metadata = [];
138 | if ($image->getMimeType()) {
139 | // Set image ContentType on remote filesystem
140 | $metadata['ContentType'] = $image->getMimeType();
141 | }
142 | $adapter->setMetadata($image->getKey(), $metadata);
143 | }
144 |
145 | // Retrieve source image metadata
146 | $metadata = null;
147 | if (!($image instanceof ImageVariation)) {
148 | $image_manipulation = new ImageInspector();
149 | $metadata = $image_manipulation->getImageMetadata($image);
150 | }
151 |
152 | try {
153 | $this->filesystem->write($image->getKey(), $image->getData(), $overwrite);
154 | $image->__friendSet('persistent', true);
155 | $this->tag($image->getKey(), $metadata);
156 | } catch (FileAlreadyExists $e) {
157 | $this->tag($image->getKey(), $metadata);
158 | throw new ObjectAlreadyExistsException(self::ERR_ALREADY_EXISTS);
159 | }
160 |
161 | return $this;
162 | }
163 |
164 | /**
165 | * Get an image/variation from the remote.
166 | *
167 | * If this image is a variation that does not exist, an attempt will be made to retrieve the parent first
168 | * then create the variation. The variation should be optionally pushed if it's Image::isPersistent() function
169 | * returns false.
170 | *
171 | * @param Image $image
172 | *
173 | * @return $this
174 | *
175 | * @throws ImageManagerException
176 | */
177 | public function pull(Image $image)
178 | {
179 | if ($image instanceof ImageVariation) {
180 | $this->pullVariation($image);
181 | } else {
182 | $this->pullSource($image);
183 | }
184 |
185 | return $this;
186 | }
187 |
188 | /**
189 | * Get meta information about the source image from the cache layer.
190 | *
191 | * @param Image|string $image
192 | *
193 | * @return ImageMetadata
194 | */
195 | public function getMetadata($image)
196 | {
197 | // If the $image is a variation, refer to its parent
198 | // for the metadata.
199 | if ($image instanceof ImageVariation) {
200 | $img_key = $image->getKey(true);
201 | } elseif ($image instanceof Image) {
202 | $img_key = $image->getKey();
203 | } else {
204 | $img_key = $image;
205 | }
206 |
207 | // Retrieve from cache array if image metadata exists
208 | if (isset($this->metadata_cache[$img_key])) {
209 | return $this->metadata_cache[$img_key];
210 | }
211 |
212 | $item = $this->cache_pool->getItem('remote.'.$img_key);
213 | $metadata = ImageMetadata::deserialise($item->get());
214 |
215 | // Set cache item
216 | $this->metadata_cache[$img_key] = $metadata;
217 |
218 | return $metadata;
219 | }
220 |
221 | /**
222 | * Pull a source (or variation) image.
223 | *
224 | * @param Image $image
225 | *
226 | * @throws NotExistsException
227 | */
228 | protected function pullSource(Image $image)
229 | {
230 | // Image is a source image
231 | if ($this->tagExists($image->getKey()) === false) {
232 | if (!$this->validate_tags || !$this->validateTag($image)) {
233 | throw new NotExistsException(self::ERR_NOT_EXISTS);
234 | }
235 | }
236 |
237 | try {
238 | // Get source data
239 | $image->setData($this->filesystem->read($image->getKey()));
240 | $image->__friendSet('persistent', true);
241 | } catch (FileNotFoundException $e) {
242 | // Image not found
243 | $this->untag($image->getKey());
244 | throw new NotExistsException(self::ERR_NOT_EXISTS);
245 | }
246 | }
247 |
248 | /**
249 | * Pull a variation image, if the variation does not exist, try pulling the source and creating the variation.
250 | *
251 | * @param ImageVariation $image
252 | *
253 | * @throws NotExistsException
254 | */
255 | protected function pullVariation(ImageVariation $image)
256 | {
257 | try {
258 | // First, check if the variation exists on the remote
259 | $this->pullSource($image);
260 | } catch (NotExistsException $e) {
261 | // Variation does not exist, try pulling the parent data and creating the variation
262 | try {
263 | $parent = new Image($image->getKey(true));
264 |
265 | if ($this->tagExists($image->getKey(true)) === false) {
266 | if (!$this->validate_tags || !$this->validateTag($parent)) {
267 | throw new NotExistsException(self::ERR_PARENT_NOT_EXISTS);
268 | }
269 | }
270 |
271 | $data = $this->filesystem->read($image->getKey(true));
272 |
273 | // Resample
274 | $parent->setData($data);
275 | $this->hydrateVariation($parent, $image);
276 | $parent->flush();
277 | } catch (FileNotFoundException $e) {
278 | // No image exists
279 | throw new NotExistsException(self::ERR_PARENT_NOT_EXISTS);
280 | }
281 | }
282 | }
283 |
284 | /**
285 | * Mark an image as existing or not existing on the remote.
286 | *
287 | * This function has no effect if there is no cache pool.
288 | *
289 | * @param Image $image
290 | * @param bool $exists
291 | *
292 | * @return $this
293 | */
294 | public function setImageExists(Image $image, $exists)
295 | {
296 | if ($exists) {
297 | $this->tag($image->getKey());
298 | } else {
299 | $this->untag($image->getKey());
300 | }
301 |
302 | return $this;
303 | }
304 |
305 | /**
306 | * Mark a file as existing on the remote.
307 | * If metadata object is populated, that metadata will be stored
308 | * against the image tag stored in the cache layer.
309 | *
310 | * @param string $key
311 | * @param ImageMetadata|null $metadata
312 | *
313 | * @return $this
314 | */
315 | protected function tag($key, ImageMetadata $metadata = null)
316 | {
317 | if (!$this->cache_pool) {
318 | return null;
319 | }
320 |
321 | $item = $this->cache_pool->getItem('remote.'.$key);
322 |
323 | if (null !== $metadata) {
324 | $value = $metadata->serialise();
325 | } else {
326 | $value = 1;
327 | }
328 |
329 | $item->set($value, null);
330 |
331 | return $this;
332 | }
333 |
334 | /**
335 | * Mark a file as absent on the remote.
336 | *
337 | * @param string $key
338 | *
339 | * @return $this
340 | */
341 | protected function untag($key)
342 | {
343 | if (!$this->cache_pool) {
344 | return null;
345 | }
346 |
347 | $item = $this->cache_pool->getItem('remote.'.$key);
348 | $item->delete();
349 |
350 | return $this;
351 | }
352 |
353 | /**
354 | * Check if a file exists on the remote.
355 | *
356 | * Returns null if caching isn't available (and unsure), else a boolean value
357 | *
358 | * @param string $key
359 | *
360 | * @return bool|null
361 | */
362 | protected function tagExists($key)
363 | {
364 | if (!$this->cache_pool) {
365 | return null;
366 | }
367 |
368 | $item = $this->cache_pool->getItem('remote.'.$key);
369 |
370 | return $item->exists();
371 | }
372 |
373 | /**
374 | * Delete an image from the remote.
375 | *
376 | * @param Image $image
377 | *
378 | * @return $this
379 | */
380 | public function remove(Image $image)
381 | {
382 | $this->filesystem->delete($image->getKey());
383 | $this->untag($image->getKey());
384 |
385 | return $this;
386 | }
387 |
388 | /**
389 | * Save the image to the local filesystem.
390 | *
391 | * The extension of the filename is ignored, either the original format or the variation format will be used.
392 | * If the image is not hydrated a pull will be attempted.
393 | *
394 | * @param Image $image
395 | * @param string $filename Path to save the image
396 | *
397 | * @return $this
398 | */
399 | public function save(Image $image, $filename)
400 | {
401 | if (!$image->isHydrated()) {
402 | // Auto-pull
403 | $this->pull($image);
404 | }
405 |
406 | file_put_contents($filename, $image->getData());
407 |
408 | return $this;
409 | }
410 |
411 | /**
412 | * Hydrate and render an image variation with parent data.
413 | *
414 | * You can use this to create a variation with a source image
415 | *
416 | * @param Image $parent
417 | * @param ImageVariation $variation
418 | *
419 | * @return ImageVariation
420 | *
421 | * @throws BadImageException
422 | * @throws ImageManagerException
423 | */
424 | protected function hydrateVariation(Image $parent, ImageVariation &$variation)
425 | {
426 | if (!$parent->isHydrated()) {
427 | throw new ImageManagerException('Parent: '.self::ERR_NOT_HYDRATED);
428 | }
429 |
430 | $quality = $variation->getQuality() ?: 90;
431 |
432 | if ($quality < 1) {
433 | $quality = 1;
434 | } elseif ($quality > 100) {
435 | $quality = 100;
436 | }
437 |
438 | $variation->setData(null);
439 | $input = $parent->getData();
440 |
441 | foreach ($this->encoders as $encoder) {
442 | if ($encoder->supports($input)) {
443 | $encoder->setData($input);
444 | $variation->setData(
445 | $encoder->createVariation(
446 | $variation->getFormat(),
447 | $quality,
448 | $variation->getDimensions(),
449 | $variation->getCropDimensions()
450 | )
451 | );
452 | $encoder->setData(null);
453 | break;
454 | }
455 | }
456 |
457 | if (!$variation->getData()) {
458 | throw new NoSupportedEncoderException('There is no known encoder for this data type');
459 | }
460 |
461 | return $variation;
462 | }
463 |
464 | /**
465 | * Create a new image variation from a local source image.
466 | *
467 | * @param Image $source
468 | * @param ImageFormat $format
469 | * @param int $quality
470 | * @param ImageDimensions|null $dimensions
471 | * @param ImageCropDimensions|null $crop_dimensions
472 | *
473 | * @return ImageVariation
474 | *
475 | * @throws ImageManagerException
476 | * @throws NoSupportedEncoderException
477 | */
478 | public function createVariation(
479 | Image $source,
480 | ImageFormat $format,
481 | $quality = ImageVariation::DEFAULT_QUALITY,
482 | ImageDimensions $dimensions = null,
483 | ImageCropDimensions $crop_dimensions = null
484 | ) {
485 | $var = new ImageVariation($source->getKey(), $format, $quality, $dimensions, $crop_dimensions);
486 |
487 | return $this->hydrateVariation($source, $var);
488 | }
489 |
490 | /**
491 | * Create a new image from a filename and hydrate it.
492 | *
493 | * @param string $filename
494 | * @param string|null $key
495 | *
496 | * @return Image
497 | *
498 | * @throws IoException
499 | */
500 | public function loadFromFile($filename, $key = null)
501 | {
502 | if (!is_readable($filename)) {
503 | throw new IoException('File not readable: '.$filename);
504 | }
505 |
506 | if (!$key) {
507 | $key = basename($filename);
508 | }
509 |
510 | $image = new Image($key);
511 | $image->setData(file_get_contents($filename));
512 |
513 | return $image;
514 | }
515 |
516 | /**
517 | * Create a new image from memory and hydrate it.
518 | *
519 | * @param string $data
520 | * @param string $key
521 | *
522 | * @return Image
523 | */
524 | public function load($data, $key)
525 | {
526 | $image = new Image($key);
527 | $image->setData($data);
528 |
529 | return $image;
530 | }
531 |
532 | /**
533 | * Check if an image exists on the remote.
534 | *
535 | * This will check the cache pool if one exists, else it will talk to the remote filesystem to check if
536 | * the image exists.
537 | *
538 | * @param Image $image
539 | *
540 | * @return bool
541 | */
542 | public function exists(Image $image)
543 | {
544 | $key = $image->getKey();
545 |
546 | $tag_exists = $this->tagExists($key);
547 | if ($tag_exists !== null) {
548 | return $tag_exists;
549 | }
550 |
551 | return $this->filesystem->has($key);
552 | }
553 |
554 | /**
555 | * Get all registered encoders.
556 | *
557 | * @return EncoderInterface[]
558 | */
559 | public function getEncoders()
560 | {
561 | return $this->encoders;
562 | }
563 |
564 | /**
565 | * Set encoders.
566 | *
567 | * @param EncoderInterface[] $encoders
568 | *
569 | * @return $this
570 | */
571 | public function setEncoders(array $encoders)
572 | {
573 | $this->encoders = $encoders;
574 |
575 | return $this;
576 | }
577 |
578 | /**
579 | * Add an encoder.
580 | *
581 | * @param EncoderInterface $encoder
582 | * @param bool $prepend Prepend to the list instead of appending
583 | *
584 | * @return $this
585 | */
586 | public function addEncoder(EncoderInterface $encoder, $prepend = false)
587 | {
588 | if ($prepend) {
589 | array_unshift($this->encoders, $encoder);
590 | } else {
591 | $this->encoders[] = $encoder;
592 | }
593 |
594 | return $this;
595 | }
596 |
597 | /**
598 | * Rename a file
599 | *
600 | * TODO: Potentially fix https://github.com/KnpLabs/Gaufrette/issues/374 here.
601 | *
602 | * @param string $source_key
603 | * @param string $target_key
604 | *
605 | * @return $this
606 | */
607 | public function rename($source_key, $target_key)
608 | {
609 | $this->filesystem->rename($source_key, $target_key);
610 |
611 | $metadata = null;
612 | if ($this->tagExists($source_key)) {
613 | $metadata = $this->getMetadata($source_key);
614 | $this->untag($source_key);
615 | }
616 |
617 | $this->tag($target_key, $metadata);
618 |
619 | return $this;
620 | }
621 |
622 | /**
623 | * Check with the filesystem if the image exists and update the image key cache.
624 | *
625 | * @param Image $image
626 | *
627 | * @return bool
628 | */
629 | protected function validateTag(Image $image)
630 | {
631 | $exists = $this->filesystem->has($image->getKey());
632 | $this->setImageExists($image, $exists);
633 |
634 | return $exists;
635 | }
636 | }
637 |
--------------------------------------------------------------------------------
/src/Bravo3/ImageManager/Traits/FriendTrait.php:
--------------------------------------------------------------------------------
1 | __friends) ? $this->__friends : [];
18 |
19 | $trace = debug_backtrace();
20 | if (isset($trace[1]['class']) && in_array($trace[1]['class'], $friends)) {
21 | return $this->$key = $value;
22 | } else {
23 | throw new \Exception("Property is private");
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Entities/ImageTest.php:
--------------------------------------------------------------------------------
1 | setData(file_get_contents($fn.'image.png'));
18 | $this->assertEquals(ImageFormat::PNG, $image->getDataFormat()->value());
19 |
20 | $image->setData(file_get_contents($fn.'image.jpg'));
21 | $this->assertEquals(ImageFormat::JPEG, $image->getDataFormat()->value());
22 |
23 | $image->setData(file_get_contents($fn.'animated.gif'));
24 | $this->assertEquals(ImageFormat::GIF, $image->getDataFormat()->value());
25 |
26 | $image->setData(file_get_contents($fn.'not_an_image.png'));
27 | $this->assertNull($image->getDataFormat());
28 |
29 | $image->setData(null);
30 | $this->assertNull($image->getDataFormat());
31 | }
32 |
33 | /**
34 | * @small
35 | * @expectedException \Bravo3\ImageManager\Exceptions\ImageManagerException
36 | */
37 | public function testBadKey()
38 | {
39 | new Image('');
40 | }
41 |
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Resources/FriendlyClass.php:
--------------------------------------------------------------------------------
1 | x;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Resources/UnfriendlyClass.php:
--------------------------------------------------------------------------------
1 | x;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Resources/actually_a_png.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bravo3/image-manager/149c73c21831e699720b23c38df14510913de265/tests/Bravo3/ImageManager/Tests/Resources/actually_a_png.jpg
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Resources/animated.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bravo3/image-manager/149c73c21831e699720b23c38df14510913de265/tests/Bravo3/ImageManager/Tests/Resources/animated.gif
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Resources/image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bravo3/image-manager/149c73c21831e699720b23c38df14510913de265/tests/Bravo3/ImageManager/Tests/Resources/image.jpg
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Resources/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bravo3/image-manager/149c73c21831e699720b23c38df14510913de265/tests/Bravo3/ImageManager/Tests/Resources/image.png
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Resources/not_an_image.png:
--------------------------------------------------------------------------------
1 | This is clearly not an image.
2 |
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Resources/sample_pdf.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bravo3/image-manager/149c73c21831e699720b23c38df14510913de265/tests/Bravo3/ImageManager/Tests/Resources/sample_pdf.pdf
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Resources/sample_pdf2.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bravo3/image-manager/149c73c21831e699720b23c38df14510913de265/tests/Bravo3/ImageManager/Tests/Resources/sample_pdf2.pdf
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Resources/transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bravo3/image-manager/149c73c21831e699720b23c38df14510913de265/tests/Bravo3/ImageManager/Tests/Resources/transparent.png
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Services/ImageManagerTest.php:
--------------------------------------------------------------------------------
1 | loadFromFile($fn);
39 | $this->assertTrue($image instanceof Image);
40 |
41 | $hydrated_memory = memory_get_usage();
42 |
43 | $this->assertGreaterThanOrEqual($start_memory, $hydrated_memory, 'Hydration increased memory consumption');
44 |
45 | $im->save($image, self::$tmp_dir.'local/'.basename($fn));
46 |
47 | gc_collect_cycles();
48 | $saved_memory = memory_get_usage();
49 | $image->flush();
50 | $this->assertLessThan($saved_memory, memory_get_usage(), 'Flushing decreased memory consumption');
51 | }
52 |
53 | /**
54 | * @small
55 | * @expectedException \Bravo3\ImageManager\Exceptions\NoSupportedEncoderException
56 | */
57 | public function testBadImage()
58 | {
59 | $fn = __DIR__.'/../Resources/not_an_image.png';
60 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
61 | $img = $im->loadFromFile($fn);
62 | $var = $im->createVariation($img, ImageFormat::JPEG(), 90);
63 | $im->save($var, self::$tmp_dir.'local/invalid.jpg');
64 | }
65 |
66 | /**
67 | * @small
68 | * @expectedException \Bravo3\ImageManager\Exceptions\IoException
69 | */
70 | public function testMissingImage()
71 | {
72 | $fn = __DIR__.'/../Resources/does_not_exist.png';
73 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
74 | $im->loadFromFile($fn);
75 | }
76 |
77 | /**
78 | * @medium
79 | * @dataProvider cacheProvider
80 | *
81 | * @param array $cache
82 | */
83 | public function testRemote($cache)
84 | {
85 | $fn = __DIR__.'/../Resources/image.png';
86 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')), $cache);
87 |
88 | $image_a = $im->loadFromFile($fn, self::TEST_KEY);
89 | $this->assertTrue($image_a->isHydrated());
90 | $this->assertFalse($image_a->isPersistent());
91 |
92 | $this->assertFalse($im->exists($image_a));
93 | $im->push($image_a);
94 | $this->assertTrue($im->exists($image_a));
95 | $this->assertTrue($image_a->isPersistent());
96 |
97 | $image_b = new Image(self::TEST_KEY);
98 | $this->assertFalse($image_b->isHydrated());
99 | $this->assertFalse($image_b->isPersistent());
100 |
101 | $im->pull($image_b);
102 | $this->assertTrue($image_b->isHydrated());
103 | $this->assertTrue($image_b->isPersistent());
104 | $im->save($image_b, self::$tmp_dir.'local/pushed_and_pulled.png');
105 |
106 | $im->remove($image_b);
107 | $this->assertFalse($im->exists($image_a));
108 | }
109 |
110 | public function testRenamedAssetIsPersistent()
111 | {
112 | $fn = __DIR__.'/../Resources/image.png';
113 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')), new EphemeralCachePool());
114 |
115 | $image = $im->loadFromFile($fn, self::TEST_KEY);
116 | $im->push($image);
117 |
118 | $original_key = $image->getKey();
119 | $new_key = '/tmp/some-new-key';
120 |
121 | $im->rename($original_key, $new_key);
122 |
123 | $renamed = new Image($new_key);
124 |
125 | $im->pull($renamed);
126 |
127 | $this->assertTrue($renamed->isHydrated());
128 | $this->assertTrue($renamed->isPersistent());
129 | }
130 |
131 | /**
132 | * @expectedException \Bravo3\ImageManager\Exceptions\NotExistsException
133 | * @medium
134 | */
135 | public function testKeyValidationFail()
136 | {
137 | $fn = __DIR__.'/../Resources/image.png';
138 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')), new EphemeralCachePool());
139 |
140 | $image = $im->loadFromFile($fn, self::TEST_KEY);
141 | $this->assertTrue($image->isHydrated());
142 | $this->assertFalse($image->isPersistent());
143 | $im->push($image);
144 | $this->assertTrue($image->isPersistent());
145 |
146 | $im->setImageExists($image, false);
147 |
148 | $var = new ImageVariation(self::TEST_KEY, ImageFormat::GIF(), 50);
149 | $im->pull($var);
150 | }
151 |
152 | /**
153 | * @medium
154 | */
155 | public function testKeyValidationSuccess()
156 | {
157 | $fn = __DIR__.'/../Resources/image.png';
158 | $im = new ImageManager(
159 | new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')),
160 | new EphemeralCachePool(),
161 | [],
162 | true
163 | );
164 |
165 | $image = $im->loadFromFile($fn, self::TEST_KEY);
166 | $this->assertTrue($image->isHydrated());
167 | $this->assertFalse($image->isPersistent());
168 | $im->push($image);
169 | $this->assertTrue($image->isPersistent());
170 |
171 | $im->setImageExists($image, false);
172 |
173 | $var = new ImageVariation(self::TEST_KEY, ImageFormat::GIF(), 50);
174 | $im->pull($var);
175 | }
176 |
177 | /**
178 | * Data provider returning a real cache and a null cache.
179 | *
180 | * @return array
181 | */
182 | public function cacheProvider()
183 | {
184 | return [
185 | [null],
186 | [new EphemeralCachePool()],
187 | ];
188 | }
189 |
190 | /**
191 | * @medium
192 | */
193 | public function testVariationLocalCreate()
194 | {
195 | $fn = __DIR__.'/../Resources/image.png';
196 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
197 |
198 | $source = $im->loadFromFile($fn, self::TEST_KEY);
199 |
200 | // Create and render the variation
201 | $var = $im->createVariation($source, ImageFormat::JPEG(), 50);
202 |
203 | $this->assertFalse($var->isPersistent());
204 | $this->assertTrue($var->isHydrated());
205 |
206 | // Test the local save
207 | $im->save($var, self::$tmp_dir.'local/variation.jpg');
208 | $this->assertFalse($var->isPersistent());
209 |
210 | $im->push($var);
211 | $this->assertTrue($var->isPersistent());
212 |
213 | // Test the pull
214 | $var_pull = new ImageVariation(self::TEST_KEY, ImageFormat::JPEG(), 50);
215 | $this->assertFalse($var_pull->isPersistent());
216 | $this->assertFalse($var_pull->isHydrated());
217 |
218 | $im->pull($var_pull);
219 | $this->assertTrue($var_pull->isPersistent());
220 | $this->assertTrue($var_pull->isHydrated());
221 | $im->save($var_pull, self::$tmp_dir.'local/variation_pulled.jpg');
222 |
223 | // Test an auto-pull
224 | $var_autopull = new ImageVariation(self::TEST_KEY, ImageFormat::JPEG(), 50);
225 | $this->assertFalse($var_autopull->isPersistent());
226 | $this->assertFalse($var_autopull->isHydrated());
227 | $im->save($var_autopull, self::$tmp_dir.'local/variation_autopulled.jpg');
228 |
229 | // Image data should be identical - no loss
230 | $md5_a = md5_file(self::$tmp_dir.'local/variation.jpg');
231 | $md5_b = md5_file(self::$tmp_dir.'local/variation_pulled.jpg');
232 | $md5_c = md5_file(self::$tmp_dir.'local/variation_autopulled.jpg');
233 | $this->assertEquals($md5_a, $md5_b);
234 | $this->assertEquals($md5_a, $md5_c);
235 | }
236 |
237 | /**
238 | * @medium
239 | */
240 | public function testEncodePdf()
241 | {
242 | $fn = __DIR__.'/../Resources/sample_pdf.pdf';
243 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
244 | $im->addEncoder(new ImagickEncoder());
245 |
246 | $source = $im->loadFromFile($fn, self::TEST_KEY.'_pdf');
247 |
248 | // Create and render the variation
249 | $var = $im->createVariation($source, ImageFormat::PNG(), 90, new ImageDimensions(1200));
250 | $im->save($var, self::$tmp_dir.'local/sample_pdf.png');
251 | }
252 |
253 | /**
254 | * @medium
255 | */
256 | public function testPdfEncodeColorCorrection()
257 | {
258 | $fn = __DIR__.'/../Resources/sample_pdf2.pdf';
259 |
260 | // Define a pixel within the image which has colour white when alpha
261 | // channel ignored. Test ignores alpha channel - See below.
262 | $x_px = $y_px = 10;
263 |
264 | // Retrieve PDF background color before encoding
265 | $imagick_pdf = new \Imagick($fn);
266 | $before_bg_color = $imagick_pdf
267 | ->getImagePixelColor($x_px, $y_px)
268 | ->getColor();
269 |
270 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
271 | $imagick_encoder = new ImagickEncoder();
272 | $im->addEncoder($imagick_encoder);
273 |
274 | $source = $im->loadFromFile($fn, self::TEST_KEY.'_pdf');
275 |
276 | // Create and render the variation
277 | $var = $im->createVariation($source, ImageFormat::JPEG(), 90, new ImageDimensions(1200));
278 | $fn_jpeg = self::$tmp_dir.'local/sample_pdf2.jpg';
279 | $im->save($var, $fn_jpeg);
280 |
281 | // Check jpeg bg color
282 | $imagick_jpeg = new \Imagick($fn_jpeg);
283 | $after_bg_color = $imagick_jpeg
284 | ->getImagePixelColor($x_px, $y_px)
285 | ->getColor();
286 |
287 | // Unset alpha channel
288 | unset($before_bg_color['a']);
289 | unset($after_bg_color['a']);
290 |
291 | $this->assertEquals($before_bg_color, $after_bg_color);
292 | }
293 |
294 | /**
295 | * @medium
296 | */
297 | public function testVariationRemoteCreate()
298 | {
299 | $fn = __DIR__.'/../Resources/image.png';
300 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
301 |
302 | $source = $im->loadFromFile($fn, self::TEST_KEY_VAR);
303 | $im->push($source);
304 |
305 | $var = new ImageVariation(self::TEST_KEY_VAR, ImageFormat::GIF(), 50);
306 | $im->pull($var);
307 |
308 | $this->assertTrue($var->isHydrated());
309 | $this->assertFalse($var->isPersistent());
310 |
311 | $this->assertTrue($im->exists($source));
312 | $this->assertFalse($im->exists($var));
313 |
314 | $im->push($var);
315 |
316 | $this->assertTrue($var->isPersistent());
317 | $this->assertTrue($im->exists($var));
318 | }
319 |
320 | /**
321 | * @medium
322 | * @expectedException \Bravo3\ImageManager\Exceptions\ImageManagerException
323 | */
324 | public function testNotHydratedPush()
325 | {
326 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
327 | $var = new Image(self::TEST_KEY);
328 | $im->push($var);
329 | }
330 |
331 | /**
332 | * @medium
333 | * @expectedException \Bravo3\ImageManager\Exceptions\ImageManagerException
334 | */
335 | public function testNotHydratedSrc()
336 | {
337 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
338 | $img = new Image(self::TEST_KEY);
339 | $im->createVariation($img, ImageFormat::PNG());
340 | }
341 |
342 | /**
343 | * @medium
344 | */
345 | public function testBounds()
346 | {
347 | $fn = __DIR__.'/../Resources/image.png';
348 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
349 |
350 | $source = $im->load(file_get_contents($fn), self::TEST_KEY);
351 | $im->createVariation($source, ImageFormat::PNG(), -1);
352 | $im->createVariation($source, ImageFormat::PNG(), 150);
353 | }
354 |
355 | /**
356 | * @medium
357 | * @dataProvider cacheProvider
358 | * @expectedException \Bravo3\ImageManager\Exceptions\NotExistsException
359 | *
360 | * @param array $cache
361 | */
362 | public function testNotFoundImage($cache)
363 | {
364 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')), $cache);
365 |
366 | $image = new Image('does-not-exist');
367 | $im->pull($image);
368 | }
369 |
370 | /**
371 | * @medium
372 | * @dataProvider cacheProvider
373 | * @expectedException \Bravo3\ImageManager\Exceptions\NotExistsException
374 | *
375 | * @param array $cache
376 | */
377 | public function testNotFoundVariation($cache)
378 | {
379 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')), $cache);
380 |
381 | $image = new ImageVariation('does-not-exist', ImageFormat::PNG());
382 | $im->pull($image);
383 | }
384 |
385 | /**
386 | * @medium
387 | */
388 | public function testRemoteResample()
389 | {
390 | $fn = __DIR__.'/../Resources/image.png';
391 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
392 |
393 | $source = $im->loadFromFile($fn, self::TEST_KEY_VAR);
394 | $im->push($source);
395 |
396 | $this->assertTrue($source->isHydrated());
397 | $this->assertTrue($source->isPersistent());
398 |
399 | $var_x = new ImageVariation(
400 | self::TEST_KEY_VAR, ImageFormat::JPEG(), 90,
401 | new ImageDimensions(20)
402 | );
403 | $im->push($var_x);
404 |
405 | $var_xy_stretch = new ImageVariation(
406 | self::TEST_KEY_VAR, ImageFormat::JPEG(), 90,
407 | new ImageDimensions(200, null, false)
408 | );
409 | $im->push($var_xy_stretch);
410 |
411 | $var_y = new ImageVariation(
412 | self::TEST_KEY_VAR, ImageFormat::JPEG(), 90,
413 | new ImageDimensions(null, 200)
414 | );
415 | $im->push($var_y);
416 |
417 | $var_xy = new ImageVariation(
418 | self::TEST_KEY_VAR, ImageFormat::JPEG(), 90,
419 | new ImageDimensions(100, 200)
420 | );
421 | $im->push($var_xy);
422 |
423 | $var_xy_scale = new ImageVariation(
424 | self::TEST_KEY_VAR, ImageFormat::JPEG(), 90,
425 | new ImageDimensions(100, 200, false)
426 | );
427 | $im->push($var_xy_scale);
428 |
429 | $var_xy_stretch = new ImageVariation(
430 | self::TEST_KEY_VAR, ImageFormat::JPEG(), 90,
431 | new ImageDimensions(100, 200, false)
432 | );
433 | $im->push($var_xy_stretch);
434 |
435 | $var_x_noup = new ImageVariation(
436 | self::TEST_KEY_VAR, ImageFormat::JPEG(), 90,
437 | new ImageDimensions(1000, null, true, false)
438 | );
439 | $im->push($var_x_noup);
440 |
441 | $var_x_up = new ImageVariation(
442 | self::TEST_KEY_VAR, ImageFormat::JPEG(), 90,
443 | new ImageDimensions(1000)
444 | );
445 | $im->push($var_x_up);
446 |
447 | $var_g = new ImageVariation(
448 | self::TEST_KEY_VAR, ImageFormat::JPEG(), 90,
449 | new ImageDimensions(100, 200, true, true, true)
450 | );
451 | $im->push($var_g);
452 | }
453 |
454 | /**
455 | * @medium
456 | */
457 | public function testLocalResample()
458 | {
459 | $fn = __DIR__.'/../Resources/image.png';
460 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
461 |
462 | $source = $im->loadFromFile($fn, self::TEST_KEY_VAR);
463 | $resized = $im->createVariation($source, ImageFormat::JPEG(), 90, new ImageDimensions(100));
464 |
465 | $this->assertTrue($resized->isHydrated());
466 | $this->assertFalse($resized->isPersistent());
467 |
468 | $im->save($resized, self::$tmp_dir.'local/resized.jpg');
469 | }
470 |
471 | /**
472 | * @medium
473 | * @dataProvider cacheProvider
474 | * @expectedException \Bravo3\ImageManager\Exceptions\ObjectAlreadyExistsException
475 | *
476 | * @param array $cache
477 | */
478 | public function testOverwrite($cache)
479 | {
480 | $fn = __DIR__.'/../Resources/image.png';
481 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')), $cache);
482 |
483 | $source = $im->loadFromFile($fn, self::TEST_KEY);
484 | $im->push($source);
485 | $im->push($source, false);
486 | }
487 |
488 | /**
489 | * @medium
490 | */
491 | public function testChangeKey()
492 | {
493 | $fn = __DIR__.'/../Resources/image.png';
494 | $im = new ImageManager(new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')));
495 |
496 | $source = $im->loadFromFile($fn, self::TEST_KEY);
497 | $im->push($source);
498 |
499 | $this->assertTrue($source->isHydrated());
500 | $this->assertTrue($source->isPersistent());
501 |
502 | $source->setKey('change_key');
503 |
504 | $this->assertTrue($source->isHydrated());
505 | $this->assertFalse($source->isPersistent());
506 |
507 | $im->push($source);
508 | $this->assertTrue($source->isPersistent());
509 | }
510 |
511 | public function testMetadataRetrieval()
512 | {
513 | $inspector = new ImageInspector();
514 |
515 | $fn = __DIR__.'/../Resources/image.png';
516 | $im = new ImageManager(
517 | new Filesystem(new LocalAdapter(static::$tmp_dir.'remote')),
518 | new EphemeralCachePool(),
519 | [],
520 | true
521 | );
522 |
523 | $image = $im->loadFromFile($fn, self::TEST_KEY);
524 | $im->push($image);
525 |
526 | $metadata = $inspector->getImageMetadata($image);
527 | $this->assertEquals(ImageFormat::PNG(), $metadata->getFormat());
528 | $this->assertEquals(new ImageDimensions(300, 300), $metadata->getDimensions());
529 |
530 | // This test appears to fail on some systems, assuming it to be an OS-level issue
531 | //$this->assertEquals(new ImageDimensions(72, 72), $metadata->getResolution());
532 |
533 | $this->assertEquals(ImageOrientation::LANDSCAPE(), $metadata->getOrientation());
534 | }
535 |
536 | // --
537 |
538 | /**
539 | * Get a list of images.
540 | *
541 | * @return array
542 | */
543 | public function imageProvider()
544 | {
545 | $base = __DIR__.'/../Resources/';
546 |
547 | return [
548 | [$base.'image.jpg'],
549 | [$base.'image.png'],
550 | [$base.'transparent.png'],
551 | [$base.'animated.gif'],
552 | ];
553 | }
554 |
555 | /**
556 | * This isn't an actual test, it permits the teardown function to delete the test images
557 | * Exclude this test to keep the test images.
558 | *
559 | * @small
560 | * @group deleteTestImages
561 | */
562 | public function testDeleteTestImages()
563 | {
564 | self::$allow_delete = true;
565 | }
566 |
567 | // --
568 |
569 | public static function setUpBeforeClass()
570 | {
571 | $sys_temp = sys_get_temp_dir();
572 | if (substr($sys_temp, -1) != DIRECTORY_SEPARATOR) {
573 | $sys_temp .= DIRECTORY_SEPARATOR;
574 | }
575 |
576 | self::$tmp_dir = $sys_temp.rand(10000, 99999).DIRECTORY_SEPARATOR;
577 |
578 | mkdir(self::$tmp_dir.'local', 0777, true);
579 | mkdir(self::$tmp_dir.'remote', 0777, true);
580 | }
581 |
582 | public static function tearDownAfterClass()
583 | {
584 | if (self::$allow_delete) {
585 | self::rrmdir(self::$tmp_dir);
586 | } else {
587 | fwrite(STDERR, "\n\nTest images saved to ".self::$tmp_dir."\n");
588 | }
589 | }
590 |
591 | protected static function rrmdir($dir)
592 | {
593 | foreach (glob($dir.'/*') as $file) {
594 | if (is_dir($file)) {
595 | self::rrmdir($file);
596 | } else {
597 | unlink($file);
598 | }
599 | }
600 | rmdir($dir);
601 | }
602 | }
603 |
--------------------------------------------------------------------------------
/tests/Bravo3/ImageManager/Tests/Traits/FriendTraitTest.php:
--------------------------------------------------------------------------------
1 | __friendSet('x', 10);
17 | $this->assertEquals(10, $class->getX());
18 | }
19 |
20 | /**
21 | * @small
22 | * @expectedException \Exception
23 | */
24 | public function testEnemy()
25 | {
26 | $class = new UnfriendlyClass();
27 | $class->__friendSet('x', 10);
28 | $this->assertEquals(10, $class->getX());
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |