├── .gitignore ├── .scrutinizer.yml ├── CHANGELOG.md ├── README.md ├── composer.json ├── composer.lock ├── phpunit.xml ├── src └── Brendt │ └── Image │ ├── Config │ ├── DefaultConfigurator.php │ └── ResponsiveFactoryConfigurator.php │ ├── Exception │ ├── FileNotFoundException.php │ └── InvalidConfigurationException.php │ ├── ResponsiveFactory.php │ ├── ResponsiveImage.php │ └── Scaler │ ├── AbstractScaler.php │ ├── FileSizeScaler.php │ ├── Scaler.php │ ├── SizesScaler.php │ └── WidthScaler.php └── tests ├── Brendt └── Image │ ├── ResponsiveFactoryTest.php │ ├── ResponsiveImageTest.php │ ├── Scaler │ ├── FileSizeScalerTest.php │ ├── SizesScalerTest.php │ └── WidthScalerTest.php │ └── config │ └── DefaultConfiguratorTest.php └── img └── image.jpeg /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | .idea 3 | /tests/compile 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | build: 2 | environment: 3 | php: 4 | version: 7.0.8 5 | tests: 6 | override: 7 | - 8 | command: 'vendor/bin/phpunit' 9 | checks: 10 | php: 11 | code_rating: true 12 | duplication: true 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.9.1 4 | 5 | - Support PHP 7.0 6 | 7 | ## 1.9.0 8 | 9 | - Refactor code directories. 10 | 11 | ## 1.8.2 12 | 13 | - Cleanup Responsive Factory code. 14 | - Add caching test. 15 | 16 | ## 1.8.1 17 | 18 | - Bugfix for extension of cached files not correctly loading. 19 | 20 | ## 1.8.0 21 | 22 | - Add `includeSource` parameter. 23 | 24 | ## 1.7.3 25 | 26 | - Bugfix for invalid cache path when rebase was enabled. 27 | 28 | ## 1.7.2 29 | 30 | - Bugfix for wrong composer sources. 31 | 32 | ## 1.7.1 33 | 34 | - Bugfix for srcset not correctly rendering. 35 | 36 | ## 1.7.0 37 | 38 | - Add `sizes` scaler. This scaler can be configured to generate a fixed set of images based on pre-defined widths, 39 | provided via the `sizes` parameter. See the README for more information. 40 | 41 | ## 1.6.0 42 | 43 | - Add `rebase` parameter. 44 | - Add `maxFileSize` parameter. 45 | - Add `maxWidth` parameter. 46 | 47 | ## 1.5.1 48 | 49 | - Fix a bug so that unkown file exceptions are correctly thrown. 50 | 51 | ## 1.5.0 52 | 53 | - Removed AMP dependencies. 54 | 55 | ## 1.4.1 56 | 57 | - Temporary remove Amp because of BC breaking changes with each update. 58 | 59 | ## 1.4.0 60 | 61 | - Add optimizer options support 62 | 63 | ## 1.3.2 64 | 65 | - Add fixed amphp versions because of BC breaking changes in their library. 66 | 67 | ## 1.3.1 68 | 69 | - Several bug fixes and optimizations when running enabling the `async` option. 70 | 71 | ## 1.3.0 72 | 73 | - Add `async` option to downscale images in separate processes. PHP's `pcntl` extension is required for this to work. 74 | 75 | ## 1.2.2 76 | 77 | - Add simple construct for the ResponsiveFactory. 78 | 79 | ## 1.2.1 80 | 81 | - Fixed bug with the cache not being searched in the public path. 82 | 83 | ## 1.2.0 84 | 85 | - Update minimum requirements to PHP 7.1. 86 | - Refactor scalers to only calculate file sizes and not handle image saving. 87 | - Add `optimize` parameter to run image optimizers. Defaults to `false`. 88 | 89 | ## 1.1.2 90 | 91 | - Improve caching, scalers are now not used when images are already cached. 92 | - Set default `minFileSize` to 5 KB. 93 | 94 | ## 1.1.0 95 | 96 | - Add Scaler support 97 | - Add FileSize scaler as default. 98 | 99 | To use the old scaler: 100 | 101 | ```php 102 | $factory = new ResponsiveFactory(new DefaultConfigurator([ 103 | 'scaler' => 'width', 104 | ])); 105 | ``` 106 | 107 | The new Scaler is called `filesize` and set by default, this scaler differs from the width scaler because it scales down 108 | based on file size instead of the width of an image. 109 | 110 | ```php 111 | $factory = new ResponsiveFactory(new DefaultConfigurator([ 112 | 'scaler' => 'filesize', 113 | ])); 114 | ``` 115 | 116 | **Note**: the `stepModifier` parameter is now used different, the higher this modifier, the more images will be generated. 117 | 118 | ## 1.0.0 119 | 120 | - Use PSR naming standards. 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://scrutinizer-ci.com/g/brendt/responsive-images/badges/build.png?b=master)](https://scrutinizer-ci.com/g/brendt/responsive-images/build-status/master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/brendt/responsive-images/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/brendt/responsive-images/?branch=master) 2 | 3 | # Responsive Images 4 | 5 | Automatically generate responsive images to work with the `srcset` and `sizes` spec. ([http://responsiveimages.org/](http://responsiveimages.org/)) 6 | 7 | ```sh 8 | composer require brendt/responsive-images 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```php 14 | use Brendt\Image\ResponsiveFactory; 15 | 16 | $factory = new ResponsiveFactory(); 17 | $image = $factory->create('img/image.jpeg'); 18 | ``` 19 | 20 | ```html 21 | 23 | ``` 24 | 25 | This sample would generate something like: 26 | 27 | ```hmtl 28 | 34 | ``` 35 | 36 | ## Configuration 37 | 38 | The `ResponsiveFactory` can take a `ResponsiveFactoryConfigurator` object which will set the needed parameters. 39 | A default configurator `DefaultConfigurator` is provider out of the box, and uses the following parameters: 40 | 41 | ```php 42 | [ 43 | 'driver' => 'gd', 44 | 'includeSource' => true, 45 | 'publicPath' => './', 46 | 'sourcePath' => './', 47 | 'rebase' => false, 48 | 'enableCache' => false, 49 | 'optimize' => false, 50 | 'scaler' => 'filesize', 51 | 'stepModifier' => 0.5, 52 | 'minFileSize' => 5000, 53 | 'maxFileSize' => null, 54 | 'minWidth' => 300, 55 | 'maxWidth' => null, 56 | 'sizes' => [ 1920, 840, 300 ], 57 | 'optimizerOptions' => [], 58 | ] 59 | ``` 60 | 61 | You can override these parameters by providing and array to the `DefaultConfigurator`, 62 | or create a whole new configurator which implements `ResponsiveFactoryConfigurator`. 63 | 64 | ```php 65 | $factory = new ResponsiveFactory(new DefaultConfigurator([ 66 | 'driver' => 'imagick', 67 | 'sourcePath' => './src', 68 | 'publicPath' => './public', 69 | 'enableCache' => true, 70 | ])); 71 | ``` 72 | 73 | ### All configuration options 74 | 75 | - `driver`: the image driver to use. Defaults to `gd`. Possible options are `gd` or `imagick`. 76 | - `includeSource`: whether to include the source image in the `srcset`. Defaults to `true`. 77 | - `sourcePath`: the path to load image source files. Defaults to `./`. 78 | - `publicPath`: the path to render image files. Defaults to `./`. 79 | - `rebase`: ignore the path of the requested image when searching in the source directory. Defaults to `false`. 80 | - `enableCache`: enable or disable image caching. Enabling the cache wont' override existing images. Defaults to `false`. 81 | - `optimize`: enable or disable the use of different optimizers (if installed on the system). Defaults to `false`. 82 | - `scaler`: which scaler algorithm to use. Defaults to `filesize`. Possible options are `filesize`, `width` or `sizes`. 83 | - `stepModifier`: a percentage (between 0 and 1) which is used to create different image sizes. The higher this modifier, the more image variations will be rendered. Defaults to `0.5`. 84 | - `minFileSize`: the minimum image filesize in bytes. Defaults to `5000`B (5KB). 85 | - `maxFileSize`: the maximum image filesize in bytes. Defaults to `null`. 86 | - `minWidth`: the minimum image size in pixels. No images with size smaller than this number will be rendered. Defaults to `300` pixels. 87 | - `maxWidth`: the maximum image size in pixels. No images with size smaller than this number will be rendered. Defaults to `null`. 88 | - `sizes`: this parameter is used when the `sizes` scaler is enabled. This scaler will generate a fixed set of sizes, based on this array. 89 | The expected values are the widths of the generated images. Defaults to `[]` (empty array). 90 | - `optimizerOptions`: options to pass to the image optimizer library. See [https://github.com/psliwa/image-optimizer](https://github.com/psliwa/image-optimizer) for more information. 91 | 92 | ### Paths 93 | 94 | The `sourcePath` parameter is used to define where image source files are located. 95 | In case of the first example and above configuration, the image file should be saved in `./src/img/image.jpeg`. 96 | 97 | The `publicPath` parameter is used to save rendered images into. This path should be the public directory of your website. 98 | The above example would render images into `./public/img/image.jpeg`. 99 | 100 | #### Path rebasing 101 | 102 | When the `rebase` option is enabled, source file lookup will differ: only the filename is used to search the file in the 103 | source directory. An example would be the following. 104 | 105 | ```php 106 | // Without rebase 107 | 108 | $options = [ 109 | 'sourcePath' => './src/images', 110 | 'publicPath' => './public', 111 | ]; 112 | 113 | $image = $factory->create('/img/responsive/image.jpeg'); 114 | 115 | // Source file is searched in './src/images/img/responsive/image.jpeg' 116 | // Public files are saved in './public/img/responsive/image-x.jpg' 117 | ``` 118 | 119 | ```php 120 | // With rebase 121 | 122 | $options = [ 123 | 'sourcePath' => './src/images', 124 | 'publicPath' => './public', 125 | 'rebase' => true, 126 | ]; 127 | 128 | $image = $factory->create('/img/responsive/image.jpeg'); 129 | 130 | // Source file is searched in './src/images/image.jpeg' 131 | // Public files are saved in './public/img/responsive/image-x.jpg' 132 | ``` 133 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "brendt/responsive-images", 3 | "description" : "Create responsive images with PHP, using the srcset and sizes spec.", 4 | "license" : "MIT", 5 | "authors" : [ 6 | { 7 | "name" : "Brent", 8 | "email" : "brent@pageon.be" 9 | } 10 | ], 11 | "prefer-stable": true, 12 | "require" : { 13 | "PHP" : ">=7.0", 14 | "symfony/finder" : "^3.1", 15 | "symfony/filesystem" : "^3.1", 16 | "intervention/image": "^2.3", 17 | "ps/image-optimizer": "^1.0" 18 | }, 19 | "require-dev" : { 20 | "larapack/dd" : "^1.0", 21 | "phpunit/phpunit" : "^5.0" 22 | }, 23 | "autoload" : { 24 | "psr-4" : { 25 | "Brendt\\Image\\" : "src/Brendt/Image" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /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": "fa3f6a1672cb53a286b4a1e1ed2e993d", 8 | "content-hash": "cd2518b0f1f6246ade16b3a09fd7da1a", 9 | "packages": [ 10 | { 11 | "name": "guzzlehttp/psr7", 12 | "version": "1.4.2", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/guzzle/psr7.git", 16 | "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", 21 | "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": ">=5.4.0", 26 | "psr/http-message": "~1.0" 27 | }, 28 | "provide": { 29 | "psr/http-message-implementation": "1.0" 30 | }, 31 | "require-dev": { 32 | "phpunit/phpunit": "~4.0" 33 | }, 34 | "type": "library", 35 | "extra": { 36 | "branch-alias": { 37 | "dev-master": "1.4-dev" 38 | } 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "GuzzleHttp\\Psr7\\": "src/" 43 | }, 44 | "files": [ 45 | "src/functions_include.php" 46 | ] 47 | }, 48 | "notification-url": "https://packagist.org/downloads/", 49 | "license": [ 50 | "MIT" 51 | ], 52 | "authors": [ 53 | { 54 | "name": "Michael Dowling", 55 | "email": "mtdowling@gmail.com", 56 | "homepage": "https://github.com/mtdowling" 57 | }, 58 | { 59 | "name": "Tobias Schultze", 60 | "homepage": "https://github.com/Tobion" 61 | } 62 | ], 63 | "description": "PSR-7 message implementation that also provides common utility methods", 64 | "keywords": [ 65 | "http", 66 | "message", 67 | "request", 68 | "response", 69 | "stream", 70 | "uri", 71 | "url" 72 | ], 73 | "time": "2017-03-20 17:10:46" 74 | }, 75 | { 76 | "name": "intervention/image", 77 | "version": "2.3.12", 78 | "source": { 79 | "type": "git", 80 | "url": "https://github.com/Intervention/image.git", 81 | "reference": "5c92ea4503ccb000ce6a922efbf272638c64a68c" 82 | }, 83 | "dist": { 84 | "type": "zip", 85 | "url": "https://api.github.com/repos/Intervention/image/zipball/5c92ea4503ccb000ce6a922efbf272638c64a68c", 86 | "reference": "5c92ea4503ccb000ce6a922efbf272638c64a68c", 87 | "shasum": "" 88 | }, 89 | "require": { 90 | "ext-fileinfo": "*", 91 | "guzzlehttp/psr7": "~1.1", 92 | "php": ">=5.4.0" 93 | }, 94 | "require-dev": { 95 | "mockery/mockery": "~0.9.2", 96 | "phpunit/phpunit": "3.*" 97 | }, 98 | "suggest": { 99 | "ext-gd": "to use GD library based image processing.", 100 | "ext-imagick": "to use Imagick based image processing.", 101 | "intervention/imagecache": "Caching extension for the Intervention Image library" 102 | }, 103 | "type": "library", 104 | "extra": { 105 | "branch-alias": { 106 | "dev-master": "2.3-dev" 107 | } 108 | }, 109 | "autoload": { 110 | "psr-4": { 111 | "Intervention\\Image\\": "src/Intervention/Image" 112 | } 113 | }, 114 | "notification-url": "https://packagist.org/downloads/", 115 | "license": [ 116 | "MIT" 117 | ], 118 | "authors": [ 119 | { 120 | "name": "Oliver Vogel", 121 | "email": "oliver@olivervogel.net", 122 | "homepage": "http://olivervogel.net/" 123 | } 124 | ], 125 | "description": "Image handling and manipulation library with support for Laravel integration", 126 | "homepage": "http://image.intervention.io/", 127 | "keywords": [ 128 | "gd", 129 | "image", 130 | "imagick", 131 | "laravel", 132 | "thumbnail", 133 | "watermark" 134 | ], 135 | "time": "2017-04-22 14:50:13" 136 | }, 137 | { 138 | "name": "ps/image-optimizer", 139 | "version": "1.1.0", 140 | "source": { 141 | "type": "git", 142 | "url": "https://github.com/psliwa/image-optimizer.git", 143 | "reference": "6e7461722d798ab6e77fb7f483d679ac37b71bec" 144 | }, 145 | "dist": { 146 | "type": "zip", 147 | "url": "https://api.github.com/repos/psliwa/image-optimizer/zipball/6e7461722d798ab6e77fb7f483d679ac37b71bec", 148 | "reference": "6e7461722d798ab6e77fb7f483d679ac37b71bec", 149 | "shasum": "" 150 | }, 151 | "require": { 152 | "psr/log": "1.0.*", 153 | "symfony/options-resolver": "~2.1 | ~3.0", 154 | "symfony/process": "~2.0 | ~3.0" 155 | }, 156 | "require-dev": { 157 | "phpunit/phpunit": "^5.0" 158 | }, 159 | "type": "library", 160 | "autoload": { 161 | "psr-0": { 162 | "ImageOptimizer": "src/" 163 | } 164 | }, 165 | "notification-url": "https://packagist.org/downloads/", 166 | "license": [ 167 | "MIT" 168 | ], 169 | "authors": [ 170 | { 171 | "name": "Piotr Sliwa", 172 | "email": "peter.pl7@gmail.com" 173 | } 174 | ], 175 | "description": "Image optimization / compression library. This library is able to optimize png, jpg and gif files in very easy and handy way. It uses optipng, pngquant, pngcrush, pngout, gifsicle, jpegoptim and jpegtran tools.", 176 | "keywords": [ 177 | "compression", 178 | "image", 179 | "image optimizer", 180 | "jpegoptim", 181 | "optimization", 182 | "optipng" 183 | ], 184 | "time": "2017-03-25 15:42:56" 185 | }, 186 | { 187 | "name": "psr/http-message", 188 | "version": "1.0.1", 189 | "source": { 190 | "type": "git", 191 | "url": "https://github.com/php-fig/http-message.git", 192 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" 193 | }, 194 | "dist": { 195 | "type": "zip", 196 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", 197 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", 198 | "shasum": "" 199 | }, 200 | "require": { 201 | "php": ">=5.3.0" 202 | }, 203 | "type": "library", 204 | "extra": { 205 | "branch-alias": { 206 | "dev-master": "1.0.x-dev" 207 | } 208 | }, 209 | "autoload": { 210 | "psr-4": { 211 | "Psr\\Http\\Message\\": "src/" 212 | } 213 | }, 214 | "notification-url": "https://packagist.org/downloads/", 215 | "license": [ 216 | "MIT" 217 | ], 218 | "authors": [ 219 | { 220 | "name": "PHP-FIG", 221 | "homepage": "http://www.php-fig.org/" 222 | } 223 | ], 224 | "description": "Common interface for HTTP messages", 225 | "homepage": "https://github.com/php-fig/http-message", 226 | "keywords": [ 227 | "http", 228 | "http-message", 229 | "psr", 230 | "psr-7", 231 | "request", 232 | "response" 233 | ], 234 | "time": "2016-08-06 14:39:51" 235 | }, 236 | { 237 | "name": "psr/log", 238 | "version": "1.0.2", 239 | "source": { 240 | "type": "git", 241 | "url": "https://github.com/php-fig/log.git", 242 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" 243 | }, 244 | "dist": { 245 | "type": "zip", 246 | "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 247 | "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 248 | "shasum": "" 249 | }, 250 | "require": { 251 | "php": ">=5.3.0" 252 | }, 253 | "type": "library", 254 | "extra": { 255 | "branch-alias": { 256 | "dev-master": "1.0.x-dev" 257 | } 258 | }, 259 | "autoload": { 260 | "psr-4": { 261 | "Psr\\Log\\": "Psr/Log/" 262 | } 263 | }, 264 | "notification-url": "https://packagist.org/downloads/", 265 | "license": [ 266 | "MIT" 267 | ], 268 | "authors": [ 269 | { 270 | "name": "PHP-FIG", 271 | "homepage": "http://www.php-fig.org/" 272 | } 273 | ], 274 | "description": "Common interface for logging libraries", 275 | "homepage": "https://github.com/php-fig/log", 276 | "keywords": [ 277 | "log", 278 | "psr", 279 | "psr-3" 280 | ], 281 | "time": "2016-10-10 12:19:37" 282 | }, 283 | { 284 | "name": "symfony/filesystem", 285 | "version": "v3.2.7", 286 | "source": { 287 | "type": "git", 288 | "url": "https://github.com/symfony/filesystem.git", 289 | "reference": "64421e6479c4a8e60d790fb666bd520992861b66" 290 | }, 291 | "dist": { 292 | "type": "zip", 293 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/64421e6479c4a8e60d790fb666bd520992861b66", 294 | "reference": "64421e6479c4a8e60d790fb666bd520992861b66", 295 | "shasum": "" 296 | }, 297 | "require": { 298 | "php": ">=5.5.9" 299 | }, 300 | "type": "library", 301 | "extra": { 302 | "branch-alias": { 303 | "dev-master": "3.2-dev" 304 | } 305 | }, 306 | "autoload": { 307 | "psr-4": { 308 | "Symfony\\Component\\Filesystem\\": "" 309 | }, 310 | "exclude-from-classmap": [ 311 | "/Tests/" 312 | ] 313 | }, 314 | "notification-url": "https://packagist.org/downloads/", 315 | "license": [ 316 | "MIT" 317 | ], 318 | "authors": [ 319 | { 320 | "name": "Fabien Potencier", 321 | "email": "fabien@symfony.com" 322 | }, 323 | { 324 | "name": "Symfony Community", 325 | "homepage": "https://symfony.com/contributors" 326 | } 327 | ], 328 | "description": "Symfony Filesystem Component", 329 | "homepage": "https://symfony.com", 330 | "time": "2017-03-26 15:47:15" 331 | }, 332 | { 333 | "name": "symfony/finder", 334 | "version": "v3.2.7", 335 | "source": { 336 | "type": "git", 337 | "url": "https://github.com/symfony/finder.git", 338 | "reference": "b20900ce5ea164cd9314af52725b0bb5a758217a" 339 | }, 340 | "dist": { 341 | "type": "zip", 342 | "url": "https://api.github.com/repos/symfony/finder/zipball/b20900ce5ea164cd9314af52725b0bb5a758217a", 343 | "reference": "b20900ce5ea164cd9314af52725b0bb5a758217a", 344 | "shasum": "" 345 | }, 346 | "require": { 347 | "php": ">=5.5.9" 348 | }, 349 | "type": "library", 350 | "extra": { 351 | "branch-alias": { 352 | "dev-master": "3.2-dev" 353 | } 354 | }, 355 | "autoload": { 356 | "psr-4": { 357 | "Symfony\\Component\\Finder\\": "" 358 | }, 359 | "exclude-from-classmap": [ 360 | "/Tests/" 361 | ] 362 | }, 363 | "notification-url": "https://packagist.org/downloads/", 364 | "license": [ 365 | "MIT" 366 | ], 367 | "authors": [ 368 | { 369 | "name": "Fabien Potencier", 370 | "email": "fabien@symfony.com" 371 | }, 372 | { 373 | "name": "Symfony Community", 374 | "homepage": "https://symfony.com/contributors" 375 | } 376 | ], 377 | "description": "Symfony Finder Component", 378 | "homepage": "https://symfony.com", 379 | "time": "2017-03-20 09:32:19" 380 | }, 381 | { 382 | "name": "symfony/options-resolver", 383 | "version": "v3.2.7", 384 | "source": { 385 | "type": "git", 386 | "url": "https://github.com/symfony/options-resolver.git", 387 | "reference": "6a19be85237fe8bbd4975f86942b4763bb0da6ca" 388 | }, 389 | "dist": { 390 | "type": "zip", 391 | "url": "https://api.github.com/repos/symfony/options-resolver/zipball/6a19be85237fe8bbd4975f86942b4763bb0da6ca", 392 | "reference": "6a19be85237fe8bbd4975f86942b4763bb0da6ca", 393 | "shasum": "" 394 | }, 395 | "require": { 396 | "php": ">=5.5.9" 397 | }, 398 | "type": "library", 399 | "extra": { 400 | "branch-alias": { 401 | "dev-master": "3.2-dev" 402 | } 403 | }, 404 | "autoload": { 405 | "psr-4": { 406 | "Symfony\\Component\\OptionsResolver\\": "" 407 | }, 408 | "exclude-from-classmap": [ 409 | "/Tests/" 410 | ] 411 | }, 412 | "notification-url": "https://packagist.org/downloads/", 413 | "license": [ 414 | "MIT" 415 | ], 416 | "authors": [ 417 | { 418 | "name": "Fabien Potencier", 419 | "email": "fabien@symfony.com" 420 | }, 421 | { 422 | "name": "Symfony Community", 423 | "homepage": "https://symfony.com/contributors" 424 | } 425 | ], 426 | "description": "Symfony OptionsResolver Component", 427 | "homepage": "https://symfony.com", 428 | "keywords": [ 429 | "config", 430 | "configuration", 431 | "options" 432 | ], 433 | "time": "2017-03-21 21:44:32" 434 | }, 435 | { 436 | "name": "symfony/process", 437 | "version": "v3.2.7", 438 | "source": { 439 | "type": "git", 440 | "url": "https://github.com/symfony/process.git", 441 | "reference": "57fdaa55827ae14d617550ebe71a820f0a5e2282" 442 | }, 443 | "dist": { 444 | "type": "zip", 445 | "url": "https://api.github.com/repos/symfony/process/zipball/57fdaa55827ae14d617550ebe71a820f0a5e2282", 446 | "reference": "57fdaa55827ae14d617550ebe71a820f0a5e2282", 447 | "shasum": "" 448 | }, 449 | "require": { 450 | "php": ">=5.5.9" 451 | }, 452 | "type": "library", 453 | "extra": { 454 | "branch-alias": { 455 | "dev-master": "3.2-dev" 456 | } 457 | }, 458 | "autoload": { 459 | "psr-4": { 460 | "Symfony\\Component\\Process\\": "" 461 | }, 462 | "exclude-from-classmap": [ 463 | "/Tests/" 464 | ] 465 | }, 466 | "notification-url": "https://packagist.org/downloads/", 467 | "license": [ 468 | "MIT" 469 | ], 470 | "authors": [ 471 | { 472 | "name": "Fabien Potencier", 473 | "email": "fabien@symfony.com" 474 | }, 475 | { 476 | "name": "Symfony Community", 477 | "homepage": "https://symfony.com/contributors" 478 | } 479 | ], 480 | "description": "Symfony Process Component", 481 | "homepage": "https://symfony.com", 482 | "time": "2017-03-27 18:07:02" 483 | } 484 | ], 485 | "packages-dev": [ 486 | { 487 | "name": "doctrine/instantiator", 488 | "version": "1.0.5", 489 | "source": { 490 | "type": "git", 491 | "url": "https://github.com/doctrine/instantiator.git", 492 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 493 | }, 494 | "dist": { 495 | "type": "zip", 496 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 497 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 498 | "shasum": "" 499 | }, 500 | "require": { 501 | "php": ">=5.3,<8.0-DEV" 502 | }, 503 | "require-dev": { 504 | "athletic/athletic": "~0.1.8", 505 | "ext-pdo": "*", 506 | "ext-phar": "*", 507 | "phpunit/phpunit": "~4.0", 508 | "squizlabs/php_codesniffer": "~2.0" 509 | }, 510 | "type": "library", 511 | "extra": { 512 | "branch-alias": { 513 | "dev-master": "1.0.x-dev" 514 | } 515 | }, 516 | "autoload": { 517 | "psr-4": { 518 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 519 | } 520 | }, 521 | "notification-url": "https://packagist.org/downloads/", 522 | "license": [ 523 | "MIT" 524 | ], 525 | "authors": [ 526 | { 527 | "name": "Marco Pivetta", 528 | "email": "ocramius@gmail.com", 529 | "homepage": "http://ocramius.github.com/" 530 | } 531 | ], 532 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 533 | "homepage": "https://github.com/doctrine/instantiator", 534 | "keywords": [ 535 | "constructor", 536 | "instantiate" 537 | ], 538 | "time": "2015-06-14 21:17:01" 539 | }, 540 | { 541 | "name": "larapack/dd", 542 | "version": "1.1", 543 | "source": { 544 | "type": "git", 545 | "url": "https://github.com/larapack/dd.git", 546 | "reference": "561b5111a13d0094b59b5c81b1572489485fb948" 547 | }, 548 | "dist": { 549 | "type": "zip", 550 | "url": "https://api.github.com/repos/larapack/dd/zipball/561b5111a13d0094b59b5c81b1572489485fb948", 551 | "reference": "561b5111a13d0094b59b5c81b1572489485fb948", 552 | "shasum": "" 553 | }, 554 | "require": { 555 | "symfony/var-dumper": "*" 556 | }, 557 | "type": "package", 558 | "autoload": { 559 | "files": [ 560 | "src/helper.php" 561 | ] 562 | }, 563 | "notification-url": "https://packagist.org/downloads/", 564 | "license": [ 565 | "MIT" 566 | ], 567 | "authors": [ 568 | { 569 | "name": "Mark Topper", 570 | "email": "hi@webman.io" 571 | } 572 | ], 573 | "description": "`dd` is a helper method in Laravel. This package will add the `dd` to your application.", 574 | "time": "2016-12-15 09:34:34" 575 | }, 576 | { 577 | "name": "myclabs/deep-copy", 578 | "version": "1.6.1", 579 | "source": { 580 | "type": "git", 581 | "url": "https://github.com/myclabs/DeepCopy.git", 582 | "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102" 583 | }, 584 | "dist": { 585 | "type": "zip", 586 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102", 587 | "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102", 588 | "shasum": "" 589 | }, 590 | "require": { 591 | "php": ">=5.4.0" 592 | }, 593 | "require-dev": { 594 | "doctrine/collections": "1.*", 595 | "phpunit/phpunit": "~4.1" 596 | }, 597 | "type": "library", 598 | "autoload": { 599 | "psr-4": { 600 | "DeepCopy\\": "src/DeepCopy/" 601 | } 602 | }, 603 | "notification-url": "https://packagist.org/downloads/", 604 | "license": [ 605 | "MIT" 606 | ], 607 | "description": "Create deep copies (clones) of your objects", 608 | "homepage": "https://github.com/myclabs/DeepCopy", 609 | "keywords": [ 610 | "clone", 611 | "copy", 612 | "duplicate", 613 | "object", 614 | "object graph" 615 | ], 616 | "time": "2017-04-12 18:52:22" 617 | }, 618 | { 619 | "name": "phpdocumentor/reflection-common", 620 | "version": "1.0", 621 | "source": { 622 | "type": "git", 623 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 624 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" 625 | }, 626 | "dist": { 627 | "type": "zip", 628 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 629 | "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", 630 | "shasum": "" 631 | }, 632 | "require": { 633 | "php": ">=5.5" 634 | }, 635 | "require-dev": { 636 | "phpunit/phpunit": "^4.6" 637 | }, 638 | "type": "library", 639 | "extra": { 640 | "branch-alias": { 641 | "dev-master": "1.0.x-dev" 642 | } 643 | }, 644 | "autoload": { 645 | "psr-4": { 646 | "phpDocumentor\\Reflection\\": [ 647 | "src" 648 | ] 649 | } 650 | }, 651 | "notification-url": "https://packagist.org/downloads/", 652 | "license": [ 653 | "MIT" 654 | ], 655 | "authors": [ 656 | { 657 | "name": "Jaap van Otterdijk", 658 | "email": "opensource@ijaap.nl" 659 | } 660 | ], 661 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 662 | "homepage": "http://www.phpdoc.org", 663 | "keywords": [ 664 | "FQSEN", 665 | "phpDocumentor", 666 | "phpdoc", 667 | "reflection", 668 | "static analysis" 669 | ], 670 | "time": "2015-12-27 11:43:31" 671 | }, 672 | { 673 | "name": "phpdocumentor/reflection-docblock", 674 | "version": "3.1.1", 675 | "source": { 676 | "type": "git", 677 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 678 | "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" 679 | }, 680 | "dist": { 681 | "type": "zip", 682 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", 683 | "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", 684 | "shasum": "" 685 | }, 686 | "require": { 687 | "php": ">=5.5", 688 | "phpdocumentor/reflection-common": "^1.0@dev", 689 | "phpdocumentor/type-resolver": "^0.2.0", 690 | "webmozart/assert": "^1.0" 691 | }, 692 | "require-dev": { 693 | "mockery/mockery": "^0.9.4", 694 | "phpunit/phpunit": "^4.4" 695 | }, 696 | "type": "library", 697 | "autoload": { 698 | "psr-4": { 699 | "phpDocumentor\\Reflection\\": [ 700 | "src/" 701 | ] 702 | } 703 | }, 704 | "notification-url": "https://packagist.org/downloads/", 705 | "license": [ 706 | "MIT" 707 | ], 708 | "authors": [ 709 | { 710 | "name": "Mike van Riel", 711 | "email": "me@mikevanriel.com" 712 | } 713 | ], 714 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 715 | "time": "2016-09-30 07:12:33" 716 | }, 717 | { 718 | "name": "phpdocumentor/type-resolver", 719 | "version": "0.2.1", 720 | "source": { 721 | "type": "git", 722 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 723 | "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" 724 | }, 725 | "dist": { 726 | "type": "zip", 727 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", 728 | "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", 729 | "shasum": "" 730 | }, 731 | "require": { 732 | "php": ">=5.5", 733 | "phpdocumentor/reflection-common": "^1.0" 734 | }, 735 | "require-dev": { 736 | "mockery/mockery": "^0.9.4", 737 | "phpunit/phpunit": "^5.2||^4.8.24" 738 | }, 739 | "type": "library", 740 | "extra": { 741 | "branch-alias": { 742 | "dev-master": "1.0.x-dev" 743 | } 744 | }, 745 | "autoload": { 746 | "psr-4": { 747 | "phpDocumentor\\Reflection\\": [ 748 | "src/" 749 | ] 750 | } 751 | }, 752 | "notification-url": "https://packagist.org/downloads/", 753 | "license": [ 754 | "MIT" 755 | ], 756 | "authors": [ 757 | { 758 | "name": "Mike van Riel", 759 | "email": "me@mikevanriel.com" 760 | } 761 | ], 762 | "time": "2016-11-25 06:54:22" 763 | }, 764 | { 765 | "name": "phpspec/prophecy", 766 | "version": "v1.7.0", 767 | "source": { 768 | "type": "git", 769 | "url": "https://github.com/phpspec/prophecy.git", 770 | "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" 771 | }, 772 | "dist": { 773 | "type": "zip", 774 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", 775 | "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", 776 | "shasum": "" 777 | }, 778 | "require": { 779 | "doctrine/instantiator": "^1.0.2", 780 | "php": "^5.3|^7.0", 781 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", 782 | "sebastian/comparator": "^1.1|^2.0", 783 | "sebastian/recursion-context": "^1.0|^2.0|^3.0" 784 | }, 785 | "require-dev": { 786 | "phpspec/phpspec": "^2.5|^3.2", 787 | "phpunit/phpunit": "^4.8 || ^5.6.5" 788 | }, 789 | "type": "library", 790 | "extra": { 791 | "branch-alias": { 792 | "dev-master": "1.6.x-dev" 793 | } 794 | }, 795 | "autoload": { 796 | "psr-0": { 797 | "Prophecy\\": "src/" 798 | } 799 | }, 800 | "notification-url": "https://packagist.org/downloads/", 801 | "license": [ 802 | "MIT" 803 | ], 804 | "authors": [ 805 | { 806 | "name": "Konstantin Kudryashov", 807 | "email": "ever.zet@gmail.com", 808 | "homepage": "http://everzet.com" 809 | }, 810 | { 811 | "name": "Marcello Duarte", 812 | "email": "marcello.duarte@gmail.com" 813 | } 814 | ], 815 | "description": "Highly opinionated mocking framework for PHP 5.3+", 816 | "homepage": "https://github.com/phpspec/prophecy", 817 | "keywords": [ 818 | "Double", 819 | "Dummy", 820 | "fake", 821 | "mock", 822 | "spy", 823 | "stub" 824 | ], 825 | "time": "2017-03-02 20:05:34" 826 | }, 827 | { 828 | "name": "phpunit/php-code-coverage", 829 | "version": "4.0.8", 830 | "source": { 831 | "type": "git", 832 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 833 | "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" 834 | }, 835 | "dist": { 836 | "type": "zip", 837 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", 838 | "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", 839 | "shasum": "" 840 | }, 841 | "require": { 842 | "ext-dom": "*", 843 | "ext-xmlwriter": "*", 844 | "php": "^5.6 || ^7.0", 845 | "phpunit/php-file-iterator": "^1.3", 846 | "phpunit/php-text-template": "^1.2", 847 | "phpunit/php-token-stream": "^1.4.2 || ^2.0", 848 | "sebastian/code-unit-reverse-lookup": "^1.0", 849 | "sebastian/environment": "^1.3.2 || ^2.0", 850 | "sebastian/version": "^1.0 || ^2.0" 851 | }, 852 | "require-dev": { 853 | "ext-xdebug": "^2.1.4", 854 | "phpunit/phpunit": "^5.7" 855 | }, 856 | "suggest": { 857 | "ext-xdebug": "^2.5.1" 858 | }, 859 | "type": "library", 860 | "extra": { 861 | "branch-alias": { 862 | "dev-master": "4.0.x-dev" 863 | } 864 | }, 865 | "autoload": { 866 | "classmap": [ 867 | "src/" 868 | ] 869 | }, 870 | "notification-url": "https://packagist.org/downloads/", 871 | "license": [ 872 | "BSD-3-Clause" 873 | ], 874 | "authors": [ 875 | { 876 | "name": "Sebastian Bergmann", 877 | "email": "sb@sebastian-bergmann.de", 878 | "role": "lead" 879 | } 880 | ], 881 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 882 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 883 | "keywords": [ 884 | "coverage", 885 | "testing", 886 | "xunit" 887 | ], 888 | "time": "2017-04-02 07:44:40" 889 | }, 890 | { 891 | "name": "phpunit/php-file-iterator", 892 | "version": "1.4.2", 893 | "source": { 894 | "type": "git", 895 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 896 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" 897 | }, 898 | "dist": { 899 | "type": "zip", 900 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 901 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", 902 | "shasum": "" 903 | }, 904 | "require": { 905 | "php": ">=5.3.3" 906 | }, 907 | "type": "library", 908 | "extra": { 909 | "branch-alias": { 910 | "dev-master": "1.4.x-dev" 911 | } 912 | }, 913 | "autoload": { 914 | "classmap": [ 915 | "src/" 916 | ] 917 | }, 918 | "notification-url": "https://packagist.org/downloads/", 919 | "license": [ 920 | "BSD-3-Clause" 921 | ], 922 | "authors": [ 923 | { 924 | "name": "Sebastian Bergmann", 925 | "email": "sb@sebastian-bergmann.de", 926 | "role": "lead" 927 | } 928 | ], 929 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 930 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 931 | "keywords": [ 932 | "filesystem", 933 | "iterator" 934 | ], 935 | "time": "2016-10-03 07:40:28" 936 | }, 937 | { 938 | "name": "phpunit/php-text-template", 939 | "version": "1.2.1", 940 | "source": { 941 | "type": "git", 942 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 943 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 944 | }, 945 | "dist": { 946 | "type": "zip", 947 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 948 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 949 | "shasum": "" 950 | }, 951 | "require": { 952 | "php": ">=5.3.3" 953 | }, 954 | "type": "library", 955 | "autoload": { 956 | "classmap": [ 957 | "src/" 958 | ] 959 | }, 960 | "notification-url": "https://packagist.org/downloads/", 961 | "license": [ 962 | "BSD-3-Clause" 963 | ], 964 | "authors": [ 965 | { 966 | "name": "Sebastian Bergmann", 967 | "email": "sebastian@phpunit.de", 968 | "role": "lead" 969 | } 970 | ], 971 | "description": "Simple template engine.", 972 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 973 | "keywords": [ 974 | "template" 975 | ], 976 | "time": "2015-06-21 13:50:34" 977 | }, 978 | { 979 | "name": "phpunit/php-timer", 980 | "version": "1.0.9", 981 | "source": { 982 | "type": "git", 983 | "url": "https://github.com/sebastianbergmann/php-timer.git", 984 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" 985 | }, 986 | "dist": { 987 | "type": "zip", 988 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 989 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 990 | "shasum": "" 991 | }, 992 | "require": { 993 | "php": "^5.3.3 || ^7.0" 994 | }, 995 | "require-dev": { 996 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 997 | }, 998 | "type": "library", 999 | "extra": { 1000 | "branch-alias": { 1001 | "dev-master": "1.0-dev" 1002 | } 1003 | }, 1004 | "autoload": { 1005 | "classmap": [ 1006 | "src/" 1007 | ] 1008 | }, 1009 | "notification-url": "https://packagist.org/downloads/", 1010 | "license": [ 1011 | "BSD-3-Clause" 1012 | ], 1013 | "authors": [ 1014 | { 1015 | "name": "Sebastian Bergmann", 1016 | "email": "sb@sebastian-bergmann.de", 1017 | "role": "lead" 1018 | } 1019 | ], 1020 | "description": "Utility class for timing", 1021 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 1022 | "keywords": [ 1023 | "timer" 1024 | ], 1025 | "time": "2017-02-26 11:10:40" 1026 | }, 1027 | { 1028 | "name": "phpunit/php-token-stream", 1029 | "version": "1.4.11", 1030 | "source": { 1031 | "type": "git", 1032 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 1033 | "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" 1034 | }, 1035 | "dist": { 1036 | "type": "zip", 1037 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", 1038 | "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", 1039 | "shasum": "" 1040 | }, 1041 | "require": { 1042 | "ext-tokenizer": "*", 1043 | "php": ">=5.3.3" 1044 | }, 1045 | "require-dev": { 1046 | "phpunit/phpunit": "~4.2" 1047 | }, 1048 | "type": "library", 1049 | "extra": { 1050 | "branch-alias": { 1051 | "dev-master": "1.4-dev" 1052 | } 1053 | }, 1054 | "autoload": { 1055 | "classmap": [ 1056 | "src/" 1057 | ] 1058 | }, 1059 | "notification-url": "https://packagist.org/downloads/", 1060 | "license": [ 1061 | "BSD-3-Clause" 1062 | ], 1063 | "authors": [ 1064 | { 1065 | "name": "Sebastian Bergmann", 1066 | "email": "sebastian@phpunit.de" 1067 | } 1068 | ], 1069 | "description": "Wrapper around PHP's tokenizer extension.", 1070 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 1071 | "keywords": [ 1072 | "tokenizer" 1073 | ], 1074 | "time": "2017-02-27 10:12:30" 1075 | }, 1076 | { 1077 | "name": "phpunit/phpunit", 1078 | "version": "5.7.19", 1079 | "source": { 1080 | "type": "git", 1081 | "url": "https://github.com/sebastianbergmann/phpunit.git", 1082 | "reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1" 1083 | }, 1084 | "dist": { 1085 | "type": "zip", 1086 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/69c4f49ff376af2692bad9cebd883d17ebaa98a1", 1087 | "reference": "69c4f49ff376af2692bad9cebd883d17ebaa98a1", 1088 | "shasum": "" 1089 | }, 1090 | "require": { 1091 | "ext-dom": "*", 1092 | "ext-json": "*", 1093 | "ext-libxml": "*", 1094 | "ext-mbstring": "*", 1095 | "ext-xml": "*", 1096 | "myclabs/deep-copy": "~1.3", 1097 | "php": "^5.6 || ^7.0", 1098 | "phpspec/prophecy": "^1.6.2", 1099 | "phpunit/php-code-coverage": "^4.0.4", 1100 | "phpunit/php-file-iterator": "~1.4", 1101 | "phpunit/php-text-template": "~1.2", 1102 | "phpunit/php-timer": "^1.0.6", 1103 | "phpunit/phpunit-mock-objects": "^3.2", 1104 | "sebastian/comparator": "^1.2.4", 1105 | "sebastian/diff": "~1.2", 1106 | "sebastian/environment": "^1.3.4 || ^2.0", 1107 | "sebastian/exporter": "~2.0", 1108 | "sebastian/global-state": "^1.1", 1109 | "sebastian/object-enumerator": "~2.0", 1110 | "sebastian/resource-operations": "~1.0", 1111 | "sebastian/version": "~1.0.3|~2.0", 1112 | "symfony/yaml": "~2.1|~3.0" 1113 | }, 1114 | "conflict": { 1115 | "phpdocumentor/reflection-docblock": "3.0.2" 1116 | }, 1117 | "require-dev": { 1118 | "ext-pdo": "*" 1119 | }, 1120 | "suggest": { 1121 | "ext-xdebug": "*", 1122 | "phpunit/php-invoker": "~1.1" 1123 | }, 1124 | "bin": [ 1125 | "phpunit" 1126 | ], 1127 | "type": "library", 1128 | "extra": { 1129 | "branch-alias": { 1130 | "dev-master": "5.7.x-dev" 1131 | } 1132 | }, 1133 | "autoload": { 1134 | "classmap": [ 1135 | "src/" 1136 | ] 1137 | }, 1138 | "notification-url": "https://packagist.org/downloads/", 1139 | "license": [ 1140 | "BSD-3-Clause" 1141 | ], 1142 | "authors": [ 1143 | { 1144 | "name": "Sebastian Bergmann", 1145 | "email": "sebastian@phpunit.de", 1146 | "role": "lead" 1147 | } 1148 | ], 1149 | "description": "The PHP Unit Testing framework.", 1150 | "homepage": "https://phpunit.de/", 1151 | "keywords": [ 1152 | "phpunit", 1153 | "testing", 1154 | "xunit" 1155 | ], 1156 | "time": "2017-04-03 02:22:27" 1157 | }, 1158 | { 1159 | "name": "phpunit/phpunit-mock-objects", 1160 | "version": "3.4.3", 1161 | "source": { 1162 | "type": "git", 1163 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 1164 | "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24" 1165 | }, 1166 | "dist": { 1167 | "type": "zip", 1168 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", 1169 | "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", 1170 | "shasum": "" 1171 | }, 1172 | "require": { 1173 | "doctrine/instantiator": "^1.0.2", 1174 | "php": "^5.6 || ^7.0", 1175 | "phpunit/php-text-template": "^1.2", 1176 | "sebastian/exporter": "^1.2 || ^2.0" 1177 | }, 1178 | "conflict": { 1179 | "phpunit/phpunit": "<5.4.0" 1180 | }, 1181 | "require-dev": { 1182 | "phpunit/phpunit": "^5.4" 1183 | }, 1184 | "suggest": { 1185 | "ext-soap": "*" 1186 | }, 1187 | "type": "library", 1188 | "extra": { 1189 | "branch-alias": { 1190 | "dev-master": "3.2.x-dev" 1191 | } 1192 | }, 1193 | "autoload": { 1194 | "classmap": [ 1195 | "src/" 1196 | ] 1197 | }, 1198 | "notification-url": "https://packagist.org/downloads/", 1199 | "license": [ 1200 | "BSD-3-Clause" 1201 | ], 1202 | "authors": [ 1203 | { 1204 | "name": "Sebastian Bergmann", 1205 | "email": "sb@sebastian-bergmann.de", 1206 | "role": "lead" 1207 | } 1208 | ], 1209 | "description": "Mock Object library for PHPUnit", 1210 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 1211 | "keywords": [ 1212 | "mock", 1213 | "xunit" 1214 | ], 1215 | "time": "2016-12-08 20:27:08" 1216 | }, 1217 | { 1218 | "name": "sebastian/code-unit-reverse-lookup", 1219 | "version": "1.0.1", 1220 | "source": { 1221 | "type": "git", 1222 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 1223 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" 1224 | }, 1225 | "dist": { 1226 | "type": "zip", 1227 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 1228 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 1229 | "shasum": "" 1230 | }, 1231 | "require": { 1232 | "php": "^5.6 || ^7.0" 1233 | }, 1234 | "require-dev": { 1235 | "phpunit/phpunit": "^5.7 || ^6.0" 1236 | }, 1237 | "type": "library", 1238 | "extra": { 1239 | "branch-alias": { 1240 | "dev-master": "1.0.x-dev" 1241 | } 1242 | }, 1243 | "autoload": { 1244 | "classmap": [ 1245 | "src/" 1246 | ] 1247 | }, 1248 | "notification-url": "https://packagist.org/downloads/", 1249 | "license": [ 1250 | "BSD-3-Clause" 1251 | ], 1252 | "authors": [ 1253 | { 1254 | "name": "Sebastian Bergmann", 1255 | "email": "sebastian@phpunit.de" 1256 | } 1257 | ], 1258 | "description": "Looks up which function or method a line of code belongs to", 1259 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 1260 | "time": "2017-03-04 06:30:41" 1261 | }, 1262 | { 1263 | "name": "sebastian/comparator", 1264 | "version": "1.2.4", 1265 | "source": { 1266 | "type": "git", 1267 | "url": "https://github.com/sebastianbergmann/comparator.git", 1268 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" 1269 | }, 1270 | "dist": { 1271 | "type": "zip", 1272 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 1273 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 1274 | "shasum": "" 1275 | }, 1276 | "require": { 1277 | "php": ">=5.3.3", 1278 | "sebastian/diff": "~1.2", 1279 | "sebastian/exporter": "~1.2 || ~2.0" 1280 | }, 1281 | "require-dev": { 1282 | "phpunit/phpunit": "~4.4" 1283 | }, 1284 | "type": "library", 1285 | "extra": { 1286 | "branch-alias": { 1287 | "dev-master": "1.2.x-dev" 1288 | } 1289 | }, 1290 | "autoload": { 1291 | "classmap": [ 1292 | "src/" 1293 | ] 1294 | }, 1295 | "notification-url": "https://packagist.org/downloads/", 1296 | "license": [ 1297 | "BSD-3-Clause" 1298 | ], 1299 | "authors": [ 1300 | { 1301 | "name": "Jeff Welch", 1302 | "email": "whatthejeff@gmail.com" 1303 | }, 1304 | { 1305 | "name": "Volker Dusch", 1306 | "email": "github@wallbash.com" 1307 | }, 1308 | { 1309 | "name": "Bernhard Schussek", 1310 | "email": "bschussek@2bepublished.at" 1311 | }, 1312 | { 1313 | "name": "Sebastian Bergmann", 1314 | "email": "sebastian@phpunit.de" 1315 | } 1316 | ], 1317 | "description": "Provides the functionality to compare PHP values for equality", 1318 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 1319 | "keywords": [ 1320 | "comparator", 1321 | "compare", 1322 | "equality" 1323 | ], 1324 | "time": "2017-01-29 09:50:25" 1325 | }, 1326 | { 1327 | "name": "sebastian/diff", 1328 | "version": "1.4.1", 1329 | "source": { 1330 | "type": "git", 1331 | "url": "https://github.com/sebastianbergmann/diff.git", 1332 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" 1333 | }, 1334 | "dist": { 1335 | "type": "zip", 1336 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", 1337 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", 1338 | "shasum": "" 1339 | }, 1340 | "require": { 1341 | "php": ">=5.3.3" 1342 | }, 1343 | "require-dev": { 1344 | "phpunit/phpunit": "~4.8" 1345 | }, 1346 | "type": "library", 1347 | "extra": { 1348 | "branch-alias": { 1349 | "dev-master": "1.4-dev" 1350 | } 1351 | }, 1352 | "autoload": { 1353 | "classmap": [ 1354 | "src/" 1355 | ] 1356 | }, 1357 | "notification-url": "https://packagist.org/downloads/", 1358 | "license": [ 1359 | "BSD-3-Clause" 1360 | ], 1361 | "authors": [ 1362 | { 1363 | "name": "Kore Nordmann", 1364 | "email": "mail@kore-nordmann.de" 1365 | }, 1366 | { 1367 | "name": "Sebastian Bergmann", 1368 | "email": "sebastian@phpunit.de" 1369 | } 1370 | ], 1371 | "description": "Diff implementation", 1372 | "homepage": "https://github.com/sebastianbergmann/diff", 1373 | "keywords": [ 1374 | "diff" 1375 | ], 1376 | "time": "2015-12-08 07:14:41" 1377 | }, 1378 | { 1379 | "name": "sebastian/environment", 1380 | "version": "2.0.0", 1381 | "source": { 1382 | "type": "git", 1383 | "url": "https://github.com/sebastianbergmann/environment.git", 1384 | "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" 1385 | }, 1386 | "dist": { 1387 | "type": "zip", 1388 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", 1389 | "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", 1390 | "shasum": "" 1391 | }, 1392 | "require": { 1393 | "php": "^5.6 || ^7.0" 1394 | }, 1395 | "require-dev": { 1396 | "phpunit/phpunit": "^5.0" 1397 | }, 1398 | "type": "library", 1399 | "extra": { 1400 | "branch-alias": { 1401 | "dev-master": "2.0.x-dev" 1402 | } 1403 | }, 1404 | "autoload": { 1405 | "classmap": [ 1406 | "src/" 1407 | ] 1408 | }, 1409 | "notification-url": "https://packagist.org/downloads/", 1410 | "license": [ 1411 | "BSD-3-Clause" 1412 | ], 1413 | "authors": [ 1414 | { 1415 | "name": "Sebastian Bergmann", 1416 | "email": "sebastian@phpunit.de" 1417 | } 1418 | ], 1419 | "description": "Provides functionality to handle HHVM/PHP environments", 1420 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1421 | "keywords": [ 1422 | "Xdebug", 1423 | "environment", 1424 | "hhvm" 1425 | ], 1426 | "time": "2016-11-26 07:53:53" 1427 | }, 1428 | { 1429 | "name": "sebastian/exporter", 1430 | "version": "2.0.0", 1431 | "source": { 1432 | "type": "git", 1433 | "url": "https://github.com/sebastianbergmann/exporter.git", 1434 | "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" 1435 | }, 1436 | "dist": { 1437 | "type": "zip", 1438 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", 1439 | "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", 1440 | "shasum": "" 1441 | }, 1442 | "require": { 1443 | "php": ">=5.3.3", 1444 | "sebastian/recursion-context": "~2.0" 1445 | }, 1446 | "require-dev": { 1447 | "ext-mbstring": "*", 1448 | "phpunit/phpunit": "~4.4" 1449 | }, 1450 | "type": "library", 1451 | "extra": { 1452 | "branch-alias": { 1453 | "dev-master": "2.0.x-dev" 1454 | } 1455 | }, 1456 | "autoload": { 1457 | "classmap": [ 1458 | "src/" 1459 | ] 1460 | }, 1461 | "notification-url": "https://packagist.org/downloads/", 1462 | "license": [ 1463 | "BSD-3-Clause" 1464 | ], 1465 | "authors": [ 1466 | { 1467 | "name": "Jeff Welch", 1468 | "email": "whatthejeff@gmail.com" 1469 | }, 1470 | { 1471 | "name": "Volker Dusch", 1472 | "email": "github@wallbash.com" 1473 | }, 1474 | { 1475 | "name": "Bernhard Schussek", 1476 | "email": "bschussek@2bepublished.at" 1477 | }, 1478 | { 1479 | "name": "Sebastian Bergmann", 1480 | "email": "sebastian@phpunit.de" 1481 | }, 1482 | { 1483 | "name": "Adam Harvey", 1484 | "email": "aharvey@php.net" 1485 | } 1486 | ], 1487 | "description": "Provides the functionality to export PHP variables for visualization", 1488 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1489 | "keywords": [ 1490 | "export", 1491 | "exporter" 1492 | ], 1493 | "time": "2016-11-19 08:54:04" 1494 | }, 1495 | { 1496 | "name": "sebastian/global-state", 1497 | "version": "1.1.1", 1498 | "source": { 1499 | "type": "git", 1500 | "url": "https://github.com/sebastianbergmann/global-state.git", 1501 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 1502 | }, 1503 | "dist": { 1504 | "type": "zip", 1505 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 1506 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 1507 | "shasum": "" 1508 | }, 1509 | "require": { 1510 | "php": ">=5.3.3" 1511 | }, 1512 | "require-dev": { 1513 | "phpunit/phpunit": "~4.2" 1514 | }, 1515 | "suggest": { 1516 | "ext-uopz": "*" 1517 | }, 1518 | "type": "library", 1519 | "extra": { 1520 | "branch-alias": { 1521 | "dev-master": "1.0-dev" 1522 | } 1523 | }, 1524 | "autoload": { 1525 | "classmap": [ 1526 | "src/" 1527 | ] 1528 | }, 1529 | "notification-url": "https://packagist.org/downloads/", 1530 | "license": [ 1531 | "BSD-3-Clause" 1532 | ], 1533 | "authors": [ 1534 | { 1535 | "name": "Sebastian Bergmann", 1536 | "email": "sebastian@phpunit.de" 1537 | } 1538 | ], 1539 | "description": "Snapshotting of global state", 1540 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1541 | "keywords": [ 1542 | "global state" 1543 | ], 1544 | "time": "2015-10-12 03:26:01" 1545 | }, 1546 | { 1547 | "name": "sebastian/object-enumerator", 1548 | "version": "2.0.1", 1549 | "source": { 1550 | "type": "git", 1551 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1552 | "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" 1553 | }, 1554 | "dist": { 1555 | "type": "zip", 1556 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", 1557 | "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", 1558 | "shasum": "" 1559 | }, 1560 | "require": { 1561 | "php": ">=5.6", 1562 | "sebastian/recursion-context": "~2.0" 1563 | }, 1564 | "require-dev": { 1565 | "phpunit/phpunit": "~5" 1566 | }, 1567 | "type": "library", 1568 | "extra": { 1569 | "branch-alias": { 1570 | "dev-master": "2.0.x-dev" 1571 | } 1572 | }, 1573 | "autoload": { 1574 | "classmap": [ 1575 | "src/" 1576 | ] 1577 | }, 1578 | "notification-url": "https://packagist.org/downloads/", 1579 | "license": [ 1580 | "BSD-3-Clause" 1581 | ], 1582 | "authors": [ 1583 | { 1584 | "name": "Sebastian Bergmann", 1585 | "email": "sebastian@phpunit.de" 1586 | } 1587 | ], 1588 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1589 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1590 | "time": "2017-02-18 15:18:39" 1591 | }, 1592 | { 1593 | "name": "sebastian/recursion-context", 1594 | "version": "2.0.0", 1595 | "source": { 1596 | "type": "git", 1597 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1598 | "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" 1599 | }, 1600 | "dist": { 1601 | "type": "zip", 1602 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", 1603 | "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", 1604 | "shasum": "" 1605 | }, 1606 | "require": { 1607 | "php": ">=5.3.3" 1608 | }, 1609 | "require-dev": { 1610 | "phpunit/phpunit": "~4.4" 1611 | }, 1612 | "type": "library", 1613 | "extra": { 1614 | "branch-alias": { 1615 | "dev-master": "2.0.x-dev" 1616 | } 1617 | }, 1618 | "autoload": { 1619 | "classmap": [ 1620 | "src/" 1621 | ] 1622 | }, 1623 | "notification-url": "https://packagist.org/downloads/", 1624 | "license": [ 1625 | "BSD-3-Clause" 1626 | ], 1627 | "authors": [ 1628 | { 1629 | "name": "Jeff Welch", 1630 | "email": "whatthejeff@gmail.com" 1631 | }, 1632 | { 1633 | "name": "Sebastian Bergmann", 1634 | "email": "sebastian@phpunit.de" 1635 | }, 1636 | { 1637 | "name": "Adam Harvey", 1638 | "email": "aharvey@php.net" 1639 | } 1640 | ], 1641 | "description": "Provides functionality to recursively process PHP variables", 1642 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1643 | "time": "2016-11-19 07:33:16" 1644 | }, 1645 | { 1646 | "name": "sebastian/resource-operations", 1647 | "version": "1.0.0", 1648 | "source": { 1649 | "type": "git", 1650 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1651 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" 1652 | }, 1653 | "dist": { 1654 | "type": "zip", 1655 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1656 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1657 | "shasum": "" 1658 | }, 1659 | "require": { 1660 | "php": ">=5.6.0" 1661 | }, 1662 | "type": "library", 1663 | "extra": { 1664 | "branch-alias": { 1665 | "dev-master": "1.0.x-dev" 1666 | } 1667 | }, 1668 | "autoload": { 1669 | "classmap": [ 1670 | "src/" 1671 | ] 1672 | }, 1673 | "notification-url": "https://packagist.org/downloads/", 1674 | "license": [ 1675 | "BSD-3-Clause" 1676 | ], 1677 | "authors": [ 1678 | { 1679 | "name": "Sebastian Bergmann", 1680 | "email": "sebastian@phpunit.de" 1681 | } 1682 | ], 1683 | "description": "Provides a list of PHP built-in functions that operate on resources", 1684 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1685 | "time": "2015-07-28 20:34:47" 1686 | }, 1687 | { 1688 | "name": "sebastian/version", 1689 | "version": "2.0.1", 1690 | "source": { 1691 | "type": "git", 1692 | "url": "https://github.com/sebastianbergmann/version.git", 1693 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1694 | }, 1695 | "dist": { 1696 | "type": "zip", 1697 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1698 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1699 | "shasum": "" 1700 | }, 1701 | "require": { 1702 | "php": ">=5.6" 1703 | }, 1704 | "type": "library", 1705 | "extra": { 1706 | "branch-alias": { 1707 | "dev-master": "2.0.x-dev" 1708 | } 1709 | }, 1710 | "autoload": { 1711 | "classmap": [ 1712 | "src/" 1713 | ] 1714 | }, 1715 | "notification-url": "https://packagist.org/downloads/", 1716 | "license": [ 1717 | "BSD-3-Clause" 1718 | ], 1719 | "authors": [ 1720 | { 1721 | "name": "Sebastian Bergmann", 1722 | "email": "sebastian@phpunit.de", 1723 | "role": "lead" 1724 | } 1725 | ], 1726 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1727 | "homepage": "https://github.com/sebastianbergmann/version", 1728 | "time": "2016-10-03 07:35:21" 1729 | }, 1730 | { 1731 | "name": "symfony/polyfill-mbstring", 1732 | "version": "v1.3.0", 1733 | "source": { 1734 | "type": "git", 1735 | "url": "https://github.com/symfony/polyfill-mbstring.git", 1736 | "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" 1737 | }, 1738 | "dist": { 1739 | "type": "zip", 1740 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", 1741 | "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", 1742 | "shasum": "" 1743 | }, 1744 | "require": { 1745 | "php": ">=5.3.3" 1746 | }, 1747 | "suggest": { 1748 | "ext-mbstring": "For best performance" 1749 | }, 1750 | "type": "library", 1751 | "extra": { 1752 | "branch-alias": { 1753 | "dev-master": "1.3-dev" 1754 | } 1755 | }, 1756 | "autoload": { 1757 | "psr-4": { 1758 | "Symfony\\Polyfill\\Mbstring\\": "" 1759 | }, 1760 | "files": [ 1761 | "bootstrap.php" 1762 | ] 1763 | }, 1764 | "notification-url": "https://packagist.org/downloads/", 1765 | "license": [ 1766 | "MIT" 1767 | ], 1768 | "authors": [ 1769 | { 1770 | "name": "Nicolas Grekas", 1771 | "email": "p@tchwork.com" 1772 | }, 1773 | { 1774 | "name": "Symfony Community", 1775 | "homepage": "https://symfony.com/contributors" 1776 | } 1777 | ], 1778 | "description": "Symfony polyfill for the Mbstring extension", 1779 | "homepage": "https://symfony.com", 1780 | "keywords": [ 1781 | "compatibility", 1782 | "mbstring", 1783 | "polyfill", 1784 | "portable", 1785 | "shim" 1786 | ], 1787 | "time": "2016-11-14 01:06:16" 1788 | }, 1789 | { 1790 | "name": "symfony/var-dumper", 1791 | "version": "v3.2.7", 1792 | "source": { 1793 | "type": "git", 1794 | "url": "https://github.com/symfony/var-dumper.git", 1795 | "reference": "81dce20f69a8b40427e1f4e6462178df87cafc03" 1796 | }, 1797 | "dist": { 1798 | "type": "zip", 1799 | "url": "https://api.github.com/repos/symfony/var-dumper/zipball/81dce20f69a8b40427e1f4e6462178df87cafc03", 1800 | "reference": "81dce20f69a8b40427e1f4e6462178df87cafc03", 1801 | "shasum": "" 1802 | }, 1803 | "require": { 1804 | "php": ">=5.5.9", 1805 | "symfony/polyfill-mbstring": "~1.0" 1806 | }, 1807 | "conflict": { 1808 | "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" 1809 | }, 1810 | "require-dev": { 1811 | "twig/twig": "~1.20|~2.0" 1812 | }, 1813 | "suggest": { 1814 | "ext-symfony_debug": "" 1815 | }, 1816 | "type": "library", 1817 | "extra": { 1818 | "branch-alias": { 1819 | "dev-master": "3.2-dev" 1820 | } 1821 | }, 1822 | "autoload": { 1823 | "files": [ 1824 | "Resources/functions/dump.php" 1825 | ], 1826 | "psr-4": { 1827 | "Symfony\\Component\\VarDumper\\": "" 1828 | }, 1829 | "exclude-from-classmap": [ 1830 | "/Tests/" 1831 | ] 1832 | }, 1833 | "notification-url": "https://packagist.org/downloads/", 1834 | "license": [ 1835 | "MIT" 1836 | ], 1837 | "authors": [ 1838 | { 1839 | "name": "Nicolas Grekas", 1840 | "email": "p@tchwork.com" 1841 | }, 1842 | { 1843 | "name": "Symfony Community", 1844 | "homepage": "https://symfony.com/contributors" 1845 | } 1846 | ], 1847 | "description": "Symfony mechanism for exploring and dumping PHP variables", 1848 | "homepage": "https://symfony.com", 1849 | "keywords": [ 1850 | "debug", 1851 | "dump" 1852 | ], 1853 | "time": "2017-03-12 16:07:05" 1854 | }, 1855 | { 1856 | "name": "symfony/yaml", 1857 | "version": "v3.2.7", 1858 | "source": { 1859 | "type": "git", 1860 | "url": "https://github.com/symfony/yaml.git", 1861 | "reference": "62b4cdb99d52cb1ff253c465eb1532a80cebb621" 1862 | }, 1863 | "dist": { 1864 | "type": "zip", 1865 | "url": "https://api.github.com/repos/symfony/yaml/zipball/62b4cdb99d52cb1ff253c465eb1532a80cebb621", 1866 | "reference": "62b4cdb99d52cb1ff253c465eb1532a80cebb621", 1867 | "shasum": "" 1868 | }, 1869 | "require": { 1870 | "php": ">=5.5.9" 1871 | }, 1872 | "require-dev": { 1873 | "symfony/console": "~2.8|~3.0" 1874 | }, 1875 | "suggest": { 1876 | "symfony/console": "For validating YAML files using the lint command" 1877 | }, 1878 | "type": "library", 1879 | "extra": { 1880 | "branch-alias": { 1881 | "dev-master": "3.2-dev" 1882 | } 1883 | }, 1884 | "autoload": { 1885 | "psr-4": { 1886 | "Symfony\\Component\\Yaml\\": "" 1887 | }, 1888 | "exclude-from-classmap": [ 1889 | "/Tests/" 1890 | ] 1891 | }, 1892 | "notification-url": "https://packagist.org/downloads/", 1893 | "license": [ 1894 | "MIT" 1895 | ], 1896 | "authors": [ 1897 | { 1898 | "name": "Fabien Potencier", 1899 | "email": "fabien@symfony.com" 1900 | }, 1901 | { 1902 | "name": "Symfony Community", 1903 | "homepage": "https://symfony.com/contributors" 1904 | } 1905 | ], 1906 | "description": "Symfony Yaml Component", 1907 | "homepage": "https://symfony.com", 1908 | "time": "2017-03-20 09:45:15" 1909 | }, 1910 | { 1911 | "name": "webmozart/assert", 1912 | "version": "1.2.0", 1913 | "source": { 1914 | "type": "git", 1915 | "url": "https://github.com/webmozart/assert.git", 1916 | "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" 1917 | }, 1918 | "dist": { 1919 | "type": "zip", 1920 | "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", 1921 | "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", 1922 | "shasum": "" 1923 | }, 1924 | "require": { 1925 | "php": "^5.3.3 || ^7.0" 1926 | }, 1927 | "require-dev": { 1928 | "phpunit/phpunit": "^4.6", 1929 | "sebastian/version": "^1.0.1" 1930 | }, 1931 | "type": "library", 1932 | "extra": { 1933 | "branch-alias": { 1934 | "dev-master": "1.3-dev" 1935 | } 1936 | }, 1937 | "autoload": { 1938 | "psr-4": { 1939 | "Webmozart\\Assert\\": "src/" 1940 | } 1941 | }, 1942 | "notification-url": "https://packagist.org/downloads/", 1943 | "license": [ 1944 | "MIT" 1945 | ], 1946 | "authors": [ 1947 | { 1948 | "name": "Bernhard Schussek", 1949 | "email": "bschussek@gmail.com" 1950 | } 1951 | ], 1952 | "description": "Assertions to validate method input/output with nice error messages.", 1953 | "keywords": [ 1954 | "assert", 1955 | "check", 1956 | "validate" 1957 | ], 1958 | "time": "2016-11-23 20:04:58" 1959 | } 1960 | ], 1961 | "aliases": [], 1962 | "minimum-stability": "stable", 1963 | "stability-flags": [], 1964 | "prefer-stable": true, 1965 | "prefer-lowest": false, 1966 | "platform": { 1967 | "php": ">=7.0" 1968 | }, 1969 | "platform-dev": [] 1970 | } 1971 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | tests/Brendt 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Brendt/Image/Config/DefaultConfigurator.php: -------------------------------------------------------------------------------- 1 | 'gd', 23 | 'publicPath' => './', 24 | 'sourcePath' => './', 25 | 'rebase' => false, 26 | 'enableCache' => false, 27 | 'optimize' => false, 28 | 'scaler' => 'filesize', 29 | 'stepModifier' => 0.5, 30 | 'minFileSize' => 5000, 31 | 'maxFileSize' => null, 32 | 'minWidth' => 300, 33 | 'maxWidth' => null, 34 | 'sizes' => [], 35 | 'optimizerOptions' => [], 36 | 'includeSource' => true, 37 | ]; 38 | 39 | /** 40 | * ResponsiveFactoryConfigurator constructor. 41 | * 42 | * @param array $config 43 | * 44 | * @throws InvalidConfigurationException 45 | */ 46 | public function __construct(array $config = []) { 47 | if (isset($config['driver']) && !in_array($config['driver'], ['gd', 'imagick'])) { 48 | throw new InvalidConfigurationException('Invalid driver. Possible drivers are `gd` and `imagick`'); 49 | } 50 | 51 | $this->config = array_merge($this->config, $config); 52 | } 53 | 54 | /** 55 | * @param ResponsiveFactory $factory 56 | * 57 | * @return void 58 | */ 59 | public function configure(ResponsiveFactory $factory) { 60 | /** @var AbstractScaler $scaler */ 61 | switch ($this->config['scaler']) { 62 | case 'filesize': 63 | $scaler = new FileSizeScaler($this); 64 | break; 65 | case 'width': 66 | $scaler = new WidthScaler($this); 67 | break; 68 | case 'sizes': 69 | default: 70 | $scaler = new SizesScaler($this); 71 | $scaler->setSizes($this->config['sizes']); 72 | break; 73 | } 74 | 75 | $factory 76 | ->setDriver($this->config['driver']) 77 | ->setPublicPath($this->config['publicPath']) 78 | ->setSourcePath($this->config['sourcePath']) 79 | ->setRebase($this->config['rebase']) 80 | ->setEnableCache($this->config['enableCache']) 81 | ->setOptimize($this->config['optimize']) 82 | ->setOptimizerOptions($this->config['optimizerOptions']) 83 | ->setScaler($scaler); 84 | } 85 | 86 | /** 87 | * @param Scaler $scaler 88 | * 89 | * @return Scaler 90 | */ 91 | public function configureScaler(Scaler $scaler) { 92 | $scaler 93 | ->setIncludeSource($this->config['includeSource']) 94 | ->setMinFileSize($this->config['minFileSize']) 95 | ->setMinWidth($this->config['minWidth']) 96 | ->setMaxFileSize($this->config['maxFileSize']) 97 | ->setMaxWidth($this->config['maxWidth']) 98 | ->setStepModifier($this->config['stepModifier']); 99 | 100 | return $scaler; 101 | } 102 | 103 | /** 104 | * @return array 105 | */ 106 | public function getConfig() { 107 | return $this->config; 108 | } 109 | 110 | /** 111 | * @param $key 112 | * 113 | * @return mixed|null 114 | */ 115 | public function get($key) { 116 | return isset($this->config[$key]) ? $this->config[$key] : null; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Brendt/Image/Config/ResponsiveFactoryConfigurator.php: -------------------------------------------------------------------------------- 1 | configure($this); 95 | 96 | $this->sourcePath = rtrim($this->sourcePath, '/'); 97 | $this->publicPath = rtrim($this->publicPath, '/'); 98 | 99 | $this->engine = new ImageManager([ 100 | 'driver' => $this->driver, 101 | ]); 102 | 103 | $this->optimizer = (new OptimizerFactory($this->optimizerOptions))->get(); 104 | $this->fs = new Filesystem(); 105 | 106 | if (!$this->fs->exists($this->publicPath)) { 107 | $this->fs->mkdir($this->publicPath); 108 | } 109 | } 110 | 111 | /** 112 | * @param string $src 113 | * 114 | * @return ResponsiveImage 115 | * @throws FileNotFoundException 116 | */ 117 | public function create($src) { 118 | $responsiveImage = new ResponsiveImage($src); 119 | $src = $responsiveImage->src(); 120 | $sourceFilename = $this->rebase ? pathinfo($src, PATHINFO_BASENAME) : $src; 121 | $sourceFile = $this->getImageFile($this->sourcePath, $sourceFilename); 122 | 123 | $filename = pathinfo($sourceFile->getFilename(), PATHINFO_FILENAME); 124 | $urlPath = '/' . trim(pathinfo($src, PATHINFO_DIRNAME), '/'); 125 | 126 | $responsiveImage->setExtension($sourceFile->getExtension()); 127 | $responsiveImage->setFileName($filename); 128 | $responsiveImage->setUrlPath($urlPath); 129 | 130 | if ($cachedResponsiveImage = $this->getCachedResponsiveImage($responsiveImage, $sourceFile)) { 131 | return $cachedResponsiveImage; 132 | } 133 | 134 | $this->fs->dumpFile("{$this->publicPath}{$src}", $sourceFile->getContents()); 135 | $this->createScaledImages($sourceFile, $responsiveImage); 136 | 137 | return $responsiveImage; 138 | } 139 | 140 | /** 141 | * @param ResponsiveImage $responsiveImage 142 | * @param SplFileInfo $imageFile 143 | * 144 | * @return ResponsiveImage|null 145 | */ 146 | public function getCachedResponsiveImage(ResponsiveImage $responsiveImage, SplFileInfo $imageFile) { 147 | $src = $responsiveImage->src(); 148 | $publicImagePath = "{$this->publicPath}{$src}"; 149 | 150 | if ($this->enableCache && $this->fs->exists($publicImagePath)) { 151 | $extension = $imageFile->getExtension(); 152 | $publicDirectory = $this->rebase ? trim(pathinfo($src, PATHINFO_DIRNAME), '/') : $imageFile->getRelativePath(); 153 | $imageFilename = pathinfo($imageFile->getFilename(), PATHINFO_FILENAME); 154 | 155 | /** @var SplFileInfo[] $cachedFiles */ 156 | $cachedFiles = Finder::create()->files()->in("{$this->publicPath}/{$publicDirectory}")->name("{$imageFilename}-*.{$extension}"); 157 | 158 | foreach ($cachedFiles as $cachedFile) { 159 | $cachedFilename = $cachedFile->getFilename(); 160 | $size = (int) str_replace(".{$extension}", '', str_replace("{$imageFilename}-", '', $cachedFilename)); 161 | 162 | $responsiveImage->addSource("{$responsiveImage->getUrlPath()}/{$cachedFilename}", $size); 163 | } 164 | 165 | return $responsiveImage; 166 | } 167 | 168 | return null; 169 | } 170 | 171 | /** 172 | * Create scaled image files and add them as sources to a Responsive Image, based on an array of file sizes: 173 | * [ 174 | * width => height, 175 | * ... 176 | * ] 177 | * 178 | * @param SplFileInfo $sourceImage 179 | * @param ResponsiveImage $responsiveImage 180 | * 181 | * @return ResponsiveImage 182 | */ 183 | public function createScaledImages(SplFileInfo $sourceImage, ResponsiveImage $responsiveImage) : ResponsiveImage { 184 | $imageObject = $this->engine->make($sourceImage->getPathname()); 185 | $urlPath = $responsiveImage->getUrlPath(); 186 | $sizes = $this->scaler->scale($sourceImage, $imageObject); 187 | 188 | foreach ($sizes as $width => $height) { 189 | $scaledFileSrc = trim("{$urlPath}/{$imageObject->filename}-{$width}.{$imageObject->extension}", '/'); 190 | $scaledFilePath = "{$this->getPublicPath()}/{$scaledFileSrc}"; 191 | $responsiveImage->addSource($scaledFileSrc, $width); 192 | 193 | $scaledImage = $imageObject->resize((int) $width, (int) $height)->encode($imageObject->extension); 194 | 195 | if (!$this->enableCache || !$this->fs->exists($scaledFilePath)) { 196 | $this->fs->dumpFile($scaledFilePath, $scaledImage); 197 | } 198 | } 199 | 200 | $imageObject->destroy(); 201 | 202 | if ($this->optimize) { 203 | $this->optimizeResponsiveImage($responsiveImage); 204 | } 205 | 206 | return $responsiveImage; 207 | } 208 | 209 | /** 210 | * Optimize all sources of a Responsive Image 211 | * 212 | * @param ResponsiveImage $responsiveImage 213 | * 214 | * @return ResponsiveImage 215 | */ 216 | private function optimizeResponsiveImage(ResponsiveImage $responsiveImage) : ResponsiveImage { 217 | foreach ($responsiveImage->getSrcset() as $imageFile) { 218 | $this->optimizer->optimize("{$this->publicPath}/{$imageFile}"); 219 | } 220 | 221 | return $responsiveImage; 222 | } 223 | 224 | /** 225 | * @param string $directory 226 | * @param string $path 227 | * 228 | * @return null|SplFileInfo 229 | * @throws FileNotFoundException 230 | */ 231 | private function getImageFile(string $directory, string $path) { 232 | $path = ltrim($path, '/'); 233 | $iterator = Finder::create()->files()->in($directory)->path($path)->getIterator(); 234 | $iterator->rewind(); 235 | 236 | $sourceImage = $iterator->current(); 237 | 238 | if (!$sourceImage) { 239 | throw new FileNotFoundException("{$this->sourcePath}/{$path}"); 240 | } 241 | 242 | return $sourceImage; 243 | } 244 | 245 | /** 246 | * @param string $driver 247 | * 248 | * @return ResponsiveFactory 249 | */ 250 | public function setDriver($driver) : ResponsiveFactory { 251 | $this->driver = $driver; 252 | 253 | return $this; 254 | } 255 | 256 | /** 257 | * @param string $publicPath 258 | * 259 | * @return ResponsiveFactory 260 | */ 261 | public function setPublicPath($publicPath) : ResponsiveFactory { 262 | $this->publicPath = $publicPath; 263 | 264 | return $this; 265 | } 266 | 267 | /** 268 | * @param boolean $enableCache 269 | * 270 | * @return ResponsiveFactory 271 | */ 272 | public function setEnableCache($enableCache) : ResponsiveFactory { 273 | $this->enableCache = $enableCache; 274 | 275 | return $this; 276 | } 277 | 278 | /** 279 | * @param string $sourcePath 280 | * 281 | * @return ResponsiveFactory 282 | */ 283 | public function setSourcePath($sourcePath) : ResponsiveFactory { 284 | $this->sourcePath = $sourcePath; 285 | 286 | return $this; 287 | } 288 | 289 | /** 290 | * @param Scaler $scaler 291 | * 292 | * @return ResponsiveFactory 293 | */ 294 | public function setScaler($scaler) : ResponsiveFactory { 295 | $this->scaler = $scaler; 296 | 297 | return $this; 298 | } 299 | 300 | /** 301 | * @param bool $optimize 302 | * 303 | * @return ResponsiveFactory 304 | */ 305 | public function setOptimize(bool $optimize) : ResponsiveFactory { 306 | $this->optimize = $optimize; 307 | 308 | return $this; 309 | } 310 | 311 | /** 312 | * @return string 313 | */ 314 | public function getPublicPath() : string { 315 | return $this->publicPath; 316 | } 317 | 318 | /** 319 | * @param mixed $optimizerOptions 320 | * 321 | * @return ResponsiveFactory 322 | */ 323 | public function setOptimizerOptions($optimizerOptions) : ResponsiveFactory { 324 | $this->optimizerOptions = $optimizerOptions; 325 | 326 | return $this; 327 | } 328 | 329 | /** 330 | * @param bool $rebase 331 | * 332 | * @return ResponsiveFactory 333 | */ 334 | public function setRebase(bool $rebase) : ResponsiveFactory { 335 | $this->rebase = $rebase; 336 | 337 | return $this; 338 | } 339 | 340 | } 341 | -------------------------------------------------------------------------------- /src/Brendt/Image/ResponsiveImage.php: -------------------------------------------------------------------------------- 1 | src = "/{$src}"; 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function src() { 53 | return $this->src; 54 | } 55 | 56 | /** 57 | * @param $sources 58 | * @param null $value 59 | * 60 | * @return ResponsiveImage 61 | */ 62 | public function addSource($sources, $value = null) { 63 | if (!is_array($sources) && $value) { 64 | $sources = [$sources => $value]; 65 | } elseif (!is_array($sources)) { 66 | return $this; 67 | } 68 | 69 | foreach ($sources as $url => $width) { 70 | $url = ltrim($url, '/'); 71 | $width = str_replace('px', '', $width); 72 | 73 | $this->srcset[$width] = "/{$url}"; 74 | } 75 | 76 | krsort($this->srcset); 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * @return string 83 | */ 84 | public function srcset() { 85 | $srcset = []; 86 | 87 | foreach ($this->srcset as $w => $url) { 88 | $srcset[] = "{$url} {$w}w"; 89 | } 90 | 91 | return implode(',', $srcset); 92 | } 93 | 94 | /** 95 | * @param array|string $sizes 96 | * @param null $value 97 | * 98 | * @return ResponsiveImage 99 | */ 100 | public function addSizes($sizes, $value = null) { 101 | if (!is_array($sizes) && $value) { 102 | $sizes = [$sizes => $value]; 103 | } elseif (!is_array($sizes)) { 104 | return $this; 105 | } 106 | 107 | foreach ($sizes as $media => $value) { 108 | $this->sizes[$media] = $value; 109 | } 110 | 111 | return $this; 112 | } 113 | 114 | /** 115 | * @return string 116 | */ 117 | public function sizes() { 118 | $sizes = []; 119 | 120 | foreach ($this->sizes as $media => $value) { 121 | $media = rtrim(ltrim($media, '('), ')'); 122 | 123 | if (is_numeric($media)) { 124 | $sizes[] = "$value"; 125 | } else { 126 | $sizes[] = "({$media}) $value"; 127 | } 128 | } 129 | 130 | return implode(', ', $sizes); 131 | } 132 | 133 | /** 134 | * @return string 135 | */ 136 | public function getFileName() { 137 | return $this->fileName; 138 | } 139 | 140 | /** 141 | * @param string $fileName 142 | */ 143 | public function setFileName($fileName) { 144 | $this->fileName = $fileName; 145 | } 146 | 147 | /** 148 | * @return string 149 | */ 150 | public function getExtension() { 151 | return $this->extension; 152 | } 153 | 154 | /** 155 | * @param string $extension 156 | */ 157 | public function setExtension($extension) { 158 | $this->extension = $extension; 159 | } 160 | 161 | /** 162 | * @return string 163 | */ 164 | public function getUrlPath() { 165 | return $this->urlPath; 166 | } 167 | 168 | /** 169 | * @param string $urlPath 170 | */ 171 | public function setUrlPath($urlPath) { 172 | $this->urlPath = $urlPath; 173 | } 174 | 175 | /** 176 | * @return \string[] 177 | */ 178 | public function getSrcset() { 179 | return $this->srcset; 180 | } 181 | 182 | /** 183 | * @return string 184 | */ 185 | public function getSrc() { 186 | return $this->src; 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /src/Brendt/Image/Scaler/AbstractScaler.php: -------------------------------------------------------------------------------- 1 | configureScaler($this); 46 | } 47 | 48 | /** 49 | * @param mixed $minFileSize 50 | * 51 | * @return AbstractScaler 52 | */ 53 | public function setMinFileSize($minFileSize) { 54 | $this->minFileSize = $minFileSize; 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * @param mixed $minWidth 61 | * 62 | * @return AbstractScaler 63 | */ 64 | public function setMinWidth($minWidth) { 65 | $this->minWidth = $minWidth; 66 | 67 | return $this; 68 | } 69 | 70 | /** 71 | * @param mixed $maxFileSize 72 | * 73 | * @return AbstractScaler 74 | */ 75 | public function setMaxFileSize($maxFileSize) { 76 | $this->maxFileSize = $maxFileSize; 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * @param mixed $maxWidth 83 | * 84 | * @return AbstractScaler 85 | */ 86 | public function setMaxWidth($maxWidth) { 87 | $this->maxWidth = $maxWidth; 88 | 89 | return $this; 90 | } 91 | 92 | /** 93 | * @param mixed $stepModifier 94 | * 95 | * @return AbstractScaler 96 | */ 97 | public function setStepModifier($stepModifier) { 98 | $this->stepModifier = $stepModifier; 99 | 100 | return $this; 101 | } 102 | 103 | /** 104 | * @param bool $includeSource 105 | * 106 | * @return Scaler 107 | */ 108 | public function setIncludeSource(bool $includeSource) : Scaler { 109 | $this->includeSource = $includeSource; 110 | 111 | return $this; 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/Brendt/Image/Scaler/FileSizeScaler.php: -------------------------------------------------------------------------------- 1 | getSize(); 19 | $width = $imageObject->getWidth(); 20 | $height = $imageObject->getHeight(); 21 | $ratio = $height / $width; 22 | $area = $width * $width * $ratio; 23 | $pixelPrice = $fileSize / $area; 24 | 25 | $sizes = []; 26 | 27 | if ($this->includeSource && (!$this->maxWidth || $width <= $this->maxWidth) && (!$this->maxFileSize || $fileSize <= $this->maxFileSize)) { 28 | $sizes[$width] = $height; 29 | } 30 | 31 | do { 32 | $fileSize = $fileSize * $this->stepModifier; 33 | $newWidth = floor(sqrt(($fileSize / $pixelPrice) / $ratio)); 34 | 35 | if ((!$this->maxFileSize || $fileSize <= $this->maxFileSize) && (!$this->maxWidth || $newWidth <= $this->maxWidth)) { 36 | $sizes[(int) $newWidth] = (int) $newWidth * $ratio; 37 | } 38 | } while ($fileSize > $this->minFileSize && $newWidth > $this->minWidth); 39 | 40 | return $sizes; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Brendt/Image/Scaler/Scaler.php: -------------------------------------------------------------------------------- 1 | sizes = $sizes; 23 | 24 | return $this; 25 | } 26 | 27 | /** 28 | * @param SplFileInfo $sourceFile 29 | * @param Image $imageObject 30 | * 31 | * @return array 32 | */ 33 | public function scale(SplFileInfo $sourceFile, Image $imageObject) : array { 34 | $imageWidth = $imageObject->getWidth(); 35 | $imageHeight = $imageObject->getHeight(); 36 | $ratio = $imageHeight / $imageWidth; 37 | 38 | $sizes = []; 39 | 40 | if ($this->includeSource) { 41 | $sizes[$imageWidth] = $imageHeight; 42 | } 43 | 44 | foreach ($this->sizes as $width) { 45 | if ($width > $imageWidth) { 46 | continue; 47 | } 48 | 49 | $sizes[$width] = round($width * $ratio); 50 | } 51 | 52 | return $sizes; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Brendt/Image/Scaler/WidthScaler.php: -------------------------------------------------------------------------------- 1 | getWidth(); 19 | $height = $imageObject->getHeight(); 20 | $sizes = []; 21 | 22 | if ($this->includeSource && (!$this->maxWidth || $width <= $this->maxWidth)) { 23 | $sizes[$width] = $height; 24 | } 25 | 26 | while ($width >= $this->minWidth) { 27 | $width = floor($width * $this->stepModifier); 28 | $height = floor($height * $this->stepModifier); 29 | 30 | if (!$this->maxWidth || $width <= $this->maxWidth) { 31 | $sizes[(int) $width] = $height; 32 | } 33 | } 34 | 35 | return $sizes; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Brendt/Image/ResponsiveFactoryTest.php: -------------------------------------------------------------------------------- 1 | fs = new Filesystem(); 34 | } 35 | 36 | protected function tearDown() { 37 | if ($this->fs->exists($this->publicPath)) { 38 | $this->fs->remove($this->publicPath); 39 | } 40 | } 41 | 42 | public function setUp() { 43 | $this->configurator = new DefaultConfigurator([ 44 | 'publicPath' => $this->publicPath, 45 | 'engine' => 'gd', 46 | 'stepModifier' => 0.5, 47 | 'scaler' => 'width', 48 | ]); 49 | } 50 | 51 | public function test_simple_construct() { 52 | new ResponsiveFactory(); 53 | } 54 | 55 | public function test_create() { 56 | $factory = new ResponsiveFactory($this->configurator); 57 | $image = $factory->create('img/image.jpeg'); 58 | 59 | $this->assertTrue($this->fs->exists("{$this->publicPath}/img/image.jpeg")); 60 | 61 | $this->assertNotEmpty($image->srcset()); 62 | } 63 | 64 | public function test_create_sets_correct_src() { 65 | $factory = new ResponsiveFactory($this->configurator); 66 | $url = 'img/image.jpeg'; 67 | $image = $factory->create($url); 68 | 69 | $this->assertEquals("/{$url}", $image->src()); 70 | } 71 | 72 | public function test_create_doesnt_render_full_width_srcset() { 73 | $factory = new ResponsiveFactory($this->configurator); 74 | $url = 'img/image.jpeg'; 75 | $publicPath = $this->configurator->getConfig()['publicPath']; 76 | $factory->create($url); 77 | 78 | $this->assertFalse($this->fs->exists("{$publicPath}/image-1920.jpeg")); 79 | } 80 | 81 | public function test_create_sets_correct_srcset() { 82 | $factory = new ResponsiveFactory($this->configurator); 83 | $url = 'img/image.jpeg'; 84 | $image = $factory->create($url); 85 | 86 | $srcset = $image->srcset(); 87 | 88 | $this->assertNotEmpty($srcset); 89 | } 90 | 91 | public function test_create_sets_default_srcset() { 92 | $factory = new ResponsiveFactory($this->configurator); 93 | $url = 'img/image.jpeg'; 94 | $image = $factory->create($url); 95 | 96 | $srcset = $image->srcset(); 97 | 98 | $this->assertContains('/img/image-1920.jpeg 1920w', $srcset); 99 | } 100 | 101 | public function test_optimizer() { 102 | $url = 'img/image.jpeg'; 103 | 104 | $normalFactory = new ResponsiveFactory($this->configurator); 105 | $optimizedFactory = new ResponsiveFactory(new DefaultConfigurator([ 106 | 'publicPath' => $this->publicPath, 107 | 'engine' => 'gd', 108 | 'stepModifier' => 0.5, 109 | 'scaler' => 'width', 110 | 'optimize' => true, 111 | 'enableCache' => false, 112 | ])); 113 | 114 | $normalImage = $normalFactory->create($url); 115 | $normalImageFiles = Finder::create()->files()->in($this->publicPath)->path(trim($normalImage->getSrc(), '/'))->getIterator(); 116 | $normalImageFiles->rewind(); 117 | /** @var SplFileInfo $normalImageFile */ 118 | $normalImageFile = $normalImageFiles->current(); 119 | $normalImageFileSize = $normalImageFile->getSize(); 120 | 121 | $optimizedImage = $optimizedFactory->create($url); 122 | $optimizedImageFiles = Finder::create()->files()->in($this->publicPath)->path(trim($optimizedImage->getSrc(), '/'))->getIterator(); 123 | $optimizedImageFiles->rewind(); 124 | /** @var SplFileInfo $optimizedImageFile */ 125 | $optimizedImageFile = $optimizedImageFiles->current(); 126 | $optimizedImageFileSize = $optimizedImageFile->getSize(); 127 | 128 | $this->assertTrue($optimizedImageFileSize <= $normalImageFileSize); 129 | } 130 | 131 | /** 132 | * @test 133 | */ 134 | public function test_rebase() { 135 | $configurator = new DefaultConfigurator([ 136 | 'publicPath' => './tests/public', 137 | 'sourcePath' => './tests/img', 138 | 'rebase' => true, 139 | 'engine' => 'gd', 140 | 'stepModifier' => 0.5, 141 | 'scaler' => 'width', 142 | ]); 143 | 144 | $responsiveFactory = new ResponsiveFactory($configurator); 145 | 146 | $image = $responsiveFactory->create('/img/responsive/image.jpeg'); 147 | 148 | $this->assertTrue($this->fs->exists('./tests/public/img/responsive/image.jpeg')); 149 | $this->assertEquals('/img/responsive/image.jpeg', $image->src()); 150 | $this->assertContains('/img/responsive', $image->srcset()); 151 | } 152 | 153 | public function test_cached_result() { 154 | $src = file_get_contents('./tests/img/image.jpeg'); 155 | 156 | if (!$this->fs->exists('./tests/public/img')) { 157 | $this->fs->mkdir('./tests/public/img'); 158 | } 159 | 160 | $this->fs->dumpFile('./tests/public/img/image.jpeg', $src); 161 | $this->fs->dumpFile('./tests/public/img/image-500.jpeg', $src); 162 | $this->fs->dumpFile('./tests/public/img/image-1000.jpeg', $src); 163 | 164 | $factory = new ResponsiveFactory(new DefaultConfigurator([ 165 | 'publicPath' => './tests/public', 166 | 'sourcePath' => './tests', 167 | 'enableCache' => true 168 | ])); 169 | $image = $factory->create('/img/image.jpeg'); 170 | 171 | $srcset = $image->getSrcset(); 172 | $this->assertArrayHasKey(500, $srcset); 173 | $this->assertArrayHasKey(1000, $srcset); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /tests/Brendt/Image/ResponsiveImageTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('/img/image.jpeg', $image->src()); 18 | } 19 | 20 | public function test_src_with_slash() { 21 | $image = new ResponsiveImage('/img/image.jpeg'); 22 | 23 | $this->assertEquals('/img/image.jpeg', $image->src()); 24 | } 25 | 26 | public function test_srcset_empty_on_construct() { 27 | $image = new ResponsiveImage('img/image.jpeg'); 28 | 29 | $this->assertEquals('', $image->srcset()); 30 | } 31 | 32 | public function test_srcset_add_single_source() { 33 | $image = new ResponsiveImage('img/image.jpeg'); 34 | $image->addSource('img/test-500.jpg', '500px'); 35 | 36 | $this->assertEquals('/img/test-500.jpg 500w', $image->srcset()); 37 | } 38 | 39 | public function test_srcset_add_multiple_sources() { 40 | $image = new ResponsiveImage('img/image.jpeg'); 41 | 42 | $image->addSource([ 43 | 'img/test.jpg' => 1920, 44 | 'img/test-300.jpg' => 300, 45 | ]); 46 | 47 | $this->assertEquals('/img/test.jpg 1920w,/img/test-300.jpg 300w', $image->srcset()); 48 | } 49 | 50 | public function test_sizes_empty_on_construct() { 51 | $image = new ResponsiveImage('img/image.jpeg'); 52 | 53 | $this->assertEquals('', $image->sizes()); 54 | } 55 | 56 | public function test_sizes_add_single_size() { 57 | $image = new ResponsiveImage('tests/img/image.jpeg'); 58 | $image->addSizes('min-width: 650px', '33vw'); 59 | 60 | $this->assertEquals('(min-width: 650px) 33vw', $image->sizes()); 61 | } 62 | 63 | public function test_sizes_add_multiple_sizes() { 64 | $image = new ResponsiveImage('tests/img/image.jpeg'); 65 | 66 | $image->addSizes([ 67 | 'min-width: 1000px' => '50vw', 68 | '100vw', 69 | ]); 70 | 71 | $this->assertEquals('(min-width: 1000px) 50vw, 100vw', $image->sizes()); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /tests/Brendt/Image/Scaler/FileSizeScalerTest.php: -------------------------------------------------------------------------------- 1 | fs = new Filesystem(); 39 | $this->engine = new ImageManager([ 40 | 'driver' => 'gd', 41 | ]); 42 | } 43 | 44 | public function __destruct() { 45 | if ($this->fs->exists($this->publicPath)) { 46 | $this->fs->remove($this->publicPath); 47 | } 48 | } 49 | 50 | public function setUp() { 51 | $this->scaler = new FileSizeScaler(new DefaultConfigurator([ 52 | 'publicPath' => $this->publicPath, 53 | ])); 54 | } 55 | 56 | public function test_scale_down() { 57 | $sourceFile = $this->createSourceFile(); 58 | $imageObject = $this->createImageObject(); 59 | 60 | $sizes = $this->scaler->scale($sourceFile, $imageObject); 61 | 62 | $this->assertTrue(count($sizes) > 1); 63 | } 64 | 65 | public function test_scale_down_with_max_width() { 66 | $sourceFile = $this->createSourceFile(); 67 | $imageObject = $this->createImageObject(); 68 | 69 | $this->scaler->setMaxWidth(1000); 70 | $sizes = $this->scaler->scale($sourceFile, $imageObject); 71 | 72 | foreach ($sizes as $width => $height) { 73 | $this->assertTrue($width < 1000); 74 | } 75 | } 76 | 77 | public function test_scale_down_with_max_filesize() { 78 | $sourceFile = $this->createSourceFile(); 79 | $imageObject = $this->createImageObject(); 80 | 81 | $this->scaler->setMaxFileSize(100000); 82 | $sizes = $this->scaler->scale($sourceFile, $imageObject); 83 | 84 | $this->assertCount(3, $sizes); 85 | } 86 | 87 | public function test_scale_down_with_include_source_disabled() { 88 | $sourceFile = $this->createSourceFile(); 89 | $imageObject = $this->createImageObject(); 90 | 91 | $this->scaler->setIncludeSource(false); 92 | $sizes = $this->scaler->scale($sourceFile, $imageObject); 93 | 94 | $this->assertFalse(array_key_exists(1920, $sizes)); 95 | } 96 | 97 | // TODO: test algorithm 98 | 99 | private function createImageObject() { 100 | $imageObject = $this->engine->make('./tests/img/image.jpeg'); 101 | 102 | return $imageObject; 103 | } 104 | 105 | private function createSourceFile() { 106 | $sourceFiles = Finder::create()->files()->in('./tests/img')->name('image.jpeg')->getIterator(); 107 | $sourceFiles->rewind(); 108 | 109 | return $sourceFiles->current(); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /tests/Brendt/Image/Scaler/SizesScalerTest.php: -------------------------------------------------------------------------------- 1 | fs = new Filesystem(); 34 | $this->engine = new ImageManager([ 35 | 'driver' => 'gd', 36 | ]); 37 | } 38 | 39 | public function __destruct() { 40 | if ($this->fs->exists($this->publicPath)) { 41 | $this->fs->remove($this->publicPath); 42 | } 43 | } 44 | 45 | public function test_scale_down() { 46 | $scaler = new SizesScaler(new DefaultConfigurator([ 47 | 'publicPath' => $this->publicPath, 48 | 'includeSource' => false, 49 | ])); 50 | $scaler->setSizes([500, 10000, 1920]); 51 | 52 | $sourceFile = $this->createSourceFile(); 53 | $imageObject = $this->createImageObject(); 54 | 55 | $sizes = $scaler->scale($sourceFile, $imageObject); 56 | 57 | $this->assertCount(2, $sizes); 58 | } 59 | 60 | public function test_scale_down_with_include_source() { 61 | $scaler = new SizesScaler(new DefaultConfigurator([ 62 | 'publicPath' => $this->publicPath, 63 | 'includeSource' => true, 64 | ])); 65 | $scaler->setSizes([500, 800]); 66 | 67 | $sourceFile = $this->createSourceFile(); 68 | $imageObject = $this->createImageObject(); 69 | 70 | $sizes = $scaler->scale($sourceFile, $imageObject); 71 | 72 | $this->assertCount(3, $sizes); 73 | } 74 | 75 | 76 | private function createImageObject() { 77 | $imageObject = $this->engine->make('./tests/img/image.jpeg'); 78 | 79 | return $imageObject; 80 | } 81 | 82 | private function createSourceFile() { 83 | $sourceFiles = Finder::create()->files()->in('./tests/img')->name('image.jpeg')->getIterator(); 84 | $sourceFiles->rewind(); 85 | 86 | return $sourceFiles->current(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/Brendt/Image/Scaler/WidthScalerTest.php: -------------------------------------------------------------------------------- 1 | fs = new Filesystem(); 40 | $this->engine = new ImageManager([ 41 | 'driver' => 'gd', 42 | ]); 43 | } 44 | 45 | public function __destruct() { 46 | if ($this->fs->exists($this->publicPath)) { 47 | $this->fs->remove($this->publicPath); 48 | } 49 | } 50 | 51 | public function setUp() { 52 | $this->scaler = new WidthScaler(new DefaultConfigurator([ 53 | 'scaler' => 'width', 54 | 'stepModifier' => 0.8, 55 | 'publicPath' => $this->publicPath 56 | ])); 57 | } 58 | 59 | public function test_scale_down() { 60 | $sourceFile = $this->createSourceFile(); 61 | $imageObject = $this->createImageObject(); 62 | 63 | $sizes = $this->scaler->scale($sourceFile, $imageObject); 64 | 65 | $this->assertTrue(count($sizes) > 1); 66 | } 67 | 68 | public function test_scale_down_with_max_width() { 69 | $sourceFile = $this->createSourceFile(); 70 | $imageObject = $this->createImageObject(); 71 | 72 | $this->scaler->setMaxWidth(1000); 73 | $sizes = $this->scaler->scale($sourceFile, $imageObject); 74 | 75 | foreach ($sizes as $width => $height) { 76 | $this->assertTrue($width < 1000); 77 | } 78 | } 79 | 80 | public function test_scale_down_with_include_source_disabled() { 81 | $sourceFile = $this->createSourceFile(); 82 | $imageObject = $this->createImageObject(); 83 | 84 | $this->scaler->setIncludeSource(false); 85 | $sizes = $this->scaler->scale($sourceFile, $imageObject); 86 | 87 | $this->assertFalse(array_key_exists(1920, $sizes)); 88 | } 89 | 90 | private function createSourceFile() { 91 | $sourceFiles = Finder::create()->files()->in('./tests/img')->name('image.jpeg')->getIterator(); 92 | $sourceFiles->rewind(); 93 | 94 | return $sourceFiles->current(); 95 | } 96 | 97 | private function createImageObject() { 98 | $imageObject = $this->engine->make('./tests/img/image.jpeg'); 99 | 100 | return $imageObject; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /tests/Brendt/Image/config/DefaultConfiguratorTest.php: -------------------------------------------------------------------------------- 1 | true, 17 | ]); 18 | 19 | $this->assertTrue($configurator->getConfig()['enableCache']); 20 | $this->assertEquals('gd', $configurator->getConfig()['driver']); 21 | } 22 | 23 | /** 24 | * @expectedException \Brendt\Image\Exception\InvalidConfigurationException 25 | */ 26 | public function test_construct_throws_exception_with_unknown_driver() { 27 | new DefaultConfigurator([ 28 | 'driver' => 'unknown', 29 | ]); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /tests/img/image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brendt/responsive-images/c3ba0c66abf404db34a1d683470b58595433471e/tests/img/image.jpeg --------------------------------------------------------------------------------