Skip to content

Commit a5d4889

Browse files
committed
fixed PHPStan errors
1 parent 5a6e4eb commit a5d4889

File tree

11 files changed

+255
-60
lines changed

11 files changed

+255
-60
lines changed

phpstan-baseline.neon

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
parameters:
2+
ignoreErrors:
3+
-
4+
message: '#^Parameter \#1 \$key \(\(int\|string\)\) of method Nette\\Utils\\ArrayHash\:\:offsetSet\(\) should be contravariant with parameter \$offset \(int\|string\|null\) of method ArrayAccess\<\(int\|string\),T\>\:\:offsetSet\(\)$#'
5+
identifier: method.childParameterType
6+
count: 1
7+
path: src/Utils/ArrayHash.php
8+
9+
-
10+
message: '#^Call to static method Nette\\Utils\\Arrays\:\:isList\(\) with list\<T\> will always evaluate to true\.$#'
11+
identifier: staticMethod.alreadyNarrowedType
12+
count: 1
13+
path: src/Utils/ArrayList.php
14+
15+
-
16+
message: '#^Property Nette\\Utils\\ArrayList\<T\>\:\:\$list \(list\<T\>\) does not accept non\-empty\-array\<int\<0, max\>, T\>\.$#'
17+
identifier: assign.propertyType
18+
count: 1
19+
path: src/Utils/ArrayList.php
20+
21+
-
22+
message: '#^Cannot access offset string on array\<mixed\>\|object\.$#'
23+
identifier: offsetAccess.nonOffsetAccessible
24+
count: 3
25+
path: src/Utils/Arrays.php
26+
27+
-
28+
message: '#^Method Nette\\Utils\\Arrays\:\:invoke\(\) has parameter \$callbacks with no signature specified for callable\.$#'
29+
identifier: missingType.callable
30+
count: 1
31+
path: src/Utils/Arrays.php
32+
33+
-
34+
message: '#^Parameter \#1 \$objectOrMethod of class Nette\\Utils\\ReflectionMethod constructor expects class\-string\|object, string given\.$#'
35+
identifier: argument.type
36+
count: 1
37+
path: src/Utils/Callback.php
38+
39+
-
40+
message: '#^Left side of && is always true\.$#'
41+
identifier: booleanAnd.leftAlwaysTrue
42+
count: 2
43+
path: src/Utils/DateTime.php
44+
45+
-
46+
message: '#^Parameter \#1 \$pathNames of method Nette\\Utils\\Finder\:\:convertToFiles\(\) expects iterable\<string\>, FilesystemIterator given\.$#'
47+
identifier: argument.type
48+
count: 1
49+
path: src/Utils/Finder.php
50+
51+
-
52+
message: '#^Parameter \#2 \$operator of static method Nette\\Utils\\Helpers\:\:compare\(\) expects ''\!\=''\|''\!\=\=''\|''\<''\|''\<\=''\|''\<\>''\|''\=''\|''\=\=''\|''\=\=\=''\|''\>''\|''\>\='', non\-falsy\-string given\.$#'
53+
identifier: argument.type
54+
count: 2
55+
path: src/Utils/Finder.php
56+
57+
-
58+
message: '#^Trying to invoke non\-falsy\-string but it might not be a callable\.$#'
59+
identifier: callable.nonCallable
60+
count: 1
61+
path: src/Utils/Finder.php
62+
63+
-
64+
message: '#^Variable \$res on left side of \?\?\= always exists and is not nullable\.$#'
65+
identifier: nullCoalesce.variable
66+
count: 1
67+
path: src/Utils/Finder.php
68+
69+
-
70+
message: '#^Match arm comparison between ''\!\=\='' and ''\!\=\='' is always true\.$#'
71+
identifier: match.alwaysTrue
72+
count: 1
73+
path: src/Utils/Helpers.php
74+
75+
-
76+
message: '#^Comparison operation "\<" between int\<1, max\> and 1 is always false\.$#'
77+
identifier: smaller.alwaysFalse
78+
count: 2
79+
path: src/Utils/Image.php
80+
81+
-
82+
message: '#^Match arm comparison between 6 and 6 is always true\.$#'
83+
identifier: match.alwaysTrue
84+
count: 2
85+
path: src/Utils/Image.php
86+
87+
-
88+
message: '#^Offset 1\|2\|3\|6\|18\|19 on array\{2\: ''jpeg'', 3\: ''png'', 1\: ''gif'', 18\: ''webp'', 19\: ''avif'', 6\: ''bmp''\} in isset\(\) always exists and is not nullable\.$#'
89+
identifier: isset.offset
90+
count: 1
91+
path: src/Utils/Image.php
92+
93+
-
94+
message: '#^Result of \|\| is always false\.$#'
95+
identifier: booleanOr.alwaysFalse
96+
count: 1
97+
path: src/Utils/Image.php
98+
99+
-
100+
message: '#^Parameter \#1 \$class of class ReflectionClassConstant constructor expects class\-string\|object, string given\.$#'
101+
identifier: argument.type
102+
count: 1
103+
path: src/Utils/Reflection.php
104+
105+
-
106+
message: '#^Right side of && is always true\.$#'
107+
identifier: booleanAnd.rightAlwaysTrue
108+
count: 2
109+
path: src/Utils/Reflection.php
110+
111+
-
112+
message: '#^Parameter \#1 \$objectOrClass of class ReflectionClass constructor expects class\-string\<T of object\>\|T of object, object\|string given\.$#'
113+
identifier: argument.type
114+
count: 1
115+
path: src/Utils/ReflectionMethod.php
116+
117+
-
118+
message: '#^Method Nette\\Utils\\Strings\:\:match\(\) should return array\<string\>\|null but returns array\<array\<int, int\|string\>\>\.$#'
119+
identifier: return.type
120+
count: 1
121+
path: src/Utils/Strings.php
122+
123+
-
124+
message: '#^Strict comparison using \!\=\= between string and false will always evaluate to true\.$#'
125+
identifier: notIdentical.alwaysTrue
126+
count: 1
127+
path: src/Utils/Strings.php
128+
129+
-
130+
message: '#^Parameter \#1 \$givenTypes of method Nette\\Utils\\Type\:\:allowsAny\(\) expects array\<string\>, list\<Nette\\Utils\\Type\|string\> given\.$#'
131+
identifier: argument.type
132+
count: 2
133+
path: src/Utils/Type.php
134+
135+
-
136+
message: '#^Parameter \#1 \$ourTypes of method Nette\\Utils\\Type\:\:allowsAll\(\) expects array\<string\>, list\<Nette\\Utils\\Type\|string\> given\.$#'
137+
identifier: argument.type
138+
count: 2
139+
path: src/Utils/Type.php
140+
141+
-
142+
message: '#^Parameter \#2 \$of of static method Nette\\Utils\\Type\:\:resolve\(\) expects ReflectionFunction\|ReflectionMethod\|ReflectionParameter\|ReflectionProperty, ReflectionFunctionAbstract\|ReflectionParameter\|ReflectionProperty given\.$#'
143+
identifier: argument.type
144+
count: 1
145+
path: src/Utils/Type.php

