diff --git a/composer.json b/composer.json index 6b9856db..001e31f2 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "nunomaduro/collision": "^8.7.0", "nunomaduro/termwind": "^2.3.0", "pestphp/pest-plugin": "^3.0.0", - "pestphp/pest-plugin-arch": "^3.0.0", + "pestphp/pest-plugin-arch": "^3.1.0", "pestphp/pest-plugin-mutate": "^3.0.5", "phpunit/phpunit": "^11.5.15" }, diff --git a/src/Console/Thanks.php b/src/Console/Thanks.php index 332c311b..88965026 100644 --- a/src/Console/Thanks.php +++ b/src/Console/Thanks.php @@ -24,7 +24,7 @@ final readonly class Thanks */ private const FUNDING_MESSAGES = [ 'Star' => 'https://github.com/pestphp/pest', - 'News' => 'https://twitter.com/pestphp', + 'News' => 'https://x.com/enunomaduro', 'Videos' => 'https://youtube.com/@nunomaduro', 'Sponsor' => 'https://github.com/sponsors/nunomaduro', ]; diff --git a/src/Expectations/OppositeExpectation.php b/src/Expectations/OppositeExpectation.php index 59a25b4a..c4b0a0a5 100644 --- a/src/Expectations/OppositeExpectation.php +++ b/src/Expectations/OppositeExpectation.php @@ -74,7 +74,10 @@ final readonly class OppositeExpectation */ public function toUse(array|string $targets): ArchExpectation { - return GroupArchExpectation::fromExpectations($this->original, array_map(fn (string $target): SingleArchExpectation => ToUse::make($this->original, $target)->opposite( + /** @var Expectation|string> $original */ + $original = $this->original; + + return GroupArchExpectation::fromExpectations($original, array_map(fn (string $target): SingleArchExpectation => ToUse::make($original, $target)->opposite( fn () => $this->throwExpectationFailedException('toUse', $target), ), is_string($targets) ? [$targets] : $targets)); } @@ -84,8 +87,11 @@ final readonly class OppositeExpectation */ public function toHaveFileSystemPermissions(string $permissions): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => substr(sprintf('%o', fileperms($object->path)), -4) !== $permissions, sprintf('permissions not to be [%s]', $permissions), FileLineFinder::where(fn (string $line): bool => str_contains($line, '|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => isset($object->reflectionClass) === false || array_filter( Reflection::getMethodsFromReflectionClass($object->reflectionClass), @@ -124,8 +133,11 @@ final readonly class OppositeExpectation */ public function toHavePropertiesDocumented(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => isset($object->reflectionClass) === false || array_filter( Reflection::getPropertiesFromReflectionClass($object->reflectionClass), @@ -144,8 +156,11 @@ final readonly class OppositeExpectation */ public function toUseStrictTypes(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! (bool) preg_match('/^<\?php\s+declare\(.*?strict_types\s?=\s?1.*?\);/', (string) file_get_contents($object->path)), 'not to use strict types', FileLineFinder::where(fn (string $line): bool => str_contains($line, '|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! str_contains((string) file_get_contents($object->path), ' === ') && ! str_contains((string) file_get_contents($object->path), ' !== '), 'to use strict equality', FileLineFinder::where(fn (string $line): bool => str_contains($line, ' === ') || str_contains($line, ' !== ')), @@ -170,8 +188,11 @@ final readonly class OppositeExpectation */ public function toBeFinal(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! enum_exists($object->name) && ! $object->reflectionClass->isFinal(), 'not to be final', FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -183,8 +204,11 @@ final readonly class OppositeExpectation */ public function toBeReadonly(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! enum_exists($object->name) && ! $object->reflectionClass->isReadOnly() && assert(true), // @phpstan-ignore-line 'not to be readonly', FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -196,8 +220,11 @@ final readonly class OppositeExpectation */ public function toBeTrait(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! $object->reflectionClass->isTrait(), 'not to be trait', FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -217,8 +244,11 @@ final readonly class OppositeExpectation */ public function toBeAbstract(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! $object->reflectionClass->isAbstract(), 'not to be abstract', FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -234,8 +264,11 @@ final readonly class OppositeExpectation { $methods = is_array($method) ? $method : [$method]; + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => array_filter( $methods, fn (string $method): bool => $object->reflectionClass->hasMethod($method), @@ -266,8 +299,11 @@ final readonly class OppositeExpectation $state = new stdClass; + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, function (ObjectDescription $object) use ($methods, &$state): bool { $reflectionMethods = isset($object->reflectionClass) ? Reflection::getMethodsFromReflectionClass($object->reflectionClass, ReflectionMethod::IS_PUBLIC) @@ -309,8 +345,11 @@ final readonly class OppositeExpectation $state = new stdClass; + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, function (ObjectDescription $object) use ($methods, &$state): bool { $reflectionMethods = isset($object->reflectionClass) ? Reflection::getMethodsFromReflectionClass($object->reflectionClass, ReflectionMethod::IS_PROTECTED) @@ -352,8 +391,11 @@ final readonly class OppositeExpectation $state = new stdClass; + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, function (ObjectDescription $object) use ($methods, &$state): bool { $reflectionMethods = isset($object->reflectionClass) ? Reflection::getMethodsFromReflectionClass($object->reflectionClass, ReflectionMethod::IS_PRIVATE) @@ -389,8 +431,11 @@ final readonly class OppositeExpectation */ public function toBeEnum(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! $object->reflectionClass->isEnum(), 'not to be enum', FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -410,8 +455,11 @@ final readonly class OppositeExpectation */ public function toBeClass(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! class_exists($object->name), 'not to be class', FileLineFinder::where(fn (string $line): bool => true), @@ -431,8 +479,11 @@ final readonly class OppositeExpectation */ public function toBeInterface(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! $object->reflectionClass->isInterface(), 'not to be interface', FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -452,8 +503,11 @@ final readonly class OppositeExpectation */ public function toExtend(string $class): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! $object->reflectionClass->isSubclassOf($class), sprintf("not to extend '%s'", $class), FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -465,8 +519,11 @@ final readonly class OppositeExpectation */ public function toExtendNothing(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => $object->reflectionClass->getParentClass() !== false, 'to extend a class', FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -490,8 +547,11 @@ final readonly class OppositeExpectation { $traits = is_array($traits) ? $traits : [$traits]; + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, function (ObjectDescription $object) use ($traits): bool { foreach ($traits as $trait) { if (in_array($trait, $object->reflectionClass->getTraitNames(), true)) { @@ -515,8 +575,11 @@ final readonly class OppositeExpectation { $interfaces = is_array($interfaces) ? $interfaces : [$interfaces]; + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, function (ObjectDescription $object) use ($interfaces): bool { foreach ($interfaces as $interface) { if ($object->reflectionClass->implementsInterface($interface)) { @@ -536,8 +599,11 @@ final readonly class OppositeExpectation */ public function toImplementNothing(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => $object->reflectionClass->getInterfaceNames() !== [], 'to implement an interface', FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -557,8 +623,11 @@ final readonly class OppositeExpectation */ public function toHavePrefix(string $prefix): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! str_starts_with($object->reflectionClass->getShortName(), $prefix), "not to have prefix '{$prefix}'", FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -570,8 +639,11 @@ final readonly class OppositeExpectation */ public function toHaveSuffix(string $suffix): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! str_ends_with($object->reflectionClass->getName(), $suffix), "not to have suffix '{$suffix}'", FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), @@ -599,7 +671,10 @@ final readonly class OppositeExpectation */ public function toBeUsed(): ArchExpectation { - return ToBeUsedInNothing::make($this->original); + /** @var Expectation|string> $original */ + $original = $this->original; + + return ToBeUsedInNothing::make($original); } /** @@ -609,7 +684,10 @@ final readonly class OppositeExpectation */ public function toBeUsedIn(array|string $targets): ArchExpectation { - return GroupArchExpectation::fromExpectations($this->original, array_map(fn (string $target): GroupArchExpectation => ToBeUsedIn::make($this->original, $target)->opposite( + /** @var Expectation|string> $original */ + $original = $this->original; + + return GroupArchExpectation::fromExpectations($original, array_map(fn (string $target): GroupArchExpectation => ToBeUsedIn::make($original, $target)->opposite( fn () => $this->throwExpectationFailedException('toBeUsedIn', $target), ), is_string($targets) ? [$targets] : $targets)); } @@ -632,8 +710,11 @@ final readonly class OppositeExpectation */ public function toBeInvokable(): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! $object->reflectionClass->hasMethod('__invoke'), 'to not be invokable', FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')) @@ -645,8 +726,11 @@ final readonly class OppositeExpectation */ public function toHaveAttribute(string $attribute): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => $object->reflectionClass->getAttributes($attribute) === [], "to not have attribute '{$attribute}'", FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')) @@ -737,8 +821,11 @@ final readonly class OppositeExpectation */ private function toBeBackedEnum(string $backingType): ArchExpectation { + /** @var Expectation|string> $original */ + $original = $this->original; + return Targeted::make( - $this->original, + $original, fn (ObjectDescription $object): bool => ! $object->reflectionClass->isEnum() || ! (new \ReflectionEnum($object->name))->isBacked() // @phpstan-ignore-line || (string) (new \ReflectionEnum($object->name))->getBackingType() !== $backingType, // @phpstan-ignore-line