From d8b32317cd23d4c9088af602169006d764b8e558 Mon Sep 17 00:00:00 2001 From: Samuel De Backer Date: Sun, 17 Apr 2022 19:20:53 +0200 Subject: [PATCH 1/7] L9 Update composer.json --- composer.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 691713e..a03c54e 100644 --- a/composer.json +++ b/composer.json @@ -8,13 +8,13 @@ ], "license": "MIT", "require": { - "php": "^7.2.5|^8.0", - "illuminate/console": "4 - 8", - "illuminate/support": "4 - 8", - "illuminate/routing": "4 - 8", - "league/flysystem": "~1.0", - "symfony/http-foundation": "2 - 5", - "symfony/http-kernel": "2 - 5", + "php": "^8.0.2", + "illuminate/console": "^9.0", + "illuminate/support": "^9.0", + "illuminate/routing": "^9.0", + "league/flysystem": "^3.0", + "symfony/http-foundation": "^6.0", + "symfony/http-kernel": "^6.0", "weotch/phpthumb": "^1.0.5", "ext-gd": "*" }, From d364305858f57e4f29ed63a094e7d6bf15da9f94 Mon Sep 17 00:00:00 2001 From: Samuel De Backer Date: Sun, 17 Apr 2022 20:25:58 +0200 Subject: [PATCH 2/7] Update config.php --- src/config/config.php | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/config/config.php b/src/config/config.php index 0ee14a4..0d6cf84 100755 --- a/src/config/config.php +++ b/src/config/config.php @@ -1,34 +1,33 @@ - public_path().'/uploads', - /** + /* * The directory where cropped images should be saved. The route to the * cropped versions is what should be rendered in your markup; it must be a * web accessible directory. * * @var string Absolute path in local fileystem * string IoC binding name of League\Flysystem\Filesystem - * string IoC binding name of League\Flysystem\Cached\CachedAdapter */ 'crops_dir' => public_path().'/uploads', - /** + /* * Maximum number of sizes to allow for a particular source file. This is to * limit scripts from filling up your hard drive with images. Set to false or * comment out to have no limit. This is disabled by default because the @@ -38,14 +37,13 @@ */ 'max_crops' => false, - /* |----------------------------------------------------------------------------- | URL parsing and generation |----------------------------------------------------------------------------- */ - /** + /* * A regex pattern that is applied to both the src url passed to * `Croppa::url()` as well as the crop path received when handling a crop * request to find the path to the src image relative to both the src_dir @@ -57,7 +55,7 @@ */ 'path' => 'uploads/(.*)$', - /** + /* * A regex pattern that works like `path` except it is only used by the * `Croppa::url($url)` generator function. If the $path url matches, it is * passed through with no Croppa URL suffixes added. Thus, it will not be @@ -68,14 +66,14 @@ */ 'ignore' => '\.(gif|GIF)$', - /** + /* * A string that is prepended to the path captured by the `path` pattern * (above) that is used to from the URL to crops. */ // 'url_prefix' => '//'.Request::getHttpHost().'/uploads/', // Local // 'url_prefix' => 'https://your-bucket.s3.amazonaws.com/uploads/', // S3 - /** + /* * Reject attempts to maliciously create images by signing the generated * request with a hash based on the request parameters and this signing key. * Set to 'app.key' to use Laravel's `app.key` config, any other string to use @@ -89,7 +87,7 @@ */ 'signing_key' => 'app.key', - /** + /* * The PHP memory limit used by the script to generate thumbnails. Some * images require a lot of memory to perform the resize, so you may increase * this memory limit. @@ -102,7 +100,7 @@ |----------------------------------------------------------------------------- */ - /** + /* * The jpeg quality of generated images. The difference between 100 and 95 * usually cuts the file size in half. Going down to 70 looks ok on photos * and will reduce filesize by more than another half but on vector files @@ -112,14 +110,14 @@ */ 'jpeg_quality' => 95, - /** + /* * Turn on interlacing to make progessive jpegs. * * @var boolean */ 'interlace' => true, - /** + /* * If the source image is smaller than the requested size, allow Croppa to * scale up the image. This will reduce in quality loss. * @@ -127,19 +125,18 @@ */ 'upscale' => false, - /** + /* * Filters for adding additional GD effects to an image and using them as parameter * in the croppa image slug. * * @var array */ 'filters' => [ - 'gray' => Bkwld\Croppa\Filters\BlackWhite::class, - 'darkgray' => Bkwld\Croppa\Filters\Darkgray::class, - 'blur' => Bkwld\Croppa\Filters\Blur::class, - 'negative' => Bkwld\Croppa\Filters\Negative::class, - 'orange' => Bkwld\Croppa\Filters\OrangeWarhol::class, + 'gray' => Bkwld\Croppa\Filters\BlackWhite::class, + 'darkgray' => Bkwld\Croppa\Filters\Darkgray::class, + 'blur' => Bkwld\Croppa\Filters\Blur::class, + 'negative' => Bkwld\Croppa\Filters\Negative::class, + 'orange' => Bkwld\Croppa\Filters\OrangeWarhol::class, 'turquoise' => Bkwld\Croppa\Filters\TurquoiseWarhol::class, ], - -); +]; From a768bb6b667f3e5ba13d416ff9253808c5cc6a2e Mon Sep 17 00:00:00 2001 From: Samuel De Backer Date: Sun, 17 Apr 2022 20:26:40 +0200 Subject: [PATCH 3/7] Update Storage.php --- src/Bkwld/Croppa/Storage.php | 273 +++++++++++++++++++++-------------- 1 file changed, 161 insertions(+), 112 deletions(-) diff --git a/src/Bkwld/Croppa/Storage.php b/src/Bkwld/Croppa/Storage.php index 552cfdd..7dd7973 100644 --- a/src/Bkwld/Croppa/Storage.php +++ b/src/Bkwld/Croppa/Storage.php @@ -1,16 +1,18 @@ -app = $app; $this->config = $config; } /** - * Factory function to create an instance and then "mount" disks + * Factory function to create an instance and then "mount" disks. * * @param Illuminate\Container\Container * @param array $config + * @param mixed $app + * * @return Bkwld\Croppa\Storage */ - static public function make($app, $config) { + public static function make($app, $config) + { return with(new static($app, $config))->mount(); } /** - * Set the crops disk + * Set the crops disk. * - * @param League\Flysystem\Filesystem - * League\Flysystem\Cached\CachedAdapter + * @param League\Flysystem\Filesystem + * @param mixed $disk */ - public function setCropsDisk($disk) { + public function setCropsDisk($disk) + { $this->crops_disk = $disk; } /** - * Get the crops disk or make via the config + * Get the crops disk or make via the config. * * @return League\Flysystem\Filesystem - * League\Flysystem\Cached\CachedAdapter */ - public function getCropsDisk() { + public function getCropsDisk() + { if (empty($this->crops_disk)) { $this->setCropsDisk($this->makeDisk($this->config['crops_dir'])); } + return $this->crops_disk; } /** - * Set the src disk + * Set the src disk. * - * @param League\Flysystem\Filesystem - * League\Flysystem\Cached\CachedAdapter + * @param League\Flysystem\Filesystem + * @param mixed $disk */ - public function setSrcDisk($disk) { + public function setSrcDisk($disk) + { $this->src_disk = $disk; } /** - * Get the src disk or make via the config + * Get the src disk or make via the config. * * @return League\Flysystem\Filesystem - * League\Flysystem\Cached\CachedAdapter */ - public function getSrcDisk() { + public function getSrcDisk() + { if (empty($this->src_disk)) { $this->setSrcDisk($this->makeDisk($this->config['src_dir'])); } + return $this->src_disk; } /** - * "Mount" disks given the config + * "Mount" disks given the config. * * @return $this */ - public function mount() { + public function mount() + { $this->setSrcDisk($this->makeDisk($this->config['src_dir'])); $this->setCropsDisk($this->makeDisk($this->config['crops_dir'])); + return $this; } /** - * Use or instantiate a Flysystem disk + * Use or instantiate a Flysystem disk. * * @param string $dir The value from one of the config dirs - * @return League\Flysystem\Filesystem | League\Flysystem\Cached\CachedAdapter + * + * @return League\Flysystem\Filesystem */ - public function makeDisk($dir) { - + public function makeDisk($dir) + { // Check if the dir refers to an IoC binding and return it if ($this->app->bound($dir) && ($instance = $this->app->make($dir)) - && (is_a($instance, 'League\Flysystem\Filesystem') - || is_a($instance, 'League\Flysystem\Cached\CachedAdapter')) - ) return $instance; + && (is_a($instance, 'League\Flysystem\Filesystem')) + ) { + return $instance; + } // Instantiate a new Flysystem instance for local dirs - return new Filesystem(new Adapter($dir)); + return new Filesystem(new LocalFilesystemAdapter($dir)); } /** - * Return whether crops are stored remotely + * Return whether crops are stored remotely. * - * @return boolean + * @return bool */ - public function cropsAreRemote() { - $adapter = $this->getCropsDisk()->getAdapter(); - - // If using a cached adapter, get the actual adapter that is being cached. - if (is_a($adapter, 'League\Flysystem\Cached\CachedAdapter')) { - $adapter = $adapter->getAdapter(); - } + public function cropsAreRemote() + { + $adapter = $this->getCropsDisk(); // Check if the crop disk is not local - return !is_a($adapter, 'League\Flysystem\Adapter\Local'); + return !is_a($adapter, LocalFilesystemAdapter::class); } /** - * Check if a remote crop exists + * Check if a remote crop exists. * * @param string $path - * @return boolean + * + * @return bool */ - public function cropExists($path) { + public function cropExists($path) + { return $this->getCropsDisk()->has($path); } /** - * Get the src image data or throw an exception + * Get the src image data or throw an exception. * * @param string $path Path to image relative to dir + * * @throws Symfony\Component\HttpKernel\Exception\NotFoundHttpException + * * @return string */ - public function readSrc($path) { + public function readSrc($path) + { $disk = $this->getSrcDisk(); - if ($disk->has($path)) return $disk->read($path); - else throw new NotFoundHttpException('Croppa: Src image is missing'); + if ($disk->has($path)) { + return $disk->read($path); + } + + throw new NotFoundHttpException('Croppa: Src image is missing'); } /** - * Write the cropped image contents to disk + * Write the cropped image contents to disk. * - * @param string $path Where to save the crop + * @param string $path Where to save the crop * @param string $contents The image data + * * @throws Exception - * @return void */ - public function writeCrop($path, $contents) { + public function writeCrop($path, $contents) + { try { $this->getCropsDisk()->write($path, $contents); - } catch(FileExistsException $e) { + } catch (FilesystemException $e) { // don't throw exception anymore as mentioned in PR #164 } - } /** - * Get a local crops disks absolute path + * Get a local crops disks absolute path. * * @return string */ - public function getLocalCropsDirPath() { + public function getLocalCropsDirPath() + { return $this->getCropsDisk()->getAdapter()->getPathPrefix(); } /** - * Delete src image + * Delete src image. * * @param string $path Path to src image */ - public function deleteSrc($path) { + public function deleteSrc($path) + { $this->getSrcDisk()->delete($path); } /** - * Delete crops + * Delete crops. + * + * @param string $path Path to src image * - * @param string $path Path to src image * @return array List of crops that were deleted */ - public function deleteCrops($path) { + public function deleteCrops($path) + { $crops = $this->listCrops($path); $disk = $this->getCropsDisk(); - foreach($crops as $crop) $disk->delete($crop); + foreach ($crops as $crop) { + $disk->delete($crop); + } + return $crops; } /** - * Delete ALL crops + * Delete ALL crops. + * + * @param string $filter A regex pattern + * @param bool $dry_run Don't actually delete any * - * @param string $filter A regex pattern - * @param boolean $dry_run Don't actually delete any * @return array List of crops that were deleted */ - public function deleteAllCrops($filter = null, $dry_run = false) { + public function deleteAllCrops($filter = null, $dry_run = false) + { $crops = $this->listAllCrops($filter); $disk = $this->getCropsDisk(); - if (!$dry_run) foreach($crops as $crop) $disk->delete($crop); + if (!$dry_run) { + foreach ($crops as $crop) { + $disk->delete($crop); + } + } + return $crops; } @@ -235,80 +268,96 @@ public function deleteAllCrops($filter = null, $dry_run = false) { * Count up the number of crops that have already been created * and return true if they are at the max number. * - * @param string $path Path to the src image - * @return boolean + * @param string $path Path to the src image + * + * @return bool */ - public function tooManyCrops($path) { - if (empty($this->config['max_crops'])) return false; + public function tooManyCrops($path) + { + if (empty($this->config['max_crops'])) { + return false; + } + return count($this->listCrops($path)) >= $this->config['max_crops']; } /** - * Find all the crops that have been generated for a src path + * Find all the crops that have been generated for a src path. + * + * @param string $path Path to a src image * - * @param string $path Path to a src image * @return array */ - public function listCrops($path) { - + public function listCrops($path) + { // Get the filename and dir $filename = basename($path); $dir = dirname($path); - if ($dir === '.') $dir = ''; // Flysystem doesn't like "." for the dir + if ($dir === '.') { + $dir = ''; + } // Flysystem doesn't like "." for the dir // Filter the files in the dir to just crops of the image path - return $this->justPaths(array_filter($this->getCropsDisk()->listContents($dir), - function($file) use ($filename) { - - // Don't return the source image, we're JUST getting crops - return $file['basename'] != $filename - + return $this->justPaths(array_filter( + $this->getCropsDisk()->listContents($dir)->toArray(), + function ($file) use ($filename) { + // Don't return the source image, we're JUST getting crops + return pathinfo($file['path'], PATHINFO_BASENAME) !== $filename // Test that the crop begins with the src's path, that the crop is FOR // the src - && strpos($file['basename'], pathinfo($filename, PATHINFO_FILENAME)) === 0 + && mb_strpos(pathinfo($file['path'], PATHINFO_FILENAME), pathinfo($filename, PATHINFO_FILENAME)) === 0 // Make sure that the crop matches that Croppa file regex && preg_match('#'.URL::PATTERN.'#', $file['path']); - })); + } + )); } /** * Find all the crops witin the crops dir, optionally applying a filtering - * regex to them + * regex to them. + * + * @param string $filter A regex pattern * - * @param string $filter A regex pattern * @return array */ - public function listAllCrops($filter = null) { - return $this->justPaths(array_filter($this->getCropsDisk()->listContents(null, true), - function($file) use ($filter) { - - // If there was a filter, force it to match - if ($filter && !preg_match("#$filter#i", $file['path'])) return; - - // Check that the file matches the pattern and get at the parts to make to - // make the path to the src - if (!preg_match('#'.URL::PATTERN.'#', $file['path'], $matches)) return false; - $src = $matches[1].'.'.$matches[5]; - - // Test that the src file exists - return $this->getSrcDisk()->has($src); - })); + public function listAllCrops($filter = null) + { + return $this->justPaths(array_filter( + $this->getCropsDisk()->listContents('', true)->toArray(), + function ($file) use ($filter) { + // If there was a filter, force it to match + if ($filter && !preg_match("#{$filter}#i", $file['path'])) { + return; + } + + // Check that the file matches the pattern and get at the parts to make to + // make the path to the src + if (!preg_match('#'.URL::PATTERN.'#', $file['path'], $matches)) { + return false; + } + $src = $matches[1].'.'.$matches[5]; + + // Test that the src file exists + return $this->getSrcDisk()->has($src); + } + )); } /** * Take a an array of results from Flysystem's listContents and get a simpler - * array of paths to the files, relative to the crops_dir + * array of paths to the files, relative to the crops_dir. + * + * @param array $files A multi-dimensionsal array from flysystem * - * @param array $files A multi-dimensionsal array from flysystem * @return array $paths */ - protected function justPaths($files) { - + protected function justPaths($files) + { // Reset the indexes to be 0 based, mostly for unit testing $files = array_values($files); // Get just the path key - return array_map(function($file) { return $file['path']; }, $files); + return array_map(function ($file) { return $file['path']; }, $files); } } From c3f1319926cbf48873879531dd4cf1241cfa0c6a Mon Sep 17 00:00:00 2001 From: Samuel De Backer Date: Sun, 17 Apr 2022 21:25:45 +0200 Subject: [PATCH 4/7] Make tests pass. --- .travis.yml | 14 +-- composer.json | 4 +- phpunit.xml | 10 +- tests/TestDelete.php | 223 +++++++++++++++++++------------------ tests/TestListAllCrops.php | 113 ++++++++++--------- tests/TestResizing.php | 159 ++++++++++++++------------ tests/TestTooManyCrops.php | 102 +++++++++-------- tests/TestUrlGenerator.php | 191 ++++++++++++++++--------------- tests/TestUrlMatching.php | 89 ++++++++------- tests/TestUrlParsing.php | 144 +++++++++++++----------- 10 files changed, 558 insertions(+), 491 deletions(-) diff --git a/.travis.yml b/.travis.yml index 71f1b08..4c02723 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,13 @@ language: php php: - - 5.5 - - 5.6 - - 7.0 - - 7.1 + - 8.0 + - 8.1 before_script: - - composer install --dev - - yarn add mocha + - composer install --dev + - yarn add mocha script: - - phpunit - - yarn mocha public/js/test + - phpunit + - yarn mocha public/js/test diff --git a/composer.json b/composer.json index a03c54e..ee97d6d 100644 --- a/composer.json +++ b/composer.json @@ -19,8 +19,8 @@ "ext-gd": "*" }, "require-dev": { - "phpunit/phpunit": "4 - 5", - "mockery/mockery": "~0.9" + "phpunit/phpunit": "^9.5.10", + "mockery/mockery": "^1.4.4" }, "suggest": { "ext-exif": "Required if you want to have Croppa auto-rotate images from devices like mobile phones based on exif meta data." diff --git a/phpunit.xml b/phpunit.xml index 5fdf9af..34cd746 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,14 +1,8 @@ - diff --git a/tests/TestDelete.php b/tests/TestDelete.php index 88ce073..b512a49 100644 --- a/tests/TestDelete.php +++ b/tests/TestDelete.php @@ -3,114 +3,121 @@ use Bkwld\Croppa\Helpers; use Bkwld\Croppa\Storage; use Bkwld\Croppa\URL; +use League\Flysystem\DirectoryListing; use PHPUnit\Framework\TestCase; -class TestDelete extends TestCase { - - public function testDeleteSrc() { - - $disk = Mockery::mock('League\Flysystem\Filesystem') - ->shouldReceive('listContents') - ->withAnyArgs() - ->andReturn([ - ['path' => 'me.jpg', 'basename' => 'me.jpg'], - ['path' => 'me-200x100.jpg', 'basename' => 'me-200x100.jpg'], - ['path' => 'me-200x200.jpg', 'basename' => 'me-200x200.jpg'], - ['path' => 'me-200x300.jpg', 'basename' => 'me-200x300.jpg'], - ['path' => 'unrelated.jpg', 'basename' => 'unrelated.jpg'], - ]) - ->shouldReceive('delete') - ->withAnyArgs() - ->once() - ->getMock(); - - $storage = new Storage(); - $storage->setSrcDisk($disk); - $this->assertNull($storage->deleteSrc('me.jpg')); - } - - public function testDeleteCrops() { - - $disk = Mockery::mock('League\Flysystem\Filesystem') - ->shouldReceive('listContents') - ->withAnyArgs() - ->andReturn([ - ['path' => 'me.jpg', 'basename' => 'me.jpg'], - ['path' => 'me-200x100.jpg', 'basename' => 'me-200x100.jpg'], - ['path' => 'me-200x200.jpg', 'basename' => 'me-200x200.jpg'], - ['path' => 'me-200x300.jpg', 'basename' => 'me-200x300.jpg'], - ['path' => 'unrelated.jpg', 'basename' => 'unrelated.jpg'], - ]) - ->shouldReceive('delete') - ->withAnyArgs() - ->times(3) - ->getMock(); - - $storage = new Storage(); - $storage->setCropsDisk($disk); - $this->assertEquals([ - 'me-200x100.jpg', - 'me-200x200.jpg', - 'me-200x300.jpg', - ], $storage->deleteCrops('me.jpg')); - } - - // Instantiate a helpers instance using mocked disks so the whole delete - // logic can be checked - private function mockHelpersForDeleting() { - - // The path is to a sub dir - $url = new URL([ - 'path' => 'uploads/(?:thumbs/)?(.*)$', - ]); - - $src = Mockery::mock('League\Flysystem\Filesystem') - ->shouldReceive('listContents') - ->withAnyArgs() - ->andReturn([ - ['path' => 'me.jpg', 'basename' => 'me.jpg'], - ['path' => 'unrelated.jpg', 'basename' => 'unrelated.jpg'], - ]) - ->shouldReceive('delete') - ->withAnyArgs() - ->once() - ->getMock(); - - $crops = Mockery::mock('League\Flysystem\Filesystem') - ->shouldReceive('listContents') - ->withAnyArgs() - ->andReturn([ - ['path' => 'me-200x100.jpg', 'basename' => 'me-200x100.jpg'], - ['path' => 'me-200x200.jpg', 'basename' => 'me-200x200.jpg'], - ['path' => 'me-200x300.jpg', 'basename' => 'me-200x300.jpg'], - ['path' => 'unrelated.jpg', 'basename' => 'unrelated.jpg'], - ]) - ->shouldReceive('delete') - ->withAnyArgs() - ->times(3) - ->getMock(); - - $storage = new Storage(); - $storage->setSrcDisk($src); - $storage->setCropsDisk($crops); - - $handler = Mockery::mock('Bkwld\Croppa\Handler'); - - return new Helpers($url, $storage, $handler); - - } - - public function testDeleteCropsInSubDir() { - $helpers = $this->mockHelpersForDeleting(); - $helpers->delete('/uploads/me.jpg'); - } - - public function testDeleteCropsInSubDirWithFullURL() { - $helpers = $this->mockHelpersForDeleting(); - $helpers->delete('http://domain.com/uploads/me.jpg'); - } - - public function tearDown() { - Mockery::close(); - } +/** + * @internal + * @coversNothing + */ +class TestDelete extends TestCase +{ + public function testDeleteSrc() + { + $disk = Mockery::mock('League\Flysystem\Filesystem') + ->shouldReceive('listContents') + ->withAnyArgs() + ->andReturn([ + ['path' => 'me.jpg'], + ['path' => 'me-200x100.jpg'], + ['path' => 'me-200x200.jpg'], + ['path' => 'me-200x300.jpg'], + ['path' => 'unrelated.jpg'], + ]) + ->shouldReceive('delete') + ->withAnyArgs() + ->once() + ->getMock(); + + $storage = new Storage(); + $storage->setSrcDisk($disk); + $this->assertNull($storage->deleteSrc('me.jpg')); + } + + public function testDeleteCrops() + { + $disk = Mockery::mock('League\Flysystem\Filesystem') + ->shouldReceive('listContents') + ->withAnyArgs() + ->andReturn(new DirectoryListing([ + ['path' => 'me.jpg'], + ['path' => 'me-200x100.jpg'], + ['path' => 'me-200x200.jpg'], + ['path' => 'me-200x300.jpg'], + ['path' => 'unrelated.jpg'], + ])) + ->shouldReceive('delete') + ->withAnyArgs() + ->times(3) + ->getMock(); + + $storage = new Storage(); + $storage->setCropsDisk($disk); + $this->assertEquals([ + 'me-200x100.jpg', + 'me-200x200.jpg', + 'me-200x300.jpg', + ], $storage->deleteCrops('me.jpg')); + } + + // Instantiate a helpers instance using mocked disks so the whole delete + // logic can be checked + private function mockHelpersForDeleting() + { + // The path is to a sub dir + $url = new URL([ + 'path' => 'uploads/(?:thumbs/)?(.*)$', + ]); + + $src = Mockery::mock('League\Flysystem\Filesystem') + ->shouldReceive('listContents') + ->withAnyArgs() + ->andReturn(new DirectoryListing([ + ['path' => 'me.jpg'], + ['path' => 'unrelated.jpg'], + ])) + ->shouldReceive('delete') + ->withAnyArgs() + ->once() + ->getMock(); + + $crops = Mockery::mock('League\Flysystem\Filesystem') + ->shouldReceive('listContents') + ->withAnyArgs() + ->andReturn(new DirectoryListing([ + ['path' => 'me-200x100.jpg'], + ['path' => 'me-200x200.jpg'], + ['path' => 'me-200x300.jpg'], + ['path' => 'unrelated.jpg'], + ])) + ->shouldReceive('delete') + ->withAnyArgs() + ->times(3) + ->getMock(); + + $storage = new Storage(); + $storage->setSrcDisk($src); + $storage->setCropsDisk($crops); + + $handler = Mockery::mock('Bkwld\Croppa\Handler'); + + return new Helpers($url, $storage, $handler); + } + + public function testDeleteCropsInSubDir() + { + $helpers = $this->mockHelpersForDeleting(); + $helpers->delete('/uploads/me.jpg'); + } + + public function testDeleteCropsInSubDirWithFullURL() + { + $helpers = $this->mockHelpersForDeleting(); + $helpers->delete('http://domain.com/uploads/me.jpg'); + } + + public function tearDown(): void + { + Mockery::close(); + } } diff --git a/tests/TestListAllCrops.php b/tests/TestListAllCrops.php index 8bd7a6c..bf0c67a 100644 --- a/tests/TestListAllCrops.php +++ b/tests/TestListAllCrops.php @@ -1,66 +1,71 @@ src_dir = Mockery::mock('League\Flysystem\Filesystem') + ->shouldReceive('has')->with('01/me.jpg')->andReturn(true) + ->shouldReceive('has')->with('02/another.jpg')->andReturn(true) + ->shouldReceive('has')->with('03/ignore.jpg')->andReturn(false) + ->getMock(); - public function setUp() { + // Mock crops dir + $this->crops_dir = Mockery::mock('League\Flysystem\Filesystem') + ->shouldReceive('listContents') + ->withAnyArgs() + ->andReturn(new DirectoryListing([ + ['path' => '01/me.jpg'], + ['path' => '01/me-too.jpg'], + ['path' => '01/me-200x100.jpg'], + ['path' => '01/me-200x200.jpg'], + ['path' => '01/me-200x300.jpg'], - // Mock src dir - $this->src_dir = Mockery::mock('League\Flysystem\Filesystem') - ->shouldReceive('has')->with('01/me.jpg')->andReturn(true) - ->shouldReceive('has')->with('02/another.jpg')->andReturn(true) - ->shouldReceive('has')->with('03/ignore.jpg')->andReturn(false) - ->getMock(); + // Stored in another src dir + ['path' => '02/another.jpg'], + ['path' => '02/another-200x300.jpg'], + ['path' => '02/unrelated.jpg'], - // Mock crops dir - $this->crops_dir = Mockery::mock('League\Flysystem\Filesystem') - ->shouldReceive('listContents') - ->withAnyArgs() - ->andReturn([ - ['path' => '01/me.jpg', 'basename' => 'me.jpg'], - ['path' => '01/me-too.jpg', 'basename' => 'me-too.jpg'], - ['path' => '01/me-200x100.jpg', 'basename' => 'me-200x100.jpg'], - ['path' => '01/me-200x200.jpg', 'basename' => 'me-200x200.jpg'], - ['path' => '01/me-200x300.jpg', 'basename' => 'me-200x300.jpg'], + // Not a crop cause there is no corresponding source file + ['path' => '03/ignore-200x200.jpg'], + ])) + ->getMock(); + } - // Stored in another src dir - ['path' => '02/another.jpg', 'basename' => 'another-200x300.jpg'], - ['path' => '02/another-200x300.jpg', 'basename' => 'another-200x300.jpg'], - ['path' => '02/unrelated.jpg', 'basename' => 'unrelated.jpg'], + public function testAll() + { + $storage = new Storage(); + $storage->setSrcDisk($this->src_dir); + $storage->setCropsDisk($this->crops_dir); + $this->assertEquals([ + '01/me-200x100.jpg', + '01/me-200x200.jpg', + '01/me-200x300.jpg', + '02/another-200x300.jpg', + ], $storage->listAllCrops()); + } - // Not a crop cause there is no corresponding source file - ['path' => '03/ignore-200x200.jpg', 'basename' => 'ignore-200x200.jpg'], - ]) - ->getMock(); - - } - - public function testAll() { - $storage = new Storage(); - $storage->setSrcDisk($this->src_dir); - $storage->setCropsDisk($this->crops_dir); - $this->assertEquals([ - '01/me-200x100.jpg', - '01/me-200x200.jpg', - '01/me-200x300.jpg', - '02/another-200x300.jpg', - ], $storage->listAllCrops()); - } - - public function testFiltered() { - $storage = new Storage(); - $storage->setSrcDisk($this->src_dir); - $storage->setCropsDisk($this->crops_dir); - $this->assertEquals([ - '02/another-200x300.jpg', - ], $storage->listAllCrops('^02/')); - } - - public function tearDown() { - Mockery::close(); - } + public function testFiltered() + { + $storage = new Storage(); + $storage->setSrcDisk($this->src_dir); + $storage->setCropsDisk($this->crops_dir); + $this->assertEquals([ + '02/another-200x300.jpg', + ], $storage->listAllCrops('^02/')); + } + public function tearDown(): void + { + Mockery::close(); + } } diff --git a/tests/TestResizing.php b/tests/TestResizing.php index df33e5c..e3191bf 100644 --- a/tests/TestResizing.php +++ b/tests/TestResizing.php @@ -3,77 +3,90 @@ use Bkwld\Croppa\Image; use PHPUnit\Framework\TestCase; -class TestResizing extends TestCase { - - private $src; - - public function setUp() { - - // Make an image - $gd = imagecreate(500, 400); - ob_start(); - imagejpeg($gd); - $this->src = ob_get_clean(); - } - - public function testPassthru() { - $image = new Image($this->src); - $size = getimagesizefromstring($image->process(null, null)->get()); - $this->assertEquals('500x400', $size[0].'x'.$size[1]); - } - - public function testWidthConstraint() { - $image = new Image($this->src); - $size = getimagesizefromstring($image->process(200, null)->get()); - $this->assertEquals('200x160', $size[0].'x'.$size[1]); - } - - public function testHeightConstraint() { - $image = new Image($this->src); - $size = getimagesizefromstring($image->process(null, 200)->get()); - $this->assertEquals('250x200', $size[0].'x'.$size[1]); - } - - public function testWidthAndHeightConstraint() { - $image = new Image($this->src); - $size = getimagesizefromstring($image->process(200, 100)->get()); - $this->assertEquals('200x100', $size[0].'x'.$size[1]); - } - - public function testTooSmall() { - $image = new Image($this->src); - $size = getimagesizefromstring($image->process(600, 600)->get()); - $this->assertEquals('400x400', $size[0].'x'.$size[1]); - } - - public function testNotWideEnough() { - $image = new Image($this->src); - $size = getimagesizefromstring($image->process(1000, 400)->get()); - $this->assertEquals('500x200', $size[0].'x'.$size[1]); - } - - public function testNotTallEnough() { - $image = new Image($this->src); - $size = getimagesizefromstring($image->process(500, 500)->get()); - $this->assertEquals('400x400', $size[0].'x'.$size[1]); - } - - public function testWidthAndHeightWithUpscale() { - $image = new Image($this->src, ['resizeUp' => true]); - $size = getimagesizefromstring($image->process(500, 500)->get()); - $this->assertEquals('500x500', $size[0].'x'.$size[1]); - } - - public function testWidthAndHeightResize() { - $image = new Image($this->src); - $size = getimagesizefromstring($image->process(200, 200, ['resize' => null])->get()); - $this->assertEquals('200x160', $size[0].'x'.$size[1]); - } - - public function testWidthAndHeightTrim() { - $image = new Image($this->src); - $size = getimagesizefromstring($image->process(200, 200, ['trim_perc' => [0.25,0.25,0.75,0.75]])->get()); - $this->assertEquals('200x200', $size[0].'x'.$size[1]); - } - +/** + * @internal + * @coversNothing + */ +class TestResizing extends TestCase +{ + private $src; + + public function setUp(): void + { + // Make an image + $gd = imagecreate(500, 400); + ob_start(); + imagejpeg($gd); + $this->src = ob_get_clean(); + } + + public function testPassthru() + { + $image = new Image($this->src); + $size = getimagesizefromstring($image->process(null, null)->get()); + $this->assertEquals('500x400', $size[0].'x'.$size[1]); + } + + public function testWidthConstraint() + { + $image = new Image($this->src); + $size = getimagesizefromstring($image->process(200, null)->get()); + $this->assertEquals('200x160', $size[0].'x'.$size[1]); + } + + public function testHeightConstraint() + { + $image = new Image($this->src); + $size = getimagesizefromstring($image->process(null, 200)->get()); + $this->assertEquals('250x200', $size[0].'x'.$size[1]); + } + + public function testWidthAndHeightConstraint() + { + $image = new Image($this->src); + $size = getimagesizefromstring($image->process(200, 100)->get()); + $this->assertEquals('200x100', $size[0].'x'.$size[1]); + } + + public function testTooSmall() + { + $image = new Image($this->src); + $size = getimagesizefromstring($image->process(600, 600)->get()); + $this->assertEquals('400x400', $size[0].'x'.$size[1]); + } + + public function testNotWideEnough() + { + $image = new Image($this->src); + $size = getimagesizefromstring($image->process(1000, 400)->get()); + $this->assertEquals('500x200', $size[0].'x'.$size[1]); + } + + public function testNotTallEnough() + { + $image = new Image($this->src); + $size = getimagesizefromstring($image->process(500, 500)->get()); + $this->assertEquals('400x400', $size[0].'x'.$size[1]); + } + + public function testWidthAndHeightWithUpscale() + { + $image = new Image($this->src, ['resizeUp' => true]); + $size = getimagesizefromstring($image->process(500, 500)->get()); + $this->assertEquals('500x500', $size[0].'x'.$size[1]); + } + + public function testWidthAndHeightResize() + { + $image = new Image($this->src); + $size = getimagesizefromstring($image->process(200, 200, ['resize' => null])->get()); + $this->assertEquals('200x160', $size[0].'x'.$size[1]); + } + + public function testWidthAndHeightTrim() + { + $image = new Image($this->src); + $size = getimagesizefromstring($image->process(200, 200, ['trim_perc' => [0.25, 0.25, 0.75, 0.75]])->get()); + $this->assertEquals('200x200', $size[0].'x'.$size[1]); + } } diff --git a/tests/TestTooManyCrops.php b/tests/TestTooManyCrops.php index 638f6f3..23b6eb8 100644 --- a/tests/TestTooManyCrops.php +++ b/tests/TestTooManyCrops.php @@ -1,53 +1,61 @@ dir = Mockery::mock('League\Flysystem\Filesystem') - ->shouldReceive('listContents') - ->withAnyArgs() - ->andReturn([ - ['path' => 'me.jpg', 'basename' => 'me.jpg'], - ['path' => 'me-too.jpg', 'basename' => 'me-too.jpg'], - ['path' => 'me-200x100.jpg', 'basename' => 'me-200x100.jpg'], - ['path' => 'me-200x200.jpg', 'basename' => 'me-200x200.jpg'], - ['path' => 'me-200x300.jpg', 'basename' => 'me-200x300.jpg'], - ['path' => 'unrelated.jpg', 'basename' => 'unrelated.jpg'], - ]) - ->getMock(); - - } - - public function testListCrops() { - $storage = new Storage(); - $storage->setCropsDisk($this->dir); - $this->assertEquals([ - 'me-200x100.jpg', - 'me-200x200.jpg', - 'me-200x300.jpg', - ], $storage->listCrops('me.jpg')); - } - - public function testAcceptableNumber() { - $storage = new Storage(null, [ 'max_crops' => 4, ]); - $storage->setCropsDisk($this->dir); - $this->assertFalse($storage->tooManyCrops('me.jpg')); - } - - public function testTooMany() { - $storage = new Storage(null, [ 'max_crops' => 3, ]); - $storage->setCropsDisk($this->dir); - $this->assertTrue($storage->tooManyCrops('me.jpg')); - } - - public function tearDown() { - Mockery::close(); - } +/** + * @internal + * @coversNothing + */ +class TestTooManyCrops extends TestCase +{ + private $dir; + + public function setUp(): void + { + // Mock flysystem + $this->dir = Mockery::mock('League\Flysystem\Filesystem') + ->shouldReceive('listContents') + ->withAnyArgs() + ->andReturn(new DirectoryListing([ + ['path' => 'me.jpg'], + ['path' => 'me-too.jpg'], + ['path' => 'me-200x100.jpg'], + ['path' => 'me-200x200.jpg'], + ['path' => 'me-200x300.jpg'], + ['path' => 'unrelated.jpg'], + ])) + ->getMock(); + } + + public function testListCrops() + { + $storage = new Storage(); + $storage->setCropsDisk($this->dir); + $this->assertEquals([ + 'me-200x100.jpg', + 'me-200x200.jpg', + 'me-200x300.jpg', + ], $storage->listCrops('me.jpg')); + } + + public function testAcceptableNumber() + { + $storage = new Storage(null, ['max_crops' => 4]); + $storage->setCropsDisk($this->dir); + $this->assertFalse($storage->tooManyCrops('me.jpg')); + } + + public function testTooMany() + { + $storage = new Storage(null, ['max_crops' => 3]); + $storage->setCropsDisk($this->dir); + $this->assertTrue($storage->tooManyCrops('me.jpg')); + } + + public function tearDown(): void + { + Mockery::close(); + } } diff --git a/tests/TestUrlGenerator.php b/tests/TestUrlGenerator.php index de0f38e..0c027c8 100644 --- a/tests/TestUrlGenerator.php +++ b/tests/TestUrlGenerator.php @@ -3,92 +3,107 @@ use Bkwld\Croppa\URL; use PHPUnit\Framework\TestCase; -class TestUrlGenerator extends TestCase { - - public function testWidthAndHeight() { - $url = new URL(); - $this->assertEquals('/path/file-200x100.png', $url->generate('/path/file.png', 200, 100)); - } - - public function testIgnore() { - $url = new URL([ 'ignore' => '\.(?:gif|GIF)$' ]); - $this->assertEquals('/path/file.gif', $url->generate('/path/file.gif', 200, 100)); - $this->assertEquals('/path/file-200x100.png', $url->generate('/path/file.png', 200, 100)); - } - - public function testNoWidthOrHeight() { - $url = new URL(); - $this->assertEquals('/path/file.png', $url->generate('/path/file.png')); - } - - public function testNoWidth() { - $url = new URL(); - $this->assertEquals('/path/file-_x100.png', $url->generate('/path/file.png', null, 100)); - } - - public function testNoHeight() { - $url = new URL(); - $this->assertEquals('/path/file-200x_.png', $url->generate('/path/file.png', 200)); - } - - public function testResize() { - $url = new URL(); - $this->assertEquals('/path/file-200x100-resize.png', $url->generate('/path/file.png', 200, 100, ['resize'])); - } - - public function testQuadrant() { - $url = new URL(); - $this->assertEquals('/path/file-200x100-quadrant(T).png', $url->generate('/path/file.png', 200, 100, ['quadrant' => 'T'])); - } - - public function testHostInSrc() { - $url = new URL(); - $this->assertEquals('/path/file-200x_.png', $url->generate('http://domain.tld/path/file.png', 200)); - $this->assertEquals('/path/file-200x_.png', $url->generate('https://domain.tld/path/file.png', 200)); - } - - public function testHostConfig() { - $url = new URL([ 'url_prefix' => '//domain.tld', ]); - $this->assertEquals('//domain.tld/path/file-200x_.png', $url->generate('/path/file.png', 200)); - } - - public function testUrlPrefixWithSchema() { - $url = new URL([ 'url_prefix' => 'https://domain.tld/' ]); - $this->assertEquals('https://domain.tld/path/file-200x_.png', $url->generate('http://domain.tld/path/file.png', 200)); - } - - public function testUrlPrefixWithPath() { - $url = new URL([ - 'url_prefix' => 'https://domain.tld/path/', - 'path' => 'path/(.*)$', - ]); - $this->assertEquals('https://domain.tld/path/file-200x_.png', $url->generate('/path/file.png', 200)); - } - - public function testCropsInSubDirectory() { - $url = new URL([ - 'path' => 'images/(?:crops/)?(.*)$', - 'url_prefix' => '/images/crops/', - ]); - $this->assertEquals('/images/crops/file-200x100.png', $url->generate('/images/file.png', 200, 100)); - } - - public function testCropsInSiblingDirectory() { - $url = new URL([ - 'path' => 'images/(.*)$', - 'url_prefix' => '/crops/', - ]); - $this->assertEquals('/crops/file-200x100.png', $url->generate('/images/file.png', 200, 100)); - } - - public function testSecure() { - - $url = new URL([ 'signing_key' => 'test' ]); - $this->assertEquals('/path/file-200x100.png?token=dc0787d205f619a2b2df8554c960072e', $url->generate('/path/file.png', 200, 100)); - - $url = new URL([ 'signing_key' => 'test' ]); - $this->assertNotEquals('/path/file-200x100.png?token=dc0787d205f619a2b2df8554c960072e', $url->generate('/path/file.png', 200, 200)); - - } - +/** + * @internal + * @coversNothing + */ +class TestUrlGenerator extends TestCase +{ + public function testWidthAndHeight() + { + $url = new URL(); + $this->assertEquals('/path/file-200x100.png', $url->generate('/path/file.png', 200, 100)); + } + + public function testIgnore() + { + $url = new URL(['ignore' => '\.(?:gif|GIF)$']); + $this->assertEquals('/path/file.gif', $url->generate('/path/file.gif', 200, 100)); + $this->assertEquals('/path/file-200x100.png', $url->generate('/path/file.png', 200, 100)); + } + + public function testNoWidthOrHeight() + { + $url = new URL(); + $this->assertEquals('/path/file.png', $url->generate('/path/file.png')); + } + + public function testNoWidth() + { + $url = new URL(); + $this->assertEquals('/path/file-_x100.png', $url->generate('/path/file.png', null, 100)); + } + + public function testNoHeight() + { + $url = new URL(); + $this->assertEquals('/path/file-200x_.png', $url->generate('/path/file.png', 200)); + } + + public function testResize() + { + $url = new URL(); + $this->assertEquals('/path/file-200x100-resize.png', $url->generate('/path/file.png', 200, 100, ['resize'])); + } + + public function testQuadrant() + { + $url = new URL(); + $this->assertEquals('/path/file-200x100-quadrant(T).png', $url->generate('/path/file.png', 200, 100, ['quadrant' => 'T'])); + } + + public function testHostInSrc() + { + $url = new URL(); + $this->assertEquals('/path/file-200x_.png', $url->generate('http://domain.tld/path/file.png', 200)); + $this->assertEquals('/path/file-200x_.png', $url->generate('https://domain.tld/path/file.png', 200)); + } + + public function testHostConfig() + { + $url = new URL(['url_prefix' => '//domain.tld']); + $this->assertEquals('//domain.tld/path/file-200x_.png', $url->generate('/path/file.png', 200)); + } + + public function testUrlPrefixWithSchema() + { + $url = new URL(['url_prefix' => 'https://domain.tld/']); + $this->assertEquals('https://domain.tld/path/file-200x_.png', $url->generate('http://domain.tld/path/file.png', 200)); + } + + public function testUrlPrefixWithPath() + { + $url = new URL([ + 'url_prefix' => 'https://domain.tld/path/', + 'path' => 'path/(.*)$', + ]); + $this->assertEquals('https://domain.tld/path/file-200x_.png', $url->generate('/path/file.png', 200)); + } + + public function testCropsInSubDirectory() + { + $url = new URL([ + 'path' => 'images/(?:crops/)?(.*)$', + 'url_prefix' => '/images/crops/', + ]); + $this->assertEquals('/images/crops/file-200x100.png', $url->generate('/images/file.png', 200, 100)); + } + + public function testCropsInSiblingDirectory() + { + $url = new URL([ + 'path' => 'images/(.*)$', + 'url_prefix' => '/crops/', + ]); + $this->assertEquals('/crops/file-200x100.png', $url->generate('/images/file.png', 200, 100)); + } + + public function testSecure() + { + $url = new URL(['signing_key' => 'test']); + $this->assertEquals('/path/file-200x100.png?token=dc0787d205f619a2b2df8554c960072e', $url->generate('/path/file.png', 200, 100)); + + $url = new URL(['signing_key' => 'test']); + $this->assertNotEquals('/path/file-200x100.png?token=dc0787d205f619a2b2df8554c960072e', $url->generate('/path/file.png', 200, 200)); + } } diff --git a/tests/TestUrlMatching.php b/tests/TestUrlMatching.php index 8c80393..6034521 100644 --- a/tests/TestUrlMatching.php +++ b/tests/TestUrlMatching.php @@ -3,51 +3,64 @@ use Bkwld\Croppa\URL; use PHPUnit\Framework\TestCase; -class TestUrlMatching extends TestCase { +/** + * @internal + * @coversNothing + */ +class TestUrlMatching extends TestCase +{ + private $url; - private $url; - public function setUp() { - $this->url = new URL([ - 'path' => 'uploads/(.*)$', - ]); - } + public function setUp(): void + { + $this->url = new URL([ + 'path' => 'uploads/(.*)$', + ]); + } - /** - * This mimics the Illuminate\Routing\Matching\UriValidator compiled regex - * https://regex101.com/r/xS3nQ2/1 - */ - public function match($path) { + /** + * This mimics the Illuminate\Routing\Matching\UriValidator compiled regex + * https://regex101.com/r/xS3nQ2/1. + * + * @param mixed $path + */ + public function match($path) + { + // The compiled regex is wrapped like this + $pattern = '#^\/(?P'.$this->url->routePattern().')$#s'; - // The compiled regex is wrapped like this - $pattern = '#^\/(?P'.$this->url->routePattern().')$#s'; + // UriValidator prepends a slash + return preg_match($pattern, '/'.$path) > 0; + } - // UriValidator prepends a slash - return preg_match($pattern, '/'.$path) > 0; - } + public function testNoParams() + { + $this->assertFalse($this->match('uploads/1/2/file.jpg')); + } - public function testNoParams() { - $this->assertFalse($this->match('uploads/1/2/file.jpg')); - } + public function testOursideDir() + { + $this->assertFalse($this->match('assets/1/2/file.jpg')); + $this->assertFalse($this->match('apple-touch-icon-152x152-precomposed.png')); + } - public function testOursideDir() { - $this->assertFalse($this->match('assets/1/2/file.jpg')); - $this->assertFalse($this->match('apple-touch-icon-152x152-precomposed.png')); - } + public function testWidth() + { + $this->assertTrue($this->match('uploads/1/2/file-200x_.jpg')); + } - public function testWidth() { - $this->assertTrue($this->match('uploads/1/2/file-200x_.jpg')); - } + public function testHeight() + { + $this->assertTrue($this->match('uploads/1/2/file-_x100.jpg')); + } - public function testHeight() { - $this->assertTrue($this->match('uploads/1/2/file-_x100.jpg')); - } - - public function testWidthAndHeight() { - $this->assertTrue($this->match('uploads/1/2/file-200x100.jpg')); - } - - public function testWidthAndHeightAndOptions() { - $this->assertTrue($this->match('uploads/1/2/file-200x100-quadrant(T).jpg')); - } + public function testWidthAndHeight() + { + $this->assertTrue($this->match('uploads/1/2/file-200x100.jpg')); + } + public function testWidthAndHeightAndOptions() + { + $this->assertTrue($this->match('uploads/1/2/file-200x100-quadrant(T).jpg')); + } } diff --git a/tests/TestUrlParsing.php b/tests/TestUrlParsing.php index d9b46e3..0d16510 100644 --- a/tests/TestUrlParsing.php +++ b/tests/TestUrlParsing.php @@ -3,79 +3,93 @@ use Bkwld\Croppa\URL; use PHPUnit\Framework\TestCase; -class TestUrlParsing extends TestCase { +/** + * @internal + * @coversNothing + */ +class TestUrlParsing extends TestCase +{ + private $url; - private $url; - public function setUp() { - $this->url = new URL([ - 'path' => 'uploads/(.*)$', - 'filters' => [ - 'gray' => Bkwld\Croppa\Filters\BlackWhite::class, - 'darkgray' => Bkwld\Croppa\Filters\Darkgray::class, - 'blur' => Bkwld\Croppa\Filters\Blur::class, - 'negative' => Bkwld\Croppa\Filters\Negative::class, - 'orange' => Bkwld\Croppa\Filters\OrangeWarhol::class, - 'turquoise' => Bkwld\Croppa\Filters\TurquoiseWarhol::class, - ], - ]); - } + public function setUp(): void + { + $this->url = new URL([ + 'path' => 'uploads/(.*)$', + 'filters' => [ + 'gray' => Bkwld\Croppa\Filters\BlackWhite::class, + 'darkgray' => Bkwld\Croppa\Filters\Darkgray::class, + 'blur' => Bkwld\Croppa\Filters\Blur::class, + 'negative' => Bkwld\Croppa\Filters\Negative::class, + 'orange' => Bkwld\Croppa\Filters\OrangeWarhol::class, + 'turquoise' => Bkwld\Croppa\Filters\TurquoiseWarhol::class, + ], + ]); + } - public function testNoParams() { - $this->assertFalse($this->url->parse('uploads/1/2/file.jpg')); - } + public function testNoParams() + { + $this->assertFalse($this->url->parse('uploads/1/2/file.jpg')); + } - public function testWidth() { - $this->assertEquals([ - '1/2/file.jpg', 200, null, [] - ], $this->url->parse('uploads/1/2/file-200x_.jpg')); - } + public function testWidth() + { + $this->assertEquals([ + '1/2/file.jpg', 200, null, [], + ], $this->url->parse('uploads/1/2/file-200x_.jpg')); + } - public function testHeight() { - $this->assertEquals([ - '1/2/file.jpg', null, 100, [] - ], $this->url->parse('uploads/1/2/file-_x100.jpg')); - } + public function testHeight() + { + $this->assertEquals([ + '1/2/file.jpg', null, 100, [], + ], $this->url->parse('uploads/1/2/file-_x100.jpg')); + } - public function testWidthAndHeight() { - $this->assertEquals([ - '1/2/file.jpg', 200, 100, [] - ], $this->url->parse('uploads/1/2/file-200x100.jpg')); - } + public function testWidthAndHeight() + { + $this->assertEquals([ + '1/2/file.jpg', 200, 100, [], + ], $this->url->parse('uploads/1/2/file-200x100.jpg')); + } - public function testWidthAndHeightAndOptions() { - $this->assertEquals([ - '1/2/file.jpg', 200, 100, ['resize' => null] - ], $this->url->parse('uploads/1/2/file-200x100-resize.jpg')); - } + public function testWidthAndHeightAndOptions() + { + $this->assertEquals([ + '1/2/file.jpg', 200, 100, ['resize' => null], + ], $this->url->parse('uploads/1/2/file-200x100-resize.jpg')); + } - public function testWidthAndHeightAndOptionsWithValue() { - $this->assertEquals([ - '1/2/file.jpg', 200, 100, ['quadrant' => ['T']] - ], $this->url->parse('uploads/1/2/file-200x100-quadrant(T).jpg')); - } + public function testWidthAndHeightAndOptionsWithValue() + { + $this->assertEquals([ + '1/2/file.jpg', 200, 100, ['quadrant' => ['T']], + ], $this->url->parse('uploads/1/2/file-200x100-quadrant(T).jpg')); + } - public function testWidthAndHeightAndOptionsWithValueList() { - $this->assertEquals([ - '1/2/file.jpg', 200, 100, ['trim_perc' => [0.25,0.25,0.75,0.75]] - ], $this->url->parse('uploads/1/2/file-200x100-trim_perc(0.25,0.25,0.75,0.75).jpg')); - } + public function testWidthAndHeightAndOptionsWithValueList() + { + $this->assertEquals([ + '1/2/file.jpg', 200, 100, ['trim_perc' => [0.25, 0.25, 0.75, 0.75]], + ], $this->url->parse('uploads/1/2/file-200x100-trim_perc(0.25,0.25,0.75,0.75).jpg')); + } - public function testFilters() { - $this->assertEquals([ - '1/2/file.jpg', 200, 100, ['filters' => [ - new Bkwld\Croppa\Filters\Blur, - new Bkwld\Croppa\Filters\Negative, - ]] - ], $this->url->parse('uploads/1/2/file-200x100-filters(blur,negative).jpg')); - } - - public function testCropsInSubDirectory() { - $url = new URL([ - 'path' => 'images/(?:crops/)?(.*)$', - ]); - $this->assertEquals([ - 'file.jpg', 200, 100, [] - ], $url->parse('images/crops/file-200x100.jpg')); - } + public function testFilters() + { + $this->assertEquals([ + '1/2/file.jpg', 200, 100, ['filters' => [ + new Bkwld\Croppa\Filters\Blur(), + new Bkwld\Croppa\Filters\Negative(), + ]], + ], $this->url->parse('uploads/1/2/file-200x100-filters(blur,negative).jpg')); + } + public function testCropsInSubDirectory() + { + $url = new URL([ + 'path' => 'images/(?:crops/)?(.*)$', + ]); + $this->assertEquals([ + 'file.jpg', 200, 100, [], + ], $url->parse('images/crops/file-200x100.jpg')); + } } From 545c55706a5f4bf60fc0318e6bc19c20898395a0 Mon Sep 17 00:00:00 2001 From: Samuel De Backer Date: Sun, 17 Apr 2022 21:33:48 +0200 Subject: [PATCH 5/7] Code style --- src/Bkwld/Croppa/Commands/Purge.php | 41 +++-- src/Bkwld/Croppa/Exception.php | 8 +- src/Bkwld/Croppa/Facade.php | 6 +- src/Bkwld/Croppa/Filters/BlackWhite.php | 4 - src/Bkwld/Croppa/Filters/Blur.php | 4 - src/Bkwld/Croppa/Filters/Darkgray.php | 5 +- src/Bkwld/Croppa/Filters/FilterInterface.php | 2 - src/Bkwld/Croppa/Filters/Negative.php | 3 - src/Bkwld/Croppa/Filters/OrangeWarhol.php | 4 - src/Bkwld/Croppa/Filters/TurquoiseWarhol.php | 3 - src/Bkwld/Croppa/Handler.php | 67 ++++--- src/Bkwld/Croppa/Helpers.php | 70 +++++--- src/Bkwld/Croppa/Image.php | 180 ++++++++++++------- src/Bkwld/Croppa/ServiceProvider.php | 87 +++++---- src/Bkwld/Croppa/URL.php | 179 +++++++++++------- 15 files changed, 390 insertions(+), 273 deletions(-) diff --git a/src/Bkwld/Croppa/Commands/Purge.php b/src/Bkwld/Croppa/Commands/Purge.php index 4365812..4aae030 100644 --- a/src/Bkwld/Croppa/Commands/Purge.php +++ b/src/Bkwld/Croppa/Commands/Purge.php @@ -1,4 +1,6 @@ -storage = $storage; } /** - * Execute the console command - * - * @return void + * Execute the console command. */ - public function handle() { + public function handle() + { $dry = $this->input->getOption('dry-run'); - foreach($this->storage->deleteAllCrops($this->input->getOption('filter'), $dry) as $path) { - $this->info(sprintf('%s %s', $path, $dry ? 'not deleted' : 'deleted' )); + foreach ($this->storage->deleteAllCrops($this->input->getOption('filter'), $dry) as $path) { + $this->info(sprintf('%s %s', $path, $dry ? 'not deleted' : 'deleted')); } } /** - * Backwards compatability with Laravel 4 - * - * @return void + * Backwards compatability with Laravel 4. */ - public function fire() { + public function fire() + { $this->handle(); } /** - * Get the console command options + * Get the console command options. * * @return array; */ - protected function getOptions() { + protected function getOptions() + { return [ ['filter', null, InputOption::VALUE_REQUIRED, 'A regex pattern that whitelists matching crop paths', null], ['dry-run', null, InputOption::VALUE_NONE, 'Only return the crops that would be deleted'], ]; } - } diff --git a/src/Bkwld/Croppa/Exception.php b/src/Bkwld/Croppa/Exception.php index eb7a4b7..67191a6 100755 --- a/src/Bkwld/Croppa/Exception.php +++ b/src/Bkwld/Croppa/Exception.php @@ -1,3 +1,7 @@ -imageFilter(IMG_FILTER_GRAYSCALE); } - } diff --git a/src/Bkwld/Croppa/Filters/Blur.php b/src/Bkwld/Croppa/Filters/Blur.php index 44bf0ed..67e8c6b 100644 --- a/src/Bkwld/Croppa/Filters/Blur.php +++ b/src/Bkwld/Croppa/Filters/Blur.php @@ -6,17 +6,13 @@ class Blur implements FilterInterface { - /** * Applies filter to given thumbnail object. * - * @param \GdThumb $thumb - * * @return \Intervention\Image\Image */ public function applyFilter(GdThumb $thumb) { return $thumb->imageFilter(IMG_FILTER_GAUSSIAN_BLUR); } - } diff --git a/src/Bkwld/Croppa/Filters/Darkgray.php b/src/Bkwld/Croppa/Filters/Darkgray.php index fcee9e8..05e2102 100644 --- a/src/Bkwld/Croppa/Filters/Darkgray.php +++ b/src/Bkwld/Croppa/Filters/Darkgray.php @@ -6,19 +6,16 @@ class Darkgray implements FilterInterface { - /** * Applies filter to given thumbnail object. * - * @param \GdThumb $thumb - * * @return \Intervention\Image\Image */ public function applyFilter(GdThumb $thumb) { $thumb->imageFilter(IMG_FILTER_GRAYSCALE); $thumb->imageFilter(IMG_FILTER_COLORIZE, -80, -80, -80); + return $thumb; } - } diff --git a/src/Bkwld/Croppa/Filters/FilterInterface.php b/src/Bkwld/Croppa/Filters/FilterInterface.php index dfd56f2..f1cc115 100644 --- a/src/Bkwld/Croppa/Filters/FilterInterface.php +++ b/src/Bkwld/Croppa/Filters/FilterInterface.php @@ -7,8 +7,6 @@ interface FilterInterface { /** - * @param \GdThumb $thumb - * * @return \GdThumb $thumb */ public function applyFilter(GdThumb $thumb); diff --git a/src/Bkwld/Croppa/Filters/Negative.php b/src/Bkwld/Croppa/Filters/Negative.php index b94a004..c5ca37d 100644 --- a/src/Bkwld/Croppa/Filters/Negative.php +++ b/src/Bkwld/Croppa/Filters/Negative.php @@ -9,8 +9,6 @@ class Negative implements FilterInterface /** * Applies filter to given thumbnail object. * - * @param \GdThumb $thumb - * * @return \Intervention\Image\Image */ public function applyFilter(GdThumb $thumb) @@ -20,5 +18,4 @@ public function applyFilter(GdThumb $thumb) return $thumb; } - } diff --git a/src/Bkwld/Croppa/Filters/OrangeWarhol.php b/src/Bkwld/Croppa/Filters/OrangeWarhol.php index 612c896..e0274f5 100644 --- a/src/Bkwld/Croppa/Filters/OrangeWarhol.php +++ b/src/Bkwld/Croppa/Filters/OrangeWarhol.php @@ -9,8 +9,6 @@ class OrangeWarhol implements FilterInterface /** * Applies filter to given thumbnail object. * - * @param \GdThumb $thumb - * * @return \Intervention\Image\Image */ public function applyFilter(GdThumb $thumb) @@ -21,6 +19,4 @@ public function applyFilter(GdThumb $thumb) return $thumb; } - - } diff --git a/src/Bkwld/Croppa/Filters/TurquoiseWarhol.php b/src/Bkwld/Croppa/Filters/TurquoiseWarhol.php index 89fee42..0892d8e 100644 --- a/src/Bkwld/Croppa/Filters/TurquoiseWarhol.php +++ b/src/Bkwld/Croppa/Filters/TurquoiseWarhol.php @@ -9,8 +9,6 @@ class TurquoiseWarhol implements FilterInterface /** * Applies filter to given thumbnail object. * - * @param \GdThumb $thumb - * * @return \Intervention\Image\Image */ public function applyFilter(GdThumb $thumb) @@ -21,5 +19,4 @@ public function applyFilter(GdThumb $thumb) return $thumb; } - } diff --git a/src/Bkwld/Croppa/Handler.php b/src/Bkwld/Croppa/Handler.php index 4c86858..f6af58d 100644 --- a/src/Bkwld/Croppa/Handler.php +++ b/src/Bkwld/Croppa/Handler.php @@ -1,4 +1,6 @@ -url = $url; $this->storage = $storage; $this->request = $request; @@ -43,14 +46,16 @@ public function __construct(URL $url, Storage $storage, Request $request, $confi } /** - * Handles a Croppa style route + * Handles a Croppa style route. * * @param string $request The `Request::path()` + * * @throws Exception + * * @return Symfony\Component\HttpFoundation\StreamedResponse */ - public function handle($request) { - + public function handle($request) + { // Validate the signing token if (($token = $this->url->signingToken($request)) && $token != $this->request->input('token') @@ -64,25 +69,24 @@ public function handle($request) { // Redirect to remote crops ... if ($this->storage->cropsAreRemote()) { return new RedirectResponse($this->url->pathToUrl($crop_path), 301); - - // ... or echo the image data to the browser - } else { - $absolute_path = $this->storage->getLocalCropsDirPath() . '/' . $crop_path; - return new BinaryFileResponse($absolute_path, 200, [ - 'Content-Type' => $this->getContentType($absolute_path), - ]); + // ... or echo the image data to the browser } - } + $absolute_path = $this->storage->getLocalCropsDirPath().'/'.$crop_path; + return new BinaryFileResponse($absolute_path, 200, [ + 'Content-Type' => $this->getContentType($absolute_path), + ]); + } /** - * Render image directly + * Render image directly. * * @param string $request_path The `Request::path()` + * * @return string The path, relative to the storage disk, to the crop */ - public function render($request_path) { - + public function render($request_path) + { // Get crop path relative to it's dir $crop_path = $this->url->relativePath($request_path); @@ -95,11 +99,15 @@ public function render($request_path) { // Parse the path. In the case there is an error (the pattern on the route // SHOULD have caught all errors with the pattern) just return - if (!$params = $this->url->parse($request_path)) return; + if (!$params = $this->url->parse($request_path)) { + return; + } list($path, $width, $height, $options) = $params; // Check if there are too many crops already - if ($this->storage->tooManyCrops($path)) throw new Exception('Croppa: Max crops'); + if ($this->storage->tooManyCrops($path)) { + throw new Exception('Croppa: Max crops'); + } // Increase memory limit, cause some images require a lot to resize if ($this->config['memory_limit'] !== null) { @@ -113,7 +121,8 @@ public function render($request_path) { ); // Process the image and write its data to disk - $this->storage->writeCrop($crop_path, + $this->storage->writeCrop( + $crop_path, $image->process($width, $height, $options)->get() ); @@ -121,24 +130,26 @@ public function render($request_path) { return $crop_path; } - /** * Symfony kept returning the MIME-type of my testing jpgs as PNGs, so * determining it explicitly via looking at the path name. * * @param string $path + * * @return string */ - public function getContentType($path) { + public function getContentType($path) + { switch (pathinfo($path, PATHINFO_EXTENSION)) { case 'jpeg': case 'jpg': return 'image/jpeg'; + case 'gif': return 'image/gif'; + case 'png': return 'image/png'; } } - } diff --git a/src/Bkwld/Croppa/Helpers.php b/src/Bkwld/Croppa/Helpers.php index 457e013..52423d1 100644 --- a/src/Bkwld/Croppa/Helpers.php +++ b/src/Bkwld/Croppa/Helpers.php @@ -1,11 +1,13 @@ -url = $url; $this->storage = $storage; $this->handler = $handler; } /** - * Delete source image and all of it's crops + * Delete source image and all of it's crops. * * @param string $url URL of src image - * @return void + * * @see Bkwld\Croppa\Storage::deleteSrc() * @see Bkwld\Croppa\Storage::deleteCrops() */ - public function delete($url) { + public function delete($url) + { $path = $this->url->relativePath($url); $this->storage->deleteSrc($path); $this->storage->deleteCrops($path); } /** - * Delete just the crops, leave the source image + * Delete just the crops, leave the source image. * * @param string $url URL of src image - * @return void + * * @see Bkwld\Croppa\Storage::deleteCrops() */ - public function reset($url) { + public function reset($url) + { $path = $this->url->relativePath($url); $this->storage->deleteCrops($path); } /** - * Create an image tag rather than just the URL. Accepts the same params as url() + * Create an image tag rather than just the URL. Accepts the same params as url(). + * + * @param string $url URL of an image that should be cropped + * @param int $width Target width + * @param int $height Target height + * @param array $options Additional Croppa options, passed as key/value pairs. Like array('resize') * - * @param string $url URL of an image that should be cropped - * @param integer $width Target width - * @param integer $height Target height - * @param array $options Additional Croppa options, passed as key/value pairs. Like array('resize') * @return string An HTML img tag for the new image + * * @see Bkwld\Croppa\URL::generate() */ - public function tag($url, $width = null, $height = null, $options = null) { - return ''; + public function tag($url, $width = null, $height = null, $options = null) + { + return ''; } /** * Pass through URL requests to URL->generate(). * - * @param string $url URL of an image that should be cropped - * @param integer $width Target width - * @param integer $height Target height - * @param array $options Additional Croppa options, passed as key/value pairs. Like array('resize') + * @param string $url URL of an image that should be cropped + * @param int $width Target width + * @param int $height Target height + * @param array $options Additional Croppa options, passed as key/value pairs. Like array('resize') + * * @return string The new path to your thumbnail + * * @see Bkwld\Croppa\URL::generate() */ - public function url($url, $width = null, $height = null, $options = null) { + public function url($url, $width = null, $height = null, $options = null) + { return $this->url->generate($url, $width, $height, $options); } - /** - * Render image + * Render image. * * @param string $url URL of an image that should be rendered + * * @return string The new path to your thumbnail + * * @see Bkwld\Croppa\URL::generate() */ - public function render($url) { + public function render($url) + { return $this->handler->render($url); } - } diff --git a/src/Bkwld/Croppa/Image.php b/src/Bkwld/Croppa/Image.php index 3a9a6c6..93c0133 100644 --- a/src/Bkwld/Croppa/Image.php +++ b/src/Bkwld/Croppa/Image.php @@ -1,67 +1,76 @@ -thumb = PhpThumbFactory::create($data, $options, true); } /** - * Take the input from the URL and apply transformations on the image + * Take the input from the URL and apply transformations on the image. * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @param array $options + * * @return $this */ - public function process($width = null, $height = null, $options = []) { + public function process($width = null, $height = null, $options = []) + { return $this ->autoRotate() ->trim($options) ->resizeAndOrCrop($width, $height, $options) - ->applyFilters($options) - ; + ->applyFilters($options); } /** * Apply filters that have been defined in the config as seperate classes. * * @param array $filters Array of filter instances + * @param mixed $options + * * @return $this */ - public function applyFilters($options) { + public function applyFilters($options) + { if (isset($options['filters']) && is_array($options['filters'])) { - array_map(function($filter) { + array_map(function ($filter) { $this->thumb = $filter->applyFilter($this->thumb); }, $options['filters']); } + return $this; } /** * Auto rotate the image based on exif data (like from phones) - * https://github.com/nik-kor/PHPThumb/blob/master/src/thumb_plugins/jpg_rotate.inc.php + * https://github.com/nik-kor/PHPThumb/blob/master/src/thumb_plugins/jpg_rotate.inc.php. * * @return $this */ - public function autoRotate() { + public function autoRotate() + { try { $this->thumb->rotateJpg(); } finally { @@ -73,33 +82,45 @@ public function autoRotate() { * Determine which trim to apply. * * @param array $options + * * @return $this */ - public function trim($options) { - if (isset($options['trim'])) return $this->trimPixels($options['trim']); - if (isset($options['trim_perc'])) return $this->trimPerc($options['trim_perc']); + public function trim($options) + { + if (isset($options['trim'])) { + return $this->trimPixels($options['trim']); + } + if (isset($options['trim_perc'])) { + return $this->trimPerc($options['trim_perc']); + } + return $this; } /** - * Trim the source before applying the crop with as offset pixels + * Trim the source before applying the crop with as offset pixels. + * + * @param array $coords Cropping instructions as pixels * - * @param array $coords Cropping instructions as pixels * @return $this */ - public function trimPixels($coords) { + public function trimPixels($coords) + { list($x1, $y1, $x2, $y2) = $coords; $this->thumb->crop($x1, $y1, $x2 - $x1, $y2 - $y1); + return $this; } /** - * Trim the source before applying the crop with offset percentages + * Trim the source before applying the crop with offset percentages. + * + * @param array $coords Cropping instructions as percentages * - * @param array $coords Cropping instructions as percentages * @return $this */ - public function trimPerc($coords) { + public function trimPerc($coords) + { list($x1, $y1, $x2, $y2) = $coords; $size = (object) $this->thumb->getCurrentDimensions(); @@ -109,22 +130,34 @@ public function trimPerc($coords) { $width = round($x2 * $size->width - $x); $height = round($y2 * $size->height - $y); $this->thumb->crop($x, $y, $width, $height); + return $this; } /** - * Determine which resize and crop to apply + * Determine which resize and crop to apply. * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @param array $options + * * @return $this */ - public function resizeAndOrCrop($width, $height, $options) { - if (!$width && !$height) return $this; - if (isset($options['quadrant'])) return $this->cropQuadrant($width, $height, $options); - if (array_key_exists('pad', $options)) $this->pad($width, $height, $options); - if (array_key_exists('resize', $options) || !$width || !$height) return $this->resize($width, $height); + public function resizeAndOrCrop($width, $height, $options) + { + if (!$width && !$height) { + return $this; + } + if (isset($options['quadrant'])) { + return $this->cropQuadrant($width, $height, $options); + } + if (array_key_exists('pad', $options)) { + $this->pad($width, $height, $options); + } + if (array_key_exists('resize', $options) || !$width || !$height) { + return $this->resize($width, $height); + } + return $this->crop($width, $height); } @@ -136,46 +169,64 @@ public function resizeAndOrCrop($width, $height, $options) { * | L | C | R | * +---+---+---+ * | | B | | - * +---+---+---+ + * +---+---+---+. * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @param array $options + * * @throws Exception + * * @return $this */ - public function cropQuadrant($width, $height, $options) { - if (!$height|| !$width) throw new Exception('Croppa: Qudrant option needs width and height'); - if (empty($options['quadrant'][0])) throw new Exception('Croppa:: No quadrant specified'); - $quadrant = strtoupper($options['quadrant'][0]); - if (!in_array($quadrant, array('T','L','C','R','B'))) throw new Exception('Croppa:: Invalid quadrant'); + public function cropQuadrant($width, $height, $options) + { + if (!$height || !$width) { + throw new Exception('Croppa: Qudrant option needs width and height'); + } + if (empty($options['quadrant'][0])) { + throw new Exception('Croppa:: No quadrant specified'); + } + $quadrant = mb_strtoupper($options['quadrant'][0]); + if (!in_array($quadrant, ['T', 'L', 'C', 'R', 'B'])) { + throw new Exception('Croppa:: Invalid quadrant'); + } $this->thumb->adaptiveResizeQuadrant($width, $height, $quadrant); + return $this; } /** - * Resize with no cropping + * Resize with no cropping. + * + * @param int $width + * @param int $height * - * @param integer $width - * @param integer $height * @return $this */ - public function resize($width, $height) { - if ($width && $height) $this->thumb->resize($width, $height); - else if (!$width) $this->thumb->resize(99999, $height); - else if (!$height) $this->thumb->resize($width, 99999); + public function resize($width, $height) + { + if ($width && $height) { + $this->thumb->resize($width, $height); + } elseif (!$width) { + $this->thumb->resize(99999, $height); + } elseif (!$height) { + $this->thumb->resize($width, 99999); + } + return $this; } /** - * Resize and crop + * Resize and crop. + * + * @param int $width + * @param int $height * - * @param integer $width - * @param integer $height * @return $this */ - public function crop($width, $height) { - + public function crop($width, $height) + { // GdThumb will not enforce the requested aspect ratio if the image is too // small, so we manually calculate what the size should be if the aspect // ratio is preserved. @@ -195,30 +246,37 @@ public function crop($width, $height) { // Do a normal adpative resize $this->thumb->adaptiveResize($width, $height); + return $this; } /** * Pad an image to desired dimensions. Moves the image into the center and fills the rest with given color. * - * @param integer $width - * @param integer $height + * @param int $width + * @param int $height * @param array $options + * * @return $this */ - public function pad($width, $height, $options) { - if (!$height || !$width) throw new Exception('Croppa: Pad option needs width and height'); + public function pad($width, $height, $options) + { + if (!$height || !$width) { + throw new Exception('Croppa: Pad option needs width and height'); + } $color = $options['pad'] ?: [255, 255, 255]; $this->thumb->resize($width, $height)->pad($width, $height, $color); + return $this; } /** - * Get the image data + * Get the image data. * * @return string Image data */ - public function get() { + public function get() + { return $this->thumb->getImageAsString(); } } diff --git a/src/Bkwld/Croppa/ServiceProvider.php b/src/Bkwld/Croppa/ServiceProvider.php index 9331549..a18d95e 100755 --- a/src/Bkwld/Croppa/ServiceProvider.php +++ b/src/Bkwld/Croppa/ServiceProvider.php @@ -1,22 +1,25 @@ -app; if (defined(get_class($app).'::VERSION')) { - return intval($app::VERSION); + return (int) ($app::VERSION); } if (is_callable([$app, 'version'])) { preg_match('/(\((\d+\.\d+\.\d+)\))/', $app->version(), $v); if (isset($v[2])) { - return -intval($v[2]); + return -(int) ($v[2]); } } @@ -25,41 +28,41 @@ public function version() { /** * Register the service provider. - * - * @return void */ - public function register() { - + public function register() + { // Version specific registering if (abs($this->version()) >= 5) { $this->registerLaravel5Lumen(); } // Bind the Croppa URL generator and parser - $this->app->singleton('Bkwld\Croppa\URL', function($app) { + $this->app->singleton('Bkwld\Croppa\URL', function ($app) { return new URL($this->getConfig()); }); // Handle the request for an image, this cooridnates the main logic - $this->app->singleton('Bkwld\Croppa\Handler', function($app) { - return new Handler($app['Bkwld\Croppa\URL'], + $this->app->singleton('Bkwld\Croppa\Handler', function ($app) { + return new Handler( + $app['Bkwld\Croppa\URL'], $app['Bkwld\Croppa\Storage'], $app['request'], - $this->getConfig()); + $this->getConfig() + ); }); // Interact with the disk - $this->app->singleton('Bkwld\Croppa\Storage', function($app) { + $this->app->singleton('Bkwld\Croppa\Storage', function ($app) { return new Storage($app, $this->getConfig()); }); // API for use in apps - $this->app->singleton('Bkwld\Croppa\Helpers', function($app) { + $this->app->singleton('Bkwld\Croppa\Helpers', function ($app) { return new Helpers($app['Bkwld\Croppa\URL'], $app['Bkwld\Croppa\Storage'], $app['Bkwld\Croppa\Handler']); }); // Register command to delte all crops - $this->app->singleton('Bkwld\Croppa\Commands\Purge', function($app) { + $this->app->singleton('Bkwld\Croppa\Commands\Purge', function ($app) { return new Commands\Purge($app['Bkwld\Croppa\Storage']); }); @@ -68,27 +71,24 @@ public function register() { } /** - * Register specific logic for Laravel/Lumen 5. Merges package config with user config - * - * @return void + * Register specific logic for Laravel/Lumen 5. Merges package config with user config. */ - public function registerLaravel5Lumen() { + public function registerLaravel5Lumen() + { $this->mergeConfigFrom(__DIR__.'/../../config/config.php', 'croppa'); } - /** * Bootstrap the application events. - * - * @return void */ - public function boot() { + public function boot() + { // Version specific booting if ($this->version() == 4) { $this->bootLaravel4(); - } else if ($this->version() >= 5) { + } elseif ($this->version() >= 5) { $this->bootLaravel(); - } else if ($this->version() <= -5) { + } elseif ($this->version() <= -5) { $this->bootLumen(); } else { throw new Exception('Unsupported Laravel version'); @@ -108,41 +108,39 @@ public function boot() { /** * Boot specific logic for Laravel 4. Tells Laravel about the package for auto - * namespacing of config files - * - * @return void + * namespacing of config files. */ - public function bootLaravel4() { + public function bootLaravel4() + { $this->package('bkwld/croppa'); } /** * Boot specific logic for Laravel 5. Registers the config file for publishing - * to app directory - * - * @return void + * to app directory. */ - public function bootLaravel() { + public function bootLaravel() + { $this->publishes([ - __DIR__.'/../../config/config.php' => config_path('croppa.php') + __DIR__.'/../../config/config.php' => config_path('croppa.php'), ], 'croppa'); } /** - * Boot specific logic for Lumen. Load custom croppa config file - * - * @return void + * Boot specific logic for Lumen. Load custom croppa config file. */ - public function bootLumen() { + public function bootLumen() + { $this->app->configure('croppa'); } /** - * Get the configuration, which is keyed differently in L5 vs l4 + * Get the configuration, which is keyed differently in L5 vs l4. * * @return array */ - public function getConfig() { + public function getConfig() + { $key = abs($this->version()) >= 5 ? 'croppa' : 'croppa::config'; $config = $this->app->make('config')->get($key); @@ -160,7 +158,8 @@ public function getConfig() { * * @return array */ - public function provides() { + public function provides() + { return [ 'Bkwld\Croppa\URL', 'Bkwld\Croppa\Handler', diff --git a/src/Bkwld/Croppa/URL.php b/src/Bkwld/Croppa/URL.php index df0e278..123f785 100644 --- a/src/Bkwld/Croppa/URL.php +++ b/src/Bkwld/Croppa/URL.php @@ -1,31 +1,34 @@ -config = $config; } @@ -33,14 +36,15 @@ public function __construct($config = []) { * Insert Croppa parameter suffixes into a URL. For use as a helper in views * when rendering image src attributes. * - * @param string $url URL of an image that should be cropped - * @param integer $width Target width - * @param integer $height Target height - * @param array $options Addtional Croppa options, passed as key/value pairs. Like array('resize') + * @param string $url URL of an image that should be cropped + * @param int $width Target width + * @param int $height Target height + * @param array $options Addtional Croppa options, passed as key/value pairs. Like array('resize') + * * @return string The new path to your thumbnail */ - public function generate($url, $width = null, $height = null, $options = null) { - + public function generate($url, $width = null, $height = null, $options = null) + { // Extract the path from a URL and remove it's leading slash $path = $this->toPath($url); @@ -51,54 +55,75 @@ public function generate($url, $width = null, $height = null, $options = null) { } // Defaults - if (empty($path)) return; // Don't allow empty strings - if (!$width && !$height) return $this->pathToUrl($path); // Pass through if empty + if (empty($path)) { + return; + } // Don't allow empty strings + if (!$width && !$height) { + return $this->pathToUrl($path); + } // Pass through if empty $width = $width ? round($width) : '_'; $height = $height ? round($height) : '_'; // Produce width, height, and options $suffix = '-'.$width.'x'.$height; if ($options && is_array($options)) { - foreach($options as $key => $val) { - if (is_numeric($key)) $suffix .= '-'.$val; - elseif (is_array($val)) $suffix .= '-'.$key.'('.implode(',',$val).')'; - else $suffix .= '-'.$key.'('.$val.')'; + foreach ($options as $key => $val) { + if (is_numeric($key)) { + $suffix .= '-'.$val; + } elseif (is_array($val)) { + $suffix .= '-'.$key.'('.implode(',', $val).')'; + } else { + $suffix .= '-'.$key.'('.$val.')'; + } } } // Assemble the new path $parts = pathinfo($path); - $path = trim($parts['dirname'],'/').'/'.$parts['filename'].$suffix; - if (isset($parts['extension'])) $path .= '.'.$parts['extension']; + $path = trim($parts['dirname'], '/').'/'.$parts['filename'].$suffix; + if (isset($parts['extension'])) { + $path .= '.'.$parts['extension']; + } $url = $this->pathToUrl($path); // Secure with hash token - if ($token = $this->signingToken($url)) $url .= '?token='.$token; + if ($token = $this->signingToken($url)) { + $url .= '?token='.$token; + } // Return the $url return $url; } /** - * Extract the path from a URL and remove it's leading slash + * Extract the path from a URL and remove it's leading slash. * * @param string $url + * * @return string path */ - public function toPath($url) { + public function toPath($url) + { return ltrim(parse_url($url, PHP_URL_PATH), '/'); } /** - * Append host to the path if it was defined + * Append host to the path if it was defined. * * @param string $path Request path (with leading slash) + * * @return string */ - public function pathToUrl($path) { - if (empty($this->config['url_prefix'])) return '/'.$path; - else if (empty($this->config['path'])) return rtrim($this->config['url_prefix'], '/').'/'.$path; - else return rtrim($this->config['url_prefix'], '/').'/'.$this->relativePath($path); + public function pathToUrl($path) + { + if (empty($this->config['url_prefix'])) { + return '/'.$path; + } + if (empty($this->config['path'])) { + return rtrim($this->config['url_prefix'], '/').'/'.$path; + } + + return rtrim($this->config['url_prefix'], '/').'/'.$this->relativePath($path); } /** @@ -106,9 +131,12 @@ public function pathToUrl($path) { * return nothing. * * @param string path or url + * @param mixed $url + * * @return string|void */ - public function signingToken($url) { + public function signingToken($url) + { if (isset($this->config['signing_key']) && ($key = $this->config['signing_key'])) { return md5($key.basename($url)); @@ -119,7 +147,7 @@ public function signingToken($url) { * Make the regex for the route definition. This works by wrapping both the * basic Croppa pattern and the `path` config in positive regex lookaheads so * they working like an AND condition. - * https://regex101.com/r/kO6kL1/1 + * https://regex101.com/r/kO6kL1/1. * * In the Laravel router, this gets wrapped with some extra regex before the * matching happnens and for the pattern to match correctly, the final .* needs @@ -128,18 +156,24 @@ public function signingToken($url) { * * @return string */ - public function routePattern() { - return sprintf("(?=%s)(?=%s).+", $this->config['path'], self::PATTERN); + public function routePattern() + { + return sprintf('(?=%s)(?=%s).+', $this->config['path'], self::PATTERN); } /** - * Parse a request path into Croppa instructions + * Parse a request path into Croppa instructions. * * @param string $request - * @return array | boolean + * + * @return array|bool */ - public function parse($request) { - if (!preg_match('#'.self::PATTERN.'#', $request, $matches)) return false; + public function parse($request) + { + if (!preg_match('#'.self::PATTERN.'#', $request, $matches)) { + return false; + } + return [ $this->relativePath($matches[1].'.'.$matches[5]), // Path $matches[2] == '_' ? null : (int) $matches[2], // Width @@ -150,74 +184,95 @@ public function parse($request) { /** * Take a URL or path to an image and get the path relative to the src and - * crops dirs by using the `path` config regex + * crops dirs by using the `path` config regex. * * @param string $url url or path + * * @return string */ - public function relativePath($url) { + public function relativePath($url) + { $path = $this->toPath($url); if (!preg_match('#'.$this->config['path'].'#', $path, $matches)) { - throw new Exception("$url doesn't match `{$this->config['path']}`"); + throw new Exception("{$url} doesn't match `{$this->config['path']}`"); } + return $matches[1]; } /** * Create options array where each key is an option name - * and the value if an array of the passed arguments + * and the value if an array of the passed arguments. + * + * @param string $option_params Options string in the Croppa URL style * - * @param string $option_params Options string in the Croppa URL style * @return array */ - public function options($option_params) { - $options = array(); + public function options($option_params) + { + $options = []; // These will look like: "-quadrant(T)-resize" $option_params = explode('-', $option_params); // Loop through the params and make the options key value pairs - foreach($option_params as $option) { - if (!preg_match('#(\w+)(?:\(([\w,.]+)\))?#i', $option, $matches)) continue; - if (isset($matches[2])) $options[$matches[1]] = explode(',', $matches[2]); - else $options[$matches[1]] = null; + foreach ($option_params as $option) { + if (!preg_match('#(\w+)(?:\(([\w,.]+)\))?#i', $option, $matches)) { + continue; + } + if (isset($matches[2])) { + $options[$matches[1]] = explode(',', $matches[2]); + } else { + $options[$matches[1]] = null; + } } // Map filter names to filter class instances or remove the config. $options['filters'] = $this->buildfilters($options); - if (empty($options['filters'])) unset($options['filters']); + if (empty($options['filters'])) { + unset($options['filters']); + } // Return new options array return $options; } /** - * Build filter class instancees + * Build filter class instancees. * - * @param array $options - * @return array|null Array of filter instances + * @param array $options + * + * @return null|array Array of filter instances */ - public function buildFilters($options) { - if (empty($options['filters']) || !is_array($options['filters'])) return []; - return array_filter(array_map(function($filter) { - if (empty($this->config['filters'][$filter])) return; - return new $this->config['filters'][$filter]; + public function buildFilters($options) + { + if (empty($options['filters']) || !is_array($options['filters'])) { + return []; + } + + return array_filter(array_map(function ($filter) { + if (empty($this->config['filters'][$filter])) { + return; + } + + return new $this->config['filters'][$filter](); }, $options['filters'])); } /** * Take options in the URL and options from the config file and produce a - * config array in the format that PhpThumb expects + * config array in the format that PhpThumb expects. * * @param array $options The url options from `parseOptions()` + * * @return array */ - public function phpThumbConfig($options) { + public function phpThumbConfig($options) + { return [ 'jpegQuality' => isset($options['quality']) ? $options['quality'][0] : $this->config['jpeg_quality'], 'interlace' => isset($options['interlace']) ? $options['interlace'][0] : $this->config['interlace'], 'resizeUp' => isset($options['upscale']) ? $options['upscale'][0] : $this->config['upscale'], ]; } - } From 11bc63934bc1db196e50825e786d09fc9d53e7fd Mon Sep 17 00:00:00 2001 From: Samuel De Backer Date: Sun, 17 Apr 2022 21:34:47 +0200 Subject: [PATCH 6/7] spaces in place of tabs --- composer.json | 92 +++++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/composer.json b/composer.json index ee97d6d..c1a19ab 100644 --- a/composer.json +++ b/composer.json @@ -1,48 +1,48 @@ { - "name": "bkwld/croppa", - "description": "Image thumbnail creation through specially formatted URLs for Laravel", - "authors": [ - { - "name": "Robert Reinhard" - } - ], - "license": "MIT", - "require": { - "php": "^8.0.2", - "illuminate/console": "^9.0", - "illuminate/support": "^9.0", - "illuminate/routing": "^9.0", - "league/flysystem": "^3.0", - "symfony/http-foundation": "^6.0", - "symfony/http-kernel": "^6.0", - "weotch/phpthumb": "^1.0.5", - "ext-gd": "*" - }, - "require-dev": { - "phpunit/phpunit": "^9.5.10", - "mockery/mockery": "^1.4.4" - }, - "suggest": { - "ext-exif": "Required if you want to have Croppa auto-rotate images from devices like mobile phones based on exif meta data." - }, - "conflict": { - "league/flysystem-cached-adapter": "<1.0.2" - }, - "autoload": { - "psr-0": { - "Bkwld\\Croppa": "src/" - } - }, - "minimum-stability": "dev", - "prefer-stable": true, - "extra": { - "laravel": { - "providers": [ - "Bkwld\\Croppa\\ServiceProvider" - ], - "aliases": { - "Croppa": "Bkwld\\Croppa\\Facade" - } - } - } + "name": "bkwld/croppa", + "description": "Image thumbnail creation through specially formatted URLs for Laravel", + "authors": [ + { + "name": "Robert Reinhard" + } + ], + "license": "MIT", + "require": { + "php": "^8.0.2", + "illuminate/console": "^9.0", + "illuminate/support": "^9.0", + "illuminate/routing": "^9.0", + "league/flysystem": "^3.0", + "symfony/http-foundation": "^6.0", + "symfony/http-kernel": "^6.0", + "weotch/phpthumb": "^1.0.5", + "ext-gd": "*" + }, + "require-dev": { + "phpunit/phpunit": "^9.5.10", + "mockery/mockery": "^1.4.4" + }, + "suggest": { + "ext-exif": "Required if you want to have Croppa auto-rotate images from devices like mobile phones based on exif meta data." + }, + "conflict": { + "league/flysystem-cached-adapter": "<1.0.2" + }, + "autoload": { + "psr-0": { + "Bkwld\\Croppa": "src/" + } + }, + "minimum-stability": "dev", + "prefer-stable": true, + "extra": { + "laravel": { + "providers": [ + "Bkwld\\Croppa\\ServiceProvider" + ], + "aliases": { + "Croppa": "Bkwld\\Croppa\\Facade" + } + } + } } From ba4c9d72ee52d4cf3ec5774f1b730cc1994478dd Mon Sep 17 00:00:00 2001 From: Samuel De Backer Date: Sun, 17 Apr 2022 21:34:57 +0200 Subject: [PATCH 7/7] 4 spaces indentation --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4de3405..1ad2afa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "dependencies": {}, - "devDependencies": { - "mocha": "^3.5.3" - } + "dependencies": {}, + "devDependencies": { + "mocha": "^3.5.3" + } }