Skip to content

Commit

Permalink
Add PHPstan (#205)
Browse files Browse the repository at this point in the history
* init phpstan

* fix first set of issues

* add todo for missing use case

* explicitly transform floats to int

* imagepng() expects quality param to be an integer between 0 and 9

* method returns float, variable where stored is also typed as float

* define array structures

* add more array type hints

* change interface params to match the actual implementations

* Fix styling

* add phpstan workflow

* change missing types to int|float

* Fix styling

* move exif stuff to baseline

* replace intval() with (int)

(int) is consider the best practice as it is up to 6x faster

* Fix styling

* int/float fixes

* Fix styling

---------

Co-authored-by: Nielsvanpach <Nielsvanpach@users.noreply.github.com>
  • Loading branch information
Nielsvanpach and Nielsvanpach authored Dec 1, 2023
1 parent d239296 commit db37120
Show file tree
Hide file tree
Showing 18 changed files with 195 additions and 51 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: PHPStan

on:
push:
paths:
- '**.php'
- 'phpstan.neon.dist'
- '.github/workflows/phpstan.yml'

jobs:
phpstan:
name: phpstan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
coverage: none
tools: phpstan

- name: Install composer dependencies
uses: ramsey/composer-install@v2

- name: Run PHPStan
run: ./vendor/bin/phpstan --error-format=github
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"require-dev": {
"pestphp/pest": "^2.0",
"phpstan/phpstan": "^1.10",
"spatie/pest-plugin-snapshots": "^2.1",
"spatie/pixelmatch-php": "^1.0",
"spatie/ray": "^1.37",
Expand All @@ -44,7 +45,9 @@
}
},
"scripts": {
"test": "vendor/bin/pest"
"test": "vendor/bin/pest",
"analyse": "phpstan analyse --memory-limit=1G --no-progress",
"baseline": "./vendor/bin/phpstan analyse --memory-limit=2G --generate-baseline"
},
"config": {
"sort-packages": true,
Expand Down
86 changes: 86 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
parameters:
ignoreErrors:
-
message: "#^Access to an undefined property Spatie\\\\Image\\\\Drivers\\\\Color\\:\\:\\$alpha\\.$#"
count: 1
path: src/Drivers/Gd/GdColor.php

-
message: "#^Access to an undefined property Spatie\\\\Image\\\\Drivers\\\\Color\\:\\:\\$blue\\.$#"
count: 1
path: src/Drivers/Gd/GdColor.php

-
message: "#^Access to an undefined property Spatie\\\\Image\\\\Drivers\\\\Color\\:\\:\\$green\\.$#"
count: 1
path: src/Drivers/Gd/GdColor.php

-
message: "#^Access to an undefined property Spatie\\\\Image\\\\Drivers\\\\Color\\:\\:\\$red\\.$#"
count: 1
path: src/Drivers/Gd/GdColor.php

-
message: "#^Access to an undefined property Spatie\\\\Image\\\\Drivers\\\\ImageDriver\\:\\:\\$image\\.$#"
count: 1
path: src/Drivers/Gd/GdDriver.php

-
message: "#^Method Spatie\\\\Image\\\\Drivers\\\\Gd\\\\GdDriver\\:\\:exif\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Drivers/Gd/GdDriver.php

-
message: "#^Property Spatie\\\\Image\\\\Drivers\\\\Gd\\\\GdDriver\\:\\:\\$exif type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Drivers/Gd/GdDriver.php

-
message: "#^Unreachable statement \\- code above always terminates\\.$#"
count: 1
path: src/Drivers/Gd/GdDriver.php

-
message: "#^Method Spatie\\\\Image\\\\Drivers\\\\ImageDriver\\:\\:exif\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Drivers/ImageDriver.php

-
message: "#^Call to an undefined method Spatie\\\\Image\\\\Drivers\\\\Color\\:\\:getAlphaValue\\(\\)\\.$#"
count: 1
path: src/Drivers/Imagick/ImagickColor.php

-
message: "#^Call to an undefined method Spatie\\\\Image\\\\Drivers\\\\Color\\:\\:getBlueValue\\(\\)\\.$#"
count: 1
path: src/Drivers/Imagick/ImagickColor.php

-
message: "#^Call to an undefined method Spatie\\\\Image\\\\Drivers\\\\Color\\:\\:getGreenValue\\(\\)\\.$#"
count: 1
path: src/Drivers/Imagick/ImagickColor.php

-
message: "#^Call to an undefined method Spatie\\\\Image\\\\Drivers\\\\Color\\:\\:getRedValue\\(\\)\\.$#"
count: 1
path: src/Drivers/Imagick/ImagickColor.php

-
message: "#^Access to an undefined property Spatie\\\\Image\\\\Drivers\\\\ImageDriver\\:\\:\\$image\\.$#"
count: 2
path: src/Drivers/Imagick/ImagickDriver.php

-
message: "#^Method Spatie\\\\Image\\\\Drivers\\\\Imagick\\\\ImagickDriver\\:\\:exif\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Drivers/Imagick/ImagickDriver.php

-
message: "#^Property Spatie\\\\Image\\\\Drivers\\\\Imagick\\\\ImagickDriver\\:\\:\\$exif type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Drivers/Imagick/ImagickDriver.php

-
message: "#^Match expression does not handle remaining value\\: Spatie\\\\Image\\\\Enums\\\\Fit\\:\\:Crop$#"
count: 1
path: src/Enums/Fit.php
10 changes: 10 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
includes:
- phpstan-baseline.neon

parameters:
level: 6
paths:
- src/

ignoreErrors:
- '#Unsafe usage of new static#'
33 changes: 18 additions & 15 deletions src/Drivers/Color.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@

abstract class Color
{
abstract public function initFromInteger(int $value);
abstract public function initFromInteger(int $value): self;

abstract public function initFromArray(array $value);
/** @param array<positive-int> $value */
abstract public function initFromArray(array $value): self;

abstract public function initFromString(string $value);
abstract public function initFromString(string $value): self;

abstract public function initFromObject(ImagickPixel $value);
abstract public function initFromObject(ImagickPixel $value): self;

abstract public function initFromRgb(int $red, int $green, int $blue);
abstract public function initFromRgb(int $red, int $green, int $blue): self;

abstract public function initFromRgba(int $red, int $green, int $blue, float $alpha);
abstract public function initFromRgba(int $red, int $green, int $blue, float $alpha): self;

abstract public function getInt(): int;

abstract public function getHex(string $prefix): string;

/** @return array<positive-int> */
abstract public function getArray(): array;

abstract public function getRgba(): string;
Expand Down Expand Up @@ -60,6 +62,7 @@ public function format(ColorFormat $colorFormat): mixed
};
}

