Skip to content

Commit

Permalink
phpstan: Streamline vendor file location with local dev-env (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
nilmerg authored Feb 12, 2024
2 parents 1150af1 + 0396567 commit f14ee18
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 79 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
os: ['ubuntu-latest']

steps:
Expand All @@ -31,7 +31,7 @@ jobs:
tools: phpcs

- name: Setup dependencies
run: composer require -n --no-progress overtrue/phplint
run: composer require -n --no-progress overtrue/phplint phpstan/phpstan

- name: PHP Lint
if: ${{ ! cancelled() }}
Expand All @@ -43,7 +43,7 @@ jobs:

- name: PHPStan
if: ${{ ! cancelled() }}
uses: php-actions/phpstan@v3
run: ./vendor/bin/phpstan analyse

test:
name: Unit tests with php ${{ matrix.php }} on ${{ matrix.os }}
Expand All @@ -55,7 +55,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
os: ['ubuntu-latest']

steps:
Expand Down
41 changes: 3 additions & 38 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,46 +1,11 @@
parameters:
ignoreErrors:
-
message: "#^Cannot cast mixed to string\\.$#"
count: 2
path: src/Filter.php

-
message: "#^Dead catch \\- Exception is never thrown in the try block\\.$#"
message: "#^Dead catch \\- Throwable is never thrown in the try block\\.$#"
count: 1
path: src/Filter.php

-
message: "#^Class ipl\\\\Stdlib\\\\Filter\\\\Chain implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#"
count: 1
path: src/Filter/Chain.php

-
message: "#^Cannot access offset 'data' on mixed\\.$#"
count: 1
path: src/PriorityQueue.php

-
message: "#^Cannot access offset 'priority' on mixed\\.$#"
count: 1
path: src/PriorityQueue.php

-
message: "#^Cannot access offset 0 on mixed\\.$#"
count: 1
path: src/PriorityQueue.php

-
message: "#^Class ipl\\\\Stdlib\\\\PriorityQueue extends generic class SplPriorityQueue but does not specify its types\\: TPriority, TValue$#"
count: 1
path: src/PriorityQueue.php

-
message: "#^Trying to invoke mixed but it's not a callable\\.$#"
message: "#^Parameter \\#1 \\$(str|string) of function strtolower expects string, mixed given\\.$#"
count: 2
path: src/Seq.php

-
message: "#^Parameter \\#1 \\$separator of function explode expects non\\-empty\\-string, string given\\.$#"
count: 3
path: src/Str.php
path: src/Filter.php
4 changes: 1 addition & 3 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ parameters:
checkFunctionNameCase: true
checkInternalClassCaseSensitivity: true
treatPhpDocTypesAsCertain: false
checkGenericClassInNonGenericObjectType: false

paths:
- src

scanDirectories:
- vendor

ignoreErrors:
-
messages:
Expand Down
48 changes: 24 additions & 24 deletions src/Filter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace ipl\Stdlib;

use Exception;
use InvalidArgumentException;
use ipl\Stdlib\Filter\All;
use ipl\Stdlib\Filter\Any;
Expand All @@ -18,6 +17,7 @@
use ipl\Stdlib\Filter\Rule;
use ipl\Stdlib\Filter\Unequal;
use ipl\Stdlib\Filter\Unlike;
use Throwable;

class Filter
{
Expand Down Expand Up @@ -258,19 +258,14 @@ protected function performEqualityMatch($value, $rowValue, $ignoreCase = false)
{
if ($ignoreCase && is_string($rowValue)) {
$rowValue = strtolower($rowValue);
/** @var string|string[] $value {@see self::normalizeTypes} ensures this is the case */
$value = is_array($value)
? array_map(function ($val) {
return strtolower((string) $val);
}, $value)
: strtolower((string) $value);
? array_map('strtolower', $value)
: ($value === null ? null : strtolower($value)); // phpstan is wrong here
}

if (is_array($value)) {
return in_array($rowValue, $value, true);
} elseif (! is_string($value)) {
if (is_string($rowValue)) {
$value = (string) $value;
}
}

return $rowValue === $value;
Expand All @@ -279,23 +274,26 @@ protected function performEqualityMatch($value, $rowValue, $ignoreCase = false)
/**
* Apply similarity matching rules on the given row value
*
* @param string|string[] $value
* @param string $rowValue
* @param mixed $value
* @param mixed $rowValue
* @param bool $ignoreCase
*
* @return bool
*/
protected function performSimilarityMatch($value, $rowValue, $ignoreCase = false)
{
if ($ignoreCase) {
if ($ignoreCase && is_string($rowValue)) {
$rowValue = strtolower($rowValue);
/** @var string|string[] $value {@see self::normalizeTypes} ensures this is the case */
$value = is_array($value)
? array_map('strtolower', $value)
: strtolower($value);
: ($value === null ? null : strtolower($value)); // phpstan is wrong here
}

if (is_array($value)) {
return in_array($rowValue, $value, true);
} elseif (! is_string($value) || ! is_string($rowValue)) {
return $this->performEqualityMatch($value, $rowValue);
}

$wildcardSubSegments = preg_split('~\*~', $value);
Expand Down Expand Up @@ -394,7 +392,10 @@ public static function greaterThan($column, $value)
*/
protected function matchGreaterThan(GreaterThan $rule, $row)
{
return $this->extractValue($rule->getColumn(), $row) > $rule->getValue();
$rowValue = $this->extractValue($rule->getColumn(), $row);
$value = $rule->getValue();

return $rowValue !== null && $value !== null && $rowValue > $value;
}

/**
Expand All @@ -421,11 +422,9 @@ public static function lessThan($column, $value)
protected function matchLessThan(LessThan $rule, $row)
{
$rowValue = $this->extractValue($rule->getColumn(), $row);
if ($rowValue === null) {
return false;
}
$value = $rule->getValue();

return $rowValue < $rule->getValue();
return $rowValue !== null && $value !== null && $rowValue < $value;
}

/**
Expand All @@ -451,7 +450,10 @@ public static function greaterThanOrEqual($column, $value)
*/
protected function matchGreaterThanOrEqual(GreaterThanOrEqual $rule, $row)
{
return $this->extractValue($rule->getColumn(), $row) >= $rule->getValue();
$rowValue = $this->extractValue($rule->getColumn(), $row);
$value = $rule->getValue();

return $rowValue !== null && $value !== null && $rowValue >= $value;
}

/**
Expand All @@ -478,11 +480,9 @@ public static function lessThanOrEqual($column, $value)
protected function matchLessThanOrEqual(LessThanOrEqual $rule, $row)
{
$rowValue = $this->extractValue($rule->getColumn(), $row);
if ($rowValue === null) {
return false;
}
$value = $rule->getValue();

return $rowValue <= $rule->getValue();
return $rowValue !== null && $value !== null && $rowValue <= $value;
}

/**
Expand Down Expand Up @@ -538,7 +538,7 @@ protected function extractValue($column, $row)
{
try {
return $row->{$column};
} catch (Exception $_) {
} catch (Throwable $_) {
return null;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/PriorityQueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public function yieldAll()
$queue->setExtractFlags(static::EXTR_BOTH);

foreach ($queue as $item) {
/** @var array{priority: array{0: mixed, 1: int}, data: mixed} $item */
yield $item['priority'][0] => $item['data'];
}
}
Expand Down
14 changes: 10 additions & 4 deletions src/Seq.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ public static function find($traversable, $needle, $caseSensitive = true)
$item = strtolower($item);
}

if ($usesCallback && $needle($item)) {
return [$key, $originalItem];
if ($usesCallback) {
/** @var Closure $needle */
if ($needle($item)) {
return [$key, $originalItem];
}
} elseif ($item === $needle) {
return [$key, $originalItem];
}
Expand Down Expand Up @@ -99,8 +102,11 @@ public static function findValue($traversable, $needle, $caseSensitive = true)
$key = strtolower($key);
}

if ($usesCallback && $needle($key)) {
return $item;
if ($usesCallback) {
/** @var Closure $needle */
if ($needle($key)) {
return $item;
}
} elseif ($key === $needle) {
return $item;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Str.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static function startsWith(?string $subject, string $start, bool $caseSen
*/
public static function symmetricSplit(?string $subject, string $delimiter, int $limit, $default = null)
{
if ($subject === null) {
if ($subject === null || empty($delimiter)) {
return array_pad([], $limit, $default);
}

Expand All @@ -78,7 +78,7 @@ public static function symmetricSplit(?string $subject, string $delimiter, int $
*/
public static function trimSplit(?string $subject, string $delimiter = ',', int $limit = null)
{
if ($subject === null) {
if ($subject === null || empty($delimiter)) {
return [];
}

Expand Down
86 changes: 82 additions & 4 deletions tests/FilterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -457,10 +457,6 @@ public function testConditionsAreValueTypeAgnostic()
Filter::match(Filter::equal('some_id', null), ['some_id' => null]),
"Filter\Equal fails to match NULL"
);
$this->assertFalse(
Filter::match(Filter::equal('some_id', 0), ['some_id' => null]),
"Filter\Equal doesn't compare NULL strictly"
);
$this->assertTrue(
Filter::match(Filter::greaterThan('length', '21'), ['length' => 22]),
"Filter\GreaterThan fails to match strings with integers"
Expand All @@ -471,6 +467,88 @@ public function testConditionsAreValueTypeAgnostic()
);
}

public function testConditionsNeverEvaluateToTrueForNullValues()
{
$this->assertFalse(
Filter::match(Filter::equal('some_id', 0), ['some_id' => null]),
"Filter\Equal doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::equal('some_id', null), ['some_id' => 0]),
"Filter\Equal doesn't compare NULL always to FALSE"
);

$this->assertFalse(
Filter::match(Filter::equal('some_id', null), ['some_id' => '']),
"Filter\Equal doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::equal('some_id', ''), ['some_id' => null]),
"Filter\Equal doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::equal('some_col', null)->ignoreCase(), ['some_col' => '']),
"Filter\Equal doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::equal('some_col', '')->ignoreCase(), ['some_col' => null]),
"Filter\Equal doesn't compare NULL always to FALSE"
);

$this->assertFalse(
Filter::match(Filter::like('some_id', null), ['some_id' => '']),
"Filter\Like doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::like('some_id', ''), ['some_id' => null]),
"Filter\Like doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::like('some_col', null)->ignoreCase(), ['some_col' => '']),
"Filter\Like doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::like('some_col', '')->ignoreCase(), ['some_col' => null]),
"Filter\Like doesn't compare NULL always to FALSE"
);

$this->assertFalse(
Filter::match(Filter::greaterThan('some_id', 1), ['some_id' => null]),
"Filter\GreaterThan doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::greaterThan('some_id', null), ['some_id' => -1]),
"Filter\GreaterThan doesn't compare NULL always to FALSE"
);

$this->assertFalse(
Filter::match(Filter::greaterThanOrEqual('some_id', 1), ['some_id' => null]),
"Filter\GreaterThanOrEqual doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::greaterThanOrEqual('some_id', null), ['some_id' => -1]),
"Filter\GreaterThanOrEqual doesn't compare NULL always to FALSE"
);

$this->assertFalse(
Filter::match(Filter::lessThan('some_id', -1), ['some_id' => null]),
"Filter\GreaterThan doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::lessThan('some_id', null), ['some_id' => 1]),
"Filter\GreaterThan doesn't compare NULL always to FALSE"
);

$this->assertFalse(
Filter::match(Filter::lessThanOrEqual('some_id', -1), ['some_id' => null]),
"Filter\GreaterThanOrEqual doesn't compare NULL always to FALSE"
);
$this->assertFalse(
Filter::match(Filter::lessThanOrEqual('some_id', null), ['some_id' => 1]),
"Filter\GreaterThanOrEqual doesn't compare NULL always to FALSE"
);
}

public function testConditionsCanBeCloned()
{
$condition1 = Filter::equal('host', 'localhost');
Expand Down

0 comments on commit f14ee18

Please sign in to comment.