Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/static-analysis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Static Analysis (only informative)
name: Static Analysis

on: [push, pull_request]

Expand All @@ -15,4 +15,3 @@ jobs:

- run: composer install --no-progress --prefer-dist
- run: composer phpstan -- --no-progress
continue-on-error: true # is only informative
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
coverage: none

- run: composer install --no-progress --prefer-dist
- run: vendor/bin/tester tests -s -C
- run: composer tester
- if: failure()
uses: actions/upload-artifact@v4
with:
Expand All @@ -42,7 +42,7 @@ jobs:
coverage: none

- run: composer install --no-progress --prefer-dist
- run: vendor/bin/tester -p phpdbg tests -s -C --coverage ./coverage.xml --coverage-src ./src
- run: composer tester -- -p phpdbg --coverage ./coverage.xml --coverage-src ./src
- run: wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.4.3/php-coveralls.phar
- env:
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"require-dev": {
"nette/tester": "^2.5",
"tracy/tracy": "^2.9",
"phpstan/phpstan-nette": "^2.0@stable",
"phpstan/phpstan": "^2.0@stable",
"jetbrains/phpstorm-attributes": "^1.2"
},
"conflict": {
Expand Down
105 changes: 98 additions & 7 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,18 +1,109 @@
parameters:
level: 5
level: 6

paths:
- src
- tests/types

bootstrapFiles:
- tests/phpstan-bootstrap.php

excludePaths:
- src/compatibility.php
- src/Iterators/Mapper.php
- src/Utils/ObjectHelpers.php

ignoreErrors:
# PHPStan does not support dynamic by reference return used by Nette\Utils\Strings::pcre()
- '#Undefined variable: \$m#'
# Intentional design pattern: new static() for inheritance support in fluent interfaces
-
identifier: new.static
paths:
- src/Utils/ArrayHash.php
- src/Utils/ArrayList.php
- src/Utils/DateTime.php
- src/Utils/Finder.php
- src/Utils/Html.php
- src/Utils/Image.php

# Runtime validation: ArrayAccess methods receive mixed types, validation is necessary
-
identifier: function.alreadyNarrowedType
paths:
- src/Utils/ArrayHash.php
- src/Utils/ArrayList.php

# Runtime validation: isList check validates input at runtime
-
identifier: staticMethod.alreadyNarrowedType
path: src/Utils/ArrayList.php

# Runtime validation: is_callable check validates callback at runtime
-
identifier: function.alreadyNarrowedType
path: src/Utils/Strings.php

# Intentional pattern: using && for short-circuit evaluation with side effects
-
identifier: booleanAnd.leftAlwaysTrue
path: src/Utils/DateTime.php
reportUnmatched: false

# Intentional pattern: assignment in condition with && operator
-
identifier: booleanAnd.rightAlwaysTrue
path: src/Utils/Reflection.php

# PHP 8.3+: getBytesFromString() doesn't exist on PHP 8.2
-
identifier: method.notFound
path: src/Utils/Random.php
reportUnmatched: false

# Intentional pattern: ??= for caching filter results, variable is declared via reference
-
identifier: nullCoalesce.variable
path: src/Utils/Finder.php

# Type test files: assertType() is the side effect, comparison warnings are expected
-
identifier: greater.alwaysTrue
path: tests/types/utils-types.php

# Image.php: Callback signature intentionally simplified (doesn't use int parameter)
-
identifier: argument.type
path: src/Utils/Image.php
count: 1

# Image.php: Defensive validation even though phpDoc specifies positive-int
-
identifiers:
- smaller.alwaysFalse
- booleanOr.alwaysFalse
path: src/Utils/Image.php

# Image.php: isset() check for type safety even though offset always exists
-
identifier: isset.offset
path: src/Utils/Image.php

# Image.php: Match arms document all supported types
-
identifier: match.alwaysTrue
path: src/Utils/Image.php

# Image.php: Return type annotation conveys intent (ImageType constants are ints)
-
identifier: return.type
path: src/Utils/Image.php
count: 1

# PHPStan does not support RecursiveIteratorIterator proxying unknown method calls to inner iterator
- '#RecursiveIteratorIterator::getSubPathName\(\)#'
# Image.php: By-ref parameter type narrowing in isPercent()
-
identifier: parameterByRef.type
path: src/Utils/Image.php

# static cannot be changed to maintain backward compatibility
- '#Unsafe usage of new static\(\)#'
# Iterables.php: Anonymous classes can't properly resolve template types from enclosing method
-
identifier: argument.templateType
path: src/Utils/Iterables.php
5 changes: 4 additions & 1 deletion src/HtmlStringable.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
namespace Nette;


/**
* Represents object convertible to HTML string.
*/
interface HtmlStringable
{
/**
* Returns string in HTML format
* Returns string in HTML format.
*/
function __toString(): string;
}
Expand Down
29 changes: 7 additions & 22 deletions src/Iterators/CachingIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@


/**
* Smarter caching iterator.
* Enhanced caching iterator with first/last/counter tracking.
*
* @template TKey
* @template TValue
* @extends \CachingIterator<TKey, TValue, \Iterator<TKey, TValue>>
* @property-read bool $first
* @property-read bool $last
* @property-read bool $empty
Expand All @@ -31,6 +34,9 @@ class CachingIterator extends \CachingIterator implements \Countable
private int $counter = 0;


/**
* @param iterable<TKey, TValue>|\stdClass $iterable
*/
public function __construct(iterable|\stdClass $iterable)
{
$iterable = $iterable instanceof \stdClass
Expand Down Expand Up @@ -58,45 +64,30 @@ public function isLast(?int $gridWidth = null): bool
}


/**
* Is the iterator empty?
*/
public function isEmpty(): bool
{
return $this->counter === 0;
}


/**
* Is the counter odd?
*/
public function isOdd(): bool
{
return $this->counter % 2 === 1;
}


/**
* Is the counter even?
*/
public function isEven(): bool
{
return $this->counter % 2 === 0;
}


/**
* Returns the counter.
*/
public function getCounter(): int
{
return $this->counter;
}


/**
* Returns the count of elements.
*/
public function count(): int
{
$inner = $this->getInnerIterator();
Expand Down Expand Up @@ -131,18 +122,12 @@ public function rewind(): void
}


/**
* Returns the next key.
*/
public function getNextKey(): mixed
{
return $this->getInnerIterator()->key();
}


/**
* Returns the next element.
*/
public function getNextValue(): mixed
{
return $this->getInnerIterator()->current();
Expand Down
5 changes: 2 additions & 3 deletions src/Iterators/Mapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
*/
class Mapper extends \IteratorIterator
{
/** @var callable */
private $callback;
private \Closure $callback;


public function __construct(\Traversable $iterator, callable $callback)
{
parent::__construct($iterator);
$this->callback = $callback;
$this->callback = $callback(...);
}


Expand Down
3 changes: 3 additions & 0 deletions src/SmartObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
trait SmartObject
{
/**
* @param list<mixed> $args
* @return mixed
* @throws MemberAccessException
*/
Expand All @@ -47,6 +48,8 @@ public function __call(string $name, array $args)


/**
* @param list<mixed> $args
* @return never
* @throws MemberAccessException
*/
public static function __callStatic(string $name, array $args)
Expand Down
2 changes: 1 addition & 1 deletion src/StaticClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


/**
* Static class.
* Prevents instantiation.
*/
trait StaticClass
{
Expand Down
2 changes: 1 addition & 1 deletion src/Translator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


/**
* Translator adapter.
* Translation provider.
*/
interface Translator
{
Expand Down
6 changes: 1 addition & 5 deletions src/Utils/ArrayHash.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


/**
* Provides objects to work as array.
* Array-like object with property access.
* @template T
* @implements \IteratorAggregate<array-key, T>
* @implements \ArrayAccess<array-key, T>
Expand All @@ -39,7 +39,6 @@ public static function from(array $array, bool $recursive = true): static


/**
* Returns an iterator over all items.
* @return \Iterator<array-key, T>
*/
public function &getIterator(): \Iterator
Expand All @@ -50,9 +49,6 @@ public function &getIterator(): \Iterator
}


/**
* Returns items count.
*/
public function count(): int
{
return count((array) $this);
Expand Down
9 changes: 3 additions & 6 deletions src/Utils/ArrayList.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@


/**
* Provides the base class for a generic list (items can be accessed by index).
* Generic list with integer indices.
* @template T
* @implements \IteratorAggregate<int, T>
* @implements \ArrayAccess<int, T>
*/
class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
{
/** @var list<T> */
private array $list = [];


Expand All @@ -41,7 +42,6 @@ public static function from(array $array): static


/**
* Returns an iterator over all items.
* @return \Iterator<int, T>
*/
public function &getIterator(): \Iterator
Expand All @@ -52,9 +52,6 @@ public function &getIterator(): \Iterator
}


/**
* Returns items count.
*/
public function count(): int
{
return count($this->list);
Expand All @@ -63,7 +60,7 @@ public function count(): int

/**
* Replaces or appends an item.
* @param int|null $index
* @param ?int $index
* @param T $value
* @throws Nette\OutOfRangeException
*/
Expand Down
Loading