/** @return array<int> */
protected function rgbaFromString(string $colorValue): array
{
// parse color string in hexidecimal format like #cccccc or cccccc or ccc
Expand All @@ -73,21 +76,21 @@ protected function rgbaFromString(string $colorValue): array

if (preg_match($hexPattern, $colorValue, $matches)) {
$result = [];
$result[0] = strlen($matches[1]) == '1' ? hexdec($matches[1].$matches[1]) : hexdec($matches[1]);
$result[1] = strlen($matches[2]) == '1' ? hexdec($matches[2].$matches[2]) : hexdec($matches[2]);
$result[2] = strlen($matches[3]) == '1' ? hexdec($matches[3].$matches[3]) : hexdec($matches[3]);
$result[0] = strlen($matches[1]) == '1' ? (int) hexdec($matches[1].$matches[1]) : (int) hexdec($matches[1]);
$result[1] = strlen($matches[2]) == '1' ? (int) hexdec($matches[2].$matches[2]) : (int) hexdec($matches[2]);
$result[2] = strlen($matches[3]) == '1' ? (int) hexdec($matches[3].$matches[3]) : (int) hexdec($matches[3]);
$result[3] = 1;
} elseif (preg_match($rgbPattern, $colorValue, $matches)) {
$result = [];
$result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0;
$result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0;
$result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0;
$result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? (int) ($matches[1]) : 0;
$result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? (int) ($matches[2]) : 0;
$result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? (int) ($matches[3]) : 0;
$result[3] = 1;
} elseif (preg_match($rgbaPattern, $colorValue, $matches)) {
$result = [];
$result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0;
$result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0;
$result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0;
$result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? (int) ($matches[1]) : 0;
$result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? (int) ($matches[2]) : 0;
$result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? (int) ($matches[3]) : 0;
$result[3] = ($matches[4] >= 0 && $matches[4] <= 1) ? $matches[4] : 0;
} else {
throw InvalidColor::make($colorValue);
Expand Down
1 change: 1 addition & 0 deletions src/Drivers/Concerns/CalculatesCropOffsets.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/** @mixin \Spatie\Image\Drivers\ImageDriver */
trait CalculatesCropOffsets
{
/** @return array<positive-int> */
protected function calculateCropOffsets(int $width, int $height, CropPosition $position): array
{
[$offsetPercentageX, $offsetPercentageY] = $position->offsetPercentages();
Expand Down
7 changes: 4 additions & 3 deletions src/Drivers/Concerns/CalculatesFocalCropCoordinates.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@
/** @mixin \Spatie\Image\Drivers\ImageDriver */
trait CalculatesFocalCropCoordinates
{
protected function calculateFocalCropCoordinates(int $width, int $height, $cropCenterX, $cropCenterY): array
/** @return array<positive-int> */
protected function calculateFocalCropCoordinates(int $width, int $height, int $cropCenterX, int $cropCenterY): array
{
$width = min($width, $this->getWidth());
$height = min($height, $this->getHeight());

if ($cropCenterX > 0) {
$maxCropCenterX = $this->getWidth() - $width;
$cropCenterX = (int) $cropCenterX - ($width / 2);
$cropCenterX = (int) ($cropCenterX - ($width / 2));
$cropCenterX = min($maxCropCenterX, $cropCenterX);
$cropCenterX = max(0, $cropCenterX);
}

if ($cropCenterY > 0) {
$maxCropCenterY = $this->getHeight() - $height;
$cropCenterY = (int) $cropCenterY - ($height / 2);
$cropCenterY = (int) ($cropCenterY - ($height / 2));
$cropCenterY = min($maxCropCenterY, $cropCenterY);
$cropCenterY = max(0, $cropCenterY);
}
Expand Down
1 change: 1 addition & 0 deletions src/Drivers/Concerns/GetsOrientationFromExif.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

trait GetsOrientationFromExif
{
/** @param array{'Orientation'?: int} $exif */
public function getOrientationFromExif(array $exif): Orientation
{
if (! isset($exif['Orientation'])) {
Expand Down
4 changes: 3 additions & 1 deletion src/Drivers/Gd/GdColor.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public function initFromArray(array $value): self
[$red, $green, $blue] = $value;
$this->alpha = 0;

} else {
throw InvalidColor::make($value);
}

$this->red = $red;
Expand Down Expand Up @@ -143,6 +145,6 @@ private function alpha2gd(float $input): int
$newMin = 127;
$newMax = 0;

return ceil(((($input - $oldMin) * ($newMax - $newMin)) / ($oldMax - $oldMin)) + $newMin);
return (int) ceil(((($input - $oldMin) * ($newMax - $newMin)) / ($oldMax - $oldMin)) + $newMin);
}
}
5 changes: 3 additions & 2 deletions src/Drivers/Gd/GdDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ public function crop(int $width, int $height, CropPosition $position = CropPosit
return $this->manualCrop($width, $height, $offsetX, $offsetY);
}

public function focalCrop(int $width, int $height, $cropCenterX = null, $cropCenterY = null): self
public function focalCrop(int $width, int $height, int $cropCenterX = null, int $cropCenterY = null): self
{
[$width, $height, $cropCenterX, $cropCenterY] = $this->calculateFocalCropCoordinates(
$width,
Expand Down Expand Up @@ -664,13 +664,14 @@ public function quality(int $quality): self
return $this;
}

/** @return int<-1, 9> */
protected function pngCompression(): int
{
if ($this->quality === -1) {
return -1;
}

return round((100 - $this->quality) / 10);
return (int) round((100 - $this->quality) / 10);
}

public function format(string $format): ImageDriver
Expand Down
8 changes: 6 additions & 2 deletions src/Drivers/ImageDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Spatie\Image\Enums\AlignPosition;
use Spatie\Image\Enums\BorderType;
use Spatie\Image\Enums\ColorFormat;
use Spatie\Image\Enums\Constraint;
use Spatie\Image\Enums\CropPosition;
use Spatie\Image\Enums\Fit;
use Spatie\Image\Enums\FlipDirection;
Expand Down Expand Up @@ -89,11 +90,14 @@ public function insert(

public function image(): mixed;

/** @param array<Constraint> $constraints */
public function resize(int $width, int $height, array $constraints): self;

public function width(int $width): self;
/** @param array<Constraint> $constraints */
public function width(int $width, array $constraints = []): self;

public function height(int $height): self;
/** @param array<Constraint> $constraints */
public function height(int $height, array $constraints = []): self;

public function border(int $width, BorderType $type, string $color = '000000'): self;

Expand Down
16 changes: 8 additions & 8 deletions src/Drivers/Imagick/ImagickColor.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public function getInt(): int
$red = $this->getRedValue();
$green = $this->getGreenValue();
$blue = $this->getBlueValue();
$alpha = intval(round($this->getAlphaValue() * 255));
$alpha = (int) (round($this->getAlphaValue() * 255));

return ($alpha << 24) + ($red << 16) + ($green << 8) + $blue;
}
Expand Down Expand Up @@ -127,17 +127,17 @@ public function differs(Color $color, int $tolerance = 0): bool

public function getRedValue(): int
{
return round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255);
return (int) round($this->pixel->getColorValue(Imagick::COLOR_RED) * 255);
}

public function getGreenValue(): int
{
return round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255);
return (int) round($this->pixel->getColorValue(Imagick::COLOR_GREEN) * 255);
}

public function getBlueValue(): int
{
return round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255);
return (int) round($this->pixel->getColorValue(Imagick::COLOR_BLUE) * 255);
}

public function getAlphaValue(): float
Expand All @@ -146,10 +146,10 @@ public function getAlphaValue(): float
}

private function setPixel(
$red,
$green,
$blue,
$alpha = null
int|float $red,
int|float $green,
int|float $blue,
int|float $alpha = null
): ImagickPixel {
$alpha = is_null($alpha) ? 1 : $alpha;

Expand Down
Loading

0 comments on commit db37120

Please sign in to comment.