From d73d0f40cc244ad2541c37dcc2b8ec15b4bf5768 Mon Sep 17 00:00:00 2001 From: Reliq Date: Thu, 23 May 2024 09:11:46 +0200 Subject: [PATCH] v5 - Support Laravel ^11, upgrade to Intervention Image v3 (#26) * update composer.json * update grumphp, add pint * wip: upgrade ImageDispenser to use Invervention 3 * require php:^8.2 * update ImageDispenser * update composer scripts * fix tests --- .github/workflows/unit-tests.yml | 27 +- composer.json | 22 +- config/config.php | 4 +- ecs.php | 16 - grumphp.yml | 15 +- phpunit.xml | 21 +- pint.json | 3 + src/Concern/Guide.php | 44 +- src/Contract/ConfigProvider.php | 8 +- src/Contract/ImageDemand.php | 7 +- src/Contract/ImageDispenser.php | 11 +- src/Contract/ImageManager.php | 26 ++ src/Contract/Logger.php | 11 - src/Demand/Dummy.php | 37 +- src/Demand/ExistingImage.php | 30 +- src/Demand/Image.php | 41 +- src/Demand/Resize.php | 43 +- src/Demand/Thumbnail.php | 35 +- src/Service/ConfigProvider.php | 56 ++- src/Service/ImageDispenser.php | 150 +++---- src/Service/ImageManager.php | 34 ++ src/Service/ImageUploader.php | 23 +- src/Service/Logger.php | 12 - src/ServiceProvider.php | 85 ++-- tests/Unit/Demand/DummyTest.php | 53 +-- tests/Unit/Demand/ExistingImageTest.php | 53 ++- tests/Unit/Demand/ImageTest.php | 73 +--- tests/Unit/Demand/ResizeTest.php | 55 +-- tests/Unit/Demand/ThumbnailTest.php | 18 +- tests/Unit/Service/ImageDispenserTest.php | 506 ++++++++-------------- tests/Unit/Service/ImageUploaderTest.php | 136 ++---- tests/Unit/TestCase.php | 2 +- 32 files changed, 631 insertions(+), 1026 deletions(-) delete mode 100644 ecs.php create mode 100644 pint.json create mode 100644 src/Contract/ImageManager.php delete mode 100644 src/Contract/Logger.php create mode 100644 src/Service/ImageManager.php delete mode 100644 src/Service/Logger.php diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index a219917..fc4680e 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.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 @@ -56,7 +35,7 @@ jobs: uses: codecov/codecov-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} #required - file: ./coverage.xml #optional + file: ./build/coverage.xml #optional flags: unittests #optional name: codecov-umbrella #optional fail_ci_if_error: true #optional (default = false) diff --git a/composer.json b/composer.json index 84ca3db..ab92ffb 100644 --- a/composer.json +++ b/composer.json @@ -22,21 +22,21 @@ } ], "require": { - "php": "^7.4 || ^8.0", - "illuminate/support": "6 - 10", - "intervention/image": "^2.4", - "intervention/imagecache": "^2.0", - "reliqarts/laravel-common": "5.4 - 6", + "php": "^8.2", + "illuminate/support": "^11.1", + "intervention/image": "^3.6", + "reliqarts/laravel-common": "^8.0", "ext-json": "*", "ext-fileinfo": "*", "anhskohbo/no-captcha": "@dev" }, "require-dev": { - "orchestra/testbench": "4 - 8", - "phpro/grumphp": "^1.0", + "laravel/pint": "^1.15", + "orchestra/testbench": "^9.0", + "phpro/grumphp": "^2.5", "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.3", - "symplify/easy-coding-standard": ">=8.2" + "phpunit/phpunit": "^11.0", + "yieldstudio/grumphp-laravel-pint": "^1.0" }, "autoload": { "psr-4": { @@ -46,8 +46,8 @@ }, "scripts": { "test": "phpunit", - "test:ci": "phpunit --verbose --colors=auto --coverage-clover=coverage.xml", - "test:unit": "phpunit --testsuite=Unit --verbose --coverage-clover=coverage.xml" + "test:ci": "phpunit --colors=auto --coverage-clover=build/coverage.xml", + "test:unit": "phpunit --testsuite=Unit --verbose --coverage-clover=build/coverage.xml" }, "config": { "sort-packages": true, 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/ecs.php b/ecs.php deleted file mode 100644 index 5ce1d8a..0000000 --- a/ecs.php +++ /dev/null @@ -1,16 +0,0 @@ -import(SetList::CLEAN_CODE); - $containerConfigurator->import(SetList::PSR_12); - - $parameters = $containerConfigurator->parameters(); - $parameters->set(Option::LINE_ENDING, "\n"); - $parameters->set(Option::PATHS, [__DIR__ . '/src', __DIR__ . '/tests']); -}; diff --git a/grumphp.yml b/grumphp.yml index 29ab23d..1734b03 100644 --- a/grumphp.yml +++ b/grumphp.yml @@ -1,15 +1,12 @@ grumphp: + extensions: + - YieldStudio\GrumPHPLaravelPint\ExtensionLoader tasks: - ecs: - config: ~ - level: ~ - paths: [ ] - files_on_pre_commit: false - triggered_by: [ 'php' ] - clear-cache: false - no-progress-bar: true + laravel_pint: + config: pint.json + preset: laravel phpunit: config_file: ~ - testsuite: Unit + testsuite: ~ group: [ ] always_execute: false diff --git a/phpunit.xml b/phpunit.xml index 997326e..634a364 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,21 +1,12 @@ - - - - src - - - src/Concerns - + + - - tests - tests/Feature @@ -32,4 +23,12 @@ + + + src + + + src/Concerns + + diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..684f282 --- /dev/null +++ b/pint.json @@ -0,0 +1,3 @@ +{ + "preset": "psr12" +} diff --git a/src/Concern/Guide.php b/src/Concern/Guide.php index 3aee764..61f5b0b 100644 --- a/src/Concern/Guide.php +++ b/src/Concern/Guide.php @@ -10,7 +10,6 @@ use Illuminate\Http\Request; use ReliqArts\GuidedImage\Contract\GuidedImage; use ReliqArts\GuidedImage\Contract\ImageDispenser; -use ReliqArts\GuidedImage\Demand\Dummy; use ReliqArts\GuidedImage\Demand\Resize; use ReliqArts\GuidedImage\Demand\Thumbnail; use ReliqArts\GuidedImage\Result; @@ -36,59 +35,30 @@ public function emptyCache(ImageDispenser $imageDispenser): JsonResponse ); } - /** - * @param mixed $width - * @param mixed $height - * @param mixed $aspect - * @param mixed $upSize - */ public function resized( ImageDispenser $imageDispenser, Request $request, GuidedImage $guidedImage, - $width, - $height, - $aspect = true, - $upSize = false + mixed $width, + mixed $height, + mixed $aspect = true, + mixed $upSize = false ): Response { $demand = new Resize($request, $guidedImage, $width, $height, $aspect, $upSize); return $imageDispenser->getResizedImage($demand); } - /** - * @param mixed $method - * @param mixed $width - * @param mixed $height - */ public function thumb( ImageDispenser $imageDispenser, Request $request, GuidedImage $guidedImage, - $method, - $width, - $height + mixed $method, + mixed $width, + mixed $height ): Response { $demand = new Thumbnail($request, $guidedImage, $method, $width, $height); return $imageDispenser->getImageThumbnail($demand); } - - /** - * @param mixed $width - * @param mixed $height - * @param mixed $color - * @param mixed $fill - */ - public function dummy( - ImageDispenser $imageDispenser, - $width, - $height, - $color = null, - $fill = null - ): Response { - $demand = new Dummy($width, $height, $color, $fill); - - return $imageDispenser->getDummyImage($demand); - } } 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/ImageDemand.php b/src/Contract/ImageDemand.php index 2850090..8cdd675 100644 --- a/src/Contract/ImageDemand.php +++ b/src/Contract/ImageDemand.php @@ -12,10 +12,5 @@ public function getWidth(): ?int; public function getHeight(): ?int; - public function returnObject(): bool; - - /** - * @param mixed $value - */ - public function isValueConsideredNull($value): bool; + public function isValueConsideredNull(mixed $value): bool; } diff --git a/src/Contract/ImageDispenser.php b/src/Contract/ImageDispenser.php index 70eaf20..c28d13f 100644 --- a/src/Contract/ImageDispenser.php +++ b/src/Contract/ImageDispenser.php @@ -4,32 +4,33 @@ 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; +use RuntimeException; use Symfony\Component\HttpFoundation\Response; 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); /** * Get dummy Guided Image. * - * @return Image|Response + * @throws RuntimeException */ - public function getDummyImage(Dummy $demand); + 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 @@ +color = $color; - $this->filling = $filling; + parent::__construct($width, $height); } public function getColor(): string { return $this->isValueConsideredNull($this->color) ? self::DEFAULT_COLOR : $this->color; } - - /** - * @return mixed - */ - public function fill() - { - return $this->isValueConsideredNull($this->filling) ? null : $this->filling; - } } diff --git a/src/Demand/ExistingImage.php b/src/Demand/ExistingImage.php index 1fa92b7..b2877a8 100644 --- a/src/Demand/ExistingImage.php +++ b/src/Demand/ExistingImage.php @@ -9,29 +9,13 @@ abstract class ExistingImage extends Image { - /** - * @var Request - */ - private Request $request; - - /** - * @var GuidedImage - */ - private GuidedImage $guidedImage; - - /** - * ExistingImage constructor. - * - * @param mixed $width - * @param mixed $height - * @param mixed $returnObject - */ - public function __construct(Request $request, GuidedImage $guidedImage, $width, $height, $returnObject = null) - { - parent::__construct($width, $height, $returnObject); - - $this->request = $request; - $this->guidedImage = $guidedImage; + public function __construct( + private readonly Request $request, + private readonly GuidedImage $guidedImage, + mixed $width, + mixed $height + ) { + parent::__construct($width, $height); } final public function getRequest(): Request diff --git a/src/Demand/Image.php b/src/Demand/Image.php index 43e61ba..d71ab36 100644 --- a/src/Demand/Image.php +++ b/src/Demand/Image.php @@ -8,54 +8,21 @@ abstract class Image implements ImageDemand { - /** - * @var mixed - */ - private $width; - - /** - * @var mixed - */ - private $height; - - /** - * @var mixed - */ - private $returnObject; - - /** - * Image constructor. - * - * @param mixed $width - * @param mixed $height - * @param mixed $returnObject - */ - public function __construct($width, $height, $returnObject = null) + public function __construct(private readonly mixed $width, private readonly mixed $height) { - $this->width = $width; - $this->height = $height; - $this->returnObject = $returnObject; } final public function getWidth(): ?int { - return $this->isValueConsideredNull($this->width) ? null : (int)$this->width; + return $this->isValueConsideredNull($this->width) ? null : (int) $this->width; } final public function getHeight(): ?int { - return $this->isValueConsideredNull($this->height) ? null : (int)$this->height; - } - - final public function returnObject(): bool - { - return !$this->isValueConsideredNull($this->returnObject); + return $this->isValueConsideredNull($this->height) ? null : (int) $this->height; } - /** - * @param mixed $value - */ - final public function isValueConsideredNull($value): bool + final public function isValueConsideredNull(mixed $value): bool { return in_array($value, static::NULLS, true); } diff --git a/src/Demand/Resize.php b/src/Demand/Resize.php index 5cb7018..45515e9 100644 --- a/src/Demand/Resize.php +++ b/src/Demand/Resize.php @@ -11,47 +11,30 @@ final class Resize extends ExistingImage { public const ROUTE_TYPE_NAME = 'resize'; - /** - * @var mixed - */ - private $maintainAspectRatio; - - /** - * @var mixed - */ - private $allowUpSizing; - - /** - * Resized constructor. - * - * @param mixed $width - * @param mixed $height - * @param mixed $aspect - * @param mixed $upSize - * @param mixed $returnObject - */ public function __construct( Request $request, GuidedImage $guidedImage, - $width, - $height, - $aspect = true, - $upSize = null, - $returnObject = null + mixed $width, + mixed $height, + private readonly mixed $maintainAspectRatio = true, + private readonly mixed $allowUpSizing = null, + private readonly mixed $returnObject = false ) { - parent::__construct($request, $guidedImage, $width, $height, $returnObject); - - $this->maintainAspectRatio = $aspect; - $this->allowUpSizing = $upSize; + parent::__construct($request, $guidedImage, $width, $height); } public function maintainAspectRatio(): bool { - return !$this->isValueConsideredNull($this->maintainAspectRatio); + return ! $this->isValueConsideredNull($this->maintainAspectRatio); } public function allowUpSizing(): bool { - return !$this->isValueConsideredNull($this->allowUpSizing); + return ! $this->isValueConsideredNull($this->allowUpSizing); + } + + public function returnObject(): bool + { + return ! $this->isValueConsideredNull($this->returnObject); } } diff --git a/src/Demand/Thumbnail.php b/src/Demand/Thumbnail.php index 187c384..0f478d7 100644 --- a/src/Demand/Thumbnail.php +++ b/src/Demand/Thumbnail.php @@ -12,32 +12,22 @@ final class Thumbnail extends ExistingImage public const ROUTE_TYPE_NAME = 'thumb'; private const METHOD_CROP = 'crop'; + + private const METHOD_COVER = 'cover'; + private const METHOD_FIT = 'fit'; - private const METHODS = [self::METHOD_CROP, self::METHOD_FIT]; - /** - * @var string - */ - private string $method; + private const METHODS = [self::METHOD_CROP, self::METHOD_COVER, self::METHOD_FIT]; - /** - * Thumbnail constructor. - * - * @param mixed $width - * @param mixed $height - * @param mixed $returnObject - */ public function __construct( Request $request, GuidedImage $guidedImage, - string $method, + private readonly string $method, $width, $height, - $returnObject = null + private readonly bool $returnObject = false ) { - parent::__construct($request, $guidedImage, $width, $height, $returnObject); - - $this->method = $method; + parent::__construct($request, $guidedImage, $width, $height); } /** @@ -45,6 +35,12 @@ public function __construct( */ public function getMethod(): string { + // since intervention/image v3; fit() was replaced by cover() and other methods + // see https://image.intervention.io/v3/introduction/upgrade + if ($this->method === self::METHOD_FIT) { + return self::METHOD_COVER; + } + return $this->method; } @@ -52,4 +48,9 @@ public function isValid(): bool { return in_array($this->method, self::METHODS, true); } + + public function returnObject(): bool + { + return $this->returnObject; + } } diff --git a/src/Service/ConfigProvider.php b/src/Service/ConfigProvider.php index 6959114..5f886a2 100644 --- a/src/Service/ConfigProvider.php +++ b/src/Service/ConfigProvider.php @@ -10,46 +10,84 @@ final class ConfigProvider implements ConfigProviderContract { private const CONFIG_KEY_ALLOWED_EXTENSIONS = 'allowed_extensions'; + private const CONFIG_KEY_ROUTES_CONTROLLERS = 'routes.controllers'; + private const CONFIG_KEY_ROUTES_PREFIX = 'routes.prefix'; + private const CONFIG_KEY_GUIDED_MODEL = 'model'; + private const CONFIG_KEY_GUIDED_MODEL_NAMESPACE = 'model_namespace'; + private const CONFIG_KEY_ROUTES_BINDINGS_WITH_GROUP = 'routes.bindings.%s'; + private const CONFIG_KEY_IMAGE_RULES = 'rules'; + private const CONFIG_KEY_IMAGES_TABLE = 'database.image_table'; + private const CONFIG_KEY_IMAGEABLES_TABLE = 'database.imageables_table'; + private const CONFIG_KEY_STORAGE_UPLOAD_DIRECTORY = 'storage.upload_dir'; + private const CONFIG_KEY_STORAGE_CACHE_DISK = 'storage.cache_disk'; + private const CONFIG_KEY_STORAGE_UPLOAD_DISK = 'storage.upload_disk'; + private const CONFIG_KEY_STORAGE_CACHE_DIR = 'storage.cache_dir'; + private const CONFIG_KEY_STORAGE_CACHE_SUB_DIR_RESIZED = 'storage.cache_sub_dir_resized'; + private const CONFIG_KEY_STORAGE_CACHE_SUB_DIR_THUMBS = 'storage.cache_sub_dir_thumbs'; + private const CONFIG_KEY_STORAGE_GENERATE_UPLOAD_DATE_SUB_DIRECTORIES = 'storage.generate_upload_date_sub_directories'; + private const CONFIG_KEY_HEADERS_CACHE_DAYS = 'headers.cache_days'; + private const CONFIG_KEY_HEADERS_ADDITIONAL = 'headers.additional'; - private const CONFIG_KEY_IMAGE_ENCODING_FORMAT = 'encoding.format'; + + private const CONFIG_KEY_IMAGE_ENCODING_MIME_TYPE = 'encoding.mime_type'; + private const CONFIG_KEY_IMAGE_ENCODING_QUALITY = 'encoding.quality'; + private const CONFIG_KEY_DISPENSER_RAW_IMAGE_FALLBACK_ENABLED = 'dispenser.raw_image_fallback_enabled'; private const DEFAULT_ALLOWED_EXTENSIONS = ['gif', 'jpg', 'jpeg', 'png']; + private const DEFAULT_ROUTES_PREFIX = 'image'; + private const DEFAULT_GUIDED_MODEL = 'Image'; + private const DEFAULT_GUIDED_MODEL_NAMESPACE = 'App\\'; + private const DEFAULT_IMAGES_TABLE = 'images'; + private const DEFAULT_IMAGEABLES_TABLE = 'imageables'; + private const DEFAULT_IMAGE_RULES = 'required|mimes:png,gif,jpeg|max:2048'; + private const DEFAULT_UPLOAD_DIRECTORY = 'uploads/images'; + private const DEFAULT_STORAGE_CACHE_DIR = 'images'; + private const DEFAULT_STORAGE_CACHE_DISK = 'local'; + private const DEFAULT_STORAGE_UPLOAD_DISK = 'public'; + private const DEFAULT_STORAGE_CACHE_SUB_DIR_THUMBS = '.thumbs'; + private const DEFAULT_STORAGE_CACHE_SUB_DIR_RESIZED = '.resized'; + private const DEFAULT_STORAGE_GENERATE_UPLOAD_DATE_SUB_DIRECTORIES = false; + private const DEFAULT_HEADER_CACHE_DAYS = 366; + private const DEFAULT_ADDITIONAL_HEADERS = []; - private const DEFAULT_IMAGE_ENCODING_FORMAT = 'png'; + + private const DEFAULT_IMAGE_ENCODING_MIME_TYPE = 'image/png'; + private const DEFAULT_IMAGE_ENCODING_QUALITY = 90; + private const DEFAULT_DISPENSER_RAW_IMAGE_FALLBACK_ENABLED = false; private const KEY_PREFIX = 'prefix'; @@ -121,7 +159,7 @@ public function getGuidedModelNamespace(bool $lowered = false): string */ public function getRouteGroupBindings(array $bindings = [], string $groupKey = self::ROUTE_GROUP_KEY_PUBLIC): array { - $defaults = self::ROUTE_GROUP_KEY_PUBLIC === $groupKey ? [self::KEY_PREFIX => $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 62f44df..c1b2ccc 100644 --- a/src/Service/ImageDispenser.php +++ b/src/Service/ImageDispenser.php @@ -9,16 +9,15 @@ use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Http\Request; use Illuminate\Http\Response; -use Intervention\Image\Constraint; -use Intervention\Image\Exception\NotReadableException; -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; @@ -29,42 +28,42 @@ final class ImageDispenser implements ImageDispenserContract { private const KEY_IMAGE_URL = 'image url'; + private const KEY_CACHE_FILE = 'cache file'; - private const RESPONSE_HTTP_OK = Response::HTTP_OK; - private const RESPONSE_HTTP_NOT_FOUND = Response::HTTP_NOT_FOUND; + + private const RESPONSE_HTTP_OK = SymfonyResponse::HTTP_OK; + + private const RESPONSE_HTTP_NOT_FOUND = SymfonyResponse::HTTP_NOT_FOUND; + 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; - private ConfigProvider $configProvider; private Filesystem $cacheDisk; + private Filesystem $uploadDisk; - private string $imageEncodingFormat; + + private string $imageEncodingMimeType; + private int $imageEncodingQuality; - private ImageManager $imageManager; - private Logger $logger; + private string $thumbsCachePath; + private string $resizedCachePath; - private FileHelper $fileHelper; - /** - * ImageDispenser constructor. - */ public function __construct( - ConfigProvider $configProvider, + private readonly ConfigProvider $configProvider, FilesystemManager $filesystemManager, - ImageManager $imageManager, - Logger $logger, - FileHelper $fileHelper + private readonly ImageManager $imageManager, + private readonly Logger $logger, + private readonly FileHelper $fileHelper ) { - $this->configProvider = $configProvider; $this->cacheDisk = $filesystemManager->disk($configProvider->getCacheDiskName()); $this->uploadDisk = $filesystemManager->disk($configProvider->getUploadDiskName()); - $this->imageManager = $imageManager; - $this->imageEncodingFormat = $configProvider->getImageEncodingFormat(); + $this->imageEncodingMimeType = $configProvider->getImageEncodingMimeType(); $this->imageEncodingQuality = $configProvider->getImageEncodingQuality(); - $this->logger = $logger; - $this->fileHelper = $fileHelper; $this->prepCacheDirectories(); } @@ -72,30 +71,23 @@ public function __construct( /** * {@inheritdoc} * - * @return Image|SymfonyResponse + * @throws RuntimeException */ - public function getDummyImage(Dummy $demand) + public function getDummyImage(Dummy $demand): ImageInterface { - $image = $this->imageManager->canvas( + return $this->imageManager->create( $demand->getWidth(), - $demand->getHeight(), - $demand->getColor() - ); - $image = $image->fill($demand->fill()); - - // Return object or actual image - return $demand->returnObject() - ? $image - : $image->response(); + $demand->getHeight() + )->fill($demand->getColor()); } /** * {@inheritdoc} * - * @return Image|SymfonyResponse|void + * @return ImageInterface|SymfonyResponse|void + * * @throws InvalidArgumentException * @throws RuntimeException - * @noinspection PhpRedundantCatchClauseInspection */ public function getResizedImage(Resize $demand) { @@ -117,18 +109,12 @@ public function getResizedImage(Resize $demand) $image = $this->makeImageWithEncoding($this->cacheDisk->path($cacheFilePath)); } else { $image = $this->makeImageWithEncoding($this->getImageFullUrl($guidedImage)); - $image->resize( - $width, - $height, - static function (Constraint $constraint) use ($demand) { - if ($demand->maintainAspectRatio()) { - $constraint->aspectRatio(); - } - if ($demand->allowUpSizing()) { - $constraint->upsize(); - } - } - ); + $sizingMethod = $demand->allowUpSizing() ? 'resize' : 'resizeDown'; + if ($demand->maintainAspectRatio()) { + $sizingMethod = $demand->allowUpSizing() ? 'scale' : 'scaleDown'; + } + + $image->{$sizingMethod}($width, $height); $image->save($this->cacheDisk->path($cacheFilePath)); } @@ -141,8 +127,8 @@ static function (Constraint $constraint) use ($demand) { self::RESPONSE_HTTP_OK, $this->getImageHeaders($cacheFilePath, $demand->getRequest(), $image) ?: [] ); - } catch (NotReadableException $exception) { - return $this->handleNotReadableException($exception, $guidedImage); + } catch (RuntimeException $exception) { + return $this->handleRuntimeException($exception, $guidedImage); } catch (FileNotFoundException $exception) { $this->logger->error( sprintf( @@ -162,7 +148,8 @@ static function (Constraint $constraint) use ($demand) { /** * {@inheritdoc} * - * @return Image|SymfonyResponse|void + * @return ImageInterface|SymfonyResponse|never + * * @throws InvalidArgumentException * @throws RuntimeException * @@ -170,7 +157,7 @@ static function (Constraint $constraint) use ($demand) { */ public function getImageThumbnail(Thumbnail $demand) { - if (!$demand->isValid()) { + if (! $demand->isValid()) { $this->logger->warning( sprintf('Invalid demand for thumbnail image. Method: %s', $demand->getMethod()), [ @@ -198,9 +185,9 @@ 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 - ->make($this->getImageFullUrl($guidedImage)) + ->read($this->getImageFullUrl($guidedImage)) ->{$method}( $width, $height @@ -218,8 +205,8 @@ public function getImageThumbnail(Thumbnail $demand) self::RESPONSE_HTTP_OK, $this->getImageHeaders($cacheFilePath, $demand->getRequest(), $image) ?: [] ); - } catch (NotReadableException $exception) { - return $this->handleNotReadableException($exception, $guidedImage); + } catch (RuntimeException $exception) { + return $this->handleRuntimeException($exception, $guidedImage); } catch (FileNotFoundException $exception) { $this->logger->error( sprintf( @@ -248,9 +235,9 @@ 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 = sprintf('%s/%s', $image->dirname, $image->basename); + $filePath = $image->origin()->filePath(); $lastModified = $this->cacheDisk->lastModified($cacheFilePath); $modifiedSince = $request->header('If-Modified-Since', ''); $etagHeader = trim($request->header('If-None-Match', '')); @@ -268,8 +255,8 @@ private function getImageHeaders(string $cacheFilePath, Request $request, Image return array_merge( $this->getDefaultHeaders(), [ - 'Content-Type' => $image->mime, - 'Content-Disposition' => sprintf('inline; filename=%s', $image->filename), + 'Content-Type' => $image->origin()->mediaType(), + 'Content-Disposition' => sprintf('inline; filename=%s', basename($image->origin()->filePath())), 'Last-Modified' => date(DATE_RFC822, $lastModified), 'Etag' => $etagFile, ] @@ -281,11 +268,11 @@ private function prepCacheDirectories(): void $this->resizedCachePath = $this->configProvider->getResizedCachePath(); $this->thumbsCachePath = $this->configProvider->getThumbsCachePath(); - if (!$this->cacheDisk->exists($this->thumbsCachePath)) { + if (! $this->cacheDisk->exists($this->thumbsCachePath)) { $this->cacheDisk->makeDirectory($this->thumbsCachePath); } - if (!$this->cacheDisk->exists($this->resizedCachePath)) { + if (! $this->cacheDisk->exists($this->resizedCachePath)) { $this->cacheDisk->makeDirectory($this->resizedCachePath); } } @@ -304,21 +291,20 @@ private function getDefaultHeaders(): array } /** - * @param mixed $data - * @param mixed ...$encoding + * @throws RuntimeException */ - private function makeImageWithEncoding($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($encoder); - return $this->imageManager - ->make($data) - ->encode(...$encoding); + return $this->imageManager->read($encodedImage->toFilePointer()); } /** @@ -330,20 +316,16 @@ private function getImageFullUrl(GuidedImage $guidedImage): string } /** - * @param NotReadableException $exception - * @param GuidedImage $guidedImage - * - * @return BinaryFileResponse * @throws RuntimeException */ - private function handleNotReadableException( - NotReadableException $exception, + private function handleRuntimeException( + RuntimeException $exception, GuidedImage $guidedImage ): BinaryFileResponse { $errorMessage = 'Intervention image creation failed with NotReadableException;'; $context = ['imageUrl' => $this->getImageFullUrl($guidedImage)]; - if (!$this->configProvider->isRawImageFallbackEnabled()) { + if (! $this->configProvider->isRawImageFallbackEnabled()) { $this->logger->error( sprintf('%s %s. Raw image fallback is disabled.', $errorMessage, $exception->getMessage()), $context 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 347d729..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; @@ -23,17 +23,27 @@ final class ImageUploader implements ImageUploaderContract { private const ERROR_INVALID_IMAGE = 'Invalid image size or type.'; + private const KEY_FILE = 'file'; + private const MESSAGE_IMAGE_REUSED = 'Image reused.'; + private const UPLOAD_DATE_SUB_DIRECTORIES_PATTERN = 'Y/m/d/H/i'; + private const UPLOAD_VISIBILITY = Filesystem::VISIBILITY_PUBLIC; + private const TEMP_FILE_PREFIX = 'LGI_'; private ConfigProvider $configProvider; + private Filesystem $uploadDisk; + private FileHelper $fileHelper; + private ValidationFactory $validationFactory; + private GuidedImage $guidedImage; + private Logger $logger; /** @@ -62,7 +72,7 @@ public function __construct( */ public function upload(UploadedFile $imageFile, bool $isUrlUpload = false): Result { - if (!$this->validate($imageFile, $isUrlUpload)) { + if (! $this->validate($imageFile, $isUrlUpload)) { return new Result(false, self::ERROR_INVALID_IMAGE); } @@ -73,10 +83,9 @@ public function upload(UploadedFile $imageFile, bool $isUrlUpload = false): Resu ->where(UploadedImage::KEY_SIZE, $uploadedImage->getSize()) ->first(); - if (!empty($existing)) { + if (! empty($existing)) { $result = new Result(true); - /** @noinspection PhpIncompatibleReturnTypeInspection */ return $result ->addMessage(self::MESSAGE_IMAGE_REUSED) ->setExtra($existing); @@ -137,7 +146,7 @@ public function uploadFromUrl(string $url): Result $this->fileHelper->unlink($tempFile); return $result; - } catch (FileNotFoundException | FileException $exception) { + } catch (FileNotFoundException|FileException $exception) { throw UrlUploadFailed::forUrl($url, $exception); } } @@ -158,7 +167,7 @@ private function validatePostUpload(UploadedFile $imageFile): bool [self::KEY_FILE => $this->configProvider->getImageRules()] ); - return $this->validateFileExtension($imageFile) && !$validator->fails(); + return $this->validateFileExtension($imageFile) && ! $validator->fails(); } private function validateFileExtension(UploadedFile $imageFile): bool @@ -174,7 +183,7 @@ private function getUploadDestination(): string { $destination = $this->configProvider->getUploadDirectory(); - if (!$this->configProvider->generateUploadDateSubDirectories()) { + if (! $this->configProvider->generateUploadDateSubDirectories()) { return $destination; } 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..a8a9e64 100644 --- a/tests/Unit/Demand/DummyTest.php +++ b/tests/Unit/Demand/DummyTest.php @@ -4,26 +4,22 @@ namespace ReliqArts\GuidedImage\Tests\Unit\Demand; +use Exception; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use ReliqArts\GuidedImage\Demand\Dummy; /** - * Class DummyTest. - * - * @coversDefaultClass \ReliqArts\GuidedImage\Demand\Dummy - * * @internal */ +#[CoversClass(Dummy::class)] final class DummyTest extends TestCase { /** - * @dataProvider colorDataProvider - * @covers ::__construct - * @covers ::getColor - * @covers ::isValueConsideredNull - * - * @param mixed $color + * @throws Exception */ - public function testGetColor($color, string $expectedResult): void + #[DataProvider('colorDataProvider')] + public function testGetColor(mixed $color, string $expectedResult): void { $demand = new Dummy( self::DIMENSION, @@ -34,27 +30,7 @@ public function testGetColor($color, string $expectedResult): void self::assertSame($expectedResult, $demand->getColor()); } - /** - * @dataProvider fillDataProvider - * @covers ::__construct - * @covers ::fill - * @covers ::isValueConsideredNull - * - * @param mixed $fill - */ - public function testFill($fill, ?string $expectedResult): void - { - $demand = new Dummy( - self::DIMENSION, - self::DIMENSION, - null, - $fill - ); - - self::assertSame($expectedResult, $demand->fill()); - } - - public function colorDataProvider(): array + public static function colorDataProvider(): array { return [ ['0f0', '0f0'], @@ -66,17 +42,4 @@ public function colorDataProvider(): array [null, Dummy::DEFAULT_COLOR], ]; } - - public function fillDataProvider(): array - { - return [ - ['0f0', '0f0'], - ['n', null], - ['_', null], - ['false', null], - ['null', null], - [false, null], - [null, null], - ]; - } } diff --git a/tests/Unit/Demand/ExistingImageTest.php b/tests/Unit/Demand/ExistingImageTest.php index 6ea852c..c757532 100644 --- a/tests/Unit/Demand/ExistingImageTest.php +++ b/tests/Unit/Demand/ExistingImageTest.php @@ -4,54 +4,51 @@ namespace ReliqArts\GuidedImage\Tests\Unit\Demand; -use PHPUnit\Framework\MockObject\MockObject; +use Exception; +use PHPUnit\Framework\Attributes\CoversClass; use ReliqArts\GuidedImage\Demand\ExistingImage; +use ReliqArts\GuidedImage\Demand\Thumbnail; /** - * Class ExistingImageTest. - * - * @coversDefaultClass \ReliqArts\GuidedImage\Demand\ExistingImage - * * @internal */ +#[CoversClass(ExistingImage::class)] final class ExistingImageTest extends TestCase { /** - * @covers ::__construct - * @covers ::getRequest + * @throws Exception */ public function testGetRequest(): void { - $demand = $this->getExistingImageDemand(self::DIMENSION, self::DIMENSION, null); - - self::assertSame($this->request->reveal(), $demand->getRequest()); + self::assertSame( + $this->request->reveal(), + $this->getExistingImageDemand()->getRequest() + ); } /** - * @covers ::__construct - * @covers ::getGuidedImage + * @throws Exception */ public function testGetGuidedImage(): void { - $demand = $this->getExistingImageDemand(self::DIMENSION, self::DIMENSION, null); - - self::assertSame($this->guidedImage->reveal(), $demand->getGuidedImage()); + self::assertSame( + $this->guidedImage->reveal(), + $this->getExistingImageDemand() + ->getGuidedImage() + ); } /** - * @param $width - * @param $height - * @param null $returnObject - * - * @return ExistingImage|MockObject + * @throws Exception */ - private function getExistingImageDemand( - $width, - $height, - $returnObject = null - ): MockObject { - return $this->getMockBuilder(ExistingImage::class) - ->setConstructorArgs([$this->request->reveal(), $this->guidedImage->reveal(), $width, $height, $returnObject]) - ->getMockForAbstractClass(); + private function getExistingImageDemand(): ExistingImage + { + return new Thumbnail( + $this->request->reveal(), + $this->guidedImage->reveal(), + 'crop', + self::DIMENSION, + self::DIMENSION + ); } } diff --git a/tests/Unit/Demand/ImageTest.php b/tests/Unit/Demand/ImageTest.php index 4f58b17..dca6cc5 100644 --- a/tests/Unit/Demand/ImageTest.php +++ b/tests/Unit/Demand/ImageTest.php @@ -4,64 +4,41 @@ namespace ReliqArts\GuidedImage\Tests\Unit\Demand; -use PHPUnit\Framework\MockObject\MockObject; +use Exception; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use ReliqArts\GuidedImage\Demand\Dummy; use ReliqArts\GuidedImage\Demand\Image; /** - * Class ImageTest. - * - * @coversDefaultClass \ReliqArts\GuidedImage\Demand\Image - * * @internal */ +#[CoversClass(Image::class)] final class ImageTest extends TestCase { /** - * @dataProvider widthAndHeightDataProvider - * @covers ::__construct - * @covers ::getWidth - * @covers ::isValueConsideredNull - * - * @param mixed $width + * @throws Exception */ + #[DataProvider('widthAndHeightDataProvider')] public function testGetWidth($width, ?int $expectedResult): void { - $demand = $this->getImageDemand($width, self::DIMENSION, null); + $demand = $this->getImageDemand($width, self::DIMENSION); self::assertSame($expectedResult, $demand->getWidth()); } /** - * @dataProvider widthAndHeightDataProvider - * @covers ::__construct - * @covers ::getHeight - * @covers ::isValueConsideredNull - * - * @param mixed $height + * @throws Exception */ + #[DataProvider('widthAndHeightDataProvider')] public function testGetHeight($height, ?int $expectedResult): void { - $demand = $this->getImageDemand(self::DIMENSION, $height, null); + $demand = $this->getImageDemand(self::DIMENSION, $height); self::assertSame($expectedResult, $demand->getHeight()); } - /** - * @dataProvider imageFlagDataProvider - * @covers ::__construct - * @covers ::isValueConsideredNull - * @covers ::returnObject - * - * @param mixed $returnObject - */ - public function testReturnObject($returnObject, bool $expectedResult): void - { - $demand = $this->getImageDemand(self::DIMENSION, self::DIMENSION, $returnObject); - - self::assertSame($expectedResult, $demand->returnObject()); - } - - public function widthAndHeightDataProvider(): array + public static function widthAndHeightDataProvider(): array { return [ [200, 200], @@ -75,30 +52,8 @@ public function widthAndHeightDataProvider(): array ]; } - public function imageFlagDataProvider(): array - { - return [ - [true, true], - ['n', false], - ['_', false], - ['false', false], - ['null', false], - [false, false], - [null, false], - ]; - } - - /** - * @param $width - * @param $height - * @param null $returnObject - * - * @return Image|MockObject - */ - private function getImageDemand($width, $height, $returnObject = null): MockObject + private function getImageDemand($width, $height): Image { - return $this->getMockBuilder(Image::class) - ->setConstructorArgs([$width, $height, $returnObject]) - ->getMockForAbstractClass(); + return new Dummy($width, $height); } } diff --git a/tests/Unit/Demand/ResizeTest.php b/tests/Unit/Demand/ResizeTest.php index 1c96fed..e7329a1 100644 --- a/tests/Unit/Demand/ResizeTest.php +++ b/tests/Unit/Demand/ResizeTest.php @@ -6,26 +6,21 @@ namespace ReliqArts\GuidedImage\Tests\Unit\Demand; +use Exception; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use ReliqArts\GuidedImage\Demand\Resize; /** - * Class ImageTest. - * - * @coversDefaultClass \ReliqArts\GuidedImage\Demand\Resize - * * @internal */ +#[CoversClass(Resize::class)] final class ResizeTest extends TestCase { /** - * @dataProvider resizeDimensionDataProvider - * @covers ::__construct - * @covers ::getWidth - * @covers ::isValueConsideredNull - * @covers \ReliqArts\GuidedImage\Demand\Image::__construct - * - * @param mixed $width + * @throws Exception */ + #[DataProvider('resizeDimensionDataProvider')] public function testGetWidth($width, ?int $expectedResult): void { $demand = new Resize( @@ -39,14 +34,9 @@ public function testGetWidth($width, ?int $expectedResult): void } /** - * @dataProvider resizeDimensionDataProvider - * @covers ::__construct - * @covers ::getHeight - * @covers ::isValueConsideredNull - * @covers \ReliqArts\GuidedImage\Demand\Image::__construct - * - * @param mixed $height + * @throws Exception */ + #[DataProvider('resizeDimensionDataProvider')] public function testGetHeight($height, ?int $expectedResult): void { $demand = new Resize( @@ -60,14 +50,9 @@ public function testGetHeight($height, ?int $expectedResult): void } /** - * @dataProvider resizeFlagDataProvider - * @covers ::__construct - * @covers ::isValueConsideredNull - * @covers ::maintainAspectRatio - * @covers \ReliqArts\GuidedImage\Demand\Image::__construct - * - * @param mixed $maintainAspectRatio + * @throws Exception */ + #[DataProvider('resizeFlagDataProvider')] public function testMaintainAspectRatio($maintainAspectRatio, bool $expectedResult): void { $demand = new Resize( @@ -82,13 +67,9 @@ public function testMaintainAspectRatio($maintainAspectRatio, bool $expectedResu } /** - * @dataProvider resizeFlagDataProvider - * @covers ::__construct - * @covers ::allowUpSizing - * @covers \ReliqArts\GuidedImage\Demand\Image::__construct - * - * @param mixed $upSize + * @throws Exception */ + #[DataProvider('resizeFlagDataProvider')] public function testAllowUpSizing($upSize, bool $expectedResult): void { $demand = new Resize( @@ -104,13 +85,9 @@ public function testAllowUpSizing($upSize, bool $expectedResult): void } /** - * @dataProvider resizeFlagDataProvider - * @covers ::__construct - * @covers ::returnObject - * @covers \ReliqArts\GuidedImage\Demand\Image::__construct - * - * @param mixed $returnObject + * @throws Exception */ + #[DataProvider('resizeFlagDataProvider')] public function testReturnObject($returnObject, bool $expectedResult): void { $demand = new Resize( @@ -126,7 +103,7 @@ public function testReturnObject($returnObject, bool $expectedResult): void self::assertSame($expectedResult, $demand->returnObject()); } - public function resizeDimensionDataProvider(): array + public static function resizeDimensionDataProvider(): array { return [ [self::DIMENSION, self::DIMENSION], @@ -139,7 +116,7 @@ public function resizeDimensionDataProvider(): array ]; } - public function resizeFlagDataProvider(): array + public static function resizeFlagDataProvider(): array { return [ [true, true], diff --git a/tests/Unit/Demand/ThumbnailTest.php b/tests/Unit/Demand/ThumbnailTest.php index e511674..072fae9 100644 --- a/tests/Unit/Demand/ThumbnailTest.php +++ b/tests/Unit/Demand/ThumbnailTest.php @@ -4,22 +4,21 @@ namespace ReliqArts\GuidedImage\Tests\Unit\Demand; +use Exception; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use ReliqArts\GuidedImage\Demand\Thumbnail; /** - * Class ThumbnailTest. - * - * @coversDefaultClass \ReliqArts\GuidedImage\Demand\Thumbnail - * * @internal */ +#[CoversClass(Thumbnail::class)] final class ThumbnailTest extends TestCase { /** - * @dataProvider isValidDataProvider - * @covers ::__construct - * @covers ::isValid + * @throws Exception */ + #[DataProvider('isValidDataProvider')] public function testIsValid(string $method, bool $expectedResult): void { $demand = new Thumbnail( @@ -33,11 +32,12 @@ public function testIsValid(string $method, bool $expectedResult): void self::assertSame($expectedResult, $demand->isValid()); } - public function isValidDataProvider(): array + public static function isValidDataProvider(): array { return [ - ['fit', true], ['crop', true], + ['cover', true], + ['fit', true], ['grab', false], ['spook', false], ]; diff --git a/tests/Unit/Service/ImageDispenserTest.php b/tests/Unit/Service/ImageDispenserTest.php index 2ae7dd1..112fae1 100644 --- a/tests/Unit/Service/ImageDispenserTest.php +++ b/tests/Unit/Service/ImageDispenserTest.php @@ -1,120 +1,117 @@ 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); @@ -122,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 @@ -178,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); @@ -205,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() @@ -214,8 +214,7 @@ protected function setUp(): void } /** - * @covers ::__construct - * @covers ::emptyCache + * @throws RuntimeException */ public function testEmptyCache(): void { @@ -234,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 { @@ -312,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) @@ -337,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); @@ -352,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 { @@ -372,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) @@ -399,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); @@ -412,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) @@ -457,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 { @@ -499,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) @@ -523,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 @@ -548,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 { @@ -580,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); @@ -611,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 ); @@ -656,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) @@ -717,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()); @@ -732,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 { @@ -773,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 @@ -794,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 { @@ -813,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 ); @@ -838,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( @@ -857,34 +707,54 @@ public function testGetImageThumbnailWhenImageRetrievalFails(): void $this->subject->getImageThumbnail($demand); } - /** - * @param Response $imageResponse - * - * @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 + ); + } } diff --git a/tests/Unit/Service/ImageUploaderTest.php b/tests/Unit/Service/ImageUploaderTest.php index fff2008..b494d4b 100644 --- a/tests/Unit/Service/ImageUploaderTest.php +++ b/tests/Unit/Service/ImageUploaderTest.php @@ -1,9 +1,9 @@ configProvider = $this->prophesize(ConfigProvider::class); - $this->filesystemManager = $this->prophesize(FilesystemManager::class); + $filesystemManager = $this->prophesize(FilesystemManager::class); $this->fileHelper = $this->prophesize(FileHelper::class); $this->validationFactory = $this->prophesize(ValidationFactory::class); $this->validator = $this->prophesize(Validator::class); @@ -138,7 +109,7 @@ protected function setUp(): void ->shouldBeCalledTimes(1) ->willReturn(1); - $this->filesystemManager + $filesystemManager ->disk(self::UPLOAD_DISK_NAME) ->shouldBeCalledTimes(1) ->willReturn($this->uploadDisk); @@ -182,7 +153,7 @@ protected function setUp(): void $this->subject = new ImageUploader( $this->configProvider->reveal(), - $this->filesystemManager->reveal(), + $filesystemManager->reveal(), $this->fileHelper->reveal(), $this->validationFactory->reveal(), $this->guidedImage->reveal(), @@ -191,17 +162,6 @@ protected function setUp(): void } /** - * @covers ::__construct - * @covers ::getUploadDestination - * @covers ::upload - * @covers ::validate - * @covers ::validateFileExtension - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::toArray - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getDestination - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getFile - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getFilename - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getSize - * * @throws Exception */ public function testUpload(): void @@ -227,16 +187,6 @@ function ($argument) { } /** - * @covers ::__construct - * @covers ::getUploadDestination - * @covers ::upload - * @covers ::validate - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::toArray - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getDestination - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getFile - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getFilename - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getSize - * * @throws Exception */ public function testUploadWhenFileShouldBeReused(): void @@ -276,10 +226,6 @@ public function testUploadWhenFileShouldBeReused(): void } /** - * @covers ::__construct - * @covers ::upload - * @covers ::validate - * * @throws Exception */ public function testUploadWhenValidationFails(): void @@ -332,15 +278,6 @@ public function testUploadWhenValidationFails(): void } /** - * @covers ::__construct - * @covers ::upload - * @covers ::validate - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::toArray - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getDestination - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getFile - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getFilename - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getSize - * * @throws Exception */ public function testUploadWhenFileUploadFails(): void @@ -371,18 +308,6 @@ public function testUploadWhenFileUploadFails(): void } /** - * @covers ::__construct - * @covers ::uploadFromUrl - * @covers ::getUploadDestination - * @covers ::upload - * @covers ::validate - * @covers ::validateFileExtension - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::toArray - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getDestination - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getFile - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getFilename - * @covers \ReliqArts\GuidedImage\Model\UploadedImage::getSize - * * @throws Exception */ public function testUploadFromUrl(): void @@ -458,23 +383,18 @@ function ($argument) { self::assertTrue($result->isSuccess()); } - /** - * @return MockInterface|UploadedFile - */ - private function getUploadedFileMock( - string $filename = 'myimage', - string $mimeType = 'image/jpeg', - string $extension = 'jpg', - int $size = 80000 - ): UploadedFile { + private function getUploadedFileMock(): UploadedFile + { + $filename = 'my-image'; + return Mockery::mock( UploadedFile::class, [ 'getFilename' => $filename, 'getClientOriginalName' => $filename, - 'getClientOriginalExtension' => $extension, - 'getMimeType' => $mimeType, - 'getSize' => $size, + 'getClientOriginalExtension' => 'jpg', + 'getMimeType' => 'image/jpeg', + 'getSize' => 80000, 'getRealPath' => $filename, 'move' => null, ] diff --git a/tests/Unit/TestCase.php b/tests/Unit/TestCase.php index 8d3fa44..bf2fc20 100644 --- a/tests/Unit/TestCase.php +++ b/tests/Unit/TestCase.php @@ -17,6 +17,6 @@ protected function setUp(): void { parent::setUp(); - $this->setGroups(array_merge($this->getGroups(), [self::GROUP])); + $this->setGroups(array_merge($this->groups(), [self::GROUP])); } }