Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

phpstan: Streamline vendor file location with local dev-env #51

Merged
merged 5 commits into from
Feb 12, 2024
Merged
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
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
Loading