Skip to content
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
22 changes: 9 additions & 13 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
php-version: '8.4'
coverage: none
extensions: mbstring

Expand All @@ -40,14 +40,15 @@ jobs:
run: >-
composer run cs-check

- name: Run backwards compatibility check
run: >-
composer run bc-check

- name: Run static analysis
run: >-
composer run static-analysis

# Disabled for version 2.0, since there is no BC promise at this time.
# - name: Run backwards compatibility check
# run: >-
# composer run bc-check

tests:
name: Tests

Expand All @@ -56,11 +57,6 @@ jobs:
strategy:
matrix:
php-version:
- "7.2"
- "7.3"
- "7.4"
- "8.0"
- "8.1"
- "8.2"
- "8.3"
- "8.4"
Expand All @@ -76,7 +72,7 @@ jobs:
php-version: "${{ matrix.php-version }}"
coverage: none
extensions: mbstring
tools: phpunit:8
tools: phpunit:11

- name: Install dependencies
run: composer install
Expand All @@ -92,7 +88,7 @@ jobs:
strategy:
matrix:
php-version:
- "7.4"
- "8.2"
- "8.3"
- "8.4"
- "8.5"
Expand All @@ -107,7 +103,7 @@ jobs:
php-version: "${{ matrix.php-version }}"
coverage: none
extensions: mbstring
tools: phpunit:8
tools: phpunit:11

- name: Install dependencies
run: composer install
Expand Down
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
Changelog
=========

## UNRELEASED
## 2.0.0 (UNRELEASED)

### Changed

- **BREAKING**: Minimum PHP version is now 8.2 (was 7.2).
- Updated CI/CD test matrix to test PHP 8.2, 8.3, 8.4, and 8.5.
- Updated development tools (PHPUnit, Psalm, PHP-CS-Fixer) to supported versions.
- Added explicit return types and parameter types to all methods in both source code and tests.
- Remove deprecated `isTraversable` (use `isIterable` or `isInstanceOf` instead).

## 1.12.0

Expand Down
43 changes: 22 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ Method | Description
`resource($value, $type = null, $message = '')` | Check that a value is a resource
`isCallable($value, $message = '')` | Check that a value is a callable
`isArray($value, $message = '')` | Check that a value is an array
`isTraversable($value, $message = '')` (deprecated) | Check that a value is an array or a `\Traversable`
`isIterable($value, $message = '')` | Check that a value is an array or a `\Traversable`
`isCountable($value, $message = '')` | Check that a value is an array or a `\Countable`
`isInstanceOf($value, $class, $message = '')` | Check that a value is an `instanceof` a class
Expand All @@ -112,26 +111,28 @@ Method | Description

### Comparison Assertions

Method | Description
----------------------------------------------- | ------------------------------------------------------------------
`true($value, $message = '')` | Check that a value is `true`
`false($value, $message = '')` | Check that a value is `false`
`notFalse($value, $message = '')` | Check that a value is not `false`
`null($value, $message = '')` | Check that a value is `null`
`notNull($value, $message = '')` | Check that a value is not `null`
`isEmpty($value, $message = '')` | Check that a value is `empty()`
`notEmpty($value, $message = '')` | Check that a value is not `empty()`
`eq($value, $value2, $message = '')` | Check that a value equals another (`==`)
`notEq($value, $value2, $message = '')` | Check that a value does not equal another (`!=`)
`same($value, $value2, $message = '')` | Check that a value is identical to another (`===`)
`notSame($value, $value2, $message = '')` | Check that a value is not identical to another (`!==`)
`greaterThan($value, $value2, $message = '')` | Check that a value is greater than another
`greaterThanEq($value, $value2, $message = '')` | Check that a value is greater than or equal to another
`lessThan($value, $value2, $message = '')` | Check that a value is less than another
`lessThanEq($value, $value2, $message = '')` | Check that a value is less than or equal to another
`range($value, $min, $max, $message = '')` | Check that a value is within a range
`inArray($value, array $values, $message = '')` | Check that a value is one of a list of values
`oneOf($value, array $values, $message = '')` | Check that a value is one of a list of values (alias of `inArray`)
Method | Description
----------------------------------------------------- | ------------------------------------------------------------------
`true($value, $message = '')` | Check that a value is `true`
`false($value, $message = '')` | Check that a value is `false`
`notFalse($value, $message = '')` | Check that a value is not `false`
`null($value, $message = '')` | Check that a value is `null`
`notNull($value, $message = '')` | Check that a value is not `null`
`isEmpty($value, $message = '')` | Check that a value is `empty()`
`notEmpty($value, $message = '')` | Check that a value is not `empty()`
`eq($value, $value2, $message = '')` | Check that a value equals another (`==`)
`notEq($value, $value2, $message = '')` | Check that a value does not equal another (`!=`)
`same($value, $value2, $message = '')` | Check that a value is identical to another (`===`)
`notSame($value, $value2, $message = '')` | Check that a value is not identical to another (`!==`)
`greaterThan($value, $value2, $message = '')` | Check that a value is greater than another
`greaterThanEq($value, $value2, $message = '')` | Check that a value is greater than or equal to another
`lessThan($value, $value2, $message = '')` | Check that a value is less than another
`lessThanEq($value, $value2, $message = '')` | Check that a value is less than or equal to another
`range($value, $min, $max, $message = '')` | Check that a value is within a range
`inArray($value, array $values, $message = '')` | Check that a value is one of a list of values
`notInArray($value, array $values, $message = '')` | Check that a value is not one of a list of values
`oneOf($value, array $values, $message = '')` | Check that a value is one of a list of values (alias of `inArray`)
`notOneOf($value, array $values, $message = '')` | Check that a value is not one of a list of values (alias of `notInArray`)

