diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index a219917..7f7a4ec 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -8,30 +8,9 @@ jobs: strategy: max-parallel: 10 matrix: - laravel-version: ['^6.0', '^7.0', '^8.0', '^9.0', '^10.0'] + laravel-version: ['^11.0', '^11.1'] preference: ['stable'] - php-version: ['7.4', '8.0', '8.1', '8.2'] - exclude: - - laravel-version: ^6.0 - php-version: 8.0 - - laravel-version: ^6.0 - php-version: 8.1 - - laravel-version: ^6.0 - php-version: 8.2 - - laravel-version: ^7.0 - php-version: 8.0 - - laravel-version: ^7.0 - php-version: 8.1 - - laravel-version: ^7.0 - php-version: 8.2 - - laravel-version: ^9.0 - php-version: 7.4 - - laravel-version: ^9.0 - php-version: 8.2 - - laravel-version: ^10.0 - php-version: 7.4 - - laravel-version: ^10.0 - php-version: 8.0 + php-version: ['8.2', '8.3'] name: Laravel ${{ matrix.laravel-version }} (${{ matrix.preference }}) on PHP ${{ matrix.php-version }} steps: - name: Checkout diff --git a/config/config.php b/config/config.php index 1799c9d..23d45fa 100644 --- a/config/config.php +++ b/config/config.php @@ -21,7 +21,7 @@ // image encoding @see: http://image.intervention.io/api/encode 'encoding' => [ - 'format' => env('GUIDED_IMAGE_ENCODING_FORMAT', 'png'), + 'mime_type' => env('GUIDED_IMAGE_ENCODING_MIME_TYPE', 'image/png'), 'quality' => env('GUIDED_IMAGE_ENCODING_QUALITY', 90), ], @@ -92,5 +92,5 @@ 'dispenser' => [ // whether raw image should be served as fallback if NotReadableException occurs 'raw_image_fallback_enabled' => env('GUIDED_IMAGE_DISPENSER_RAW_IMAGE_FALLBACK_ENABLED', false), - ] + ], ]; diff --git a/phpunit.xml b/phpunit.xml index 997326e..7330a50 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,12 +1,6 @@ - - - - src - - - src/Concerns - + + @@ -32,4 +26,12 @@ + + + src + + + src/Concerns + + diff --git a/src/Contract/ConfigProvider.php b/src/Contract/ConfigProvider.php index 7286c47..6dbeff8 100644 --- a/src/Contract/ConfigProvider.php +++ b/src/Contract/ConfigProvider.php @@ -27,8 +27,7 @@ public function getRoutePrefix(): string; /** * Get image model for guided image routes. * - * @param bool $lowered whether model should be returned in lowercase form - * + * @param bool $lowered whether model should be returned in lowercase form * @return string model name */ public function getGuidedModelName(bool $lowered = false): string; @@ -36,8 +35,7 @@ public function getGuidedModelName(bool $lowered = false): string; /** * Get image model namespace for guided image routes. * - * @param bool $lowered whether model should be returned in lowercase form - * + * @param bool $lowered whether model should be returned in lowercase form * @return string model namespace */ public function getGuidedModelNamespace(bool $lowered = false): string; @@ -73,7 +71,7 @@ public function getAdditionalHeaders(): array; public function getCacheDirectory(): string; - public function getImageEncodingFormat(): string; + public function getImageEncodingMimeType(): string; public function getImageEncodingQuality(): int; diff --git a/src/Contract/ImageDispenser.php b/src/Contract/ImageDispenser.php index 5657b1c..c28d13f 100644 --- a/src/Contract/ImageDispenser.php +++ b/src/Contract/ImageDispenser.php @@ -4,7 +4,7 @@ namespace ReliqArts\GuidedImage\Contract; -use Intervention\Image\Image; +use Intervention\Image\Interfaces\ImageInterface; use ReliqArts\GuidedImage\Demand\Dummy; use ReliqArts\GuidedImage\Demand\Resize; use ReliqArts\GuidedImage\Demand\Thumbnail; @@ -14,14 +14,14 @@ interface ImageDispenser { /** - * @return Image|Response + * @return ImageInterface|Response */ public function getImageThumbnail(Thumbnail $demand); /** * Get a resized Guided Image. * - * @return Image|Response + * @return ImageInterface|Response */ public function getResizedImage(Resize $demand); @@ -30,7 +30,7 @@ public function getResizedImage(Resize $demand); * * @throws RuntimeException */ - public function getDummyImage(Dummy $demand): Image; + public function getDummyImage(Dummy $demand): ImageInterface; public function emptyCache(): bool; } diff --git a/src/Contract/ImageManager.php b/src/Contract/ImageManager.php new file mode 100644 index 0000000..0a2cbff --- /dev/null +++ b/src/Contract/ImageManager.php @@ -0,0 +1,26 @@ + $this->getRoutePrefix()] : []; + $defaults = $groupKey === self::ROUTE_GROUP_KEY_PUBLIC ? [self::KEY_PREFIX => $this->getRoutePrefix()] : []; $bindings = array_merge( $this->configAccessor->get(sprintf(self::CONFIG_KEY_ROUTES_BINDINGS_WITH_GROUP, $groupKey), []), @@ -159,7 +197,7 @@ public function getUploadDirectory(): string public function generateUploadDateSubDirectories(): bool { - return (bool)$this->configAccessor->get( + return (bool) $this->configAccessor->get( self::CONFIG_KEY_STORAGE_GENERATE_UPLOAD_DATE_SUB_DIRECTORIES, self::DEFAULT_STORAGE_GENERATE_UPLOAD_DATE_SUB_DIRECTORIES ); @@ -189,7 +227,7 @@ public function getThumbsCachePath(): string public function getCacheDaysHeader(): int { - return (int)$this->configAccessor->get(self::CONFIG_KEY_HEADERS_CACHE_DAYS, self::DEFAULT_HEADER_CACHE_DAYS); + return (int) $this->configAccessor->get(self::CONFIG_KEY_HEADERS_CACHE_DAYS, self::DEFAULT_HEADER_CACHE_DAYS); } public function getAdditionalHeaders(): array @@ -205,17 +243,17 @@ public function getCacheDirectory(): string ); } - public function getImageEncodingFormat(): string + public function getImageEncodingMimeType(): string { return $this->configAccessor->get( - self::CONFIG_KEY_IMAGE_ENCODING_FORMAT, - self::DEFAULT_IMAGE_ENCODING_FORMAT + self::CONFIG_KEY_IMAGE_ENCODING_MIME_TYPE, + self::DEFAULT_IMAGE_ENCODING_MIME_TYPE ); } public function getImageEncodingQuality(): int { - return (int)$this->configAccessor->get( + return (int) $this->configAccessor->get( self::CONFIG_KEY_IMAGE_ENCODING_QUALITY, self::DEFAULT_IMAGE_ENCODING_QUALITY ); diff --git a/src/Service/ImageDispenser.php b/src/Service/ImageDispenser.php index a12283d..c1b2ccc 100644 --- a/src/Service/ImageDispenser.php +++ b/src/Service/ImageDispenser.php @@ -9,15 +9,15 @@ use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Http\Request; use Illuminate\Http\Response; -use Intervention\Image\Exceptions\RuntimeException as InterventionRuntimeException; -use Intervention\Image\Image; -use Intervention\Image\ImageManager; +use Intervention\Image\Encoders\AutoEncoder; +use Intervention\Image\Interfaces\ImageInterface; use InvalidArgumentException; +use ReliqArts\Contract\Logger; use ReliqArts\GuidedImage\Contract\ConfigProvider; use ReliqArts\GuidedImage\Contract\FileHelper; use ReliqArts\GuidedImage\Contract\GuidedImage; use ReliqArts\GuidedImage\Contract\ImageDispenser as ImageDispenserContract; -use ReliqArts\GuidedImage\Contract\Logger; +use ReliqArts\GuidedImage\Contract\ImageManager; use ReliqArts\GuidedImage\Demand\Dummy; use ReliqArts\GuidedImage\Demand\Resize; use ReliqArts\GuidedImage\Demand\Thumbnail; @@ -37,7 +37,7 @@ final class ImageDispenser implements ImageDispenserContract private const ONE_DAY_IN_SECONDS = 60 * 60 * 24; - private const DEFAULT_IMAGE_ENCODING_FORMAT = 'png'; + private const DEFAULT_IMAGE_ENCODING_MIME_TYPE = 'image/png'; private const DEFAULT_IMAGE_ENCODING_QUALITY = 90; @@ -45,7 +45,7 @@ final class ImageDispenser implements ImageDispenserContract private Filesystem $uploadDisk; - private string $imageEncodingFormat; + private string $imageEncodingMimeType; private int $imageEncodingQuality; @@ -62,7 +62,7 @@ public function __construct( ) { $this->cacheDisk = $filesystemManager->disk($configProvider->getCacheDiskName()); $this->uploadDisk = $filesystemManager->disk($configProvider->getUploadDiskName()); - $this->imageEncodingFormat = $configProvider->getImageEncodingFormat(); + $this->imageEncodingMimeType = $configProvider->getImageEncodingMimeType(); $this->imageEncodingQuality = $configProvider->getImageEncodingQuality(); $this->prepCacheDirectories(); @@ -73,7 +73,7 @@ public function __construct( * * @throws RuntimeException */ - public function getDummyImage(Dummy $demand): Image + public function getDummyImage(Dummy $demand): ImageInterface { return $this->imageManager->create( $demand->getWidth(), @@ -84,7 +84,7 @@ public function getDummyImage(Dummy $demand): Image /** * {@inheritdoc} * - * @return Image|SymfonyResponse|void + * @return ImageInterface|SymfonyResponse|void * * @throws InvalidArgumentException * @throws RuntimeException @@ -127,8 +127,8 @@ public function getResizedImage(Resize $demand) self::RESPONSE_HTTP_OK, $this->getImageHeaders($cacheFilePath, $demand->getRequest(), $image) ?: [] ); - } catch (InterventionRuntimeException $exception) { - return $this->handleInterventionRuntimeException($exception, $guidedImage); + } catch (RuntimeException $exception) { + return $this->handleRuntimeException($exception, $guidedImage); } catch (FileNotFoundException $exception) { $this->logger->error( sprintf( @@ -148,7 +148,7 @@ public function getResizedImage(Resize $demand) /** * {@inheritdoc} * - * @return Image|SymfonyResponse|void + * @return ImageInterface|SymfonyResponse|never * * @throws InvalidArgumentException * @throws RuntimeException @@ -185,7 +185,7 @@ public function getImageThumbnail(Thumbnail $demand) if ($this->cacheDisk->exists($cacheFilePath)) { $image = $this->makeImageWithEncoding($this->cacheDisk->path($cacheFilePath)); } else { - /** @var Image $image */ + /** @var ImageInterface $image */ $image = $this->imageManager ->read($this->getImageFullUrl($guidedImage)) ->{$method}( @@ -205,8 +205,8 @@ public function getImageThumbnail(Thumbnail $demand) self::RESPONSE_HTTP_OK, $this->getImageHeaders($cacheFilePath, $demand->getRequest(), $image) ?: [] ); - } catch (InterventionRuntimeException $exception) { - return $this->handleInterventionRuntimeException($exception, $guidedImage); + } catch (RuntimeException $exception) { + return $this->handleRuntimeException($exception, $guidedImage); } catch (FileNotFoundException $exception) { $this->logger->error( sprintf( @@ -235,7 +235,7 @@ public function emptyCache(): bool * * @return array image headers */ - private function getImageHeaders(string $cacheFilePath, Request $request, Image $image): array + private function getImageHeaders(string $cacheFilePath, Request $request, ImageInterface $image): array { $filePath = $image->origin()->filePath(); $lastModified = $this->cacheDisk->lastModified($cacheFilePath); @@ -291,22 +291,18 @@ private function getDefaultHeaders(): array } /** - * @param mixed ...$encoding - * - * @throws InterventionRuntimeException + * @throws RuntimeException */ - private function makeImageWithEncoding(mixed $data, ...$encoding): Image + private function makeImageWithEncoding(mixed $data): ImageInterface { - if (empty($encoding)) { - $encoding = [ - $this->imageEncodingFormat ?: self::DEFAULT_IMAGE_ENCODING_FORMAT, - $this->imageEncodingQuality ?: self::DEFAULT_IMAGE_ENCODING_QUALITY, - ]; - } + $encoder = new AutoEncoder( + $this->imageEncodingMimeType ?: self::DEFAULT_IMAGE_ENCODING_MIME_TYPE, + quality: $this->imageEncodingQuality ?: self::DEFAULT_IMAGE_ENCODING_QUALITY, + ); $encodedImage = $this->imageManager ->read($data) - ->encode(...$encoding); + ->encode($encoder); return $this->imageManager->read($encodedImage->toFilePointer()); } @@ -322,8 +318,8 @@ private function getImageFullUrl(GuidedImage $guidedImage): string /** * @throws RuntimeException */ - private function handleInterventionRuntimeException( - InterventionRuntimeException $exception, + private function handleRuntimeException( + RuntimeException $exception, GuidedImage $guidedImage ): BinaryFileResponse { $errorMessage = 'Intervention image creation failed with NotReadableException;'; diff --git a/src/Service/ImageManager.php b/src/Service/ImageManager.php new file mode 100644 index 0000000..6d831f8 --- /dev/null +++ b/src/Service/ImageManager.php @@ -0,0 +1,34 @@ +manager->create($width, $height); + } + + /** + * @throws RuntimeException + */ + public function read(mixed $input, array|string|DecoderInterface $decoders = []): ImageInterface + { + return $this->manager->read($input, $decoders); + } +} diff --git a/src/Service/ImageUploader.php b/src/Service/ImageUploader.php index eb2c07c..e322277 100644 --- a/src/Service/ImageUploader.php +++ b/src/Service/ImageUploader.php @@ -9,11 +9,11 @@ use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Contracts\Validation\Factory as ValidationFactory; use Illuminate\Http\UploadedFile; +use ReliqArts\Contract\Logger; use ReliqArts\GuidedImage\Contract\ConfigProvider; use ReliqArts\GuidedImage\Contract\FileHelper; use ReliqArts\GuidedImage\Contract\GuidedImage; use ReliqArts\GuidedImage\Contract\ImageUploader as ImageUploaderContract; -use ReliqArts\GuidedImage\Contract\Logger; use ReliqArts\GuidedImage\Exception\UrlUploadFailed; use ReliqArts\GuidedImage\Model\UploadedImage; use ReliqArts\GuidedImage\Result; diff --git a/src/Service/Logger.php b/src/Service/Logger.php deleted file mode 100644 index c12c51e..0000000 --- a/src/Service/Logger.php +++ /dev/null @@ -1,12 +0,0 @@ -handleMigrations(); } + /** + * @throws InvalidArgumentException + */ public function register(): void { $this->configProvider = new ConfigProvider( @@ -67,8 +72,11 @@ public function register(): void $this->getConfigKey() ) ); + $guidedModelFQCN = $this->configProvider->getGuidedModelNamespace() - . $this->configProvider->getGuidedModelName(); + .$this->configProvider->getGuidedModelName(); + + $this->app->bind(GuidedImage::class, $guidedModelFQCN); $this->app->singleton( ConfigProviderContract::class, @@ -78,32 +86,39 @@ function (): ConfigProviderContract { ); $this->app->singleton( - LoggerContract::class, - function (): LoggerContract { - $logger = new Logger($this->getLoggerName()); - $logFile = storage_path(sprintf('logs/%s.log', $this->getLogFilename())); - $logger->pushHandler(new StreamHandler($logFile, Logger::DEBUG)); - - return $logger; - } + ImageManagerContract::class, + ImageManager::class ); $this->app->singleton( - ImageDispenserContract::class, - ImageDispenser::class + FileHelperContract::class, + FileHelper::class ); + $logger = $this->createLogger(); + $this->app->singleton( ImageUploaderContract::class, - ImageUploader::class + fn (): ImageUploaderContract => new ImageUploader( + $this->configProvider, + resolve(FilesystemManager::class), + resolve(FileHelperContract::class), + resolve(ValidationFactory::class), + resolve(GuidedImage::class), + $logger, + ) ); $this->app->singleton( - FileHelperContract::class, - FileHelper::class + ImageDispenserContract::class, + fn (): ImageDispenserContract => new ImageDispenser( + $this->configProvider, + resolve(FilesystemManager::class), + resolve(ImageManagerContract::class), + $logger, + resolve(FileHelperContract::class) + ) ); - - $this->app->bind(GuidedImage::class, $guidedModelFQCN); } public function provides(): array @@ -147,10 +162,9 @@ private function handleRoutes(): void $router = $this->app->make('router'); $modelName = $this->configProvider->getGuidedModelName(); - if (!$this->app->routesAreCached()) { - $router->model(strtolower($modelName), $this->configProvider->getGuidedModelNamespace() . $modelName); + if (! $this->app->routesAreCached()) { + $router->model(strtolower($modelName), $this->configProvider->getGuidedModelNamespace().$modelName); - /** @noinspection PhpIncludeInspection */ require_once sprintf('%s/routes/web.php', $this->getAssetDirectory()); } } @@ -159,4 +173,15 @@ private function handleMigrations(): void { $this->loadMigrationsFrom(sprintf('%s/database/migrations', $this->getAssetDirectory())); } + + /** + * @throws InvalidArgumentException + */ + private function createLogger(): LoggerContract + { + /** @var LoggerFactory $loggerFactory */ + $loggerFactory = resolve(LoggerFactory::class); + + return $loggerFactory->create($this->getLoggerName(), $this->getLogFileBasename()); + } } diff --git a/tests/Unit/Demand/DummyTest.php b/tests/Unit/Demand/DummyTest.php index c9c467d..10dc984 100644 --- a/tests/Unit/Demand/DummyTest.php +++ b/tests/Unit/Demand/DummyTest.php @@ -4,6 +4,7 @@ namespace ReliqArts\GuidedImage\Tests\Unit\Demand; +use Exception; use ReliqArts\GuidedImage\Demand\Dummy; /** @@ -17,11 +18,14 @@ final class DummyTest extends TestCase { /** * @dataProvider colorDataProvider + * * @covers ::__construct * @covers ::getColor * @covers ::isValueConsideredNull * - * @param mixed $color + * @param mixed $color + * + * @throws Exception */ public function testGetColor($color, string $expectedResult): void { @@ -36,11 +40,12 @@ public function testGetColor($color, string $expectedResult): void /** * @dataProvider fillDataProvider + * * @covers ::__construct * @covers ::fill * @covers ::isValueConsideredNull * - * @param mixed $fill + * @param mixed $fill */ public function testFill($fill, ?string $expectedResult): void { @@ -54,7 +59,7 @@ public function testFill($fill, ?string $expectedResult): void self::assertSame($expectedResult, $demand->fill()); } - public function colorDataProvider(): array + public static function colorDataProvider(): array { return [ ['0f0', '0f0'], @@ -67,7 +72,7 @@ public function colorDataProvider(): array ]; } - public function fillDataProvider(): array + public static function fillDataProvider(): array { return [ ['0f0', '0f0'], diff --git a/tests/Unit/Service/ImageDispenserTest.php b/tests/Unit/Service/ImageDispenserTest.php index ce0da01..112fae1 100644 --- a/tests/Unit/Service/ImageDispenserTest.php +++ b/tests/Unit/Service/ImageDispenserTest.php @@ -3,44 +3,47 @@ * @noinspection PhpParamsInspection * @noinspection PhpUndefinedMethodInspection * @noinspection PhpStrictTypeCheckingInspection + * @noinspection PhpVoidFunctionResultUsedInspection */ declare(strict_types=1); namespace ReliqArts\GuidedImage\Tests\Unit\Service; +use Exception; use Illuminate\Contracts\Filesystem\Factory as FilesystemManager; use Illuminate\Contracts\Filesystem\FileNotFoundException; use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Filesystem\FilesystemAdapter; use Illuminate\Http\Request; -use Intervention\Image\Exception\NotReadableException; -use Intervention\Image\Image; -use Intervention\Image\ImageManager; +use Intervention\Image\Interfaces\EncodedImageInterface; +use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Origin; +use InvalidArgumentException; use Mockery; use Mockery\MockInterface; +use PHPUnit\Framework\Attributes\CoversClass; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; +use ReliqArts\Contract\Logger; use ReliqArts\GuidedImage\Contract\ConfigProvider; use ReliqArts\GuidedImage\Contract\FileHelper; use ReliqArts\GuidedImage\Contract\ImageDispenser as ImageDispenserContract; -use ReliqArts\GuidedImage\Contract\Logger; +use ReliqArts\GuidedImage\Contract\ImageManager; use ReliqArts\GuidedImage\Demand\Dummy; use ReliqArts\GuidedImage\Demand\Resize; use ReliqArts\GuidedImage\Demand\Thumbnail; use ReliqArts\GuidedImage\Service\ImageDispenser; use ReliqArts\GuidedImage\Tests\Fixtures\Model\GuidedImage; use ReliqArts\GuidedImage\Tests\Unit\TestCase; +use RuntimeException; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** - * Class ImageDispenserTest. - * - * @coversDefaultClass \ReliqArts\GuidedImage\Service\ImageDispenser - * * @internal */ +#[CoversClass(ImageDispenser::class)] final class ImageDispenserTest extends TestCase { private const CACHE_DISK_NAME = 'local'; @@ -53,69 +56,47 @@ final class ImageDispenserTest extends TestCase private const LAST_MODIFIED = 21343; - private const IMAGE_NAME = 'my-image'; - - private const IMAGE_URL = '//image_url'; - private const FILE_HASH = '4387904830a4245a8ab767e5937d722c'; private const CACHE_FILE_NAME_FORMAT_RESIZED = '%s/%d-%d-_-%d_%d_%s'; private const CACHE_FILE_FORMAT_THUMBNAIL = '%s/%d-%d-_-%s_%s'; - private const IMAGE_WIDTH = 100; + private const IMAGE_NAME = 'my-image'; - private const IMAGE_HEIGHT = 200; + private const IMAGE_URL = '//image_url'; - private const THUMBNAIL_METHOD_CROP = 'crop'; + private const IMAGE_WIDTH = 100; - private const THUMBNAIL_METHOD_FIT = 'fit'; + private const IMAGE_HEIGHT = 200; private const IMAGE_ENCODING_FORMAT = 'png'; private const IMAGE_ENCODING_QUALITY = 90; - private const UPLOAD_DISK_NAME = 'public'; + private const IMAGE_MEDIA_TYPE = 'image/png'; - /** - * @var ConfigProvider|ObjectProphecy - */ - private ObjectProphecy $configProvider; + private const IMAGE_FILE_PATH = 'image.png'; - /** - * @var FilesystemManager|ObjectProphecy - */ - private ObjectProphecy $filesystemManager; + private const IMAGE_CONTENT_RAW = 'RAW'; - /** - * @var Filesystem|FilesystemAdapter|ObjectProphecy - */ - private ObjectProphecy $cacheDisk; + private const THUMBNAIL_METHOD_CROP = 'crop'; - /** - * @var Filesystem|FilesystemAdapter|ObjectProphecy - */ - private ObjectProphecy $uploadDisk; + private const THUMBNAIL_METHOD_COVER = 'cover'; - /** - * @var ImageManager|ObjectProphecy - */ - private ObjectProphecy $imageManager; + private const UPLOAD_DISK_NAME = 'public'; - /** - * @var Logger|ObjectProphecy - */ - private ObjectProphecy $logger; + private const FOO_RESOURCE = 'resource'; - /** - * @var ObjectProphecy|Request - */ - private ObjectProphecy $request; + private ObjectProphecy|Filesystem|FilesystemAdapter $cacheDisk; - /** - * @var GuidedImage|ObjectProphecy - */ - private ObjectProphecy $guidedImage; + private ObjectProphecy|ImageManager $imageManager; + + private ObjectProphecy|Logger $logger; + + private ObjectProphecy|Request $request; + + private ObjectProphecy|GuidedImage $guidedImage; private string $cacheThumbs; @@ -123,14 +104,14 @@ final class ImageDispenserTest extends TestCase private ImageDispenserContract $subject; + /** + * @throws RuntimeException + */ protected function setUp(): void { parent::setUp(); - $this->configProvider = $this->prophesize(ConfigProvider::class); - $this->filesystemManager = $this->prophesize(FilesystemManager::class); $this->cacheDisk = $this->prophesize(FilesystemAdapter::class); - $this->uploadDisk = $this->prophesize(FilesystemAdapter::class); $this->imageManager = $this->prophesize(ImageManager::class); $this->logger = $this->prophesize(Logger::class); $this->request = $this->prophesize(Request::class); @@ -138,55 +119,58 @@ protected function setUp(): void $this->cacheResized = self::CACHE_RESIZED_SUB_DIRECTORY; $this->cacheThumbs = self::CACHE_THUMBS_SUB_DIRECTORY; + $configProvider = $this->prophesize(ConfigProvider::class); $fileHelper = $this->prophesize(FileHelper::class); + $filesystemManager = $this->prophesize(FilesystemManager::class); + $uploadDisk = $this->prophesize(FilesystemAdapter::class); - $this->configProvider + $configProvider ->getCacheDiskName() ->shouldBeCalledTimes(1) ->willReturn(self::CACHE_DISK_NAME); - $this->configProvider + $configProvider ->getUploadDiskName() ->shouldBeCalledTimes(1) ->willReturn(self::UPLOAD_DISK_NAME); - $this->configProvider + $configProvider ->getResizedCachePath() ->shouldBeCalledTimes(1) ->willReturn(self::CACHE_RESIZED_SUB_DIRECTORY); - $this->configProvider + $configProvider ->getThumbsCachePath() ->shouldBeCalledTimes(1) ->willReturn(self::CACHE_THUMBS_SUB_DIRECTORY); - $this->configProvider + $configProvider ->getCacheDaysHeader() ->willReturn(2); - $this->configProvider + $configProvider ->getAdditionalHeaders() ->willReturn([]); - $this->configProvider - ->getImageEncodingFormat() + $configProvider + ->getImageEncodingMimeType() ->willReturn(self::IMAGE_ENCODING_FORMAT); - $this->configProvider + $configProvider ->getImageEncodingQuality() ->willReturn(self::IMAGE_ENCODING_QUALITY); - $this->configProvider + $configProvider ->isRawImageFallbackEnabled() ->willReturn(false); - $this->filesystemManager + $filesystemManager ->disk(self::CACHE_DISK_NAME) ->shouldBeCalledTimes(1) ->willReturn($this->cacheDisk); - $this->filesystemManager + $filesystemManager ->disk(self::UPLOAD_DISK_NAME) ->shouldBeCalledTimes(1) - ->willReturn($this->uploadDisk); + ->willReturn($uploadDisk); $this->cacheDisk ->exists($this->cacheResized) ->shouldBeCalledTimes(1) ->willReturn(false); $this->cacheDisk - ->makeDirectory($this->cacheResized, Argument::cetera()) + ->makeDirectory($this->cacheResized) ->shouldBeCalledTimes(1) ->willReturn(true); $this->cacheDisk @@ -194,14 +178,14 @@ protected function setUp(): void ->shouldBeCalledTimes(1) ->willReturn(false); $this->cacheDisk - ->makeDirectory($this->cacheThumbs, Argument::cetera()) + ->makeDirectory($this->cacheThumbs) ->shouldBeCalledTimes(1) ->willReturn(true); $this->cacheDisk ->lastModified(Argument::type('string')) ->willReturn(self::LAST_MODIFIED); - $this->uploadDisk + $uploadDisk ->url(self::IMAGE_URL) ->willReturn(self::IMAGE_URL); @@ -221,8 +205,8 @@ protected function setUp(): void ->willReturn(self::IMAGE_URL); $this->subject = new ImageDispenser( - $this->configProvider->reveal(), - $this->filesystemManager->reveal(), + $configProvider->reveal(), + $filesystemManager->reveal(), $this->imageManager->reveal(), $this->logger->reveal(), $fileHelper->reveal() @@ -230,8 +214,7 @@ protected function setUp(): void } /** - * @covers ::__construct - * @covers ::emptyCache + * @throws RuntimeException */ public function testEmptyCache(): void { @@ -250,72 +233,30 @@ public function testEmptyCache(): void } /** - * @covers ::__construct - * @covers ::getDummyImage - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl + * @throws RuntimeException */ public function testGetDummyImage(): void { $width = self::IMAGE_WIDTH; $height = self::IMAGE_HEIGHT; $color = 'fee'; - $fill = 'f00'; - $imageResponse = new Response(); - $image = $this->getImageMock($imageResponse); - - $this->imageManager - ->canvas($width, $height, $color) - ->shouldBeCalledTimes(1) - ->willReturn($image); - - $result = $this->subject->getDummyImage( - new Dummy($width, $height, $color, $fill) - ); - - self::assertSame($imageResponse, $result); - } - - /** - * @covers ::__construct - * @covers ::getDummyImage - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl - */ - public function testGetDummyImageWhenImageInstanceIsExpected(): void - { - $width = self::IMAGE_WIDTH; - $height = self::IMAGE_HEIGHT; - $color = 'fee'; - $fill = 'f00'; $image = $this->getImageMock(); $this->imageManager - ->canvas($width, $height, $color) + ->create($width, $height) ->shouldBeCalledTimes(1) ->willReturn($image); $result = $this->subject->getDummyImage( - new Dummy($width, $height, $color, $fill, true) + new Dummy($width, $height, $color) ); self::assertSame($image, $result); } /** - * @covers ::__construct - * @covers ::getDefaultHeaders - * @covers ::getImageHeaders - * @covers ::getResizedImage - * @covers ::makeImageWithEncoding - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getGuidedImage - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getRequest - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getHeight - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getWidth - * - * @throws FileNotFoundException + * @throws RuntimeException + * @throws InvalidArgumentException */ public function testGetResizedImage(): void { @@ -328,16 +269,8 @@ public function testGetResizedImage(): void $width, $height ); - $cacheFile = sprintf( - self::CACHE_FILE_NAME_FORMAT_RESIZED, - $this->cacheResized, - $width, - $height, - 1, - 0, - self::IMAGE_NAME - ); - $imageContent = 'RAW'; + $cacheFile = $this->getCacheFilename($width, $height); + $imageContent = self::IMAGE_CONTENT_RAW; $this->cacheDisk ->exists($cacheFile) @@ -353,11 +286,11 @@ public function testGetResizedImage(): void ->willReturn($imageContent); $this->imageManager - ->make($cacheFile) + ->read($cacheFile) ->shouldNotBeCalled(); $this->imageManager - ->make(self::IMAGE_URL) - ->shouldBeCalledTimes(1) + ->read(Argument::in([self::IMAGE_URL, self::FOO_RESOURCE])) + ->shouldBeCalledTimes(2) ->willReturn($image); $result = $this->subject->getResizedImage($demand); @@ -368,15 +301,7 @@ public function testGetResizedImage(): void } /** - * @covers ::__construct - * @covers ::getResizedImage - * @covers ::makeImageWithEncoding - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getGuidedImage - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getRequest - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getHeight - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getWidth + * @throws RuntimeException|InvalidArgumentException */ public function testGetResizedImageWhenImageInstanceIsExpected(): void { @@ -388,19 +313,9 @@ public function testGetResizedImageWhenImageInstanceIsExpected(): void $this->guidedImage->reveal(), $width, $height, - true, - false, - true - ); - $cacheFile = sprintf( - self::CACHE_FILE_NAME_FORMAT_RESIZED, - $this->cacheResized, - $width, - $height, - 1, - 0, - self::IMAGE_NAME + returnObject: true ); + $cacheFile = $this->getCacheFilename($width, $height); $this->cacheDisk ->exists($cacheFile) @@ -415,11 +330,11 @@ public function testGetResizedImageWhenImageInstanceIsExpected(): void ->shouldNotBeCalled(); $this->imageManager - ->make($cacheFile) + ->read($cacheFile) ->shouldNotBeCalled(); $this->imageManager - ->make(self::IMAGE_URL) - ->shouldBeCalledTimes(1) + ->read(Argument::in([self::IMAGE_URL, self::FOO_RESOURCE])) + ->shouldBeCalledTimes(2) ->willReturn($image); $result = $this->subject->getResizedImage($demand); @@ -428,39 +343,14 @@ public function testGetResizedImageWhenImageInstanceIsExpected(): void } /** - * @covers ::__construct - * @covers ::getDefaultHeaders - * @covers ::getImageHeaders - * @covers ::getResizedImage - * @covers ::makeImageWithEncoding - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getGuidedImage - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getRequest - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getHeight - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getWidth + * @throws Exception */ public function testGetResizedImageWhenCacheFileExists(): void { $width = self::IMAGE_WIDTH; $height = self::IMAGE_HEIGHT; $image = $this->getImageMock(); - $demand = new Resize( - $this->request->reveal(), - $this->guidedImage->reveal(), - $width, - $height - ); - $cacheFile = sprintf( - self::CACHE_FILE_NAME_FORMAT_RESIZED, - $this->cacheResized, - $width, - $height, - 1, - 0, - self::IMAGE_NAME - ); - $imageContent = 'RAW'; + $cacheFile = $this->getCacheFilename($width, $height); $this->cacheDisk ->exists($cacheFile) @@ -473,36 +363,32 @@ public function testGetResizedImageWhenCacheFileExists(): void $this->cacheDisk ->get($cacheFile) ->shouldBeCalledTimes(1) - ->willReturn($imageContent); + ->willReturn(self::IMAGE_CONTENT_RAW); $this->imageManager - ->make($cacheFile) - ->shouldBeCalledTimes(1) + ->read(Argument::in([$cacheFile, self::FOO_RESOURCE])) + ->shouldBeCalledTimes(2) ->willReturn($image); $this->imageManager - ->make(self::IMAGE_URL) + ->read(self::IMAGE_URL) ->shouldNotBeCalled(); - $result = $this->subject->getResizedImage($demand); + $result = $this->subject->getResizedImage( + new Resize( + $this->request->reveal(), + $this->guidedImage->reveal(), + $width, + $height + ) + ); self::assertInstanceOf(Response::class, $result); self::assertSame(self::RESPONSE_HTTP_OK, $result->getStatusCode()); - self::assertSame($imageContent, $result->getOriginalContent()); + self::assertSame(self::IMAGE_CONTENT_RAW, $result->getOriginalContent()); } /** - * @covers ::__construct - * @covers ::getDefaultHeaders - * @covers ::getImageHeaders - * @covers ::getResizedImage - * @covers ::makeImageWithEncoding - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl - * @covers ::handleNotReadableException - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getGuidedImage - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getRequest - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getHeight - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getWidth + * @throws Exception */ public function testGetResizedWhenImageRetrievalFails(): void { @@ -515,15 +401,7 @@ public function testGetResizedWhenImageRetrievalFails(): void $width, $height ); - $cacheFile = sprintf( - self::CACHE_FILE_NAME_FORMAT_RESIZED, - $this->cacheResized, - $width, - $height, - 1, - 0, - self::IMAGE_NAME - ); + $cacheFile = $this->getCacheFilename($width, $height); $this->cacheDisk ->exists($cacheFile) @@ -539,11 +417,11 @@ public function testGetResizedWhenImageRetrievalFails(): void ->willThrow(FileNotFoundException::class); $this->imageManager - ->make($cacheFile) + ->read($cacheFile) ->shouldNotBeCalled(); $this->imageManager - ->make(self::IMAGE_URL) - ->shouldBeCalledTimes(1) + ->read(Argument::in([self::IMAGE_URL, self::FOO_RESOURCE])) + ->shouldBeCalledTimes(2) ->willReturn($image); $this->guidedImage @@ -564,17 +442,7 @@ public function testGetResizedWhenImageRetrievalFails(): void } /** - * @covers ::__construct - * @covers ::getDefaultHeaders - * @covers ::getImageHeaders - * @covers ::getImageThumbnail - * @covers ::makeImageWithEncoding - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getGuidedImage - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getRequest - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getHeight - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getWidth + * @throws Exception */ public function testGetImageThumbnail(): void { @@ -596,26 +464,26 @@ public function testGetImageThumbnail(): void $demand->getMethod(), self::IMAGE_NAME ); - $imageContent = 'RAW'; + $imageContent = self::IMAGE_CONTENT_RAW; $this->cacheDisk ->exists($cacheFile) ->shouldBeCalledTimes(1) ->willReturn(false); - $this->cacheDisk - ->path($cacheFile) - ->shouldBeCalledTimes(1) - ->willReturn($cacheFile); $this->cacheDisk ->get($cacheFile) ->shouldBeCalledTimes(1) ->willReturn($imageContent); + $this->cacheDisk + ->path($cacheFile) + ->shouldBeCalledTimes(1) + ->willReturn($cacheFile); $this->imageManager - ->make($cacheFile) + ->read($cacheFile) ->shouldNotBeCalled(); $this->imageManager - ->make(self::IMAGE_URL) + ->read(self::IMAGE_URL) ->shouldBeCalledTimes(1) ->willReturn($image); @@ -627,35 +495,19 @@ public function testGetImageThumbnail(): void } /** - * @covers ::__construct - * @covers ::getImageThumbnail - * @covers ::makeImageWithEncoding - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getGuidedImage - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getRequest - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getHeight - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getWidth + * @throws Exception */ public function testGetImageThumbnailWhenImageInstanceIsExpected(): void { $width = self::IMAGE_WIDTH; $height = self::IMAGE_HEIGHT; $image = $this->getImageMock(); - $demand = new Thumbnail( - $this->request->reveal(), - $this->guidedImage->reveal(), - self::THUMBNAIL_METHOD_CROP, - $width, - $height, - true - ); $cacheFile = sprintf( self::CACHE_FILE_FORMAT_THUMBNAIL, $this->cacheThumbs, $width, $height, - $demand->getMethod(), + self::THUMBNAIL_METHOD_CROP, self::IMAGE_NAME ); @@ -672,52 +524,44 @@ public function testGetImageThumbnailWhenImageInstanceIsExpected(): void ->shouldNotBeCalled(); $this->imageManager - ->make($cacheFile) + ->read($cacheFile) ->shouldNotBeCalled(); $this->imageManager - ->make(self::IMAGE_URL) + ->read(Argument::in([self::IMAGE_URL, self::FOO_RESOURCE])) ->shouldBeCalledTimes(1) ->willReturn($image); - $result = $this->subject->getImageThumbnail($demand); + $result = $this->subject->getImageThumbnail( + new Thumbnail( + $this->request->reveal(), + $this->guidedImage->reveal(), + self::THUMBNAIL_METHOD_CROP, + $width, + $height, + true + ) + ); self::assertSame($image, $result); } /** - * @covers ::__construct - * @covers ::getDefaultHeaders - * @covers ::getImageHeaders - * @covers ::getImageThumbnail - * @covers ::makeImageWithEncoding - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getGuidedImage - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getRequest - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getHeight - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getWidth + * @throws Exception */ public function testGetImageThumbnailWhenCacheFileExists(): void { $width = self::IMAGE_WIDTH; $height = self::IMAGE_HEIGHT; $image = $this->getImageMock(); - $demand = new Thumbnail( - $this->request->reveal(), - $this->guidedImage->reveal(), - self::THUMBNAIL_METHOD_CROP, - $width, - $height - ); $cacheFile = sprintf( self::CACHE_FILE_FORMAT_THUMBNAIL, $this->cacheThumbs, $width, $height, - $demand->getMethod(), + self::THUMBNAIL_METHOD_CROP, self::IMAGE_NAME ); - $imageContent = 'RAW'; + $imageContent = self::IMAGE_CONTENT_RAW; $this->cacheDisk ->exists($cacheFile) @@ -733,14 +577,22 @@ public function testGetImageThumbnailWhenCacheFileExists(): void ->willReturn($imageContent); $this->imageManager - ->make($cacheFile) - ->shouldBeCalledTimes(1) + ->read(Argument::in([$cacheFile, self::FOO_RESOURCE])) + ->shouldBeCalledTimes(2) ->willReturn($image); $this->imageManager - ->make(self::IMAGE_URL) + ->read(self::IMAGE_URL) ->shouldNotBeCalled(); - $result = $this->subject->getImageThumbnail($demand); + $result = $this->subject->getImageThumbnail( + new Thumbnail( + $this->request->reveal(), + $this->guidedImage->reveal(), + self::THUMBNAIL_METHOD_CROP, + $width, + $height + ) + ); self::assertInstanceOf(Response::class, $result); self::assertSame(self::RESPONSE_HTTP_OK, $result->getStatusCode()); @@ -748,15 +600,7 @@ public function testGetImageThumbnailWhenCacheFileExists(): void } /** - * @covers ::__construct - * @covers ::getImageThumbnail - * @covers ::makeImageWithEncoding - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getGuidedImage - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getRequest - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getHeight - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getWidth + * @throws Exception */ public function testGetImageThumbnailWhenDemandIsInvalid(): void { @@ -789,10 +633,10 @@ public function testGetImageThumbnailWhenDemandIsInvalid(): void ->shouldNotBeCalled(); $this->imageManager - ->make($cacheFile) + ->read($cacheFile) ->shouldNotBeCalled(); $this->imageManager - ->make(self::IMAGE_URL) + ->read(self::IMAGE_URL) ->shouldNotBeCalled(); $this->logger @@ -810,17 +654,7 @@ public function testGetImageThumbnailWhenDemandIsInvalid(): void } /** - * @covers ::__construct - * @covers ::getDefaultHeaders - * @covers ::getImageHeaders - * @covers ::getImageThumbnail - * @covers ::makeImageWithEncoding - * @covers ::prepCacheDirectories - * @covers ::getImageFullUrl - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getGuidedImage - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getRequest - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getHeight - * @covers \ReliqArts\GuidedImage\Demand\ExistingImage::getWidth + * @throws Exception */ public function testGetImageThumbnailWhenImageRetrievalFails(): void { @@ -829,7 +663,7 @@ public function testGetImageThumbnailWhenImageRetrievalFails(): void $demand = new Thumbnail( $this->request->reveal(), $this->guidedImage->reveal(), - self::THUMBNAIL_METHOD_FIT, + self::THUMBNAIL_METHOD_COVER, $width, $height ); @@ -854,12 +688,12 @@ public function testGetImageThumbnailWhenImageRetrievalFails(): void ->shouldNotBeCalled(); $this->imageManager - ->make($cacheFile) + ->read($cacheFile) ->shouldNotBeCalled(); $this->imageManager - ->make(self::IMAGE_URL) + ->read(self::IMAGE_URL) ->shouldBeCalledTimes(1) - ->willThrow(NotReadableException::class); + ->willThrow(RuntimeException::class); $this->logger ->error( @@ -873,32 +707,54 @@ public function testGetImageThumbnailWhenImageRetrievalFails(): void $this->subject->getImageThumbnail($demand); } - /** - * @return Image|MockInterface - */ - private function getImageMock(?Response $imageResponse = null): MockInterface + private function getImageMock(): ImageInterface|MockInterface { - $imageMethodNames = [ + /** @var Origin|MockInterface $imageOrigin */ + $imageOrigin = Mockery::mock(Origin::class); + $imageOrigin->shouldReceive('mediaType') + ->andReturn(self::IMAGE_MEDIA_TYPE); + $imageOrigin->shouldReceive('filePath') + ->andReturn(self::IMAGE_FILE_PATH); + + $imageMethods = [ 'fill', - 'resize', 'save', - 'encode', + 'resize', + 'resizeDown', + 'scale', + 'scaleDown', self::THUMBNAIL_METHOD_CROP, - self::THUMBNAIL_METHOD_FIT, + self::THUMBNAIL_METHOD_COVER, ]; - /** @var Image|MockInterface $image */ - $image = Mockery::mock( - Image::class, - [ - 'response' => $imageResponse ?? new Response(), - ] - ); + /** @var ImageInterface|MockInterface $image */ + $image = Mockery::mock(ImageInterface::class); $image->dirname = 'directory'; $image->basename = 'basename'; - $image - ->shouldReceive(...$imageMethodNames) + $image->shouldReceive(...$imageMethods) ->andReturn($image); + $image->shouldReceive('origin') + ->andReturn($imageOrigin); + + /** @var EncodedImageInterface|MockInterface $encodedImage */ + $encodedImage = Mockery::mock(EncodedImageInterface::class); + $encodedImage->shouldReceive('toFilePointer') + ->andReturn(self::FOO_RESOURCE); + $image->shouldReceive('encode') + ->andReturn($encodedImage); return $image; } + + private function getCacheFilename(int $width, int $height): string + { + return sprintf( + self::CACHE_FILE_NAME_FORMAT_RESIZED, + $this->cacheResized, + $width, + $height, + 1, + 0, + self::IMAGE_NAME + ); + } }