From c492dbd6ed4b2ac7c4c834d91b5cc8c79e3ea0c4 Mon Sep 17 00:00:00 2001 From: pataar Date: Wed, 31 Jan 2024 21:08:39 +0100 Subject: [PATCH] Upgrade minimum PHP version and dependencies --- .github/workflows/main.yml | 28 ++++++++--------- .gitignore | 1 + README.md | 2 +- composer.json | 6 ++-- docker/gd.Dockerfile | 8 +++-- docker/gmp.Dockerfile | 8 +++-- docker/imagick.Dockerfile | 11 ++++--- phpunit.xml | 30 +++++++----------- src/Hash.php | 6 ++-- src/ImageHash.php | 26 +++++++--------- src/Implementations/AverageHash.php | 7 ++--- src/Implementations/BlockHash.php | 26 ++++++---------- src/Implementations/DifferenceHash.php | 9 ++---- src/Implementations/PerceptualHash.php | 12 ++------ tests/CompatibilityTest.php | 27 +++++++++++------ tests/ImageHashTest.php | 9 ++---- tests/ImplementationTest.php | 42 ++++++++------------------ 17 files changed, 111 insertions(+), 147 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a2b758a..60a0847 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,18 +7,18 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: ["7.1", "7.2", "7.3", "7.4", "8.0"] - extensions: ["gd", "imagick"] - name: PHP ${{ matrix.php-versions }} - ${{ matrix.extensions }} + php-version: ["8.1", "8.2", "8.3"] + extension: ["gd, :imagick", "imagick, :gd"] # the ':" prefix is used to unload the other extension + name: PHP ${{ matrix.php-version }} - ${{ matrix.extension }} steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-versions }} - extensions: ${{ matrix.extensions }} + php-version: ${{ matrix.php-version }} + extensions: ${{ matrix.extension }} coverage: xdebug - name: Check environment @@ -27,15 +27,15 @@ jobs: composer --version - name: Get composer cache directory - id: composercache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: - path: ${{ steps.composercache.outputs.dir }} - key: ${{ matrix.os }}-composer-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.os }}-composer-${{ matrix.php-versions }}- + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ matrix.os }}-composer-${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ matrix.os }}-composer-${{ matrix.php-version }}- - name: Install dependencies run: composer install --prefer-dist @@ -49,8 +49,8 @@ jobs: - name: Upload coverage to Codecov env: OS: ${{ matrix.os }} - PHP: ${{ matrix.php-versions }} - uses: codecov/codecov-action@v1 + PHP: ${{ matrix.php-version }} + uses: codecov/codecov-action@v3 with: file: build/logs/clover.xml env_vars: OS,PHP diff --git a/.gitignore b/.gitignore index 5a22558..725f983 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /vendor composer.lock +.phpunit.cache .phpunit.result.cache \ No newline at end of file diff --git a/README.md b/README.md index 113816c..245b663 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ This code was inspired/based on: Requirements ------------ - - PHP 7.1 or higher + - PHP 8.1 or higher - The [gd](http://php.net/manual/en/book.image.php) or [imagick](http://php.net/manual/en/book.imagick.php) extension - Optionally, install the [GMP](http://php.net/manual/en/book.gmp.php) extension for faster fingerprint comparisons diff --git a/composer.json b/composer.json index 2ad770d..4dba835 100644 --- a/composer.json +++ b/composer.json @@ -14,11 +14,11 @@ } ], "require": { - "php": "^7.1|^8.0", - "intervention/image": "^2.4" + "php": "^8.1", + "intervention/image": "^3.3" }, "require-dev": { - "phpunit/phpunit": "^7|^8|^9", + "phpunit/phpunit": "^10", "php-coveralls/php-coveralls": "^2.0" }, "suggest": { diff --git a/docker/gd.Dockerfile b/docker/gd.Dockerfile index efb205c..792829b 100644 --- a/docker/gd.Dockerfile +++ b/docker/gd.Dockerfile @@ -1,9 +1,11 @@ -ARG PHP_VERSION=7.2 -ARG COMPOSER_VERSION=1.8 +ARG PHP_VERSION=8.1 +ARG COMPOSER_VERSION=2 -FROM composer:${COMPOSER_VERSION} +FROM composer:${COMPOSER_VERSION} as composer FROM php:${PHP_VERSION}-cli +COPY --from=composer /usr/bin/composer /usr/bin/composer + RUN apt-get update && \ apt-get install libpng-dev libjpeg-dev --no-install-recommends -qy && \ rm -rf /var/lib/apt/lists/* && \ diff --git a/docker/gmp.Dockerfile b/docker/gmp.Dockerfile index 627b57c..dd4ed48 100644 --- a/docker/gmp.Dockerfile +++ b/docker/gmp.Dockerfile @@ -1,9 +1,11 @@ -ARG PHP_VERSION=7.2 -ARG COMPOSER_VERSION=1.8 +ARG PHP_VERSION=8.1 +ARG COMPOSER_VERSION=2 -FROM composer:${COMPOSER_VERSION} +FROM composer:${COMPOSER_VERSION} as composer FROM php:${PHP_VERSION}-cli +COPY --from=composer /usr/bin/composer /usr/bin/composer + RUN apt-get update && \ apt-get install libgmp-dev libpng-dev libjpeg-dev --no-install-recommends -qy && \ rm -rf /var/lib/apt/lists/* && \ diff --git a/docker/imagick.Dockerfile b/docker/imagick.Dockerfile index 723fb96..72638e1 100644 --- a/docker/imagick.Dockerfile +++ b/docker/imagick.Dockerfile @@ -1,11 +1,14 @@ -ARG PHP_VERSION=7.2 -ARG COMPOSER_VERSION=1.8 +ARG PHP_VERSION=8.1 +ARG COMPOSER_VERSION=2 -FROM composer:${COMPOSER_VERSION} +FROM composer:${COMPOSER_VERSION} as composer FROM php:${PHP_VERSION}-cli +COPY --from=composer /usr/bin/composer /usr/bin/composer + RUN apt-get update && \ apt-get install libmagickwand-dev --no-install-recommends -qy && \ rm -rf /var/lib/apt/lists/* && \ ln -s /usr/include/x86_64-linux-gnu/gmp.h /usr/include/gmp.h && \ - pecl install imagick && docker-php-ext-enable imagick + pecl install imagick && \ + docker-php-ext-enable imagick diff --git a/phpunit.xml b/phpunit.xml index 18ee86d..6443448 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,21 +1,13 @@ - - - - ./tests/ - - - - - src/ - - + + + + ./tests/ + + + + + src/ + + diff --git a/src/Hash.php b/src/Hash.php index 112bd5d..a03005f 100644 --- a/src/Hash.php +++ b/src/Hash.php @@ -11,14 +11,14 @@ class Hash implements JsonSerializable * * @var string */ - protected $binaryValue; + protected string $binaryValue; /** * Hash will be split in several integers if longer than PHP_INT_SIZE * * @var int[]|null */ - protected $integers = null; + protected ?array $integers = null; /** * @param string $binaryValue @@ -35,7 +35,7 @@ private function __construct(string $binaryValue) * * @return self */ - public static function fromBits($bits): self + public static function fromBits(array|string $bits): self { if (\is_array($bits)) { $bits = implode('', $bits); diff --git a/src/ImageHash.php b/src/ImageHash.php index 4567750..9c59808 100644 --- a/src/ImageHash.php +++ b/src/ImageHash.php @@ -1,5 +1,7 @@ driver->make($image); + $image = $this->driver->read($image); return $this->implementation->hash($image); } @@ -43,7 +39,7 @@ public function hash($image): Hash * @param mixed $resource2 * @return int */ - public function compare($resource1, $resource2): int + public function compare(mixed $resource1, mixed $resource2): int { $hash1 = $this->hash($resource1); $hash2 = $this->hash($resource2); @@ -58,7 +54,7 @@ public function distance(Hash $hash1, Hash $hash2): int protected function createResource(string $data): Image { - return $this->driver->make($data); + return $this->driver->read($data); } protected function defaultImplementation(): Implementation @@ -69,11 +65,11 @@ protected function defaultImplementation(): Implementation protected function defaultDriver(): ImageManager { if (extension_loaded('imagick')) { - return new ImageManager(['driver' => 'imagick']); + return new ImageManager(new ImagickDriver()); } - + if (extension_loaded('gd')) { - return new ImageManager(['driver' => 'gd']); + return new ImageManager(new GdDriver()); } throw new RuntimeException('Please install GD or ImageMagick'); diff --git a/src/Implementations/AverageHash.php b/src/Implementations/AverageHash.php index d84a5a3..b500cc0 100644 --- a/src/Implementations/AverageHash.php +++ b/src/Implementations/AverageHash.php @@ -6,10 +6,7 @@ class AverageHash implements Implementation { - /** - * @var int - */ - protected $size; + protected int $size; public function __construct(int $size = 8) { @@ -25,7 +22,7 @@ public function hash(Image $image): Hash $pixels = []; for ($y = 0; $y < $this->size; $y++) { for ($x = 0; $x < $this->size; $x++) { - $rgb = $resized->pickColor($x, $y); + $rgb = $resized->pickColor($x, $y)->toArray(); $pixels[] = (int) floor(($rgb[0] * 0.299) + ($rgb[1] * 0.587) + ($rgb[2] * 0.114)); } } diff --git a/src/Implementations/BlockHash.php b/src/Implementations/BlockHash.php index a0fdb3b..602523a 100644 --- a/src/Implementations/BlockHash.php +++ b/src/Implementations/BlockHash.php @@ -19,15 +19,9 @@ class BlockHash implements Implementation */ const QUICK = 'quick'; - /** - * @var string - */ - protected $mode; + protected string $mode; - /** - * @var int - */ - protected $size; + protected int $size; public function __construct(int $size = 16, $mode = self::PRECISE) { @@ -54,8 +48,8 @@ public function hash(Image $image): Hash private function even(Image $image): Hash { - $width = $image->getWidth(); - $height = $image->getHeight(); + $width = $image->width(); + $height = $image->height(); $blocksizeX = (int) floor($width / $this->size); $blocksizeY = (int) floor($height / $this->size); @@ -69,7 +63,7 @@ private function even(Image $image): Hash for ($ix = 0; $ix < $blocksizeX; $ix++) { $cx = $x * $blocksizeX + $ix; $cy = $y * $blocksizeY + $iy; - $rgb = $image->pickColor($cx, $cy); + $rgb = $image->pickColor($cx, $cy)->toArray(); $value += $rgb[0] + $rgb[1] + $rgb[2]; } } @@ -83,8 +77,8 @@ private function even(Image $image): Hash private function uneven(Image $image): Hash { - $imageWidth = $image->getWidth(); - $imageHeight = $image->getHeight(); + $imageWidth = $image->width(); + $imageHeight = $image->height(); $evenX = $imageWidth % $this->size === 0; $evenY = $imageHeight % $this->size === 0; $blockWidth = $imageWidth / $this->size; @@ -103,7 +97,7 @@ private function uneven(Image $image): Hash $weightTop = 1; $weightBottom = 0; } else { - $yMod = ($y + 1) % $blockHeight; + $yMod = fmod($y + 1, $blockHeight); $yFrac = $yMod - (int) floor($yMod); $yInt = $yMod - $yFrac; @@ -120,7 +114,7 @@ private function uneven(Image $image): Hash } for ($x = 0; $x < $imageWidth; $x++) { - $rgb = $image->pickColor($x, $y); + $rgb = $image->pickColor($x, $y)->toArray(); $value = $rgb[0] + $rgb[1] + $rgb[2]; if ($evenX) { @@ -128,7 +122,7 @@ private function uneven(Image $image): Hash $weightLeft = 1; $weightRight = 0; } else { - $xMod = ($x + 1) % $blockWidth; + $xMod = fmod($x + 1, $blockWidth); $xFrac = $xMod - (int) floor($xMod); $xInt = $xMod - $xFrac; diff --git a/src/Implementations/DifferenceHash.php b/src/Implementations/DifferenceHash.php index 1625533..2754c31 100644 --- a/src/Implementations/DifferenceHash.php +++ b/src/Implementations/DifferenceHash.php @@ -6,10 +6,7 @@ class DifferenceHash implements Implementation { - /** - * @var int - */ - protected $size; + protected int $size; public function __construct(int $size = 8) { @@ -28,12 +25,12 @@ public function hash(Image $image): Hash $bits = []; for ($y = 0; $y < $height; $y++) { // Get the pixel value for the leftmost pixel. - $rgb = $resized->pickColor(0, $y); + $rgb = $resized->pickColor(0, $y)->toArray(); $left = (int) floor(($rgb[0] * 0.299) + ($rgb[1] * 0.587) + ($rgb[2] * 0.114)); for ($x = 1; $x < $width; $x++) { // Get the pixel value for each pixel starting from position 1. - $rgb = $resized->pickColor($x, $y); + $rgb = $resized->pickColor($x, $y)->toArray(); $right = (int) floor(($rgb[0] * 0.299) + ($rgb[1] * 0.587) + ($rgb[2] * 0.114)); // Each hash bit is set based on whether the left pixel is brighter than the right pixel. diff --git a/src/Implementations/PerceptualHash.php b/src/Implementations/PerceptualHash.php index e151e16..64ed063 100644 --- a/src/Implementations/PerceptualHash.php +++ b/src/Implementations/PerceptualHash.php @@ -17,15 +17,9 @@ class PerceptualHash implements Implementation */ const MEDIAN = 'median'; - /** - * @var int - */ - protected $size; + protected int $size; - /** - * @var string - */ - protected $comparisonMethod; + protected string $comparisonMethod; public function __construct(int $size = 32, string $comparisonMethod = self::AVERAGE) { @@ -49,7 +43,7 @@ public function hash(Image $image): Hash for ($y = 0; $y < $this->size; $y++) { for ($x = 0; $x < $this->size; $x++) { - $rgb = $resized->pickColor($x, $y); + $rgb = $resized->pickColor($x, $y)->toArray(); $row[$x] = (int) floor(($rgb[0] * 0.299) + ($rgb[1] * 0.587) + ($rgb[2] * 0.114)); } $rows[$y] = $this->calculateDCT($row); diff --git a/tests/CompatibilityTest.php b/tests/CompatibilityTest.php index f2c3de8..e976d02 100644 --- a/tests/CompatibilityTest.php +++ b/tests/CompatibilityTest.php @@ -1,15 +1,18 @@ fn() => new GdDriver(), 'imagick' => fn() => new ImagickDriver()] as $extension => $driver) { + if (!extension_loaded($extension)) { continue; } - $hasher = new ImageHash($implementation, new ImageManager(['driver' => $driver])); + $hasher = new ImageHash($implementation, new ImageManager($driver())); $hash = $hasher->hash($path); + $hex = $hash->toHex(); - if ($precalculated !== $hash->toHex()) { - $this->addWarning(\get_class($implementation)." $driver generated a different hash ".$hash->toHex().' instead of '.$precalculated); + if ($precalculated !== $hex) { + $this->addWarning(get_class($implementation)." $extension generated a different hash ".$hex.' instead of '.$precalculated); } } $this->expectNotToPerformAssertions(); } + + private function addWarning(string $message): void + { + trigger_error($message, E_USER_WARNING); + } } diff --git a/tests/ImageHashTest.php b/tests/ImageHashTest.php index 11a77aa..539d12a 100644 --- a/tests/ImageHashTest.php +++ b/tests/ImageHashTest.php @@ -1,15 +1,12 @@ expectException(NotReadableException::class); + $this->expectException(DecoderException::class); $this->imageHash->hash('nonImageString'); } diff --git a/tests/ImplementationTest.php b/tests/ImplementationTest.php index e0c6a24..b492cb5 100644 --- a/tests/ImplementationTest.php +++ b/tests/ImplementationTest.php @@ -6,22 +6,17 @@ use Jenssegers\ImageHash\Implementations\BlockHash; use Jenssegers\ImageHash\Implementations\DifferenceHash; use Jenssegers\ImageHash\Implementations\PerceptualHash; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class ImplementationTest extends TestCase { - /** - * @var int - */ - private $threshold = 10; + private int $threshold = 10; - /** - * @var bool - */ - private $debug = true; + private bool $debug = true; - public function provideImplementations() - { + public static function provideImplementations(): array + { return [ [new AverageHash()], [new DifferenceHash()], @@ -32,26 +27,17 @@ public function provideImplementations() ]; } - /** - * @return array - */ - protected function getSimilarImages() - { + protected function getSimilarImages(): array + { return glob(__DIR__ . '/images/forest/*'); } - /** - * @return array - */ - protected function getDifferentImages() - { + protected function getDifferentImages(): array + { return glob(__DIR__ . '/images/office/*'); } - /** - * @dataProvider provideImplementations - * @param Implementation $implementation - */ + #[DataProvider('provideImplementations')] public function testEqualHashes(Implementation $implementation) { $sum = 0; @@ -81,10 +67,7 @@ public function testEqualHashes(Implementation $implementation) $this->debug("[" . get_class($implementation) . "] Total score: $sum"); } - /** - * @dataProvider provideImplementations - * @param Implementation $implementation - */ + #[DataProvider('provideImplementations')] public function testDifferentHashes(Implementation $implementation) { $sum = 0; @@ -114,8 +97,7 @@ public function testDifferentHashes(Implementation $implementation) $this->debug("[" . get_class($implementation) . "] Total score: $sum"); } - protected function debug($message) - { + protected function debug(string $message): void { if ($this->debug) { echo PHP_EOL . $message; }