### String Assertions

Expand Down
89 changes: 64 additions & 25 deletions bin/src/MixinGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
namespace Webmozart\Assert\Bin;

use ArrayAccess;
use Closure;
use Countable;
use ReflectionClass;
use ReflectionException;
use ReflectionIntersectionType;
use ReflectionMethod;
use ReflectionType;
use ReflectionUnionType;
use RuntimeException;
use Throwable;
use Webmozart\Assert\Assert;

use function array_map;
use function implode;
use function rtrim;

final class MixinGenerator
Expand Down Expand Up @@ -56,6 +60,8 @@ public function generate(): string
<<<'PHP'
<?php

declare(strict_types=1);

%s
PHP
,
Expand All @@ -69,9 +75,7 @@ private function namespace(): string

$namespace = sprintf("namespace %s;\n\n", $assert->getNamespaceName());
$namespace .= sprintf("use %s;\n", ArrayAccess::class);
$namespace .= sprintf("use %s;\n", Closure::class);
$namespace .= sprintf("use %s;\n", Countable::class);
$namespace .= sprintf("use %s;\n", Throwable::class);
$namespace .= "\n";

$namespace .= $this->trait($assert);
Expand Down Expand Up @@ -200,6 +204,8 @@ private function assertion(ReflectionMethod $method, string $methodNameTemplate,
$parameters = [];
/** @psalm-var array<string, scalar|null> $parametersDefaults */
$parametersDefaults = [];
/** @var array<string, string> $parameterTypes */
$parameterTypes = [];
$parametersReflection = $method->getParameters();

foreach ($parametersReflection as $parameterReflection) {
Expand All @@ -212,6 +218,18 @@ private function assertion(ReflectionMethod $method, string $methodNameTemplate,

$parametersDefaults[$parameterReflection->name] = $defaultValue;
}

if ($parameterReflection->hasType()) {
if ($parameterReflection->name === 'value' && $typeTemplate) {
$parameterTypes[$parameterReflection->name] = match ($typeTemplate) {
'%s|null' => $this->reduceParameterType($parameterReflection->getType()),
'iterable<%s>' => 'iterable',
'iterable<%s|null>' => '?iterable',
};
} else {
$parameterTypes[$parameterReflection->name] = $this->reduceParameterType($parameterReflection->getType());
}
}
}

if (in_array($newMethodName, $this->skipMethods, true)) {
Expand Down Expand Up @@ -278,7 +296,7 @@ private function assertion(ReflectionMethod $method, string $methodNameTemplate,
$phpdocLines[] = trim($comment);
}

if ('deprecated' === $key || 'psalm-pure' === $key) {
if ('deprecated' === $key || 'psalm-pure' === $key || 'psalm-assert' === $key || 'see' === $key) {
$phpdocLines[] = '';
}
}
Expand All @@ -296,7 +314,24 @@ private function assertion(ReflectionMethod $method, string $methodNameTemplate,
$phpdocLinesDeduplicatedEmptyLines[] = $line;
}

return $this->staticMethod($newMethodName, $parameters, $parametersDefaults, $phpdocLinesDeduplicatedEmptyLines, $indent, $body);
return $this->staticMethod($newMethodName, $parameters, $parameterTypes, $parametersDefaults, $phpdocLinesDeduplicatedEmptyLines, $indent, $body);
}

private function reduceParameterType(ReflectionType $type): string
{
if ($type instanceof ReflectionIntersectionType) {
return implode('&', array_map([$this, 'reduceParameterType'], $type->getTypes()));
}

if ($type instanceof ReflectionUnionType) {
return implode('|', array_map([$this, 'reduceParameterType'], $type->getTypes()));
}

if ($type->getName() === 'mixed') {
return $type->getName();
}

return ($type->allowsNull() ? '?' : '') . $type->getName();
}

private function applyTypeTemplate(string $type, string $typeTemplate): string
Expand Down Expand Up @@ -353,26 +388,27 @@ private function findLongestTypeAndName(array $values): array
}