phpstan.neon

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,44 @@
11
parameters:
2-
level: 5
2+
level: 7
33

44
paths:
55
- src
66

7+
excludePaths:
8+
- src/compatibility.php
9+
- src/Iterators/Mapper.php
10+
- src/Utils/ObjectHelpers.php
11+
712
bootstrapFiles:
813
- tests/phpstan-bootstrap.php
914

1015
ignoreErrors:
11-
# PHPStan does not support dynamic by reference return used by Nette\Utils\Strings::pcre()
12-
- '#Undefined variable: \$m#'
16+
# Intentional design pattern: new static() for inheritance support in fluent interfaces
17+
-
18+
identifier: new.static
19+
paths:
20+
- src/Utils/ArrayHash.php
21+
- src/Utils/ArrayList.php
22+
- src/Utils/DateTime.php
23+
- src/Utils/Finder.php
24+
- src/Utils/Html.php
25+
- src/Utils/Image.php
26+
27+
# Runtime validation: type narrowing checks that are necessary at runtime
28+
-
29+
identifier: function.alreadyNarrowedType
30+
paths:
31+
- src/Utils/ArrayHash.php
32+
- src/Utils/ArrayList.php
33+
- src/Utils/Strings.php
1334

14-
# PHPStan does not support RecursiveIteratorIterator proxying unknown method calls to inner iterator
15-
- '#RecursiveIteratorIterator::getSubPathName\(\)#'
35+
# Works with arbitrary PHP callbacks, precise callable types not practical
36+
-
37+
identifier: missingType.callable
38+
paths:
39+
- src/Utils/Callback.php
40+
- src/Utils/Image.php
41+
- src/Utils/Strings.php
1642

17-
# static cannot be changed to maintain backward compatibility
18-
- '#Unsafe usage of new static\(\)#'
43+
includes:
44+
- phpstan-baseline.neon