/**
* @psalm-param list<string> $parameters
* @psalm-param array<string, scalar|null> $defaultValues
* @psalm-param list<string> $phpdocLines
* @psalm-param list<string> $parameters
* @psalm-param array<string, scalar|null> $defaults
* @psalm-param list<string> $phpdocLines
* @psalm-param callable(string,string):string $body
*
* @param string $name
* @param string[] $parameters
* @param string[] $defaultValues
* @param array $phpdocLines
* @param int $indent
* @param callable $body
* @param string $name
* @param string[] $parameters
* @param array<string, string> $types
* @param string[] $defaults
* @param array $phpdocLines
* @param int $indent
* @param callable $body
*
* @return string
*/
private function staticMethod(string $name, array $parameters, array $defaultValues, array $phpdocLines, int $indent, callable $body): string
private function staticMethod(string $name, array $parameters, array $types, array $defaults, array $phpdocLines, int $indent, callable $body): string
{
$indentation = str_repeat(' ', $indent);

$staticFunction = $this->phpdoc($phpdocLines, $indent)."\n";
$staticFunction .= $indentation.'public static function '.$name.$this->functionParameters($parameters, $defaultValues)."\n"
$staticFunction .= $indentation.'public static function '.$name.$this->functionParameters($parameters, $types, $defaults).": void\n"
.$indentation."{\n";

$firstParameter = '$'.array_shift($parameters);
Expand All @@ -387,15 +423,16 @@ private function staticMethod(string $name, array $parameters, array $defaultVal
}

/**
* @psalm-param list<string> $parameters
* @psalm-param array<string, scalar|null> $defaultValues
* @psalm-param list<string> $parameters
* @psalm-param array<string, scalar|null> $defaults
*
* @param string[] $parameters
* @param string[] $defaultValues
* @param string[] $parameters
* @param array<string, string> $types
* @param string[] $defaults
*
* @return string
*/
private function functionParameters(array $parameters, array $defaultValues): string
private function functionParameters(array $parameters, array $types, array $defaults): string
{
$result = '';

Expand All @@ -404,10 +441,12 @@ private function functionParameters(array $parameters, array $defaultValues): st
$result .= ', ';
}

$result .= '$'.$parameter;
Assert::keyExists($types, $parameter);

$result .= $types[$parameter].' $'.$parameter;

if (array_key_exists($parameter, $defaultValues)) {
$defaultValue = null === $defaultValues[$parameter] ? 'null' : var_export($defaultValues[$parameter], true);
if (array_key_exists($parameter, $defaults)) {
$defaultValue = null === $defaults[$parameter] ? 'null' : var_export($defaults[$parameter], true);

$result .= ' = '.$defaultValue;
}
Expand Down
8 changes: 6 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
},
{
"name": "Woody Gilk",
"email": "woody.gilk@gmail.com"
}
],
"require": {
"php": "^7.2 || ^8.0",
"php": "^8.2",
"ext-ctype": "*",
"ext-date": "*",
"ext-filter": "*"
Expand All @@ -37,7 +41,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.10-dev"
"dev-feature/2-0": "2.0-dev"
}
},
"scripts": {
Expand Down
33 changes: 14 additions & 19 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>

<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
verbose="true"
>
<testsuites>
<testsuite name="Webmozart Assert Test Suite">
<directory>./tests/</directory>
<exclude>./tests/static-analysis</exclude>
</testsuite>
</testsuites>

<!-- Whitelist for code coverage -->
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
</whitelist>
</filter>
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="Webmozart Assert Test Suite">
<directory>./tests/</directory>
<exclude>./tests/static-analysis</exclude>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">./src/</directory>
</include>
</source>
</phpunit>
Loading