src/Utils/Arrays.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@ public static function associate(array $array, $path): array|\stdClass
328328
$x = $row[$parts[$i]];
329329
$row = null;
330330
}
331+
break; // '=' is always the final operation
332+
331333
} elseif ($part === '->') {
332334
if (isset($parts[++$i])) {
333335
if ($x === null) {

src/Utils/Callback.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,18 @@ final class Callback
2828
*/
2929
public static function invokeSafe(string $function, array $args, callable $onError): mixed
3030
{
31-
$prev = set_error_handler(function (int $severity, string $message, string $file, int $line) use ($onError, &$prev, $function): ?bool {
31+
$prev = set_error_handler(function (int $severity, string $message, string $file, int $line) use ($onError, &$prev, $function): bool {
3232
if ($file === __FILE__) {
3333
$msg = ini_get('html_errors')
3434
? Html::htmlToText($message)
3535
: $message;
3636
$msg = preg_replace("#^$function\\(.*?\\): #", '', $msg);
3737
if ($onError($msg, $severity) !== false) {
38-
return null;
38+
return true;
3939
}
4040
}
4141

42-
return $prev ? $prev(...func_get_args()) : false;
42+
return $prev ? $prev(...func_get_args()) !== false : false;
4343
});
4444

4545
try {
@@ -103,6 +103,7 @@ public static function toReflection($callable): \ReflectionMethod|\ReflectionFun
103103
} elseif (is_object($callable) && !$callable instanceof \Closure) {
104104
return new ReflectionMethod($callable, '__invoke');
105105
} else {
106+
assert($callable instanceof \Closure || is_string($callable));
106107
return new \ReflectionFunction($callable);
107108
}
108109
}
@@ -121,7 +122,7 @@ public static function isStatic(callable $callable): bool
121122
* Unwraps closure created by Closure::fromCallable().
122123
* @return \Closure|array{object|class-string, string}|string
123124
*/
124-
public static function unwrap(\Closure $closure): callable|array
125+
public static function unwrap(\Closure $closure): \Closure|array|string
125126
{
126127
$r = new \ReflectionFunction($closure);
127128
$class = $r->getClosureScopeClass()?->name;

src/Utils/FileSystem.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public static function copy(string $origin, string $target, bool $overwrite = tr
5252
} elseif (is_dir($origin)) {
5353
static::createDir($target);
5454
foreach (new \FilesystemIterator($target) as $item) {
55+
\assert($item instanceof \SplFileInfo);
5556
static::delete($item->getPathname());
5657
}
5758

@@ -112,6 +113,7 @@ public static function delete(string $path): void
112113
}
113114
} elseif (is_dir($path)) {
114115
foreach (new \FilesystemIterator($path) as $item) {
116+
\assert($item instanceof \SplFileInfo);
115117
static::delete($item->getPathname());
116118
}
117119

@@ -251,6 +253,7 @@ public static function makeWritable(string $path, int $dirMode = 0o777, int $fil
251253
}
252254
} elseif (is_dir($path)) {
253255
foreach (new \FilesystemIterator($path) as $item) {
256+
\assert($item instanceof \SplFileInfo);
254257
static::makeWritable($item->getPathname(), $dirMode, $fileMode);
255258
}
256259

src/Utils/Finder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ public function size(string $operator, ?int $size = null): static
289289

290290
[, $operator, $size, $unit] = $matches;
291291
$units = ['' => 1, 'k' => 1e3, 'm' => 1e6, 'g' => 1e9];
292-
$size *= $units[strtolower($unit)];
292+
$size = (float) $size * $units[strtolower($unit)];
293293
$operator = $operator ?: '=';
294294
}
295295

src/Utils/Image.php

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,9 @@ public static function fromBlank(int $width, int $height, ImageColor|array|null
227227

228228
$image = new static(imagecreatetruecolor($width, $height));
229229
if ($color) {
230-
$image->alphaBlending(false);
231-
$image->filledRectangle(0, 0, $width - 1, $height - 1, $color);
232-
$image->alphaBlending(true);
230+
imagealphablending($image->image, false);
231+
imagefilledrectangle($image->image, 0, 0, $width - 1, $height - 1, $image->resolveColor($color));
232+
imagealphablending($image->image, true);
233233
}
234234

235235
return $image;
@@ -478,19 +478,19 @@ public static function calculateSize(
478478
}
479479

480480
if ($mode & self::OrBigger) {
481-
$scale = [max($scale)];
481+
$scale = [max($scale ?: [1])];
482482
}
483483

484484
if ($mode & self::ShrinkOnly) {
485485
$scale[] = 1;
486486
}
487487

488-
$scale = min($scale);
488+
$scale = min($scale ?: [1]);
489489
$newWidth = (int) round($srcWidth * $scale);
490490
$newHeight = (int) round($srcHeight * $scale);
491491
}
492492

493-
return [max($newWidth, 1), max($newHeight, 1)];
493+
return [max((int) $newWidth, 1), max((int) $newHeight, 1)];
494494
}
495495

496496

@@ -505,7 +505,7 @@ public function crop(int|string $left, int|string $top, int|string $width, int|s
505505
$this->image = imagecrop($this->image, $r);
506506
imagesavealpha($this->image, true);
507507
} else {
508-
$newImage = static::fromBlank($r['width'], $r['height'], ImageColor::rgb(0, 0, 0, 0))->getImageResource();
508+
$newImage = static::fromBlank(max(1, $r['width']), max(1, $r['height']), ImageColor::rgb(0, 0, 0, 0))->getImageResource();
509509
imagecopy($newImage, $this->image, 0, 0, $r['x'], $r['y'], $r['width'], $r['height']);
510510
$this->image = $newImage;
511511
}
@@ -553,9 +553,9 @@ public static function calculateCutout(
553553
$top = 0;
554554
}
555555

556-
$newWidth = min($newWidth, $srcWidth - $left);
557-
$newHeight = min($newHeight, $srcHeight - $top);
558-
return [$left, $top, $newWidth, $newHeight];
556+
$newWidth = (int) min($newWidth, $srcWidth - $left);
557+
$newHeight = (int) min($newHeight, $srcHeight - $top);
558+
return [(int) $left, (int) $top, $newWidth, $newHeight];
559559
}
560560

561561

@@ -587,13 +587,8 @@ public function place(self $image, int|string $left = 0, int|string $top = 0, in
587587
$width = $image->getWidth();
588588
$height = $image->getHeight();
589589

590-
if (self::isPercent($left)) {
591-
$left = (int) round(($this->getWidth() - $width) / 100 * $left);
592-
}
593-
594-
if (self::isPercent($top)) {
595-
$top = (int) round(($this->getHeight() - $height) / 100 * $top);
596-
}
590+
$left = (int) (self::isPercent($left) ? round(($this->getWidth() - $width) / 100 * $left) : $left);
591+
$top = (int) (self::isPercent($top) ? round(($this->getHeight() - $height) / 100 * $top) : $top);
597592

598593
$output = $input = $image->image;
599594
if ($opacity < 100) {
@@ -606,7 +601,7 @@ public function place(self $image, int|string $left = 0, int|string $top = 0, in
606601
imagealphablending($output, false);
607602
if (!$image->isTrueColor()) {
608603
$input = $output;
609-
imagefilledrectangle($output, 0, 0, $width, $height, imagecolorallocatealpha($output, 0, 0, 0, 127));
604+
imagefilledrectangle($output, 0, 0, $width, $height, (int) imagecolorallocatealpha($output, 0, 0, 0, 127));
610605
imagecopy($output, $image->image, 0, 0, 0, 0, $width, $height);
611606
}
612607

@@ -774,6 +769,7 @@ public function __call(string $name, array $args): mixed
774769
$args[$key] = $value->getImageResource();
775770

776771
} elseif ($value instanceof ImageColor || (is_array($value) && isset($value['red']))) {
772+
/** @var ImageColor|array{red: int, green: int, blue: int, alpha?: int} $value */
777773
$args[$key] = $this->resolveColor($value);
778774
}
779775
}
@@ -789,10 +785,11 @@ public function __clone()
789785
{
790786
ob_start(fn() => '');
791787
imagepng($this->image, null, 0);
792-
$this->setImageResource(imagecreatefromstring(ob_get_clean()));
788+
$this->setImageResource(imagecreatefromstring(ob_get_clean()) ?: throw new Nette\ShouldNotHappenException);
793789
}
794790

795791

792+
/** @param-out int|float $num */
796793
private static function isPercent(int|string &$num): bool
797794
{
798795
if (is_string($num) && str_ends_with($num, '%')) {
@@ -821,8 +818,13 @@ public function __serialize(): array
821818
*/
822819
public function resolveColor(ImageColor|array $color): int
823820
{
824-
$color = $color instanceof ImageColor ? $color->toRGBA() : array_values($color + ['alpha' => 0]);
825-
return imagecolorallocatealpha($this->image, ...$color) ?: imagecolorresolvealpha($this->image, ...$color);
821+
if (!$color instanceof ImageColor) {
822+
$color = ImageColor::rgb($color['red'], $color['green'], $color['blue'], 1 - ($color['alpha'] ?? 0) / 127);
823+
}
824+
825+
[$r, $g, $b, $a] = $color->toRGBA();
826+
return imagecolorallocatealpha($this->image, $r, $g, $b, $a)
827+
?: imagecolorresolvealpha($this->image, $r, $g, $b, $a);
826828
}
827829

828830

0 commit comments

Comments
 (0)