From c8c60658d55e16a9e69f08649677d52f94cbed5a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 04:58:26 +0000 Subject: [PATCH] Update dependency tomasvotruba/type-coverage to ^2.3.0 --- tools/.phpstan/composer.json | 2 +- tools/.phpstan/composer.lock | 94 +- tools/.phpstan/vendor/composer/installed.json | 100 +- tools/.phpstan/vendor/composer/installed.php | 19 +- .../tomasvotruba/type-coverage/README.md | 403 ++ .../composer-dependency-analyser.php | 7 +- .../tomasvotruba/type-coverage/composer.json | 21 +- .../packages/type-perfect/LICENSE | 25 + .../type-perfect/config/extension.neon | 94 + .../PublicClassMethodParamTypesCollector.php | 81 + .../MethodCallArgTypesCollector.php | 77 + .../MethodCallableCollector.php | 63 + .../type-perfect/src/Configuration.php | 80 + .../src/Enum/Types/ResolvedTypes.php | 10 + .../src/Guard/EmptyIssetGuard.php | 37 + .../ClassMethodCallReferenceResolver.php | 55 + .../Collector/PublicClassMethodMatcher.php | 67 + .../src/NodeFinder/ClassMethodNodeFinder.php | 51 + .../src/NodeFinder/MethodCallNodeFinder.php | 59 + .../src/NodeFinder/ReturnNodeFinder.php | 69 + .../src/NodeVisitor/CallableNodeVisitor.php | 42 + .../src/PhpDoc/ApiDocStmtAnalyzer.php | 37 + .../src/Printer/CollectorMetadataPrinter.php | 248 + .../src/Printer/NodeComparator.php | 25 + .../src/Reflection/MethodNodeAnalyser.php | 46 + .../src/Reflection/ReflectionParser.php | 84 + .../NarrowPrivateClassMethodParamTypeRule.php | 180 + .../NarrowPublicClassMethodParamTypeRule.php | 165 + .../src/Rules/NarrowReturnObjectTypeRule.php | 140 + .../src/Rules/NoArrayAccessOnObjectRule.php | 81 + .../src/Rules/NoEmptyOnObjectRule.php | 54 + .../src/Rules/NoIssetOnObjectRule.php | 60 + .../src/Rules/NoMixedMethodCallerRule.php | 88 + .../src/Rules/NoMixedPropertyFetcherRule.php | 62 + .../src/Rules/NoParamTypeRemovalRule.php | 100 + .../src/Rules/ReturnNullOverFalseRule.php | 101 + .../src/ValueObject/MethodCallReference.php | 24 + .../DifferentClassSameMethodCallName.php | 31 + .../Fixture/DoubleShot.php | 18 + .../Fixture/Fixture.php | 22 + .../Fixture/SkipAbstractBase.php | 16 + .../Fixture/SkipAlreadyCorrectType.php | 22 + .../Fixture/SkipCorrectUnionType.php | 32 + ...pDuplicatedCallOfSameMethodWithComment.php | 26 + .../Fixture/SkipGenericType.php | 31 + .../Fixture/SkipMayOverrideArg.php | 27 + .../Fixture/SkipMixed.php | 18 + .../Fixture/SkipMultipleUsed.php | 27 + .../Fixture/SkipNoArgs.php | 15 + .../Fixture/SkipNotFromThis.php | 14 + .../Fixture/SkipNotPrivate.php | 22 + .../Fixture/SkipOptedOut.php | 23 + .../Fixture/SkipParentNotIf.php | 15 + .../Fixture/SkipRecursive.php | 30 + .../Fixture/SkipSameGeneric.php | 34 + .../Fixture/SkipVariadics.php | 21 + ...rowPrivateClassMethodParamTypeRuleTest.php | 74 + .../Source/AnotherClassWithRun.php | 15 + .../Source/ConceptBase.php | 10 + .../Source/ConceptImpl1.php | 9 + .../Source/MyIterator.php | 12 + .../Source/MyPreise.php | 9 + .../Fixture/ExplicitlyNullableParams.php | 21 + .../FirstClassCallables/CallVariadics.php | 15 + .../FirstClassCallables/SomeCalledMethod.php | 12 + .../Fixture/Generics/SkipPassedGenerics.php | 24 + .../Fixture/Generics/SkipSameGeneric.php | 19 + .../Fixture/HandleDefaultValue.php | 30 + .../Fixture/PublicDoubleShot.php | 12 + .../Fixture/SkipApiMarked.php | 17 + .../Fixture/SkipCallable.php | 18 + .../Fixture/SkipClassStringPassed.php | 12 + .../Fixture/SkipClosure.php | 18 + .../Fixture/SkipDateTimeMix.php | 23 + .../Fixture/SkipDefault.php | 21 + .../Fixture/SkipEnum.php | 19 + .../Fixture/SkipEqualUnionType.php | 44 + .../Fixture/SkipExpectedClassType.php | 14 + .../Fixture/SkipMixedAndString.php | 12 + .../Fixture/SkipNonPublicClassMethod.php | 17 + .../Fixture/SkipNullableCompare.php | 14 + .../Fixture/SkipProperlyFilledParamType.php | 12 + .../Fixture/SkipResource.php | 33 + .../Fixture/SkipSelf.php | 17 + .../Fixture/SkipThisPassedExactType.php | 14 + .../SkipUsedInternallyForSecondType.php | 20 + .../Fixture/ThisPassedFromInterface.php | 14 + ...rrowPublicClassMethodParamTypeRuleTest.php | 202 + .../ExpectedClassString/FirstTypedCaller.php | 17 + .../ExpectedClassString/SecondTypedCaller.php | 17 + .../FirstClassTypedCaller.php | 17 + .../SecondClassTypedCaller.php | 17 + .../ExpectedNodeApi/CallWithProperty.php | 17 + .../Source/ExpectedThisType/CallByThis.php | 15 + .../CallByThisFromInterface.php | 16 + .../Source/ExpectedType/FirstTypedCaller.php | 15 + .../OnlyFirstTypeCalledOutside.php | 16 + .../Source/ExpectedType/SecondTypedCaller.php | 15 + .../ExpectedUnion/CallUnionArrayType.php | 31 + .../Source/ExpectedUnion/CallUnionType.php | 22 + .../ExpectedUnion/CallUnionTypeFlipped.php | 22 + .../CallUnionTypeTernaryIfElse.php | 22 + .../CallUnionTypeTernaryIfElseFlipped.php | 22 + .../Source/FirstCaller.php | 15 + .../Source/MixedAndString/FirstCaller.php | 15 + .../Source/MixedAndString/SecondCaller.php | 15 + .../Source/MixedAndString/ThirdCaller.php | 15 + .../Source/NullableParam/FirstNullable.php | 16 + .../Source/NullableParam/SecondNullable.php | 16 + .../Source/PassMeAsType.php | 10 + .../Source/PickReasonEnum.php | 11 + .../Source/SecondCaller.php | 15 + .../Source/SkipSameGeneric/MyGroup.php | 10 + .../Source/SkipSameGeneric/MyIterator.php | 12 + .../Source/SkipSameGeneric/MyService.php | 26 + .../Source/SomeInterface.php | 8 + .../Fixture/SkipSomeContract.php | 17 + .../Fixture/SkipSpecificReturnType.php | 15 + .../Fixture/SomeAbstractReturnType.php | 16 + .../NarrowReturnObjectTypeRuleTest.php | 51 + .../Source/AbstractControl.php | 9 + .../Source/SomeContract.php | 12 + .../Source/SpecificControl.php | 9 + .../Fixture/ArrayAccessOnNestedObject.php | 16 + .../Fixture/ArrayAccessOnObject.php | 16 + .../Fixture/SkipIterator.php | 15 + .../Fixture/SkipOnArray.php | 13 + .../Fixture/SkipSplFixedArray.php | 15 + .../Fixture/SkipUriElement.php | 15 + .../Fixture/SkipWeakMap.php | 15 + .../Fixture/SkipXml.php | 15 + .../Fixture/SkipXmlElementForeach.php | 17 + .../NoArrayAccessOnObjectRuleTest.php | 55 + .../ChildOfSomeClassWithArrayAccess.php | 9 + .../Source/SomeClassWithArrayAccess.php | 26 + .../Source/SomeClassWithSplFixedArray.php | 11 + .../Fixture/EmptyOnObject.php | 23 + .../Fixture/SkipEmptyOnArray.php | 15 + .../SkipEmptyOnArrayNestedOnObject.php | 17 + .../Fixture/SkipPhpDocType.php | 12 + .../Fixture/SkipPossibleUndefinedVariable.php | 19 + .../NoEmptyOnObjectRuleTest.php | 51 + .../Fixture/IssetOnObject.php | 23 + .../Fixture/SkipIssetOnArray.php | 15 + .../SkipIssetOnArrayNestedOnObject.php | 17 + .../Fixture/SkipIssetOnPropertyFetch.php | 27 + .../Fixture/SkipPhpDocType.php | 12 + .../Fixture/SkipPossibleUndefinedVariable.php | 19 + .../NoIssetOnObjectRuleTest.php | 52 + .../Rules/NoIssetOnObjectRule/Source/Foo.php | 13 + .../Fixture/MagicMethodName.php | 13 + .../Fixture/SkipKnownCallerType.php | 15 + .../Fixture/SkipMockObject.php | 19 + .../Fixture/SkipPHPUnitMock.php | 21 + .../Fixture/UnknownCallerType.php | 13 + .../NoMixedMethodCallerRuleTest.php | 54 + .../Source/KnownType.php | 12 + .../Source/SomeFinalClass.php | 10 + .../Fixture/DynamicName.php | 13 + .../Fixture/SkipDynamicNameWithKnownType.php | 15 + .../Fixture/SkipKnownFetcherType.php | 15 + .../Fixture/UnknownPropertyFetcher.php | 13 + .../NoMixedPropertyFetcherRuleTest.php | 53 + .../Source/KnownType.php | 10 + .../Fixture/AnInterface.php | 10 + .../Fixture/AnInterfaceOther.php | 12 + ...sDifferentParameterWithInterfaceMethod.php | 16 + .../HasDifferentParameterWithParentMethod.php | 12 + .../Fixture/ParentClass.php | 12 + .../Fixture/RemoveParentType.php | 14 + .../Fixture/SkipConstruct.php | 20 + ...kipHasSameParameterWithInterfaceMethod.php | 18 + .../SkipHasSameParameterWithParentMethod.php | 12 + .../Fixture/SkipIndirectRemoval.php | 18 + .../Fixture/SkipNoParent.php | 9 + .../Fixture/SkipNoType.php | 14 + .../Fixture/SkipNotHasParentMethod.php | 12 + .../Fixture/SkipPhpDocType.php | 17 + .../Fixture/SkipPresentType.php | 15 + .../NoParamTypeRemovalRuleTest.php | 67 + .../Source/NoTypeInterface.php | 10 + .../Source/PhpDocType.php | 15 + .../Source/SomeNode.php | 10 + .../Source/SomeRectorInterface.php | 10 + .../Fixture/CheckResultFromOtherMethod.php | 29 + .../Fixture/CheckResultFromOtherMethod2.php | 31 + .../Fixture/ReturnFalseOnly.php | 17 + .../Fixture/SkipReturnBool.php | 17 + .../ReturnNullOverFalseRuleTest.php | 50 + .../type-perfect/tests/SomeClass.php.inc | 13 + .../tests/config/included_services.neon | 2 + .../vendor/webmozart/assert/CHANGELOG.md | 315 + .../.phpstan/vendor/webmozart/assert/LICENSE | 20 + .../vendor/webmozart/assert/README.md | 313 + .../vendor/webmozart/assert/composer.json | 71 + .../vendor/webmozart/assert/src/Assert.php | 2620 ++++++++ .../vendor/webmozart/assert/src/HasAssert.php | 179 + .../assert/src/InvalidArgumentException.php | 17 + .../vendor/webmozart/assert/src/Mixin.php | 5827 +++++++++++++++++ .../webmozart/assert/src/PsalmPlugin.php | 50 + 200 files changed, 15475 insertions(+), 40 deletions(-) create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/LICENSE create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/config/extension.neon create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/ClassMethod/PublicClassMethodParamTypesCollector.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/MethodCall/MethodCallArgTypesCollector.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/MethodCallableNode/MethodCallableCollector.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Configuration.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Enum/Types/ResolvedTypes.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Guard/EmptyIssetGuard.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Matcher/ClassMethodCallReferenceResolver.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Matcher/Collector/PublicClassMethodMatcher.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/ClassMethodNodeFinder.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/MethodCallNodeFinder.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/ReturnNodeFinder.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeVisitor/CallableNodeVisitor.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/PhpDoc/ApiDocStmtAnalyzer.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Printer/CollectorMetadataPrinter.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Printer/NodeComparator.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Reflection/MethodNodeAnalyser.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Reflection/ReflectionParser.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowPrivateClassMethodParamTypeRule.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowPublicClassMethodParamTypeRule.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowReturnObjectTypeRule.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoArrayAccessOnObjectRule.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoEmptyOnObjectRule.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoIssetOnObjectRule.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoMixedMethodCallerRule.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoMixedPropertyFetcherRule.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoParamTypeRemovalRule.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/ReturnNullOverFalseRule.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/ValueObject/MethodCallReference.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/DifferentClassSameMethodCallName.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/DoubleShot.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/Fixture.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipAbstractBase.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipAlreadyCorrectType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipCorrectUnionType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipDuplicatedCallOfSameMethodWithComment.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipGenericType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMayOverrideArg.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMixed.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMultipleUsed.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNoArgs.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNotFromThis.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNotPrivate.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipOptedOut.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipParentNotIf.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipRecursive.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipSameGeneric.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipVariadics.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/NarrowPrivateClassMethodParamTypeRuleTest.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Source/AnotherClassWithRun.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Source/ConceptBase.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Source/ConceptImpl1.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Source/MyIterator.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Source/MyPreise.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/ExplicitlyNullableParams.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/FirstClassCallables/CallVariadics.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/FirstClassCallables/SomeCalledMethod.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/Generics/SkipPassedGenerics.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/Generics/SkipSameGeneric.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/HandleDefaultValue.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/PublicDoubleShot.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipApiMarked.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipCallable.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipClassStringPassed.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipClosure.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipDateTimeMix.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipDefault.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipEnum.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipEqualUnionType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipExpectedClassType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipMixedAndString.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipNonPublicClassMethod.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipNullableCompare.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipProperlyFilledParamType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipResource.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipSelf.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipThisPassedExactType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipUsedInternallyForSecondType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/ThisPassedFromInterface.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/NarrowPublicClassMethodParamTypeRuleTest.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassString/FirstTypedCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassString/SecondTypedCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassType/FirstClassTypedCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassType/SecondClassTypedCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedNodeApi/CallWithProperty.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedThisType/CallByThis.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedThisType/CallByThisFromInterface.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/FirstTypedCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/OnlyFirstTypeCalledOutside.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/SecondTypedCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionArrayType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeFlipped.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeTernaryIfElse.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeTernaryIfElseFlipped.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/FirstCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/FirstCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/SecondCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/ThirdCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/NullableParam/FirstNullable.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/NullableParam/SecondNullable.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/PassMeAsType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/PickReasonEnum.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SecondCaller.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SkipSameGeneric/MyGroup.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SkipSameGeneric/MyIterator.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SkipSameGeneric/MyService.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SomeInterface.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowReturnObjectTypeRule/Fixture/SkipSomeContract.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowReturnObjectTypeRule/Fixture/SkipSpecificReturnType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowReturnObjectTypeRule/Fixture/SomeAbstractReturnType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowReturnObjectTypeRule/NarrowReturnObjectTypeRuleTest.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowReturnObjectTypeRule/Source/AbstractControl.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowReturnObjectTypeRule/Source/SomeContract.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowReturnObjectTypeRule/Source/SpecificControl.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Fixture/ArrayAccessOnNestedObject.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Fixture/ArrayAccessOnObject.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Fixture/SkipIterator.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Fixture/SkipOnArray.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Fixture/SkipSplFixedArray.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Fixture/SkipUriElement.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Fixture/SkipWeakMap.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Fixture/SkipXml.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Fixture/SkipXmlElementForeach.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/NoArrayAccessOnObjectRuleTest.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Source/ChildOfSomeClassWithArrayAccess.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Source/SomeClassWithArrayAccess.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Source/SomeClassWithSplFixedArray.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/Fixture/EmptyOnObject.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/Fixture/SkipEmptyOnArray.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/Fixture/SkipEmptyOnArrayNestedOnObject.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/Fixture/SkipPhpDocType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/Fixture/SkipPossibleUndefinedVariable.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/NoEmptyOnObjectRuleTest.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/IssetOnObject.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipIssetOnArray.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipIssetOnArrayNestedOnObject.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipIssetOnPropertyFetch.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipPhpDocType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipPossibleUndefinedVariable.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/NoIssetOnObjectRuleTest.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Source/Foo.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/MagicMethodName.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipKnownCallerType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipMockObject.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipPHPUnitMock.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/UnknownCallerType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/NoMixedMethodCallerRuleTest.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Source/KnownType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Source/SomeFinalClass.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/DynamicName.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/SkipDynamicNameWithKnownType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/SkipKnownFetcherType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/UnknownPropertyFetcher.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/NoMixedPropertyFetcherRuleTest.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Source/KnownType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/AnInterface.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/AnInterfaceOther.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/HasDifferentParameterWithInterfaceMethod.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/HasDifferentParameterWithParentMethod.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/ParentClass.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/RemoveParentType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/SkipConstruct.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/SkipHasSameParameterWithInterfaceMethod.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/SkipHasSameParameterWithParentMethod.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/SkipIndirectRemoval.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/SkipNoParent.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/SkipNoType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/SkipNotHasParentMethod.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/SkipPhpDocType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Fixture/SkipPresentType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/NoParamTypeRemovalRuleTest.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Source/NoTypeInterface.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Source/PhpDocType.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Source/SomeNode.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Source/SomeRectorInterface.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/Fixture/CheckResultFromOtherMethod.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/Fixture/CheckResultFromOtherMethod2.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/Fixture/ReturnFalseOnly.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/Fixture/SkipReturnBool.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/ReturnNullOverFalseRuleTest.php create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/SomeClass.php.inc create mode 100644 tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/config/included_services.neon create mode 100644 tools/.phpstan/vendor/webmozart/assert/CHANGELOG.md create mode 100644 tools/.phpstan/vendor/webmozart/assert/LICENSE create mode 100644 tools/.phpstan/vendor/webmozart/assert/README.md create mode 100644 tools/.phpstan/vendor/webmozart/assert/composer.json create mode 100644 tools/.phpstan/vendor/webmozart/assert/src/Assert.php create mode 100644 tools/.phpstan/vendor/webmozart/assert/src/HasAssert.php create mode 100644 tools/.phpstan/vendor/webmozart/assert/src/InvalidArgumentException.php create mode 100644 tools/.phpstan/vendor/webmozart/assert/src/Mixin.php create mode 100644 tools/.phpstan/vendor/webmozart/assert/src/PsalmPlugin.php diff --git a/tools/.phpstan/composer.json b/tools/.phpstan/composer.json index 6448ba0..a4ed808 100644 --- a/tools/.phpstan/composer.json +++ b/tools/.phpstan/composer.json @@ -3,7 +3,7 @@ "phpstan/phpstan": "^2.2.7", "phpstan/extension-installer": "^1.4.3", "phpstan/phpstan-strict-rules": "^2.0.12", - "tomasvotruba/type-coverage": "^2.2.2", + "tomasvotruba/type-coverage": "^2.3.0", "ergebnis/phpstan-rules": "^2.13.1" }, "config": { diff --git a/tools/.phpstan/composer.lock b/tools/.phpstan/composer.lock index dd527c9..44c207a 100644 --- a/tools/.phpstan/composer.lock +++ b/tools/.phpstan/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f960036a066807c312a2d48672c1e045", + "content-hash": "3579343022b2a28c7195fe30b1752bfb", "packages": [], "packages-dev": [ { @@ -246,29 +246,31 @@ }, { "name": "tomasvotruba/type-coverage", - "version": "2.2.2", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/TomasVotruba/type-coverage.git", - "reference": "25f298265c823e8fb0505169135855e1e6a91021" + "reference": "7ba48c1bf3bbb7ffe99caacc7017bae5ee72d644" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/25f298265c823e8fb0505169135855e1e6a91021", - "reference": "25f298265c823e8fb0505169135855e1e6a91021", + "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/7ba48c1bf3bbb7ffe99caacc7017bae5ee72d644", + "reference": "7ba48c1bf3bbb7ffe99caacc7017bae5ee72d644", "shasum": "" }, "require": { "php": "^8.4", - "phpstan/phpstan": "^2.2" + "phpstan/phpstan": "^2.2", + "webmozart/assert": "^1.11 || ^2.1" }, "require-dev": { "phpstan/extension-installer": "^1.4", - "phpunit/phpunit": "^12.5", + "phpunit/phpunit": "^13.2", "rector/jack": "^1.0", - "rector/rector": "^2.4", + "rector/rector": "^2.5", "shipmonk/composer-dependency-analyser": "^1.8", - "symplify/easy-coding-standard": "^13.1", + "symfony/dom-crawler": "^8.1", + "symplify/easy-coding-standard": "^13.2", "tomasvotruba/unused-public": "^2.2", "tracy/tracy": "^2.12" }, @@ -276,12 +278,14 @@ "extra": { "phpstan": { "includes": [ - "config/extension.neon" + "config/extension.neon", + "packages/type-perfect/config/extension.neon" ] } }, "autoload": { "psr-4": { + "Rector\\TypePerfect\\": "packages/type-perfect/src", "TomasVotruba\\TypeCoverage\\": "src" } }, @@ -296,7 +300,7 @@ ], "support": { "issues": "https://github.com/TomasVotruba/type-coverage/issues", - "source": "https://github.com/TomasVotruba/type-coverage/tree/2.2.2" + "source": "https://github.com/TomasVotruba/type-coverage/tree/2.3.0" }, "funding": [ { @@ -308,7 +312,73 @@ "type": "github" } ], - "time": "2026-06-07T12:35:00+00:00" + "time": "2026-07-29T20:32:37+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, + "branch-alias": { + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.4.1" + }, + "time": "2026-06-15T15:31:57+00:00" } ], "aliases": [], diff --git a/tools/.phpstan/vendor/composer/installed.json b/tools/.phpstan/vendor/composer/installed.json index aeb0329..24df1c3 100644 --- a/tools/.phpstan/vendor/composer/installed.json +++ b/tools/.phpstan/vendor/composer/installed.json @@ -251,45 +251,49 @@ }, { "name": "tomasvotruba/type-coverage", - "version": "2.2.2", - "version_normalized": "2.2.2.0", + "version": "2.3.0", + "version_normalized": "2.3.0.0", "source": { "type": "git", "url": "https://github.com/TomasVotruba/type-coverage.git", - "reference": "25f298265c823e8fb0505169135855e1e6a91021" + "reference": "7ba48c1bf3bbb7ffe99caacc7017bae5ee72d644" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/25f298265c823e8fb0505169135855e1e6a91021", - "reference": "25f298265c823e8fb0505169135855e1e6a91021", + "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/7ba48c1bf3bbb7ffe99caacc7017bae5ee72d644", + "reference": "7ba48c1bf3bbb7ffe99caacc7017bae5ee72d644", "shasum": "" }, "require": { "php": "^8.4", - "phpstan/phpstan": "^2.2" + "phpstan/phpstan": "^2.2", + "webmozart/assert": "^1.11 || ^2.1" }, "require-dev": { "phpstan/extension-installer": "^1.4", - "phpunit/phpunit": "^12.5", + "phpunit/phpunit": "^13.2", "rector/jack": "^1.0", - "rector/rector": "^2.4", + "rector/rector": "^2.5", "shipmonk/composer-dependency-analyser": "^1.8", - "symplify/easy-coding-standard": "^13.1", + "symfony/dom-crawler": "^8.1", + "symplify/easy-coding-standard": "^13.2", "tomasvotruba/unused-public": "^2.2", "tracy/tracy": "^2.12" }, - "time": "2026-06-07T12:35:00+00:00", + "time": "2026-07-29T20:32:37+00:00", "type": "phpstan-extension", "extra": { "phpstan": { "includes": [ - "config/extension.neon" + "config/extension.neon", + "packages/type-perfect/config/extension.neon" ] } }, "installation-source": "dist", "autoload": { "psr-4": { + "Rector\\TypePerfect\\": "packages/type-perfect/src", "TomasVotruba\\TypeCoverage\\": "src" } }, @@ -304,7 +308,7 @@ ], "support": { "issues": "https://github.com/TomasVotruba/type-coverage/issues", - "source": "https://github.com/TomasVotruba/type-coverage/tree/2.2.2" + "source": "https://github.com/TomasVotruba/type-coverage/tree/2.3.0" }, "funding": [ { @@ -317,6 +321,75 @@ } ], "install-path": "../tomasvotruba/type-coverage" + }, + { + "name": "webmozart/assert", + "version": "2.4.1", + "version_normalized": "2.4.1.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "reference": "2ccb7c2e821038c03a3e6e1700c570c158c55f70", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "time": "2026-06-15T15:31:57+00:00", + "type": "library", + "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, + "branch-alias": { + "dev-master": "2.0-dev", + "dev-feature/2-0": "2.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.4.1" + }, + "install-path": "../webmozart/assert" } ], "dev": true, @@ -325,6 +398,7 @@ "phpstan/extension-installer", "phpstan/phpstan", "phpstan/phpstan-strict-rules", - "tomasvotruba/type-coverage" + "tomasvotruba/type-coverage", + "webmozart/assert" ] } diff --git a/tools/.phpstan/vendor/composer/installed.php b/tools/.phpstan/vendor/composer/installed.php index 1d9c8bc..d32e9cf 100644 --- a/tools/.phpstan/vendor/composer/installed.php +++ b/tools/.phpstan/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => '__root__', 'pretty_version' => '7.0.x-dev', 'version' => '7.0.9999999.9999999-dev', - 'reference' => '0107b5050a17e24a8d66433ce851e34c2144dc30', + 'reference' => '724ed522d74918b0d84a50cdee4d0d2f7a5ba67c', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -13,7 +13,7 @@ '__root__' => array( 'pretty_version' => '7.0.x-dev', 'version' => '7.0.9999999.9999999-dev', - 'reference' => '0107b5050a17e24a8d66433ce851e34c2144dc30', + 'reference' => '724ed522d74918b0d84a50cdee4d0d2f7a5ba67c', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -56,13 +56,22 @@ 'dev_requirement' => true, ), 'tomasvotruba/type-coverage' => array( - 'pretty_version' => '2.2.2', - 'version' => '2.2.2.0', - 'reference' => '25f298265c823e8fb0505169135855e1e6a91021', + 'pretty_version' => '2.3.0', + 'version' => '2.3.0.0', + 'reference' => '7ba48c1bf3bbb7ffe99caacc7017bae5ee72d644', 'type' => 'phpstan-extension', 'install_path' => __DIR__ . '/../tomasvotruba/type-coverage', 'aliases' => array(), 'dev_requirement' => true, ), + 'webmozart/assert' => array( + 'pretty_version' => '2.4.1', + 'version' => '2.4.1.0', + 'reference' => '2ccb7c2e821038c03a3e6e1700c570c158c55f70', + 'type' => 'library', + 'install_path' => __DIR__ . '/../webmozart/assert', + 'aliases' => array(), + 'dev_requirement' => true, + ), ), ); diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/README.md b/tools/.phpstan/vendor/tomasvotruba/type-coverage/README.md index cb7e71f..4abb940 100644 --- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/README.md +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/README.md @@ -138,3 +138,406 @@ vendor/bin/phpstan
Happy coding! + +
+ +--- + +# Type Perfect + +[![Downloads](https://img.shields.io/packagist/dt/rector/type-perfect.svg?style=flat-square)](https://packagist.org/packages/rector/type-perfect/stats) + +Next level type declaration check PHPStan rules. + +We use these sets to improve code quality of our clients' code beyond PHPStan features. + +* These rules make skipped object types explicit, param types narrow and help you to fill more accurate object type hints. +* **They're easy to enable, even if your code does not pass level 0** +* They're effortless to resolve and make your code instantly more solid and reliable. + +If you care about code quality and type safety, add these 10 rules to your CI. + +
+ +These rules ship with the package [installed above](#install), there is nothing else to require. + +*Migrating from `rector/type-perfect`? Remove it from your `composer.json`, the rules live here now.* + +
+ +Every rule is opt-in and disabled by default, so you can pick the ones that fit your project. See [Configure](#configure) below for the full list. + +The 3 checks below are the simplest ones to start with: + +```yaml +parameters: + type_perfect: + no_isset_on_object: true + no_empty_on_object: true + no_array_access_on_object: true + no_param_type_removal: true +``` + +The first one makes sure we don't miss a chance to use `instanceof` to make further code know about exact object type: + +```php +private ?SomeType $someType = null; + +if (! empty($this->someType)) { + // ... +} + +if (! isset($this->someType)) { + // ... +} + +// here we only know, that $this->someType is not empty/null +``` + +:no_good: + +↓ + + +```php +if (! $this->someType instanceof SomeType) { + return; +} + +// here we know $this->someType is exactly SomeType +``` + +:heavy_check_mark: + +
+ +Second rule (`no_array_access_on_object`) checks we use explicit object methods over magic array access: + +```php +$article = new Article(); + +$id = $article['id']; +// we have no idea, what the type is +``` + +:no_good: + +↓ + +```php +$id = $article->getId(); +// we know the type is int +``` + +:heavy_check_mark: + +
+ +Last rule (`no_param_type_removal`) checks that all interface implementations follow the same method signature as the interface: + +```php +interface SomeInterface +{ + public function doSomething(int $value): void; +} + +final class SomeClass implements SomeInterface +{ + public function doSomething($value): void { // ... } +} +``` + +:no_good: + +↓ + +```php +final class SomeClass implements SomeInterface +{ + public function doSomething(int $value): void { // ... } +} +``` + +:heavy_check_mark: + +
+ +## Configure + +All rules are enabled by configuration and disabled by default. We take them from the simplest to more powerful, in the same order we apply them on legacy projects. + +You can enable them all at once: + +```yaml +parameters: + type_perfect: + # the 3 checks above + no_isset_on_object: true + no_empty_on_object: true + no_array_access_on_object: true + no_param_type_removal: true + + no_mixed_property: true + no_mixed_caller: true + null_over_false: true + narrow_param: true + narrow_return: true +``` + +Or one by one: + +
+ +## 1. Null over False + +```yaml +parameters: + type_perfect: + null_over_false: true +``` + +Bool types are typically used for on/off, yes/no responses. But sometimes, the `false` is misused as *no-result* response, where `null` would be more accurate: + +```php +public function getProduct() +{ + if (...) { + return $product; + } + + return false; +} +``` + +:no_good: + +↓ + +We should use `null` instead, as it enabled strict type declaration in form of `?Product` since PHP 7.1: + +```php +public function getProduct(): ?Product +{ + if (...) { + return $product; + } + + return null; +} +``` + +:heavy_check_mark: + +
+ +## 2. No mixed Property + +```yaml +parameters: + type_perfect: + no_mixed_property: true +``` + +This rule focuses on PHPStan blind spot while fetching a property. If we have a property with unknown type, PHPStan is not be able to analyse it. It silently ignores it. + +```php +private $someType; + +public function run() +{ + $this->someType->vale; +} +``` + +It doesn't see there is a typo in `vale` property name. It should be `value` + +:no_good: + +↓ + + +```php +private SomeType $someType; + +public function run() +{ + $this->someType->value; +} +``` + +This rule makes sure all property fetches know their type they're called on. + +:heavy_check_mark: + +
+ +## 3. No mixed Caller + +```yaml +parameters: + type_perfect: + no_mixed_caller: true +``` + +Same as above, only for method calls: + +```php +private $someType; + +public function run() +{ + $this->someType->someMetho(1, 2); +} +``` + +It doesn't see there is a typo in `someMetho` name, and that the 2nd parameter must be `string`. + +:no_good: + +↓ + + +```php +private SomeType $someType; + +public function run() +{ + $this->someType->someMethod(1, 'active'); +} +``` + +This group makes sure methods call know their type they're called on. + +:heavy_check_mark: + +
+ +## 4. Narrow Param Types + +The more narrow param type we have, the reliable the code is. `string` beats `mixed`, `int` beats `scalar` and `ExactObject` beats `stdClass`. + +```yaml +parameters: + type_perfect: + narrow_param: true +``` + +In case of `private`, but also `public` method calls, our project often knows exact types that are passed in it: + +```php +// in one file +$product->addPrice(100.52); + +// another file +$product->addPrice(52.05); +``` + +But out of from fear and "just to be safe", we keep the `addPrice()` param type empty, `mixed` or in a docblock. + +:no_good: + +↓ + +If, in 100 % cases the `float` type is passed, PHPStan knows it can be added and improve further analysis: + +```diff + +-/** +- * @param float $price +- */ +-public function addPrice($price) ++public function addPrice(float $price) +{ + $this->price = $price; +} +``` + +That's where this group comes in. It checks all the passed types, and tells us know how to narrow the param type declaration. + +:heavy_check_mark: + +
+ +## 5. Narrow Return Types + +Last but not least, the more narrow return type, the more reliable the code. + +```yaml +parameters: + type_perfect: + narrow_return: true +``` + +Where does it help? Let's say we have 2 types of talks, that do have different behavior: + +```php +final class ConferenceTalk extends Talk +{ + public function bookHotel() + { + // ... + } +} + +final class MeetupTalk extends Talk +{ + public function bookTrain() + { + // ... + } +} +``` + +Then we have a factory (repository, or services) that returns generic `Talk` type: + +```php +final class TalkFactory +{ + public function createConferenceTalk(): Talk + { + return new ConferenceTalk(); + } + + public function createMeetupTalk(): Talk + { + return new MeetupTalk(); + } +} +``` + +In this case we've just lost strict type and have to verify the type on runtime: + +```php +$talk instanceof ConferenceTalk +``` + +:no_good: + +↓ + +That's where this group comes in. In case we return the exact type, we should use exact type in return type declaration to keep the code as reliable as possible: + +```diff + final class TalkFactory + { +- public function createConferenceTalk(): Talk ++ public function createConferenceTalk(): ConferenceTalk + { + return new ConferenceTalk(); + } + +- public function createMeetupTalk(): Talk ++ public function createMeetupTalk(): MeetupTalk + { + return new MeetupTalk(); + } +} +``` + +:heavy_check_mark: + +Add sets one by one, fix what you find helpful and ignore the rest. + +
+ +Happy coding! diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer-dependency-analyser.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer-dependency-analyser.php index dfa20ef..553c5bf 100644 --- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer-dependency-analyser.php +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer-dependency-analyser.php @@ -7,4 +7,9 @@ use ShipMonk\ComposerDependencyAnalyser\Config\ErrorType; return new Configuration() // PhpParser classes are provided by phpstan/phpstan, no need to require nikic/php-parser directly - ->ignoreErrorsOnPackage('nikic/php-parser', [ErrorType::SHADOW_DEPENDENCY]); + ->ignoreErrorsOnPackage('nikic/php-parser', [ErrorType::SHADOW_DEPENDENCY]) + // type-perfect test fixtures are static analysis inputs, not real code, and reference 3rd-party classes on purpose + ->ignoreErrorsOnPath( + __DIR__ . '/packages/type-perfect/tests', + [ErrorType::UNKNOWN_CLASS, ErrorType::SHADOW_DEPENDENCY] + ); diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json b/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json index d3493db..e706ef4 100644 --- a/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/composer.json @@ -6,26 +6,30 @@ "keywords": ["static analysis", "phpstan-extension"], "require": { "php": "^8.4", - "phpstan/phpstan": "^2.2" + "phpstan/phpstan": "^2.2", + "webmozart/assert": "^1.11 || ^2.1" }, "require-dev": { "phpstan/extension-installer": "^1.4", - "phpunit/phpunit": "^12.5", - "symplify/easy-coding-standard": "^13.1", - "rector/rector": "^2.4", + "phpunit/phpunit": "^13.2", + "symplify/easy-coding-standard": "^13.2", + "rector/rector": "^2.5", "tracy/tracy": "^2.12", "tomasvotruba/unused-public": "^2.2", "rector/jack": "^1.0", - "shipmonk/composer-dependency-analyser": "^1.8" + "shipmonk/composer-dependency-analyser": "^1.8", + "symfony/dom-crawler": "^8.1" }, "autoload": { "psr-4": { - "TomasVotruba\\TypeCoverage\\": "src" + "TomasVotruba\\TypeCoverage\\": "src", + "Rector\\TypePerfect\\": "packages/type-perfect/src" } }, "autoload-dev": { "psr-4": { - "TomasVotruba\\TypeCoverage\\Tests\\": "tests" + "TomasVotruba\\TypeCoverage\\Tests\\": "tests", + "Rector\\TypePerfect\\Tests\\": "packages/type-perfect/tests" } }, "scripts": { @@ -43,7 +47,8 @@ "extra": { "phpstan": { "includes": [ - "config/extension.neon" + "config/extension.neon", + "packages/type-perfect/config/extension.neon" ] } } diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/LICENSE b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/LICENSE new file mode 100644 index 0000000..2dbf78e --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/LICENSE @@ -0,0 +1,25 @@ +The MIT License +--------------- + +Copyright (c) 2020 Tomas Votruba (https://tomasvotruba.com) + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/config/extension.neon b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/config/extension.neon new file mode 100644 index 0000000..e590d9b --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/config/extension.neon @@ -0,0 +1,94 @@ +parametersSchema: + # see https://doc.nette.org/en/schema for configuration + type_perfect: structure([ + narrow_param: bool() + narrow_return: bool() + no_mixed: bool() + null_over_false: bool() + + # replace for no_mixed + no_mixed_property: bool() + no_mixed_caller: bool() + + no_param_type_removal: bool() + no_array_access_on_object: bool() + no_isset_on_object: bool() + no_empty_on_object: bool() + ]) + +# defaults +parameters: + type_perfect: + narrow_param: false + narrow_return: false + no_mixed: false + null_over_false: false + # priority override of no_mixed + no_mixed_property: false + no_mixed_caller: false + + no_param_type_removal: false + no_array_access_on_object: false + no_isset_on_object: false + no_empty_on_object: false + +rules: + # turn on by the group name + + # no_param_type_removal + - Rector\TypePerfect\Rules\NoParamTypeRemovalRule + + # no_array_access_on_object + - Rector\TypePerfect\Rules\NoArrayAccessOnObjectRule + + # no_isset_on_object + - Rector\TypePerfect\Rules\NoIssetOnObjectRule + + # no_empty_on_object + - Rector\TypePerfect\Rules\NoEmptyOnObjectRule + + # narrow_param + - Rector\TypePerfect\Rules\NarrowPublicClassMethodParamTypeRule + - Rector\TypePerfect\Rules\NarrowPrivateClassMethodParamTypeRule + + # narrow_return + - Rector\TypePerfect\Rules\NarrowReturnObjectTypeRule + + # no_mixed + - Rector\TypePerfect\Rules\NoMixedPropertyFetcherRule + - Rector\TypePerfect\Rules\NoMixedMethodCallerRule + + # null_over_false + - Rector\TypePerfect\Rules\ReturnNullOverFalseRule + +services: + - + factory: Rector\TypePerfect\Configuration + arguments: + - %type_perfect% + + - Rector\TypePerfect\NodeFinder\ClassMethodNodeFinder + - Rector\TypePerfect\NodeFinder\MethodCallNodeFinder + - Rector\TypePerfect\NodeFinder\ReturnNodeFinder + - Rector\TypePerfect\PhpDoc\ApiDocStmtAnalyzer + - Rector\TypePerfect\Printer\NodeComparator + - Rector\TypePerfect\Reflection\ReflectionParser + - Rector\TypePerfect\Reflection\MethodNodeAnalyser + - Rector\TypePerfect\Matcher\Collector\PublicClassMethodMatcher + - Rector\TypePerfect\Matcher\ClassMethodCallReferenceResolver + - Rector\TypePerfect\Printer\CollectorMetadataPrinter + - Rector\TypePerfect\Guard\EmptyIssetGuard + + # for NarrowPublicClassMethodParamTypeByCallerTypeRule + - + class: Rector\TypePerfect\Collector\ClassMethod\PublicClassMethodParamTypesCollector + tags: [phpstan.collector] + + - + class: Rector\TypePerfect\Collector\MethodCall\MethodCallArgTypesCollector + tags: [phpstan.collector] + + - + class: Rector\TypePerfect\Collector\MethodCallableNode\MethodCallableCollector + tags: + - phpstan.collector diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/ClassMethod/PublicClassMethodParamTypesCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/ClassMethod/PublicClassMethodParamTypesCollector.php new file mode 100644 index 0000000..8f7390b --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/ClassMethod/PublicClassMethodParamTypesCollector.php @@ -0,0 +1,81 @@ + + */ +final readonly class PublicClassMethodParamTypesCollector implements Collector +{ + public function __construct( + private ApiDocStmtAnalyzer $apiDocStmtAnalyzer, + private PublicClassMethodMatcher $publicClassMethodMatcher, + private CollectorMetadataPrinter $collectorMetadataPrinter, + private Configuration $configuration + ) { + } + + public function getNodeType(): string + { + return ClassMethod::class; + } + + /** + * @param ClassMethod $node + * @return array{class-string, string, string, int}|null + */ + public function processNode(Node $node, Scope $scope): ?array + { + if (! $this->configuration->isNarrowParamEnabled()) { + return null; + } + + if ($node->params === []) { + return null; + } + + if ($this->publicClassMethodMatcher->shouldSkipClassMethod($node)) { + return null; + } + + // only if the class has no parents/implementers, to avoid class method required by contracts + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + if ($this->apiDocStmtAnalyzer->hasApiDoc($node, $classReflection)) { + return null; + } + + if ($this->publicClassMethodMatcher->shouldSkipClassReflection($classReflection)) { + return null; + } + + $methodName = $node->name->toString(); + + // is this method required by parent contract? skip it + if ($this->publicClassMethodMatcher->isUsedByParentClassOrInterface($classReflection, $methodName)) { + return null; + } + + $printedParamTypesString = $this->collectorMetadataPrinter->printParamTypesToString( + $node, + $classReflection, + $scope + ); + return [$classReflection->getName(), $methodName, $printedParamTypesString, $node->getLine()]; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/MethodCall/MethodCallArgTypesCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/MethodCall/MethodCallArgTypesCollector.php new file mode 100644 index 0000000..341d6bc --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/MethodCall/MethodCallArgTypesCollector.php @@ -0,0 +1,77 @@ + + */ +final readonly class MethodCallArgTypesCollector implements Collector +{ + public function __construct( + private ClassMethodCallReferenceResolver $classMethodCallReferenceResolver, + private CollectorMetadataPrinter $collectorMetadataPrinter, + private Configuration $configuration + ) { + } + + public function getNodeType(): string + { + return MethodCall::class; + } + + /** + * @param MethodCall $node + * @return array{string, string}|null + */ + public function processNode(Node $node, Scope $scope): ?array + { + if (! $this->configuration->isNarrowParamEnabled()) { + return null; + } + + if ($node->isFirstClassCallable() || $node->getArgs() === [] || ! $node->name instanceof Identifier) { + return null; + } + + $methodCalledOnType = $scope->getType($node->var); + $methodReflection = $scope->getMethodReflection($methodCalledOnType, $node->name->name); + if (! $methodReflection instanceof ExtendedMethodReflection) { + return null; + } + + $classMethodCallReference = $this->classMethodCallReferenceResolver->resolve($node, $scope, true); + if (! $classMethodCallReference instanceof MethodCallReference) { + return null; + } + + $classMethodReference = $this->createClassMethodReference($classMethodCallReference); + + $stringArgTypesString = $this->collectorMetadataPrinter->printArgTypesAsString( + $node, + $methodReflection, + $scope + ); + return [$classMethodReference, $stringArgTypesString]; + } + + private function createClassMethodReference(MethodCallReference $classMethodCallReference): string + { + $className = $classMethodCallReference->getClass(); + $methodName = $classMethodCallReference->getMethod(); + + return $className . '::' . $methodName; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/MethodCallableNode/MethodCallableCollector.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/MethodCallableNode/MethodCallableCollector.php new file mode 100644 index 0000000..0d52c65 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Collector/MethodCallableNode/MethodCallableCollector.php @@ -0,0 +1,63 @@ +|null> + * + * PHPStan has special node for first class callables of MethodCall + * + * @see https://github.com/phpstan/phpstan-src/blob/511c1e435fb43b8eb0ac310e6aa3230147963790/src/Analyser/NodeScopeResolver.php#L1936 + */ +final readonly class MethodCallableCollector implements Collector +{ + public function __construct( + private ClassMethodCallReferenceResolver $classMethodCallReferenceResolver, + private Configuration $configuration + ) { + } + + public function getNodeType(): string + { + return MethodCallableNode::class; + } + + /** + * @param MethodCallableNode $node + * @return array{string}|null + */ + public function processNode(Node $node, Scope $scope): ?array + { + if (! $this->configuration->isNarrowParamEnabled()) { + return null; + } + + $classMethodCallReference = $this->classMethodCallReferenceResolver->resolve($node, $scope, true); + if (! $classMethodCallReference instanceof MethodCallReference) { + return null; + } + + $classMethodReference = $this->createClassMethodReference($classMethodCallReference); + + // special case that should skip everything + return [$classMethodReference]; + } + + private function createClassMethodReference(MethodCallReference $classMethodCallReference): string + { + $className = $classMethodCallReference->getClass(); + $methodName = $classMethodCallReference->getMethod(); + + return $className . '::' . $methodName; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Configuration.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Configuration.php new file mode 100644 index 0000000..c944604 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Configuration.php @@ -0,0 +1,80 @@ + $parameters + */ + public function __construct( + private array $parameters + ) { + // enabed by default in tests + if (defined('PHPUNIT_COMPOSER_INSTALL')) { + $this->parameters['narrow_param'] = true; + $this->parameters['narrow_return'] = true; + $this->parameters['no_mixed'] = true; + $this->parameters['null_over_false'] = true; + $this->parameters['no_param_type_removal'] = true; + $this->parameters['no_array_access_on_object'] = true; + $this->parameters['no_isset_on_object'] = true; + $this->parameters['no_empty_on_object'] = true; + } + } + + public function isNarrowParamEnabled(): bool + { + return $this->parameters['narrow_param'] ?? false; + } + + public function isNarrowReturnEnabled(): bool + { + return $this->parameters['narrow_return'] ?? false; + } + + public function isNoMixedPropertyEnabled(): bool + { + if ($this->parameters['no_mixed_property']) { + return true; + } + + return $this->parameters['no_mixed'] ?? false; + } + + public function isNoMixedCallerEnabled(): bool + { + if ($this->parameters['no_mixed_caller']) { + return true; + } + + return $this->parameters['no_mixed'] ?? false; + } + + public function isNoFalsyReturnEnabled(): bool + { + return $this->parameters['null_over_false'] ?? false; + } + + public function isNoParamTypeRemovalEnabled(): bool + { + return $this->parameters['no_param_type_removal'] ?? false; + } + + public function isNoArrayAccessOnObjectEnabled(): bool + { + return $this->parameters['no_array_access_on_object'] ?? false; + } + + public function isNoIssetOnObjectEnabled(): bool + { + return $this->parameters['no_isset_on_object'] ?? false; + } + + public function isNoEmptyOnObjectEnabled(): bool + { + return $this->parameters['no_empty_on_object'] ?? false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Enum/Types/ResolvedTypes.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Enum/Types/ResolvedTypes.php new file mode 100644 index 0000000..2680c43 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Enum/Types/ResolvedTypes.php @@ -0,0 +1,10 @@ +name instanceof Expr) { + return true; + } + + if (! $scope->hasVariableType($expr->name)->yes()) { + return true; + } + + $varType = $scope->getNativeType($expr); + $varType = TypeCombinator::removeNull($varType); + return $varType->getObjectClassNames() === []; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Matcher/ClassMethodCallReferenceResolver.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Matcher/ClassMethodCallReferenceResolver.php new file mode 100644 index 0000000..efbdfe8 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Matcher/ClassMethodCallReferenceResolver.php @@ -0,0 +1,55 @@ +getName(); + $variable = $methodCallOrMethodCallable->getVar(); + } else { + $methodName = $methodCallOrMethodCallable->name; + $variable = $methodCallOrMethodCallable->var; + } + + if ($methodName instanceof Expr) { + return null; + } + + $callerType = $scope->getType($variable); + + // remove optional nullable type + if (TypeCombinator::containsNull($callerType)) { + $callerType = TypeCombinator::removeNull($callerType); + } + + if (! $allowThisType && $callerType instanceof ThisType) { + return null; + } + + if (count($callerType->getObjectClassNames()) !== 1) { + return null; + } + + // move to the class where method is defined, e.g. parent class defines the method, so it should be checked there + $className = $callerType->getObjectClassNames()[0]; + $methodNameString = $methodName->toString(); + + return new MethodCallReference($className, $methodNameString); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Matcher/Collector/PublicClassMethodMatcher.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Matcher/Collector/PublicClassMethodMatcher.php new file mode 100644 index 0000000..b95cc40 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Matcher/Collector/PublicClassMethodMatcher.php @@ -0,0 +1,67 @@ +isClass()) { + return true; + } + + return array_any(self::SKIPPED_TYPES, fn (string $skippedType): bool => $classReflection->is($skippedType)); + } + + public function isUsedByParentClassOrInterface(ClassReflection $classReflection, string $methodName): bool + { + // is this method required by parent contract? skip it + foreach ($classReflection->getInterfaces() as $parentInterfaceReflection) { + if ($parentInterfaceReflection->hasMethod($methodName)) { + return true; + } + } + + return array_any($classReflection->getParents(), fn (ClassReflection $parentClassReflection): bool => $parentClassReflection->hasMethod($methodName)); + } + + public function shouldSkipClassMethod(ClassMethod $classMethod): bool + { + if ($classMethod->isMagic()) { + return true; + } + + if ($classMethod->isStatic()) { + return true; + } + + // skip attributes + if ($classMethod->attrGroups !== []) { + return true; + } + + if (! $classMethod->isPublic()) { + return true; + } + + $doc = $classMethod->getDocComment(); + + // skip symfony action + return $doc instanceof Doc && str_contains($doc->getText(), '@Route'); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/ClassMethodNodeFinder.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/ClassMethodNodeFinder.php new file mode 100644 index 0000000..6f45c4c --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/ClassMethodNodeFinder.php @@ -0,0 +1,51 @@ +getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + $classLike = $this->reflectionParser->parseClassReflection($classReflection); + if (! $classLike instanceof Class_) { + return null; + } + + if (! $methodCall->name instanceof Identifier) { + return null; + } + + $methodCallName = $methodCall->name->toString(); + + $classMethod = $classLike->getMethod($methodCallName); + if (! $classMethod instanceof ClassMethod) { + return null; + } + + if (! $classMethod->isPrivate()) { + return null; + } + + return $classMethod; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/MethodCallNodeFinder.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/MethodCallNodeFinder.php new file mode 100644 index 0000000..040c1c5 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/MethodCallNodeFinder.php @@ -0,0 +1,59 @@ +nodeFinder = new NodeFinder(); + } + + /** + * @return MethodCall[] + */ + public function findUsages(MethodCall $methodCall, Scope $scope): array + { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return []; + } + + $classLike = $this->reflectionParser->parseClassReflection($classReflection); + if (! $classLike instanceof Class_) { + return []; + } + + $methodCalls = $this->nodeFinder->find($classLike, function (Node $node) use ($methodCall): bool { + if (! $node instanceof MethodCall) { + return false; + } + + if (! $this->nodeComparator->areNodesEqual($node->var, $methodCall->var)) { + return false; + } + + return $this->nodeComparator->areNodesEqual($node->name, $methodCall->name); + }); + + Assert::allIsInstanceOf($methodCalls, MethodCall::class); + + return $methodCalls; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/ReturnNodeFinder.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/ReturnNodeFinder.php new file mode 100644 index 0000000..471a01f --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeFinder/ReturnNodeFinder.php @@ -0,0 +1,69 @@ +findReturnsWithValues($classMethod); + if (count($returns) !== 1) { + return null; + } + + $onlyReturn = $returns[0]; + return $onlyReturn->expr; + } + + /** + * @return Return_[] + */ + public function findReturnsWithValues(ClassMethod $classMethod): array + { + $returns = []; + + $this->traverseNodesWithCallable((array) $classMethod->stmts, static function ( + Node $node + ) use (&$returns) { + // skip different scope + if ($node instanceof FunctionLike) { + return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; + } + + if (! $node instanceof Return_) { + return null; + } + + if (! $node->expr instanceof Expr) { + return null; + } + + $returns[] = $node; + }); + + return $returns; + } + + /** + * @param callable(Node $node): (int|Node|null) $callable + * @param Node[] $nodes + */ + private function traverseNodesWithCallable(array $nodes, callable $callable): void + { + $nodeTraverser = new NodeTraverser(); + + $callableNodeVisitor = new CallableNodeVisitor($callable); + $nodeTraverser->addVisitor($callableNodeVisitor); + $nodeTraverser->traverse($nodes); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeVisitor/CallableNodeVisitor.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeVisitor/CallableNodeVisitor.php new file mode 100644 index 0000000..e0bb569 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/NodeVisitor/CallableNodeVisitor.php @@ -0,0 +1,42 @@ +callable = $callable; + } + + public function enterNode(Node $node): int|Node|null + { + $originalNode = $node; + $callable = $this->callable; + + /** @var int|Node|null $newNode */ + $newNode = $callable($node); + + if ($originalNode instanceof Stmt && $newNode instanceof Expr) { + return new Expression($newNode); + } + + return $newNode; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/PhpDoc/ApiDocStmtAnalyzer.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/PhpDoc/ApiDocStmtAnalyzer.php new file mode 100644 index 0000000..c6b2307 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/PhpDoc/ApiDocStmtAnalyzer.php @@ -0,0 +1,37 @@ +hasClassReflectionApiDoc($classReflection)) { + return true; + } + + $docComment = $classMethod->getDocComment(); + if (! $docComment instanceof Doc) { + return false; + } + + return str_contains($docComment->getText(), '@api'); + } + + private function hasClassReflectionApiDoc(ClassReflection $classReflection): bool + { + if (! $classReflection->getResolvedPhpDoc() instanceof ResolvedPhpDocBlock) { + return false; + } + + $resolvedPhpDocBlock = $classReflection->getResolvedPhpDoc(); + return str_contains($resolvedPhpDocBlock->getPhpDocString(), '@api'); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Printer/CollectorMetadataPrinter.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Printer/CollectorMetadataPrinter.php new file mode 100644 index 0000000..26a1e73 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Printer/CollectorMetadataPrinter.php @@ -0,0 +1,248 @@ +standard = new Standard(); + } + + public function printArgTypesAsString( + MethodCall $methodCall, + ExtendedMethodReflection $extendedMethodReflection, + Scope $scope + ): string { + $parametersAcceptor = ParametersAcceptorSelector::selectFromArgs( + $scope, + $methodCall->getArgs(), + $extendedMethodReflection->getVariants(), + $extendedMethodReflection->getNamedArgumentsVariants() + ); + $parameters = $parametersAcceptor->getParameters(); + + $stringArgTypes = []; + foreach ($methodCall->getArgs() as $i => $arg) { + $argType = $scope->getType($arg->value); + + // we have no idea, nothing we can do + if ($argType instanceof MixedType) { + return ResolvedTypes::UNKNOWN_TYPES; + } + + if ($argType instanceof IntersectionType) { + return ResolvedTypes::UNKNOWN_TYPES; + } + + if ($argType instanceof GenericObjectType) { + return ResolvedTypes::UNKNOWN_TYPES; + } + + if ($argType instanceof PHPStanUnionType) { + return ResolvedTypes::UNKNOWN_TYPES; + } + + if ($argType instanceof ThisType) { + $argType = new ObjectType($argType->getClassName()); + } + + if (array_key_exists($i, $parameters)) { + $defaultValueType = $parameters[$i]->getDefaultValue(); + if ($defaultValueType instanceof Type) { + $argType = TypeCombinator::union($argType, $defaultValueType); + } + } + + $printedArgType = $this->printTypeToString($argType); + $printedArgType = $this->normalizeDateTime($printedArgType); + + $stringArgTypes[] = $printedArgType; + } + + return implode('|', $stringArgTypes); + } + + public function printParamTypesToString( + ClassMethod $classMethod, + ClassReflection $classReflection, + Scope $scope + ): string { + $className = $classReflection->getName(); + + $parametersReflection = []; + if ($classReflection->hasMethod($classMethod->name->name)) { + $methodReflection = $classReflection->getMethod($classMethod->name->name, $scope); + $variants = $methodReflection->getVariants(); + if (count($variants) === 1) { + $parametersReflection = $variants[0]->getParameters(); + } + } + + $printedParamTypes = []; + foreach ($classMethod->params as $i => $param) { + if ($param->type === null) { + $printedParamTypes[] = ''; + continue; + } + + $phpdocType = null; + if (array_key_exists($i, $parametersReflection)) { + $paramphpdocType = $parametersReflection[$i]->getPhpDocType(); + if (! $paramphpdocType instanceof MixedType) { + $phpdocType = $paramphpdocType; + } + } + + if ($phpdocType instanceof Type) { + $printedParamType = $this->printTypeToString($phpdocType); + } else { + $paramType = $this->transformSelfToClassName($param->type, $className); + + if ($paramType instanceof NullableType) { + // unite to phpstan type + $paramType = new UnionType([$paramType->type, new Identifier('null')]); + } + + if ($paramType instanceof UnionType || $paramType instanceof NodeIntersectionType) { + $paramType = $this->resolveSortedTypes($paramType, $className); + } + + $printedParamType = $this->standard->prettyPrint([$paramType]); + $printedParamType = str_replace('\Closure', 'callable', $printedParamType); + $printedParamType = ltrim($printedParamType, '\\'); + $printedParamType = str_replace('|\\', '|', $printedParamType); + } + + // to avoid DateTime vs DateTimeImmutable vs DateTimeInterface conflicts + $printedParamType = $this->normalizeDateTime($printedParamType); + + $printedParamTypes[] = $printedParamType; + } + + return implode('|', $printedParamTypes); + } + + private function transformSelfToClassName(Node $node, ?string $className): Node + { + if (! $node instanceof Name || $className === null) { + return $node; + } + + if ($node->toString() !== 'self') { + return $node; + } + + return new FullyQualified($className); + } + + private function resolveSortedTypes( + UnionType|NodeIntersectionType $paramType, + ?string $className + ): UnionType|NodeIntersectionType { + $typeNames = []; + + foreach ($paramType->types as $type) { + if ($type instanceof NodeIntersectionType) { + foreach ($type->types as $intersectionType) { + /** @var Identifier|Name $intersectionType */ + $intersectionType = $this->transformSelfToClassName($intersectionType, $className); + $typeNames[] = (string) $intersectionType; + } + + continue; + } + + /** @var Identifier|Name $type */ + $type = $this->transformSelfToClassName($type, $className); + $typeNames[] = (string) $type; + } + + sort($typeNames); + + $types = []; + foreach ($typeNames as $typeName) { + $types[] = new Identifier($typeName); + } + + if ($paramType instanceof NodeIntersectionType) { + return new NodeIntersectionType($types); + } + + return new UnionType($types); + } + + private function printTypeToString(Type $type): string + { + if ($type->isClassString()->yes()) { + return 'string'; + } + + if ($type->isArray()->yes()) { + return 'array'; + } + + if ($type->isBoolean()->yes()) { + return 'bool'; + } + + if ($type instanceof IntegerRangeType) { + return 'int'; + } + + if ($type instanceof ClosureType) { + return 'callable'; + } + + if (count($type->getEnumCases()) === 1) { + return $type->getEnumCases()[0] + ->getClassName(); + } + + return $type->describe(VerbosityLevel::typeOnly()); + } + + private function normalizeDateTime(string $printedType): string + { + if ($printedType === 'DateTimeImmutable') { + return 'DateTimeInterface'; + } + + if ($printedType === 'DateTime') { + return 'DateTimeInterface'; + } + + return $printedType; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Printer/NodeComparator.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Printer/NodeComparator.php new file mode 100644 index 0000000..1d1efae --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Printer/NodeComparator.php @@ -0,0 +1,25 @@ +setAttribute('comments', null); + $secondNode->setAttribute('comments', null); + + return $this->printer->prettyPrint([$firstNode]) === $this->printer->prettyPrint([$secondNode]); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Reflection/MethodNodeAnalyser.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Reflection/MethodNodeAnalyser.php new file mode 100644 index 0000000..6e99e48 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Reflection/MethodNodeAnalyser.php @@ -0,0 +1,46 @@ +matchFirstParentClassMethod($scope, $methodName) instanceof MethodReflection; + } + + public function matchFirstParentClassMethod(Scope $scope, string $methodName): ?MethodReflection + { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + // the classes have highter priority, e.g. priority in class covariance + foreach ($classReflection->getParents() as $parentClassReflection) { + if ($parentClassReflection->hasNativeMethod($methodName)) { + return $parentClassReflection->getNativeMethod($methodName); + } + } + + foreach ($classReflection->getAncestors() as $ancestorClassReflection) { + if ($classReflection === $ancestorClassReflection) { + continue; + } + + if (! $ancestorClassReflection->hasNativeMethod($methodName)) { + continue; + } + + return $ancestorClassReflection->getNativeMethod($methodName); + } + + return null; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Reflection/ReflectionParser.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Reflection/ReflectionParser.php new file mode 100644 index 0000000..ab3fc54 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Reflection/ReflectionParser.php @@ -0,0 +1,84 @@ + + */ + private array $classesByFilename = []; + + private readonly Parser $parser; + + public function __construct() + { + $parserFactory = new ParserFactory(); + $this->parser = $parserFactory->createForNewestSupportedVersion(); + } + + public function parseClassReflection(ClassReflection $classReflection): ?ClassLike + { + $fileName = $classReflection->getFileName(); + if ($fileName === null) { + return null; + } + + return $this->parseFilenameToClass($fileName); + } + + private function parseFilenameToClass(string $fileName): ClassLike|null + { + if (isset($this->classesByFilename[$fileName])) { + return $this->classesByFilename[$fileName]; + } + + try { + /** @var string $fileContents */ + $fileContents = file_get_contents($fileName); + + $stmts = $this->parser->parse($fileContents); + if (! is_array($stmts)) { + return null; + } + + // complete namespacedName variables + $nodeTraverser = new NodeTraverser(); + $nodeTraverser->addVisitor(new NameResolver()); + $nodeTraverser->traverse($stmts); + } catch (Throwable) { + // not reachable + return null; + } + + $classLike = $this->findFirstClassLike($stmts); + if (! $classLike instanceof ClassLike) { + return null; + } + + $this->classesByFilename[$fileName] = $classLike; + + return $classLike; + } + + /** + * @param Node[] $nodes + */ + private function findFirstClassLike(array $nodes): ?ClassLike + { + $nodeFinder = new NodeFinder(); + return $nodeFinder->findFirstInstanceOf($nodes, ClassLike::class); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowPrivateClassMethodParamTypeRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowPrivateClassMethodParamTypeRule.php new file mode 100644 index 0000000..000ee38 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowPrivateClassMethodParamTypeRule.php @@ -0,0 +1,180 @@ + + */ +final readonly class NarrowPrivateClassMethodParamTypeRule implements Rule +{ + public const string ERROR_MESSAGE = 'Parameter %d should use "%s" type as the only type passed to this method'; + + public function __construct( + private Configuration $configuration, + private MethodCallNodeFinder $methodCallNodeFinder, + private ClassMethodNodeFinder $classMethodNodeFinder + ) { + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return MethodCall::class; + } + + /** + * @param MethodCall $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $this->configuration->isNarrowParamEnabled() || $node->isFirstClassCallable()) { + return []; + } + + $args = $node->getArgs(); + if ($args === []) { + return []; + } + + if (! $node->var instanceof Variable) { + return []; + } + + if (! is_string($node->var->name)) { + return []; + } + + if ($node->var->name !== 'this') { + return []; + } + + return $this->validateArgVsParamTypes($args, $node, $scope); + } + + /** + * @param Arg[] $args + * @return RuleError[] + */ + private function validateArgVsParamTypes(array $args, MethodCall $methodCall, Scope $scope): array + { + $methodCallUses = $this->methodCallNodeFinder->findUsages($methodCall, $scope); + if (count($methodCallUses) > 1) { + return []; + } + + $classMethod = $this->classMethodNodeFinder->findByMethodCall($methodCall, $scope); + if (! $classMethod instanceof ClassMethod) { + return []; + } + + /** @var Param[] $params */ + $params = $classMethod->getParams(); + + $errorMessages = []; + + foreach ($args as $position => $arg) { + $param = $params[$position] ?? []; + if (! $param instanceof Param) { + continue; + } + + if ($param->variadic) { + return []; + } + + $paramRuleError = $this->validateParam($param, $position, $arg->value, $scope); + if (! $paramRuleError instanceof RuleError) { + continue; + } + + // @todo test double failed type + $errorMessages[] = $paramRuleError; + } + + return $errorMessages; + } + + private function validateParam(Param $param, int $position, Expr $expr, Scope $scope): ?RuleError + { + $type = $param->type; + + // @todo some static type mapper from php-parser to PHPStan? + if (! $type instanceof FullyQualified) { + return null; + } + + $argType = $scope->getType($expr); + if ($argType instanceof MixedType) { + return null; + } + + if ($argType instanceof TemplateType) { + return null; + } + + // not solveable yet, work with PHP 8 code only + if ($argType instanceof UnionType) { + return null; + } + + if ($argType instanceof IntersectionType || $argType instanceof GenericObjectType) { + return null; + } + + $objectType = new ObjectType($type->toString()); + + if ($objectType->equals($argType)) { + return null; + } + + $classReflection = $objectType->getClassReflection(); + if ($classReflection instanceof ClassReflection && $classReflection->isAbstract()) { + return null; + } + + // handle weird type substration cases + $paramTypeAsString = $objectType->describe(VerbosityLevel::typeOnly()); + $argTypeAsString = $argType->describe(VerbosityLevel::typeOnly()); + + if ($paramTypeAsString === $argTypeAsString) { + return null; + } + + $errorMessage = sprintf(self::ERROR_MESSAGE, $position + 1, $argTypeAsString); + + return RuleErrorBuilder::message($errorMessage) + ->identifier('typePerfect.narrowPrivateClassMethodParamType') + ->line($param->getLine()) + ->build(); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowPublicClassMethodParamTypeRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowPublicClassMethodParamTypeRule.php new file mode 100644 index 0000000..4e3072d --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowPublicClassMethodParamTypeRule.php @@ -0,0 +1,165 @@ + + */ +final readonly class NarrowPublicClassMethodParamTypeRule implements Rule +{ + public const string ERROR_MESSAGE = 'Parameters should have "%s" types as the only types passed to this method'; + + public function __construct( + private Configuration $configuration + ) { + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return CollectedDataNode::class; + } + + /** + * @param CollectedDataNode $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $this->configuration->isNarrowParamEnabled()) { + return []; + } + + $publicClassMethodCollector = $node->get(PublicClassMethodParamTypesCollector::class); + $classMethodReferenceToArgTypes = $this->resolveClassMethodReferenceToArgTypes($node); + + $ruleErrors = []; + + foreach ($publicClassMethodCollector as $filePath => $declarations) { + foreach ($declarations as [$className, $methodName, $paramTypesString, $line]) { + $uniqueCollectedArgTypesString = $this->resolveUniqueArgTypesString( + $classMethodReferenceToArgTypes, + $className, + $methodName + ); + + if ($uniqueCollectedArgTypesString === null) { + continue; + } + + if ($paramTypesString === $uniqueCollectedArgTypesString) { + continue; + } + + $ruleErrors[] = RuleErrorBuilder::message(sprintf(self::ERROR_MESSAGE, $uniqueCollectedArgTypesString)) + ->identifier('typePerfect.narrowPublicClassMethodParamType') + ->file($filePath) + ->line($line) + ->build(); + } + } + + return $ruleErrors; + } + + /** + * @return array + */ + private function resolveClassMethodReferenceToArgTypes(CollectedDataNode $collectedDataNode): array + { + $methodCallArgTypesByFilePath = $collectedDataNode->get(MethodCallArgTypesCollector::class); + + // these should be skipped completely, as we have no idea what is being passed there + $methodCallablesByFilePath = $collectedDataNode->get(MethodCallableCollector::class); + + $methodFirstClassCallables = $this->flattenCollectedData($methodCallablesByFilePath); + + // group call references and types + $classMethodReferenceToTypes = []; + + foreach ($methodCallArgTypesByFilePath as $methodCallArgTypes) { + foreach ($methodCallArgTypes as [$classMethodReference, $argTypesString]) { + // skip it + if (in_array($classMethodReference, $methodFirstClassCallables, true)) { + continue; + } + + $classMethodReferenceToTypes[$classMethodReference][] = $argTypesString; + } + } + + // resolve unique values + foreach ($classMethodReferenceToTypes as $key => $value) { + $classMethodReferenceToTypes[$key] = array_unique($value); + } + + return $classMethodReferenceToTypes; + } + + /** + * @param array $classMethodReferenceToArgTypes + */ + private function resolveUniqueArgTypesString( + array $classMethodReferenceToArgTypes, + string $className, + string $methodName + ): ?string { + $currentClassMethodReference = $className . '::' . $methodName; + + $collectedArgTypes = $classMethodReferenceToArgTypes[$currentClassMethodReference] ?? null; + if ($collectedArgTypes === null) { + return null; + } + + // we need exactly one type + if (count($collectedArgTypes) !== 1) { + return null; + } + + // one of the arg types could not be resolved, we're not sure + if (in_array(ResolvedTypes::UNKNOWN_TYPES, $collectedArgTypes, true)) { + return null; + } + + return $collectedArgTypes[0]; + } + + /** + * @param array|null)>> $methodCallablesByFilePath + * @return string[] + */ + private function flattenCollectedData(array $methodCallablesByFilePath): array + { + $methodFirstClassCallables = []; + foreach ($methodCallablesByFilePath as $methodCallables) { + foreach ($methodCallables as $methodCallable) { + // the collector returns null for nodes it cannot resolve + if (! isset($methodCallable[0])) { + continue; + } + + $methodFirstClassCallables[] = $methodCallable[0]; + } + } + + return $methodFirstClassCallables; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowReturnObjectTypeRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowReturnObjectTypeRule.php new file mode 100644 index 0000000..6e5171b --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NarrowReturnObjectTypeRule.php @@ -0,0 +1,140 @@ + + */ +final readonly class NarrowReturnObjectTypeRule implements Rule +{ + public const string ERROR_MESSAGE = 'Provide more specific return type "%s" over abstract one'; + + public function __construct( + private ReturnNodeFinder $returnNodeFinder, + private MethodNodeAnalyser $methodNodeAnalyser, + private Configuration $configuration + ) { + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return ClassMethod::class; + } + + /** + * @param ClassMethod $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $this->configuration->isNarrowReturnEnabled()) { + return []; + } + + if (! $node->returnType instanceof FullyQualified) { + return []; + } + + if ($this->shouldSkipScope($scope)) { + return []; + } + + $returnObjectType = new ObjectType($node->returnType->toString()); + if ($this->shouldSkipReturnObjectType($returnObjectType)) { + return []; + } + + $methodName = $node->name->toString(); + if ($this->methodNodeAnalyser->hasParentVendorLock($scope, $methodName)) { + return []; + } + + $returnExpr = $this->returnNodeFinder->findOnlyReturnsExpr($node); + if (! $returnExpr instanceof Expr) { + return []; + } + + $returnExprType = $scope->getType($returnExpr); + if ($this->shouldSkipReturnExprType($returnExprType)) { + return []; + } + + if ($returnObjectType->equals($returnExprType)) { + return []; + } + + // is subtype? + if (! $returnObjectType->isSuperTypeOf($returnExprType)->yes()) { + return []; + } + + if (count($returnExprType->getObjectClassNames()) !== 1) { + return []; + } + + $errorMessage = sprintf(self::ERROR_MESSAGE, $returnExprType->getObjectClassNames()[0]); + + return [ + RuleErrorBuilder::message($errorMessage) + ->identifier('typePerfect.narrowReturnObjectType') + ->build(), + ]; + } + + private function shouldSkipReturnObjectType(ObjectType $objectType): bool + { + $classReflection = $objectType->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return true; + } + + // cannot be more precise if final class + return $classReflection->isFinal(); + } + + private function shouldSkipScope(Scope $scope): bool + { + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return true; + } + + return ! $classReflection->isClass(); + } + + private function shouldSkipReturnExprType(Type $type): bool + { + if (count($type->getObjectClassNames()) !== 1) { + return true; + } + + if (count($type->getObjectClassReflections()) !== 1) { + return true; + } + + return $type->getObjectClassReflections()[0] + ->isAnonymous(); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoArrayAccessOnObjectRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoArrayAccessOnObjectRule.php new file mode 100644 index 0000000..17a4c49 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoArrayAccessOnObjectRule.php @@ -0,0 +1,81 @@ + + */ +final readonly class NoArrayAccessOnObjectRule implements Rule +{ + public const string ERROR_MESSAGE = 'Use explicit methods over array access on object'; + + /** + * @var string[] + */ + private const array ALLOWED_CLASSES = [ + 'SplFixedArray', + 'SimpleXMLElement', + 'Iterator', + 'WeakMap', + 'Aws\ResultInterface', + 'Symfony\Component\DomCrawler\AbstractUriElement', + 'Symfony\Component\Form\FormInterface', + 'Symfony\Component\OptionsResolver\Options', + ]; + + public function __construct( + private Configuration $configuration, + ) { + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return ArrayDimFetch::class; + } + + /** + * @param ArrayDimFetch $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $this->configuration->isNoArrayAccessOnObjectEnabled()) { + return []; + } + + $varType = $scope->getType($node->var); + if (! $varType instanceof ObjectType) { + return []; + } + + if ($this->isAllowedObjectType($varType)) { + return []; + } + + return [ + RuleErrorBuilder::message(self::ERROR_MESSAGE) + ->identifier('typePerfect.noArrayAccessOnObject') + ->build(), + ]; + } + + private function isAllowedObjectType(ObjectType $objectType): bool + { + return array_any(self::ALLOWED_CLASSES, fn (string $allowedClass): bool => $objectType->isInstanceOf($allowedClass)->yes()); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoEmptyOnObjectRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoEmptyOnObjectRule.php new file mode 100644 index 0000000..9ed82f2 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoEmptyOnObjectRule.php @@ -0,0 +1,54 @@ + + */ +final readonly class NoEmptyOnObjectRule implements Rule +{ + public const string ERROR_MESSAGE = 'Use instanceof instead of empty() on object'; + + public function __construct( + private EmptyIssetGuard $emptyIssetGuard, + private Configuration $configuration, + ) { + } + + public function getNodeType(): string + { + return Empty_::class; + } + + /** + * @param Empty_ $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $this->configuration->isNoEmptyOnObjectEnabled()) { + return []; + } + + if ($this->emptyIssetGuard->isLegal($node->expr, $scope)) { + return []; + } + + return [ + RuleErrorBuilder::message(self::ERROR_MESSAGE) + ->identifier('typePerfect.noEmptyOnObject') + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoIssetOnObjectRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoIssetOnObjectRule.php new file mode 100644 index 0000000..f6e3d33 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoIssetOnObjectRule.php @@ -0,0 +1,60 @@ + + */ +final readonly class NoIssetOnObjectRule implements Rule +{ + public const string ERROR_MESSAGE = 'Use instanceof instead of isset() on object'; + + public function __construct( + private EmptyIssetGuard $emptyIssetGuard, + private Configuration $configuration, + ) { + } + + public function getNodeType(): string + { + return Isset_::class; + } + + /** + * @param Isset_ $node + * + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $this->configuration->isNoIssetOnObjectEnabled()) { + return []; + } + + foreach ($node->vars as $var) { + if ($this->emptyIssetGuard->isLegal($var, $scope)) { + continue; + } + + return [ + RuleErrorBuilder::message(self::ERROR_MESSAGE) + ->identifier('typePerfect.noIssetOnObject') + ->build(), + ]; + } + + return []; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoMixedMethodCallerRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoMixedMethodCallerRule.php new file mode 100644 index 0000000..836d01f --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoMixedMethodCallerRule.php @@ -0,0 +1,88 @@ + + */ +final readonly class NoMixedMethodCallerRule implements Rule +{ + public const string ERROR_MESSAGE = 'Mixed variable in a `%s->...()` can skip important errors. Make sure the type is known'; + + public function __construct( + private Printer $printer, + private Configuration $configuration, + ) { + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return MethodCall::class; + } + + /** + * @param MethodCall $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $this->configuration->isNoMixedCallerEnabled()) { + return []; + } + + $callerType = $scope->getType($node->var); + + if (! $callerType instanceof MixedType) { + return []; + } + + if ($callerType instanceof ErrorType) { + return []; + } + + // if error, skip as well for false positive + if ($this->isPreviousTypeErrorType($node, $scope)) { + return []; + } + + $printedMethodCall = $this->printer->prettyPrintExpr($node->var); + + return [ + RuleErrorBuilder::message(sprintf(self::ERROR_MESSAGE, $printedMethodCall)) + ->identifier('typePerfect.noMixedMethodCaller') + ->build(), + ]; + } + + private function isPreviousTypeErrorType(MethodCall $methodCall, Scope $scope): bool + { + $currentMethodCall = $methodCall; + while ($currentMethodCall->var instanceof MethodCall) { + $previousType = $scope->getType($currentMethodCall->var); + if ($previousType instanceof ErrorType) { + return true; + } + + $currentMethodCall = $currentMethodCall->var; + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoMixedPropertyFetcherRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoMixedPropertyFetcherRule.php new file mode 100644 index 0000000..0e9c9e6 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoMixedPropertyFetcherRule.php @@ -0,0 +1,62 @@ + + */ +final readonly class NoMixedPropertyFetcherRule implements Rule +{ + public const string ERROR_MESSAGE = 'Mixed property fetch in a "%s->..." can skip important errors. Make sure the type is known'; + + public function __construct( + private Printer $printer, + private Configuration $configuration, + ) { + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return PropertyFetch::class; + } + + /** + * @param PropertyFetch $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $this->configuration->isNoMixedPropertyEnabled()) { + return []; + } + + $callerType = $scope->getType($node->var); + if (! $callerType instanceof MixedType) { + return []; + } + + $printedVar = $this->printer->prettyPrintExpr($node->var); + + return [ + RuleErrorBuilder::message(sprintf(self::ERROR_MESSAGE, $printedVar)) + ->identifier('typePerfect.noMixedPropertyFetcher') + ->build(), + ]; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoParamTypeRemovalRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoParamTypeRemovalRule.php new file mode 100644 index 0000000..b2592e5 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/NoParamTypeRemovalRule.php @@ -0,0 +1,100 @@ + + */ +final readonly class NoParamTypeRemovalRule implements Rule +{ + public const string ERROR_MESSAGE = 'Removing parent param type is forbidden'; + + public function __construct( + private MethodNodeAnalyser $methodNodeAnalyser, + private Configuration $configuration, + ) { + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return ClassMethod::class; + } + + /** + * @param ClassMethod $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $this->configuration->isNoParamTypeRemovalEnabled()) { + return []; + } + + if ($node->params === []) { + return []; + } + + $classMethodName = (string) $node->name; + if ($classMethodName === '__construct') { + return []; + } + + $parentClassMethodReflection = $this->methodNodeAnalyser->matchFirstParentClassMethod($scope, $classMethodName); + if (! $parentClassMethodReflection instanceof PhpMethodReflection) { + return []; + } + + foreach ($node->params as $paramPosition => $param) { + if ($param->type !== null) { + continue; + } + + $parentParamType = $this->resolveParentParamType($parentClassMethodReflection, $paramPosition); + if ($parentParamType instanceof MixedType) { + continue; + } + + // removed param type! + return [ + RuleErrorBuilder::message(self::ERROR_MESSAGE) + ->identifier('typePerfect.noParamTypeRemoval') + ->build(), + ]; + } + + return []; + } + + private function resolveParentParamType(PhpMethodReflection $phpMethodReflection, int $paramPosition): Type + { + foreach ($phpMethodReflection->getVariants() as $extendedParametersAcceptor) { + foreach ($extendedParametersAcceptor->getParameters() as $parentParamPosition => $parameterReflectionWithPhpDoc) { + if ($paramPosition !== $parentParamPosition) { + continue; + } + + return $parameterReflectionWithPhpDoc->getNativeType(); + } + } + + return new MixedType(); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/ReturnNullOverFalseRule.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/ReturnNullOverFalseRule.php new file mode 100644 index 0000000..cb6e019 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/Rules/ReturnNullOverFalseRule.php @@ -0,0 +1,101 @@ + + */ +final readonly class ReturnNullOverFalseRule implements Rule +{ + /** + * @api + */ + public const string ERROR_MESSAGE = 'Returning false in non return bool class method. Use null with type|null instead or add bool return type'; + + private NodeFinder $nodeFinder; + + public function __construct( + private Configuration $configuration, + ) { + $this->nodeFinder = new NodeFinder(); + } + + /** + * @return class-string + */ + public function getNodeType(): string + { + return ClassMethod::class; + } + + /** + * @param ClassMethod $node + * @return RuleError[] + */ + public function processNode(Node $node, Scope $scope): array + { + if (! $this->configuration->isNoFalsyReturnEnabled()) { + return []; + } + + if ($node->stmts === null) { + return []; + } + + if ($node->returnType instanceof Node) { + return []; + } + + /** @var Return_[] $returns */ + $returns = $this->nodeFinder->findInstanceOf($node->stmts, Return_::class); + + $hasFalseType = false; + $hasTrueType = false; + + foreach ($returns as $return) { + if (! $return->expr instanceof Expr) { + continue; + } + + $exprType = $scope->getType($return->expr); + if (! $exprType instanceof ConstantBooleanType) { + if ($exprType->isBoolean()->yes()) { + return []; + } + + continue; + } + + if ($exprType->getValue()) { + $hasTrueType = true; + continue; + } + + $hasFalseType = true; + } + + if (! $hasTrueType && $hasFalseType) { + return [ + RuleErrorBuilder::message(self::ERROR_MESSAGE) + ->identifier('typePerfect.nullOverFalse') + ->build(), + ]; + } + + return []; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/ValueObject/MethodCallReference.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/ValueObject/MethodCallReference.php new file mode 100644 index 0000000..9224546 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/src/ValueObject/MethodCallReference.php @@ -0,0 +1,24 @@ +class; + } + + public function getMethod(): string + { + return $this->method; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/DifferentClassSameMethodCallName.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/DifferentClassSameMethodCallName.php new file mode 100644 index 0000000..31c2587 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/DifferentClassSameMethodCallName.php @@ -0,0 +1,31 @@ +run($anotherClassWithRun); + } + + /** + * @param SomeStaticCall|MethodCall $node + */ + private function run(Node $node) + { + if ($node->name instanceof MethodCall) { + $this->run($node->name); + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/DoubleShot.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/DoubleShot.php new file mode 100644 index 0000000..a9a8d49 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/DoubleShot.php @@ -0,0 +1,18 @@ +isCheck($arg, $param); + } + private function isCheck(\PhpParser\Node $arg, \PhpParser\Node $param) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/Fixture.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/Fixture.php new file mode 100644 index 0000000..68bd53c --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/Fixture.php @@ -0,0 +1,22 @@ +isCheck($node); + } + } + + private function isCheck(Node $node) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipAbstractBase.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipAbstractBase.php new file mode 100644 index 0000000..ecafcfb --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipAbstractBase.php @@ -0,0 +1,16 @@ +isCheck($arg); + } + private function isCheck(ConceptBase $arg) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipAlreadyCorrectType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipAlreadyCorrectType.php new file mode 100644 index 0000000..f74f3dd --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipAlreadyCorrectType.php @@ -0,0 +1,22 @@ +isCheck($node); + } + } + + private function isCheck(MethodCall $node) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipCorrectUnionType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipCorrectUnionType.php new file mode 100644 index 0000000..9480df8 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipCorrectUnionType.php @@ -0,0 +1,32 @@ +isIncludeOnceOrRequireOnce($node)) { + return []; + } + + return []; + } + + /** + * @param Assign|Return_ $node + */ + private function isIncludeOnceOrRequireOnce(\PhpParser\Node $node) + { + return true; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipDuplicatedCallOfSameMethodWithComment.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipDuplicatedCallOfSameMethodWithComment.php new file mode 100644 index 0000000..632fc7d --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipDuplicatedCallOfSameMethodWithComment.php @@ -0,0 +1,26 @@ +printFile(new FileWithoutNamespace([])); + } + + private function printFile(\PhpParser\Node $node) + { + } + + public function secondMethod() + { + // some comment + $this->printFile(new FileNode([])); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipGenericType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipGenericType.php new file mode 100644 index 0000000..e62b509 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipGenericType.php @@ -0,0 +1,31 @@ +getsAGeneric($node); + } + + /** + * @param T $node + * @return void + */ + private function getsAGeneric(Node $node) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMayOverrideArg.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMayOverrideArg.php new file mode 100644 index 0000000..b2658cd --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMayOverrideArg.php @@ -0,0 +1,27 @@ +mayOverrideNode(); + + $this->isCheck($node); + + } + } + + private function isCheck(Node $node) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMixed.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMixed.php new file mode 100644 index 0000000..66a104e --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMixed.php @@ -0,0 +1,18 @@ +isCheck($arg); + } + private function isCheck(\PhpParser\Node $arg) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMultipleUsed.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMultipleUsed.php new file mode 100644 index 0000000..1919ec9 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipMultipleUsed.php @@ -0,0 +1,27 @@ +isCheck($node); + } + + if ($node instanceof PropertyFetch) { + $this->isCheck($node); + } + } + + private function isCheck(Node $node) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNoArgs.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNoArgs.php new file mode 100644 index 0000000..9009443 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNoArgs.php @@ -0,0 +1,15 @@ +execute(); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNotFromThis.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNotFromThis.php new file mode 100644 index 0000000..6543695 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNotFromThis.php @@ -0,0 +1,14 @@ +format('Y-m-d'); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNotPrivate.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNotPrivate.php new file mode 100644 index 0000000..a16c0ad --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipNotPrivate.php @@ -0,0 +1,22 @@ +isCheckNotPrivate($node); + } + } + + public function isCheckNotPrivate(Node $node) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipOptedOut.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipOptedOut.php new file mode 100644 index 0000000..a74a81b --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipOptedOut.php @@ -0,0 +1,23 @@ +isCheck($type); + } + + private function isCheck(\PHPStan\Type\Type $type) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipParentNotIf.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipParentNotIf.php new file mode 100644 index 0000000..04a51b7 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipParentNotIf.php @@ -0,0 +1,15 @@ +execute($node); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipRecursive.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipRecursive.php new file mode 100644 index 0000000..7ebbdc6 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipRecursive.php @@ -0,0 +1,30 @@ +run($node); + } + + /** + * @param SomeStaticCall|MethodCall $node + */ + private function run(Node $node): void + { + if ($node->name instanceof MethodCall) { + $this->run($node->name); + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipSameGeneric.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipSameGeneric.php new file mode 100644 index 0000000..23b8b23 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipSameGeneric.php @@ -0,0 +1,34 @@ +getPricesForArticle(); + $this->getAmounts($prices); + } + + /** + * @param MyIterator $prices + */ + private function getAmounts(MyIterator $prices): void + { + } + + /** + * @return MyIterator + */ + private function getPricesForArticle(): MyIterator + { + /** @var MyIterator */ + return new MyIterator(); + } + +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipVariadics.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipVariadics.php new file mode 100644 index 0000000..db8c922 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Fixture/SkipVariadics.php @@ -0,0 +1,21 @@ +takesVariadic(...$arr); + } + + private function takesVariadic(Node ...$node) + { + + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/NarrowPrivateClassMethodParamTypeRuleTest.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/NarrowPrivateClassMethodParamTypeRuleTest.php new file mode 100644 index 0000000..b742874 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/NarrowPrivateClassMethodParamTypeRuleTest.php @@ -0,0 +1,74 @@ + $expectedErrorsWithLines + */ + #[DataProvider('provideData')] + public function testRule(string $filePath, array $expectedErrorsWithLines): void + { + $this->analyse([$filePath], $expectedErrorsWithLines); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/SkipDuplicatedCallOfSameMethodWithComment.php', []]; + + yield [__DIR__ . '/Fixture/SkipCorrectUnionType.php', []]; + yield [__DIR__ . '/Fixture/SkipRecursive.php', []]; + yield [__DIR__ . '/Fixture/SkipMixed.php', []]; + yield [__DIR__ . '/Fixture/SkipOptedOut.php', []]; + yield [__DIR__ . '/Fixture/SkipNotFromThis.php', []]; + + yield [__DIR__ . '/Fixture/SkipParentNotIf.php', []]; + yield [__DIR__ . '/Fixture/SkipNoArgs.php', []]; + yield [__DIR__ . '/Fixture/SkipAlreadyCorrectType.php', []]; + yield [__DIR__ . '/Fixture/SkipMayOverrideArg.php', []]; + yield [__DIR__ . '/Fixture/SkipMultipleUsed.php', []]; + yield [__DIR__ . '/Fixture/SkipNotPrivate.php', []]; + + $errorMessage = sprintf(NarrowPrivateClassMethodParamTypeRule::ERROR_MESSAGE, 1, MethodCall::class); + yield [__DIR__ . '/Fixture/Fixture.php', [[$errorMessage, 19]]]; + yield [__DIR__ . '/Fixture/DifferentClassSameMethodCallName.php', [[$errorMessage, 25]]]; + + $argErrorMessage = sprintf(NarrowPrivateClassMethodParamTypeRule::ERROR_MESSAGE, 1, Arg::class); + $paramErrorMessage = sprintf(NarrowPrivateClassMethodParamTypeRule::ERROR_MESSAGE, 2, Param::class); + yield [__DIR__ . '/Fixture/DoubleShot.php', [[$argErrorMessage, 15], [$paramErrorMessage, 15]]]; + yield [__DIR__ . '/Fixture/SkipGenericType.php', []]; + yield [__DIR__ . '/Fixture/SkipAbstractBase.php', []]; + yield [__DIR__ . '/Fixture/SkipVariadics.php', []]; + yield [__DIR__ . '/Fixture/SkipSameGeneric.php', []]; + } + + /** + * @return string[] + */ + #[Override] + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../config/extension.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(NarrowPrivateClassMethodParamTypeRule::class); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Source/AnotherClassWithRun.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Source/AnotherClassWithRun.php new file mode 100644 index 0000000..0b3ba33 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPrivateClassMethodParamTypeRule/Source/AnotherClassWithRun.php @@ -0,0 +1,15 @@ +test(false); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/FirstClassCallables/CallVariadics.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/FirstClassCallables/CallVariadics.php new file mode 100644 index 0000000..4d01cdd --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/FirstClassCallables/CallVariadics.php @@ -0,0 +1,15 @@ +callMe(...); + + $someCalledMethod->callMe(1000); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/FirstClassCallables/SomeCalledMethod.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/FirstClassCallables/SomeCalledMethod.php new file mode 100644 index 0000000..3b1da8c --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/FirstClassCallables/SomeCalledMethod.php @@ -0,0 +1,12 @@ + $g */ + public function doFoo(GenericA $g):void + { + } + + /** @param GenericA $g */ + function doBar($g):void { + $this->doFoo($g); + } +} + diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/Generics/SkipSameGeneric.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/Generics/SkipSameGeneric.php new file mode 100644 index 0000000..160755e --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/Generics/SkipSameGeneric.php @@ -0,0 +1,19 @@ + $_werbemasse + */ + public function setWerbemasse($_werbemasse): void + { + $this->_werbemasse = $_werbemasse; + } +} + + diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/HandleDefaultValue.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/HandleDefaultValue.php new file mode 100644 index 0000000..c040d87 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/HandleDefaultValue.php @@ -0,0 +1,30 @@ +takesUnionWithDifferentDefaultValue(1); + + $o = new HandleDefaultValue(); + $o->takesUnionWithSameTypeDefaultValue(1); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/PublicDoubleShot.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/PublicDoubleShot.php new file mode 100644 index 0000000..b156afb --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/PublicDoubleShot.php @@ -0,0 +1,12 @@ +map(function () {}); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipClassStringPassed.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipClassStringPassed.php new file mode 100644 index 0000000..3efc716 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipClassStringPassed.php @@ -0,0 +1,12 @@ +map(function () {}); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipDateTimeMix.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipDateTimeMix.php new file mode 100644 index 0000000..c64230a --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipDateTimeMix.php @@ -0,0 +1,23 @@ +markDate( + new \DateTimeImmutable('15:30') + ); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipDefault.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipDefault.php new file mode 100644 index 0000000..9575234 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipDefault.php @@ -0,0 +1,21 @@ +dontSetParamValue(); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipEnum.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipEnum.php new file mode 100644 index 0000000..1d695da --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipEnum.php @@ -0,0 +1,19 @@ +pick(PickReasonEnum::BarcodeIssue); + } +} \ No newline at end of file diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipEqualUnionType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipEqualUnionType.php new file mode 100644 index 0000000..7c6cb87 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipEqualUnionType.php @@ -0,0 +1,44 @@ +class + : $obj->var; + } + + public function runTernaryFlipped(StaticCall|MethodCall $obj) + { + return $obj instanceof MethodCall + ? $obj->var + : $obj->class; + } + + /** + * @param Node[]|Node $node + */ + public function runArrayTyped(array | Node $node) + { + } + + /** + * @param array|Node $node + */ + public function runArrayTyped2(array | Node $node) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipExpectedClassType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipExpectedClassType.php new file mode 100644 index 0000000..1c72233 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipExpectedClassType.php @@ -0,0 +1,14 @@ +callMe(1000); + } + + private function callMe($number) + { + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipNullableCompare.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipNullableCompare.php new file mode 100644 index 0000000..70f5b1a --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipNullableCompare.php @@ -0,0 +1,14 @@ +map($resource, 'name'); + + + $resource = $this->getFileResource(); + $this->map($resource, 'surname'); + } + + /** + * @return resource + */ + private function getFileResource() + { + return fopen('file.txt', 'r'); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipSelf.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipSelf.php new file mode 100644 index 0000000..f979c8e --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipSelf.php @@ -0,0 +1,17 @@ +takesSelf(new SkipSelf()); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipThisPassedExactType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipThisPassedExactType.php new file mode 100644 index 0000000..c7670e1 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/SkipThisPassedExactType.php @@ -0,0 +1,14 @@ +run(new DateTime('now')); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/ThisPassedFromInterface.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/ThisPassedFromInterface.php new file mode 100644 index 0000000..df4f8eb --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Fixture/ThisPassedFromInterface.php @@ -0,0 +1,14 @@ + $expectedErrorsWithLines + */ + #[DataProvider('provideData')] + public function testRule(array $filePaths, array $expectedErrorsWithLines): void + { + $this->analyse($filePaths, $expectedErrorsWithLines); + } + + /** + * @return Iterator>, mixed>> + */ + public static function provideData(): Iterator + { + yield [[__DIR__ . '/Fixture/Generics/SkipPassedGenerics.php'], []]; + + yield [[__DIR__ . '/Fixture/SkipDefault.php'], []]; + yield [[__DIR__ . '/Fixture/SkipResource.php'], []]; + yield [[__DIR__ . '/Fixture/SkipDateTimeMix.php'], []]; + yield [[__DIR__ . '/Fixture/SkipNonPublicClassMethod.php'], []]; + + // skip first class callables as anything can be passed there + yield [[ + __DIR__ . '/Fixture/FirstClassCallables/CallVariadics.php', + __DIR__ . '/Fixture/FirstClassCallables/SomeCalledMethod.php', + ], []]; + + // skip expected scalar type + yield [[ + __DIR__ . '/Fixture/SkipProperlyFilledParamType.php', + __DIR__ . '/Source/ExpectedType/FirstTypedCaller.php', + __DIR__ . '/Source/ExpectedType/SecondTypedCaller.php', + ], []]; + + // skip expected object type + yield [[ + __DIR__ . '/Fixture/SkipExpectedClassType.php', + __DIR__ . '/Source/ExpectedClassType/FirstClassTypedCaller.php', + __DIR__ . '/Source/ExpectedClassType/SecondClassTypedCaller.php', + ], []]; + + // skip class-string + yield [[ + __DIR__ . '/Fixture/SkipClassStringPassed.php', + __DIR__ . '/Source/ExpectedClassString/FirstTypedCaller.php', + __DIR__ . '/Source/ExpectedClassString/SecondTypedCaller.php', + ], []]; + + // skip everything in case of values is mixed + yield [[ + __DIR__ . '/Fixture/SkipMixedAndString.php', + __DIR__ . '/Source/MixedAndString/FirstCaller.php', + __DIR__ . '/Source/MixedAndString/SecondCaller.php', + ], []]; + + // skip int + string values + yield [[ + __DIR__ . '/Fixture/SkipMixedAndString.php', + __DIR__ . '/Source/MixedAndString/FirstCaller.php', + __DIR__ . '/Source/MixedAndString/ThirdCaller.php', + ], []]; + + // skip nullable compare + yield [[ + __DIR__ . '/Fixture/SkipNullableCompare.php', + __DIR__ . '/Source/NullableParam/FirstNullable.php', + __DIR__ . '/Source/NullableParam/SecondNullable.php', + ], []]; + + // skip api + yield [[ + __DIR__ . '/Fixture/SkipApiMarked.php', + __DIR__ . '/Source/ExpectedNodeApi/CallWithProperty.php', + ], []]; + + // skip equal union type + yield [[ + __DIR__ . '/Fixture/SkipEqualUnionType.php', + __DIR__ . '/Source/ExpectedUnion/CallUnionType.php', + ], []]; + + // skip equal union type flipped + yield [[ + __DIR__ . '/Fixture/SkipEqualUnionType.php', + __DIR__ . '/Source/ExpectedUnion/CallUnionTypeFlipped.php', + ], []]; + + // skip equal union type ternary if else + yield [[ + __DIR__ . '/Fixture/SkipEqualUnionType.php', + __DIR__ . '/Source/ExpectedUnion/CallUnionTypeTernaryIfElse.php', + ], []]; + + // skip equal union type ternary if else flipped + yield [[ + __DIR__ . '/Fixture/SkipEqualUnionType.php', + __DIR__ . '/Source/ExpectedUnion/CallUnionTypeTernaryIfElseFlipped.php', + ], []]; + + // skip equal union type array typed + yield [[ + __DIR__ . '/Fixture/SkipEqualUnionType.php', + __DIR__ . '/Source/ExpectedUnion/CallUnionArrayType.php', + ], []]; + + // skip this passed exact type + yield [[ + __DIR__ . '/Fixture/SkipThisPassedExactType.php', + __DIR__ . '/Source/ExpectedThisType/CallByThis.php', + ], []]; + + // skip used internally for second type + yield [[ + __DIR__ . '/Fixture/SkipUsedInternallyForSecondType.php', + __DIR__ . '/Source/ExpectedType/OnlyFirstTypeCalledOutside.php', + ], []]; + + $argErrorMessage = sprintf(NarrowPublicClassMethodParamTypeRule::ERROR_MESSAGE, 'int'); + yield [[ + __DIR__ . '/Fixture/PublicDoubleShot.php', + __DIR__ . '/Source/FirstCaller.php', + __DIR__ . '/Source/SecondCaller.php', + ], [[$argErrorMessage, 9]]]; + + // this passed from interface + $argErrorMessage = sprintf( + NarrowPublicClassMethodParamTypeRule::ERROR_MESSAGE, + CallByThisFromInterface::class + ); + yield [[ + __DIR__ . '/Fixture/ThisPassedFromInterface.php', + __DIR__ . '/Source/ExpectedThisType/CallByThisFromInterface.php', + ], [[$argErrorMessage, 11]]]; + + yield [[__DIR__ . '/Fixture/SkipSelf.php'], []]; + + yield [[__DIR__ . '/Fixture/SkipClosure.php'], []]; + + yield [[__DIR__ . '/Fixture/SkipCallable.php'], []]; + + yield [[__DIR__ . '/Fixture/SkipEnum.php'], []]; + + $argErrorMessage = sprintf(NarrowPublicClassMethodParamTypeRule::ERROR_MESSAGE, 'int'); + + yield [[__DIR__ . '/Fixture/HandleDefaultValue.php'], [[$argErrorMessage, 15]]]; + + $argErrorMessage = sprintf(NarrowPublicClassMethodParamTypeRule::ERROR_MESSAGE, 'bool'); + yield [[__DIR__ . '/Fixture/ExplicitlyNullableParams.php'], [[$argErrorMessage, 9]]]; + + // skip generics + yield [[ + __DIR__ . '/Fixture/Generics/SkipSameGeneric.php', + __DIR__ . '/Source/SkipSameGeneric/MyGroup.php', + __DIR__ . '/Source/SkipSameGeneric/MyIterator.php', + __DIR__ . '/Source/SkipSameGeneric/MyService.php', + ], []]; + } + + /** + * @return string[] + */ + #[Override] + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../config/extension.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(NarrowPublicClassMethodParamTypeRule::class); + } + + /** + * Warning, just spent hour looking for why the test does not run :D This should be implicit part of the parent + * class. + * + * @return Collector[] + */ + #[Override] + protected function getCollectors(): array + { + return self::getContainer()->getServicesByTag('phpstan.collector'); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassString/FirstTypedCaller.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassString/FirstTypedCaller.php new file mode 100644 index 0000000..35c8ce0 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassString/FirstTypedCaller.php @@ -0,0 +1,17 @@ +resolve($classReflection->getName()); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassString/SecondTypedCaller.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassString/SecondTypedCaller.php new file mode 100644 index 0000000..4d4815a --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassString/SecondTypedCaller.php @@ -0,0 +1,17 @@ +resolve($classReflection->getName()); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassType/FirstClassTypedCaller.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassType/FirstClassTypedCaller.php new file mode 100644 index 0000000..8ad53ce --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassType/FirstClassTypedCaller.php @@ -0,0 +1,17 @@ +callMeWithClassType($knownType); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassType/SecondClassTypedCaller.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassType/SecondClassTypedCaller.php new file mode 100644 index 0000000..222edca --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedClassType/SecondClassTypedCaller.php @@ -0,0 +1,17 @@ +callMeWithClassType($knownType); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedNodeApi/CallWithProperty.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedNodeApi/CallWithProperty.php new file mode 100644 index 0000000..44abed4 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedNodeApi/CallWithProperty.php @@ -0,0 +1,17 @@ +callNode($property); + } + +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedThisType/CallByThis.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedThisType/CallByThis.php new file mode 100644 index 0000000..e78ad81 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedThisType/CallByThis.php @@ -0,0 +1,15 @@ +run($this); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedThisType/CallByThisFromInterface.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedThisType/CallByThisFromInterface.php new file mode 100644 index 0000000..eb90be0 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedThisType/CallByThisFromInterface.php @@ -0,0 +1,16 @@ +run($this); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/FirstTypedCaller.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/FirstTypedCaller.php new file mode 100644 index 0000000..9eacb9d --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/FirstTypedCaller.php @@ -0,0 +1,15 @@ +callMeTwice(100); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/OnlyFirstTypeCalledOutside.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/OnlyFirstTypeCalledOutside.php new file mode 100644 index 0000000..a3e116a --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/OnlyFirstTypeCalledOutside.php @@ -0,0 +1,16 @@ +run(new stdClass()); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/SecondTypedCaller.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/SecondTypedCaller.php new file mode 100644 index 0000000..cc0e2df --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedType/SecondTypedCaller.php @@ -0,0 +1,15 @@ +callMeTwice(100); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionArrayType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionArrayType.php new file mode 100644 index 0000000..813720b --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionArrayType.php @@ -0,0 +1,31 @@ +runArrayTyped($node); + } + + public function run2(SkipEqualUnionType $skipEqualUnionType, Node $node): void + { + /** @var Node[]|Node $node */ + $node = rand(0, 1) + ? $node + : [$node]; + + $skipEqualUnionType->runArrayTyped2($node); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionType.php new file mode 100644 index 0000000..b3b11a0 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionType.php @@ -0,0 +1,22 @@ +run($value); + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeFlipped.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeFlipped.php new file mode 100644 index 0000000..a65c98b --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeFlipped.php @@ -0,0 +1,22 @@ +run($value); + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeTernaryIfElse.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeTernaryIfElse.php new file mode 100644 index 0000000..5643966 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeTernaryIfElse.php @@ -0,0 +1,22 @@ +runTernary($value); + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeTernaryIfElseFlipped.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeTernaryIfElseFlipped.php new file mode 100644 index 0000000..2ac613f --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/ExpectedUnion/CallUnionTypeTernaryIfElseFlipped.php @@ -0,0 +1,22 @@ +runTernaryFlipped($value); + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/FirstCaller.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/FirstCaller.php new file mode 100644 index 0000000..81565e0 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/FirstCaller.php @@ -0,0 +1,15 @@ +callMeTwice(100); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/FirstCaller.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/FirstCaller.php new file mode 100644 index 0000000..abf52f5 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/FirstCaller.php @@ -0,0 +1,15 @@ +resolve('string'); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/SecondCaller.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/SecondCaller.php new file mode 100644 index 0000000..c087c8d --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/SecondCaller.php @@ -0,0 +1,15 @@ +resolve($mixedValue); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/ThirdCaller.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/ThirdCaller.php new file mode 100644 index 0000000..532bda8 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/MixedAndString/ThirdCaller.php @@ -0,0 +1,15 @@ +resolve(1000); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/NullableParam/FirstNullable.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/NullableParam/FirstNullable.php new file mode 100644 index 0000000..b666861 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/NullableParam/FirstNullable.php @@ -0,0 +1,16 @@ +callNode($node); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/NullableParam/SecondNullable.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/NullableParam/SecondNullable.php new file mode 100644 index 0000000..2ace432 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/NullableParam/SecondNullable.php @@ -0,0 +1,16 @@ +callNode($node); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/PassMeAsType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/PassMeAsType.php new file mode 100644 index 0000000..ee1a0d0 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/PassMeAsType.php @@ -0,0 +1,10 @@ +callMeTwice(100); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SkipSameGeneric/MyGroup.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SkipSameGeneric/MyGroup.php new file mode 100644 index 0000000..e0e02d7 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SkipSameGeneric/MyGroup.php @@ -0,0 +1,10 @@ +articleModel->setWerbemasse($this->fetchCustomizationPositionsByArtidAndSprache()); + } + + /** + * @return MyIterator + */ + public function fetchCustomizationPositionsByArtidAndSprache(int $artid, int $spracheid): MyIterator + { + } + +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SomeInterface.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SomeInterface.php new file mode 100644 index 0000000..ee3221b --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowPublicClassMethodParamTypeRule/Source/SomeInterface.php @@ -0,0 +1,8 @@ + $expectedErrorMessagesWithLines + */ + #[DataProvider('provideData')] + public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void + { + $this->analyse([$filePath], $expectedErrorMessagesWithLines); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/SkipSpecificReturnType.php', []]; + yield [__DIR__ . '/Fixture/SkipSomeContract.php', []]; + + $errorMessage = sprintf(NarrowReturnObjectTypeRule::ERROR_MESSAGE, SpecificControl::class); + yield [__DIR__ . '/Fixture/SomeAbstractReturnType.php', [[$errorMessage, 12]]]; + } + + /** + * @return string[] + */ + #[Override] + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../config/extension.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(NarrowReturnObjectTypeRule::class); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowReturnObjectTypeRule/Source/AbstractControl.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowReturnObjectTypeRule/Source/AbstractControl.php new file mode 100644 index 0000000..8b600f0 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NarrowReturnObjectTypeRule/Source/AbstractControl.php @@ -0,0 +1,9 @@ +children() as $name => $childElement) { + return isset($childElement['some']); + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/NoArrayAccessOnObjectRuleTest.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/NoArrayAccessOnObjectRuleTest.php new file mode 100644 index 0000000..332a987 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/NoArrayAccessOnObjectRuleTest.php @@ -0,0 +1,55 @@ + $expectedErrorMessagesWithLines + */ + #[DataProvider('provideData')] + public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void + { + $this->analyse([$filePath], $expectedErrorMessagesWithLines); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/ArrayAccessOnObject.php', [[NoArrayAccessOnObjectRule::ERROR_MESSAGE, 14]]]; + yield [__DIR__ . '/Fixture/ArrayAccessOnNestedObject.php', [[NoArrayAccessOnObjectRule::ERROR_MESSAGE, 14]]]; + + yield [__DIR__ . '/Fixture/SkipIterator.php', []]; + yield [__DIR__ . '/Fixture/SkipOnArray.php', []]; + yield [__DIR__ . '/Fixture/SkipSplFixedArray.php', []]; + yield [__DIR__ . '/Fixture/SkipUriElement.php', []]; + yield [__DIR__ . '/Fixture/SkipWeakMap.php', []]; + yield [__DIR__ . '/Fixture/SkipXml.php', []]; + yield [__DIR__ . '/Fixture/SkipXmlElementForeach.php', []]; + } + + /** + * @return string[] + */ + #[Override] + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../config/extension.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(NoArrayAccessOnObjectRule::class); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Source/ChildOfSomeClassWithArrayAccess.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Source/ChildOfSomeClassWithArrayAccess.php new file mode 100644 index 0000000..a04d76d --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoArrayAccessOnObjectRule/Source/ChildOfSomeClassWithArrayAccess.php @@ -0,0 +1,9 @@ +args[9])) { + return $methodCall->args[9]; + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/Fixture/SkipPhpDocType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/Fixture/SkipPhpDocType.php new file mode 100644 index 0000000..ec18890 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/Fixture/SkipPhpDocType.php @@ -0,0 +1,12 @@ +foo = 'bar'; + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/NoEmptyOnObjectRuleTest.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/NoEmptyOnObjectRuleTest.php new file mode 100644 index 0000000..e23380b --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoEmptyOnObjectRule/NoEmptyOnObjectRuleTest.php @@ -0,0 +1,51 @@ + $expectedErrorMessagesWithLines + */ + #[DataProvider('provideData')] + public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void + { + $this->analyse([$filePath], $expectedErrorMessagesWithLines); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/EmptyOnObject.php', [[NoEmptyOnObjectRule::ERROR_MESSAGE, 19]]]; + + yield [__DIR__ . '/Fixture/SkipEmptyOnArray.php', []]; + yield [__DIR__ . '/Fixture/SkipEmptyOnArrayNestedOnObject.php', []]; + yield [__DIR__ . '/Fixture/SkipPossibleUndefinedVariable.php', []]; + yield [__DIR__ . '/Fixture/SkipPhpDocType.php', []]; + } + + /** + * @return string[] + */ + #[Override] + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../config/extension.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(NoEmptyOnObjectRule::class); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/IssetOnObject.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/IssetOnObject.php new file mode 100644 index 0000000..8509aee --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/IssetOnObject.php @@ -0,0 +1,23 @@ +args[9])) { + return $methodCall->args[9]; + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipIssetOnPropertyFetch.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipIssetOnPropertyFetch.php new file mode 100644 index 0000000..66f1bd5 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipIssetOnPropertyFetch.php @@ -0,0 +1,27 @@ +initializeFoo(); + + $this->foo->sayHello(); + } + + private function initializeFoo(): void + { + if (!isset($this->foo)) + { + $this->foo = new Foo(); + } + } +} \ No newline at end of file diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipPhpDocType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipPhpDocType.php new file mode 100644 index 0000000..a35ede9 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Fixture/SkipPhpDocType.php @@ -0,0 +1,12 @@ +foo = 'bar'; + } + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/NoIssetOnObjectRuleTest.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/NoIssetOnObjectRuleTest.php new file mode 100644 index 0000000..a6633e8 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/NoIssetOnObjectRuleTest.php @@ -0,0 +1,52 @@ + $expectedErrorMessagesWithLines + */ + #[DataProvider('provideData')] + public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void + { + $this->analyse([$filePath], $expectedErrorMessagesWithLines); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/IssetOnObject.php', [[NoIssetOnObjectRule::ERROR_MESSAGE, 19]]]; + + yield [__DIR__ . '/Fixture/SkipIssetOnArray.php', []]; + yield [__DIR__ . '/Fixture/SkipIssetOnArrayNestedOnObject.php', []]; + yield [__DIR__ . '/Fixture/SkipPossibleUndefinedVariable.php', []]; + yield [__DIR__ . '/Fixture/SkipIssetOnPropertyFetch.php', []]; + yield [__DIR__ . '/Fixture/SkipPhpDocType.php', []]; + } + + /** + * @return string[] + */ + #[Override] + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../config/extension.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(NoIssetOnObjectRule::class); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Source/Foo.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Source/Foo.php new file mode 100644 index 0000000..d69a712 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoIssetOnObjectRule/Source/Foo.php @@ -0,0 +1,13 @@ +$magic(); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipKnownCallerType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipKnownCallerType.php new file mode 100644 index 0000000..297d252 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipKnownCallerType.php @@ -0,0 +1,15 @@ +call(); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipMockObject.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipMockObject.php new file mode 100644 index 0000000..58168a7 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipMockObject.php @@ -0,0 +1,19 @@ +createMock(MagicMethodName::class); + + $mock + ->method('run') + ->willReturn(true); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipPHPUnitMock.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipPHPUnitMock.php new file mode 100644 index 0000000..ecf6e76 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/SkipPHPUnitMock.php @@ -0,0 +1,21 @@ +createMock(SomeFinalClass::class); + + $someClassMock->expects($this->once()) + ->method('some') + ->with($this->any()) + ->willReturn(1000); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/UnknownCallerType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/UnknownCallerType.php new file mode 100644 index 0000000..5a01e19 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Fixture/UnknownCallerType.php @@ -0,0 +1,13 @@ +call(); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/NoMixedMethodCallerRuleTest.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/NoMixedMethodCallerRuleTest.php new file mode 100644 index 0000000..f85d37b --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/NoMixedMethodCallerRuleTest.php @@ -0,0 +1,54 @@ + $expectedErrorsWithLines + */ + #[DataProvider('provideData')] + public function testRule(string $filePath, array $expectedErrorsWithLines): void + { + $this->analyse([$filePath], $expectedErrorsWithLines); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/SkipKnownCallerType.php', []]; + yield [__DIR__ . '/Fixture/SkipMockObject.php', []]; + yield [__DIR__ . '/Fixture/SkipPHPUnitMock.php', []]; + + $errorMessage = sprintf(NoMixedMethodCallerRule::ERROR_MESSAGE, '$someType'); + yield [__DIR__ . '/Fixture/MagicMethodName.php', [[$errorMessage, 11]]]; + + $errorMessage = sprintf(NoMixedMethodCallerRule::ERROR_MESSAGE, '$mixedType'); + yield [__DIR__ . '/Fixture/UnknownCallerType.php', [[$errorMessage, 11]]]; + } + + /** + * @return string[] + */ + #[Override] + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../config/extension.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(NoMixedMethodCallerRule::class); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Source/KnownType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Source/KnownType.php new file mode 100644 index 0000000..75536c7 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedMethodCallerRule/Source/KnownType.php @@ -0,0 +1,12 @@ +{$name}; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/SkipDynamicNameWithKnownType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/SkipDynamicNameWithKnownType.php new file mode 100644 index 0000000..c3812a1 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/SkipDynamicNameWithKnownType.php @@ -0,0 +1,15 @@ +{$name}; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/SkipKnownFetcherType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/SkipKnownFetcherType.php new file mode 100644 index 0000000..48ec8ac --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/SkipKnownFetcherType.php @@ -0,0 +1,15 @@ +name; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/UnknownPropertyFetcher.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/UnknownPropertyFetcher.php new file mode 100644 index 0000000..ef906f0 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Fixture/UnknownPropertyFetcher.php @@ -0,0 +1,13 @@ +name; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/NoMixedPropertyFetcherRuleTest.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/NoMixedPropertyFetcherRuleTest.php new file mode 100644 index 0000000..a9df18e --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/NoMixedPropertyFetcherRuleTest.php @@ -0,0 +1,53 @@ + $expectedErrorsWithLines + */ + #[DataProvider('provideData')] + public function testRule(string $filePath, array $expectedErrorsWithLines): void + { + $this->analyse([$filePath], $expectedErrorsWithLines); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/SkipDynamicNameWithKnownType.php', []]; + yield [__DIR__ . '/Fixture/SkipKnownFetcherType.php', []]; + + $message = sprintf(NoMixedPropertyFetcherRule::ERROR_MESSAGE, '$unknownType'); + yield [__DIR__ . '/Fixture/DynamicName.php', [[$message, 11]]]; + + $message = sprintf(NoMixedPropertyFetcherRule::ERROR_MESSAGE, '$unknownType'); + yield [__DIR__ . '/Fixture/UnknownPropertyFetcher.php', [[$message, 11]]]; + } + + /** + * @return string[] + */ + #[Override] + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../config/extension.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(NoMixedPropertyFetcherRule::class); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Source/KnownType.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Source/KnownType.php new file mode 100644 index 0000000..2655c86 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoMixedPropertyFetcherRule/Source/KnownType.php @@ -0,0 +1,10 @@ + $expectedErrorMessagesWithLines + */ + #[DataProvider('provideData')] + public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void + { + $this->analyse([$filePath], $expectedErrorMessagesWithLines); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/SkipConstruct.php', []]; + yield [__DIR__ . '/Fixture/SkipPhpDocType.php', []]; + yield [__DIR__ . '/Fixture/SkipPresentType.php', []]; + yield [__DIR__ . '/Fixture/SkipNoType.php', []]; + + yield [__DIR__ . '/Fixture/SkipIndirectRemoval.php', []]; + + yield [__DIR__ . '/Fixture/RemoveParentType.php', [[NoParamTypeRemovalRule::ERROR_MESSAGE, 11]]]; + + yield [__DIR__ . '/Fixture/SkipNoParent.php', []]; + yield [__DIR__ . '/Fixture/SkipNotHasParentMethod.php', []]; + yield [__DIR__ . '/Fixture/SkipHasSameParameterWithParentMethod.php', []]; + yield [__DIR__ . '/Fixture/SkipHasSameParameterWithInterfaceMethod.php', []]; + + yield [ + __DIR__ . '/Fixture/HasDifferentParameterWithParentMethod.php', + [[NoParamTypeRemovalRule::ERROR_MESSAGE, 9]], + ]; + yield [ + __DIR__ . '/Fixture/HasDifferentParameterWithInterfaceMethod.php', + [[NoParamTypeRemovalRule::ERROR_MESSAGE, 9], [NoParamTypeRemovalRule::ERROR_MESSAGE, 13]], + ]; + } + + /** + * @return string[] + */ + #[Override] + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../config/extension.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(NoParamTypeRemovalRule::class); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Source/NoTypeInterface.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Source/NoTypeInterface.php new file mode 100644 index 0000000..333fbd2 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/NoParamTypeRemovalRule/Source/NoTypeInterface.php @@ -0,0 +1,10 @@ +loadFromDb($name)) { + return false; + } + + return true; + } + + private function loadFromDb(string $name): ?array + { + if (mt_rand(1, 0)) { + return null; + } + + return ['test' => 'foo']; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/Fixture/CheckResultFromOtherMethod2.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/Fixture/CheckResultFromOtherMethod2.php new file mode 100644 index 0000000..65fe076 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/Fixture/CheckResultFromOtherMethod2.php @@ -0,0 +1,31 @@ +isPropertyVisible($name)) { + return false; + } + + return isset($this->$name); + } + + private function isPropertyVisible(string $name): bool + { + if (mt_rand(1, 0)) { + return true; + } + + return false; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/Fixture/ReturnFalseOnly.php b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/Fixture/ReturnFalseOnly.php new file mode 100644 index 0000000..0b91cf9 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/Rules/ReturnNullOverFalseRule/Fixture/ReturnFalseOnly.php @@ -0,0 +1,17 @@ + $expectedErrorMessagesWithLines + */ + #[DataProvider('provideData')] + public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void + { + $this->analyse([$filePath], $expectedErrorMessagesWithLines); + } + + /** + * @return Iterator, mixed>> + */ + public static function provideData(): Iterator + { + yield [__DIR__ . '/Fixture/ReturnFalseOnly.php', [[ReturnNullOverFalseRule::ERROR_MESSAGE, 9]]]; + yield [__DIR__ . '/Fixture/CheckResultFromOtherMethod.php', []]; + yield [__DIR__ . '/Fixture/CheckResultFromOtherMethod2.php', []]; + + yield [__DIR__ . '/Fixture/SkipReturnBool.php', []]; + } + + /** + * @return string[] + */ + #[Override] + public static function getAdditionalConfigFiles(): array + { + return [__DIR__ . '/../../../config/extension.neon']; + } + + protected function getRule(): Rule + { + return self::getContainer()->getByType(ReturnNullOverFalseRule::class); + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/SomeClass.php.inc b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/SomeClass.php.inc new file mode 100644 index 0000000..17ce850 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/SomeClass.php.inc @@ -0,0 +1,13 @@ +name; + } +} diff --git a/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/config/included_services.neon b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/config/included_services.neon new file mode 100644 index 0000000..6291a43 --- /dev/null +++ b/tools/.phpstan/vendor/tomasvotruba/type-coverage/packages/type-perfect/tests/config/included_services.neon @@ -0,0 +1,2 @@ +includes: + - ../../config/services.neon diff --git a/tools/.phpstan/vendor/webmozart/assert/CHANGELOG.md b/tools/.phpstan/vendor/webmozart/assert/CHANGELOG.md new file mode 100644 index 0000000..d62c758 --- /dev/null +++ b/tools/.phpstan/vendor/webmozart/assert/CHANGELOG.md @@ -0,0 +1,315 @@ +Changelog +========= + +## 2.4.1 + +### Fixed + +- Corrected `uuid` assertion to prevent braces and prefixes being _inside_ the value. + +## 2.4.0 + +### Changed + +- Updated docblocks for many methods, improving psalm support and type hints. + +## 2.3.0 + +### Changed + +- Clarified documentation and testing of `uniqueValues`. + +### Added + +- All assertions now support `string|callable` for the message. + +## 2.2.0 + +### Added + +- Added `isNotInstanceOfAny` assertion. + +## 2.1.6 + +### Fixed + +- Corrected docblocks for `list*` methods. + +## 2.1.5 + +### Fixed + +- Fixed regression of `instanceOf` messages + +## 2.1.4 + +### Fixed + +- Use custom message for more internal calls. + +## 2.1.3 + +### Fixed + +- Corrected `isList` type documentation. +- Corrected `isAOf`, `isInstanceOf`, etc type documentation. + +## 2.1.2 + +### Fixed + +- Changed `all*` assertion values back to `mixed`. + +## 2.1.1 + +### Fixed + +- Optimized `stringNotEmpty` by internally using `notSame`. + +## 2.1.0 + +### Fixed + +- Corrected `@param` declaration for `isMap`. +- Pass custom message to internal assertion calls. + +## 2.0.0 + +### Changed + +- **BREAKING** Minimum PHP version is now 8.2 (was 7.2). +- **BREAKING** Remove deprecated `isTraversable`, use `isIterable` or `isInstanceOf` instead. +- **BREAKING** Added `declare(strict_types=1)` to all classes. +- 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. + +### Added + +- All assertion methods now return the checked value. +- Added `notInArray` and `notOneOf`. +- Added `isInitialized`, to check if a class property is initialized. +- Added `negativeInteger` and `notNegativeInteger` +- Added `isStatic` and `notStatic` + +### Fixed + +- Corrected validation of emails with unicode characters. + +## 1.12.1 + +### Fixed + +- Exclude tools from export. + +## 1.12.0 + +### Fixed + +- Corrected messages and typos in various assertions. +- Document `void` return type. +- Prevent UUIDs with trailing newlines from validating. +- Assert values are strings before ctype checks. + +## 1.11.0 + +### Added + +* Added explicit (non-magic) `allNullOr*` methods, with `@psalm-assert` annotations, for better Psalm support. + +### Changed + +* Trait methods will now check the assertion themselves, instead of using `__callStatic` +* `isList` will now deal correctly with (modified) lists that contain `NaN` +* `reportInvalidArgument` now has a return type of `never`. + +### Removed + +* Removed `symfony/polyfill-ctype` as a dependency, and require `ext-ctype` instead. + * You can still require the `symfony/polyfill-ctype` in your project if you need it, as it provides `ext-ctype` + +## 1.10.0 + +### Added + +* On invalid assertion, we throw a `Webmozart\Assert\InvalidArgumentException` +* Added `Assert::positiveInteger()` + +### Changed + +* Using a trait with real implementations of `all*()` and `nullOr*()` methods to improve psalm compatibility. + +### Removed + +* Support for PHP <7.2 + +## 1.9.1 + +## Fixed + +* provisional support for PHP 8.0 + +## 1.9.0 + +* added better Psalm support for `all*` & `nullOr*` methods +* These methods are now understood by Psalm through a mixin. You may need a newer version of Psalm in order to use this +* added `@psalm-pure` annotation to `Assert::notFalse()` +* added more `@psalm-assert` annotations where appropriate + +## Changed + +* the `all*` & `nullOr*` methods are now declared on an interface, instead of `@method` annotations. +This interface is linked to the `Assert` class with a `@mixin` annotation. Most IDEs have supported this +for a long time, and you should not lose any autocompletion capabilities. PHPStan has supported this since +version `0.12.20`. This package is marked incompatible (with a composer conflict) with phpstan version prior to that. +If you do not use PHPStan than this does not matter. + +## 1.8.0 + +### Added + +* added `Assert::notStartsWith()` +* added `Assert::notEndsWith()` +* added `Assert::inArray()` +* added `@psalm-pure` annotations to pure assertions + +### Fixed + +* Exception messages of comparisons between `DateTime(Immutable)` objects now display their date & time. +* Custom Exception messages for `Assert::count()` now use the values to render the exception message. + +## 1.7.0 (2020-02-14) + +### Added + +* added `Assert::notFalse()` +* added `Assert::isAOf()` +* added `Assert::isAnyOf()` +* added `Assert::isNotA()` + +## 1.6.0 (2019-11-24) + +### Added + +* added `Assert::validArrayKey()` +* added `Assert::isNonEmptyList()` +* added `Assert::isNonEmptyMap()` +* added `@throws InvalidArgumentException` annotations to all methods that throw. +* added `@psalm-assert` for the list type to the `isList` assertion. + +### Fixed + +* `ResourceBundle` & `SimpleXMLElement` now pass the `isCountable` assertions. +They are countable, without implementing the `Countable` interface. +* The doc block of `range` now has the proper variables. +* An empty array will now pass `isList` and `isMap`. As it is a valid form of both. +If a non-empty variant is needed, use `isNonEmptyList` or `isNonEmptyMap`. + +### Changed + +* Removed some `@psalm-assert` annotations, that were 'side effect' assertions See: + * [#144](https://github.com/webmozart/assert/pull/144) + * [#145](https://github.com/webmozart/assert/issues/145) + * [#146](https://github.com/webmozart/assert/pull/146) + * [#150](https://github.com/webmozart/assert/pull/150) +* If you use Psalm, the minimum version needed is `3.6.0`. Which is enforced through a composer conflict. +If you don't use Psalm, then this has no impact. + +## 1.5.0 (2019-08-24) + +### Added + +* added `Assert::uniqueValues()` +* added `Assert::unicodeLetters()` +* added: `Assert::email()` +* added support for [Psalm](https://github.com/vimeo/psalm), by adding `@psalm-assert` annotations where appropriate. + +### Fixed + +* `Assert::endsWith()` would not give the correct result when dealing with a multibyte suffix. +* `Assert::length(), minLength, maxLength, lengthBetween` would not give the correct result when dealing with multibyte characters. + +**NOTE**: These 2 changes may break your assertions if you relied on the fact that multibyte characters didn't behave correctly. + +### Changed + +* The names of some variables have been updated to better reflect what they are. +* All function calls are now in their FQN form, slightly increasing performance. +* Tests are now properly ran against HHVM-3.30 and PHP nightly. + +### Deprecation + +* deprecated `Assert::isTraversable()` in favor of `Assert::isIterable()` + * This was already done in 1.3.0, but it was only done through a silenced `trigger_error`. It is now annotated as well. + +## 1.4.0 (2018-12-25) + +### Added + +* added `Assert::ip()` +* added `Assert::ipv4()` +* added `Assert::ipv6()` +* added `Assert::notRegex()` +* added `Assert::interfaceExists()` +* added `Assert::isList()` +* added `Assert::isMap()` +* added polyfill for ctype + +### Fixed + +* Special case when comparing objects implementing `__toString()` + +## 1.3.0 (2018-01-29) + +### Added + +* added `Assert::minCount()` +* added `Assert::maxCount()` +* added `Assert::countBetween()` +* added `Assert::isCountable()` +* added `Assert::notWhitespaceOnly()` +* added `Assert::natural()` +* added `Assert::notContains()` +* added `Assert::isArrayAccessible()` +* added `Assert::isInstanceOfAny()` +* added `Assert::isIterable()` + +### Fixed + +* `stringNotEmpty` will no longer report "0" is an empty string + +### Deprecation + +* deprecated `Assert::isTraversable()` in favor of `Assert::isIterable()` + +## 1.2.0 (2016-11-23) + + * added `Assert::throws()` + * added `Assert::count()` + * added extension point `Assert::reportInvalidArgument()` for custom subclasses + +## 1.1.0 (2016-08-09) + + * added `Assert::object()` + * added `Assert::propertyExists()` + * added `Assert::propertyNotExists()` + * added `Assert::methodExists()` + * added `Assert::methodNotExists()` + * added `Assert::uuid()` + +## 1.0.2 (2015-08-24) + + * integrated Style CI + * add tests for minimum package dependencies on Travis CI + +## 1.0.1 (2015-05-12) + + * added support for PHP 5.3.3 + +## 1.0.0 (2015-05-12) + + * first stable release + +## 1.0.0-beta (2015-03-19) + + * first beta release diff --git a/tools/.phpstan/vendor/webmozart/assert/LICENSE b/tools/.phpstan/vendor/webmozart/assert/LICENSE new file mode 100644 index 0000000..9e2e307 --- /dev/null +++ b/tools/.phpstan/vendor/webmozart/assert/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Bernhard Schussek + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/.phpstan/vendor/webmozart/assert/README.md b/tools/.phpstan/vendor/webmozart/assert/README.md new file mode 100644 index 0000000..fef4eda --- /dev/null +++ b/tools/.phpstan/vendor/webmozart/assert/README.md @@ -0,0 +1,313 @@ +Webmozart Assert +================ + +[![Latest Stable Version](https://poser.pugx.org/webmozart/assert/v/stable.svg)](https://packagist.org/packages/webmozart/assert) +[![Total Downloads](https://poser.pugx.org/webmozart/assert/downloads.svg)](https://packagist.org/packages/webmozart/assert) + +This library contains efficient assertions to test the input and output of +your methods. With these assertions, you can greatly reduce the amount of coding +needed to write a safe implementation. + +All assertions in the [`Assert`] class throw an `Webmozart\Assert\InvalidArgumentException` if +they fail. + +FAQ +--- + +**What's the difference to [beberlei/assert]?** + +This library is heavily inspired by Benjamin Eberlei's wonderful [assert package], +but fixes a usability issue with error messages that can't be fixed there without +breaking backwards compatibility. + +This package features usable error messages by default. However, you can also +easily write custom error messages: + +``` +Assert::string($path, 'The path is expected to be a string. Got: %s'); +``` + +In [beberlei/assert], the ordering of the `%s` placeholders is different for +every assertion. This package, on the contrary, provides consistent placeholder +ordering for all assertions: + +* `%s`: The tested value as string, e.g. `"/foo/bar"`. +* `%2$s`, `%3$s`, ...: Additional assertion-specific values, e.g. the + minimum/maximum length, allowed values, etc. + +Check the source code of the assertions to find out details about the additional +available placeholders. + +Installation +------------ + +Use [Composer] to install the package: + +```bash +composer require webmozart/assert +``` + +Example +------- + +```php +use Webmozart\Assert\Assert; + +class Employee +{ + public function __construct($id) + { + Assert::integer($id, 'The employee ID must be an integer. Got: %s'); + Assert::greaterThan($id, 0, 'The employee ID must be a positive integer. Got: %s'); + } +} +``` + +If you create an employee with an invalid ID, an exception is thrown: + +```php +new Employee('foobar'); +// => Webmozart\Assert\InvalidArgumentException: +// The employee ID must be an integer. Got: string + +new Employee(-10); +// => Webmozart\Assert\InvalidArgumentException: +// The employee ID must be a positive integer. Got: -10 +``` + +Assertions +---------- + +The [`Assert`] class provides the following assertions: + +### Type Assertions + +Method | Description +----------------------------------------------------------- | -------------------------------------------------- +`string($value, $message = '')` | Check that a value is a string +`stringNotEmpty($value, $message = '')` | Check that a value is a non-empty string +`integer($value, $message = '')` | Check that a value is an integer +`integerish($value, $message = '')` | Check that a value casts to an integer +`positiveInteger($value, $message = '')` | Check that a value is a positive (non-zero) integer +`negativeInteger($value, $message = '')` | Check that a value is a negative integer +`notNegativeInteger($value, $message = '')` | Check that a value is a non-negative integer +`float($value, $message = '')` | Check that a value is a float +`numeric($value, $message = '')` | Check that a value is numeric +`natural($value, $message = '')` | Check that a value is a non-negative integer +`boolean($value, $message = '')` | Check that a value is a boolean +`scalar($value, $message = '')` | Check that a value is a scalar +`object($value, $message = '')` | Check that a value is an object +`objectish($value, $message = '')` | Check that a value is an object or a string of a class that exists +`resource($value, $type = null, $message = '')` | Check that a value is a resource +`isInitialized($value, $property, $message = '')` | Check that a value has an initialized property +`isCallable($value, $message = '')` | Check that a value is a callable +`isArray($value, $message = '')` | Check that a value is an array +`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 +`isInstanceOfAny($value, array $classes, $message = '')` | Check that a value is an `instanceof` at least one class on the array of classes +`notInstanceOf($value, $class, $message = '')` | Check that a value is not an `instanceof` a class +`isNotInstanceOfAny($value, array $classes, $message = '')` | Check that a value is not an `instanceof` at least one class on the array of classes +`isAOf($value, $class, $message = '')` | Check that a value is of the class or has one of its parents +`isAnyOf($value, array $classes, $message = '')` | Check that a value is of at least one of the classes or has one of its parents +`isNotA($value, $class, $message = '')` | Check that a value is not of the class or has not one of its parents +`isArrayAccessible($value, $message = '')` | Check that a value can be accessed as an array +`uniqueValues($values, $message = '')` | Check that the given array contains unique values + +### 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 +`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 + +You should check that a value is a string with `Assert::string()` before making +any of the following assertions. + +Method | Description +--------------------------------------------------- | ----------------------------------------------------------------- +`contains($value, $subString, $message = '')` | Check that a string contains a substring +`notContains($value, $subString, $message = '')` | Check that a string does not contain a substring +`startsWith($value, $prefix, $message = '')` | Check that a string has a prefix +`notStartsWith($value, $prefix, $message = '')` | Check that a string does not have a prefix +`startsWithLetter($value, $message = '')` | Check that a string starts with a letter +`endsWith($value, $suffix, $message = '')` | Check that a string has a suffix +`notEndsWith($value, $suffix, $message = '')` | Check that a string does not have a suffix +`regex($value, $pattern, $message = '')` | Check that a string matches a regular expression +`notRegex($value, $pattern, $message = '')` | Check that a string does not match a regular expression +`unicodeLetters($value, $message = '')` | Check that a string contains Unicode letters only +`alpha($value, $message = '')` | Check that a string contains letters only +`digits($value, $message = '')` | Check that a string contains digits only +`alnum($value, $message = '')` | Check that a string contains letters and digits only +`lower($value, $message = '')` | Check that a string contains lowercase characters only +`upper($value, $message = '')` | Check that a string contains uppercase characters only +`length($value, $length, $message = '')` | Check that a string has a certain number of characters +`minLength($value, $min, $message = '')` | Check that a string has at least a certain number of characters +`maxLength($value, $max, $message = '')` | Check that a string has at most a certain number of characters +`lengthBetween($value, $min, $max, $message = '')` | Check that a string has a length in the given range +`uuid($value, $message = '')` | Check that a string is a valid UUID +`ip($value, $message = '')` | Check that a string is a valid IP (either IPv4 or IPv6) +`ipv4($value, $message = '')` | Check that a string is a valid IPv4 +`ipv6($value, $message = '')` | Check that a string is a valid IPv6 +`email($value, $message = '')` | Check that a string is a valid e-mail address +`notWhitespaceOnly($value, $message = '')` | Check that a string contains at least one non-whitespace character + +### File Assertions + +Method | Description +----------------------------------- | -------------------------------------------------- +`fileExists($value, $message = '')` | Check that a value is an existing path +`file($value, $message = '')` | Check that a value is an existing file +`directory($value, $message = '')` | Check that a value is an existing directory +`readable($value, $message = '')` | Check that a value is a readable path +`writable($value, $message = '')` | Check that a value is a writable path + +### Object Assertions + +Method | Description +----------------------------------------------------- | -------------------------------------------------- +`classExists($value, $message = '')` | Check that a value is an existing class name +`subclassOf($value, $class, $message = '')` | Check that a class is a subclass of another +`interfaceExists($value, $message = '')` | Check that a value is an existing interface name +`implementsInterface($value, $class, $message = '')` | Check that a class implements an interface +`propertyExists($value, $property, $message = '')` | Check that a property exists in a class/object +`propertyNotExists($value, $property, $message = '')` | Check that a property does not exist in a class/object +`methodExists($value, $method, $message = '')` | Check that a method exists in a class/object +`methodNotExists($value, $method, $message = '')` | Check that a method does not exist in a class/object + +### Array Assertions + +Method | Description +-------------------------------------------------- | ------------------------------------------------------------------ +`keyExists($array, $key, $message = '')` | Check that a key exists in an array +`keyNotExists($array, $key, $message = '')` | Check that a key does not exist in an array +`validArrayKey($key, $message = '')` | Check that a value is a valid array key (int or string) +`count($array, $number, $message = '')` | Check that an array contains a specific number of elements +`minCount($array, $min, $message = '')` | Check that an array contains at least a certain number of elements +`maxCount($array, $max, $message = '')` | Check that an array contains at most a certain number of elements +`countBetween($array, $min, $max, $message = '')` | Check that an array has a count in the given range +`isList($array, $message = '')` | Check that an array is a non-associative list +`isNonEmptyList($array, $message = '')` | Check that an array is a non-associative list, and not empty +`isMap($array, $message = '')` | Check that an array is associative and has strings as keys +`isNonEmptyMap($array, $message = '')` | Check that an array is associative and has strings as keys, and is not empty + +### Function Assertions + +Method | Description +------------------------------------------| ----------------------------------------------------------------------------------------------------- +`throws($closure, $class, $message = '')` | Check that a function throws a certain exception. Subclasses of the exception class will be accepted. +`isStatic($closure, $message = '')` | Check that a function is static. +`notStatic($closure, $message = '')` | Check that a function is not static. + +### Collection Assertions + +All of the above assertions can be prefixed with `all*()` to test the contents +of an array or a `\Traversable`: + +```php +Assert::allIsInstanceOf($employees, 'Acme\Employee'); +``` + +### Nullable Assertions + +All of the above assertions can be prefixed with `nullOr*()` to run the +assertion only if the value is not `null`: + +```php +Assert::nullOrString($middleName, 'The middle name must be a string or null. Got: %s'); +``` + +### Lazy assertion messages + +Assertion messages can be provided as callables to avoid unnecessary computation when the assertion passes: + +```php +Assert::string($value, fn() => expensiveMessage()); +``` + +The callable will only be executed if the assertion fails. + +### Extending Assert + +The `Assert` class comes with a few methods, which can be overridden to change the class behaviour. You can also extend it to +add your own assertions. + + +#### Overriding methods + +Overriding the following methods in your assertion class allows you to change the behaviour of the assertions: + +* `public static function __callStatic($name, $arguments)` + * This method is used to 'create' the `nullOr` and `all` versions of the assertions. +* `protected static function valueToString($value)` + * This method is used for error messages, to convert the value to a string value for displaying. You could use this for representing a value object with a `__toString` method for example. +* `protected static function typeToString($value)` + * This method is used for error messages, to convert a value to a string representing its type. +* `protected static function strlen($value)` + * This method is used to calculate string length for relevant methods, using the `mb_strlen` if available and useful. +* `protected static function reportInvalidArgument($message)` + * This method is called when an assertion fails, with the specified error message. Here you can throw your own exception, or log something. + +## Static analysis support + +Where applicable, assertion functions are annotated to support Psalm's +[Assertion syntax](https://psalm.dev/docs/annotating_code/assertion_syntax/). + +A native Psalm plugin can be enabled to also add type inference for return types (new in 2.x): + +```php +vendor/bin/psalm-plugin enable webmozart/assert +``` + +A dedicated [PHPStan Plugin](https://github.com/phpstan/phpstan-webmozart-assert) is +required for proper type support. + +Authors +------- + +* [Bernhard Schussek] a.k.a. [@webmozart] +* [The Community Contributors] + +Contribute +---------- + +Contributions to the package are always welcome! + +* Report any bugs or issues you find on the [issue tracker]. +* You can grab the source code at the package's [Git repository]. + +License +------- + +All contents of this package are licensed under the [MIT license]. + +[beberlei/assert]: https://github.com/beberlei/assert +[assert package]: https://github.com/beberlei/assert +[Composer]: https://getcomposer.org +[Bernhard Schussek]: https://webmozarts.com +[The Community Contributors]: https://github.com/webmozart/assert/graphs/contributors +[issue tracker]: https://github.com/webmozart/assert/issues +[Git repository]: https://github.com/webmozart/assert +[@webmozart]: https://twitter.com/webmozart +[MIT license]: LICENSE +[`Assert`]: src/Assert.php diff --git a/tools/.phpstan/vendor/webmozart/assert/composer.json b/tools/.phpstan/vendor/webmozart/assert/composer.json new file mode 100644 index 0000000..6c816f6 --- /dev/null +++ b/tools/.phpstan/vendor/webmozart/assert/composer.json @@ -0,0 +1,71 @@ +{ + "name": "webmozart/assert", + "description": "Assertions to validate method input/output with nice error messages.", + "license": "MIT", + "keywords": [ + "assert", + "check", + "validate" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "require": { + "php": "^8.2", + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Webmozart\\Assert\\Bin\\": "bin/src", + "Webmozart\\Assert\\Tests\\": "tests/" + } + }, + "extra": { + "psalm": { + "pluginClass": "Webmozart\\Assert\\PsalmPlugin" + }, + "branch-alias": { + "dev-feature/2-0": "2.0-dev", + "dev-master": "2.0-dev" + } + }, + "scripts": { + "install-tools": [ + "composer --working-dir=tools/php-cs-fixer install", + "composer --working-dir=tools/phpunit install", + "composer --working-dir=tools/psalm install", + "composer --working-dir=tools/roave-bc-check install" + ], + "update-tools": [ + "composer --working-dir=tools/php-cs-fixer update", + "composer --working-dir=tools/phpunit update", + "composer --working-dir=tools/psalm update", + "composer --working-dir=tools/roave-bc-check update" + ], + "generate-mixin": "php bin/generate.php", + "bc-check": "./tools/roave-bc-check/vendor/bin/roave-backward-compatibility-check", + "cs-check" : "./tools/php-cs-fixer/vendor/bin/php-cs-fixer check", + "cs-fix": "./tools/php-cs-fixer/vendor/bin/php-cs-fixer fix", + "static-analysis": "./tools/psalm/vendor/bin/psalm --threads=4 --root=$(pwd)", + "test": "./tools/phpunit/vendor/bin/phpunit" + } +} diff --git a/tools/.phpstan/vendor/webmozart/assert/src/Assert.php b/tools/.phpstan/vendor/webmozart/assert/src/Assert.php new file mode 100644 index 0000000..31c861b --- /dev/null +++ b/tools/.phpstan/vendor/webmozart/assert/src/Assert.php @@ -0,0 +1,2620 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Webmozart\Assert; + +use ArrayAccess; +use Closure; +use Countable; +use DateTime; +use DateTimeImmutable; +use ReflectionFunction; +use ReflectionProperty; +use Throwable; +use Traversable; + +/** + * Efficient assertions to validate the input/output of your methods. + * + * @since 1.0 + * + * @author Bernhard Schussek + */ +class Assert +{ + use Mixin; + + /** + * @psalm-pure + * + * @psalm-assert string $value + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function string(mixed $value, string|callable $message = ''): string + { + if (!\is_string($value)) { + $message = static::resolveMessage($message); + + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a string. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert non-empty-string $value + * @param string|callable():string $message + * + * @return non-empty-string + * + * @throws InvalidArgumentException + */ + public static function stringNotEmpty(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + static::notSame($value, '', $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert int $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function integer(mixed $value, string|callable $message = ''): int + { + if (!\is_int($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an integer. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert numeric $value + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function integerish(mixed $value, string|callable $message = ''): int|float|string + { + if (!\is_numeric($value) || $value != (int) $value) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an integerish value. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert positive-int $value + * + * @param string|callable():string $message + * + * @return positive-int + * + * @throws InvalidArgumentException + */ + public static function positiveInteger(mixed $value, string|callable $message = ''): int + { + static::integer($value, $message); + + if ($value < 1) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a positive integer. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * @psalm-assert non-negative-int $value + * @param string|callable():string $message + * + * @return non-negative-int + * + * @throws InvalidArgumentException + */ + public static function notNegativeInteger(mixed $value, string|callable $message = ''): int + { + static::integer($value, $message); + + if ($value < 0) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a non negative integer. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * @psalm-assert negative-int $value + * @param string|callable():string $message + * + * @return negative-int + * + * @throws InvalidArgumentException + */ + public static function negativeInteger(mixed $value, string|callable $message = ''): int + { + static::integer($value, $message); + + if ($value >= 0) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a negative integer. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert float $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function float(mixed $value, string|callable $message = ''): float + { + if (!\is_float($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a float. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert numeric $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function numeric(mixed $value, string|callable $message = ''): int|float|string + { + if (!\is_numeric($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a numeric. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert positive-int|0 $value + * + * @param string|callable():string $message + * + * @return positive-int|0 + * + * @throws InvalidArgumentException + */ + public static function natural(mixed $value, string|callable $message = ''): int + { + if (!\is_int($value) || $value < 0) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a non-negative integer. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert bool $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function boolean(mixed $value, string|callable $message = ''): bool + { + if (!\is_bool($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a boolean. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert scalar $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function scalar(mixed $value, string|callable $message = ''): int|bool|float|string + { + if (!\is_scalar($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a scalar. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert object $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function object(mixed $value, string|callable $message = ''): object + { + if (!\is_object($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an object. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert object|class-string $value + * + * @param string|callable():string $message + * + * @return object|class-string + * + * @throws InvalidArgumentException + */ + public static function objectish(mixed $value, string|callable $message = ''): object|string + { + if (!\is_object($value) && !\is_string($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an objectish value. Got: %s', + static::typeToString($value) + )); + } + + if (\is_string($value) && !\class_exists($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected class to be defined. Got: %s', + $value + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert resource $value + * + * @param string|callable():string $message + * + * @see https://www.php.net/manual/en/function.get-resource-type.php + * + * @return resource + * + * @throws InvalidArgumentException + */ + public static function resource(mixed $value, ?string $type = null, string|callable $message = ''): mixed + { + if (!\is_resource($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a resource. Got: %s', + static::typeToString($value), + $type // User supplied message might include the second placeholder. + )); + } + + if ($type && $type !== \get_resource_type($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a resource of type %2$s. Got: %s', + static::typeToString($value), + $type + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert object $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function isInitialized(mixed $value, string $property, string|callable $message = ''): object + { + Assert::object($value); + + $reflectionProperty = new ReflectionProperty($value, $property); + + if (!$reflectionProperty->isInitialized($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected property %s to be initialized.', + $property, + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert callable $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function isCallable(mixed $value, string|callable $message = ''): callable + { + if (!\is_callable($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a callable. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert array $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function isArray(mixed $value, string|callable $message = ''): array + { + if (!\is_array($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an array. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert array|ArrayAccess $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function isArrayAccessible(mixed $value, string|callable $message = ''): array|ArrayAccess + { + if (!\is_array($value) && !($value instanceof ArrayAccess)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an array accessible. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert countable $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function isCountable(mixed $value, string|callable $message = ''): array|Countable + { + if (!\is_array($value) && !($value instanceof Countable)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a countable. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function isIterable(mixed $value, string|callable $message = ''): iterable + { + if (!\is_array($value) && !($value instanceof Traversable)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an iterable. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T of object + * + * @psalm-assert T $value + * + * @param string|callable():string $message + * @param class-string $class + * + * @return T + * + * @throws InvalidArgumentException + */ + public static function isInstanceOf(mixed $value, mixed $class, string|callable $message = ''): object + { + static::string($class, 'Expected class as a string. Got: %s'); + + if (!($value instanceof $class)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an instance of %2$s. Got: %s', + static::typeToString($value), + $class + )); + } + + return $value; + } + + /** + * @template T of object + * + * @psalm-assert object $value + * + * @param string|callable():string $message + * @param class-string $class + * + * @throws InvalidArgumentException + */ + public static function notInstanceOf(mixed $value, mixed $class, string|callable $message = ''): object + { + static::string($class, 'Expected class as a string. Got: %s'); + + if (!\is_object($value) || $value instanceof $class) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an instance other than %2$s. Got: %s', + static::typeToString($value), + $class + )); + } + + return $value; + } + + /** + * @template T of object + * + * @psalm-assert T $value + * + * @param iterable> $classes + * @param string|callable():string $message + * + * @return T + * + * @throws InvalidArgumentException + */ + public static function isInstanceOfAny(mixed $value, mixed $classes, string|callable $message = ''): object + { + static::isIterable($classes); + + foreach ($classes as $class) { + static::string($class, 'Expected class as a string. Got: %s'); + + if ($value instanceof $class) { + return $value; + } + } + + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an instance of any of %2$s. Got: %s', + static::typeToString($value), + \implode(', ', \array_map(static::valueToString(...), \iterator_to_array($classes))) + )); + } + + /** + * @template T + * + * @psalm-assert object|class-string $value + * + * @param T $value + * @param string|callable():string $message + * + * @return T + * + * @throws InvalidArgumentException + */ + public static function isNotInstanceOfAny(mixed $value, mixed $classes, string|callable $message = ''): mixed + { + static::isIterable($classes); + + foreach ($classes as $class) { + static::string($class, 'Expected class as a string. Got: %s'); + + if ($value instanceof $class) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected not an instance of %2$s. Got: %s', + static::typeToString($value), + \implode(', ', \array_map(static::valueToString(...), \iterator_to_array($classes))) + )); + } + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T of object + * + * @psalm-assert T|class-string $value + * + * @param string|callable():string $message + * @param class-string $class + * + * @return T|class-string + * + * @throws InvalidArgumentException + */ + public static function isAOf(mixed $value, mixed $class, string|callable $message = ''): object|string + { + static::string($class, 'Expected class as a string. Got: %s'); + + if (!\is_a($value, $class, \is_string($value))) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an instance of this class or to this class among its parents "%2$s". Got: %s', + static::valueToString($value), + $class + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T + * + * @psalm-assert object|class-string $value + * + * @param T $value + * @param string|callable():string $message + * + * @return object|class-string + * + * @throws InvalidArgumentException + */ + public static function isNotA(mixed $value, mixed $class, string|callable $message = ''): object|string + { + static::objectish($value, $message); + static::string($class, 'Expected class as a string. Got: %s'); + + if (\is_a($value, $class, \is_string($value))) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an instance of this class or to this class among its parents other than "%2$s". Got: %s', + static::valueToString($value), + $class + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert T $value + * + * @template T as object + * + * @param array> $classes + * @param string|callable():string $message + * + * @return T + * @throws InvalidArgumentException + */ + public static function isAnyOf(mixed $value, mixed $classes, string|callable $message = ''): object|string + { + static::objectish($value, $message); + static::isIterable($classes); + + foreach ($classes as $class) { + static::string($class, 'Expected class as a string. Got: %s'); + + if (\is_a($value, $class, \is_string($value))) { + return $value; + } + } + + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an instance of any of this classes or any of those classes among their parents "%2$s". Got: %s', + static::valueToString($value), + \implode(', ', \iterator_to_array($classes)) + )); + } + + /** + * @psalm-pure + * + * @psalm-assert empty $value + * + * @param string|callable():string $message + * + * @return empty + * + * @throws InvalidArgumentException + */ + public static function isEmpty(mixed $value, string|callable $message = ''): mixed + { + if (!empty($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an empty value. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert !empty $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function notEmpty(mixed $value, string|callable $message = ''): mixed + { + if (empty($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a non-empty value. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert null $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function null(mixed $value, string|callable $message = ''): null + { + if (null !== $value) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected null. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert !null $value + * + * @param string|callable():string $message + * + * @template T + * @param T|null $value + * @return T + * @throws InvalidArgumentException + */ + public static function notNull(mixed $value, string|callable $message = ''): mixed + { + if (null === $value) { + $message = self::resolveMessage($message); + static::reportInvalidArgument( + $message ?: 'Expected a value other than null.' + ); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert true $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function true(mixed $value, string|callable $message = ''): true + { + if (true !== $value) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to be true. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert false $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function false(mixed $value, string|callable $message = ''): false + { + if (false !== $value) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to be false. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert !false $value + * + * @param string|callable():string $message + * + * @template T + * @param T|false $value + * @return T + * @throws InvalidArgumentException + */ + public static function notFalse(mixed $value, string|callable $message = ''): mixed + { + if (false === $value) { + $message = self::resolveMessage($message); + static::reportInvalidArgument( + $message ?: 'Expected a value other than false.' + ); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * @param string $value + * + * @throws InvalidArgumentException + */ + public static function ip(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + if (false === \filter_var($value, \FILTER_VALIDATE_IP)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to be an IP. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * @param string $value + * + * @throws InvalidArgumentException + */ + public static function ipv4(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + if (false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to be an IPv4. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * @param string $value + * + * @throws InvalidArgumentException + */ + public static function ipv6(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + if (false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to be an IPv6. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * @param string $value + * + * @throws InvalidArgumentException + */ + public static function email(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + if (false === \filter_var($value, FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to be a valid e-mail address. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * Does non-strict comparisons on the items, so ['3', 3] will not pass the assertion. + * Note: objects with identical properties are also considered equal. + * + * @psalm-assert array $values + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function uniqueValues(mixed $values, string|callable $message = ''): array + { + static::isArray($values); + + $allValues = \count($values); + $uniqueValues = \count(\array_unique($values)); + + if ($allValues !== $uniqueValues) { + $difference = $allValues - $uniqueValues; + + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an array of unique values, but %s of them %s duplicated', + $difference, + 1 === $difference ? 'is' : 'are' + )); + } + + return $values; + } + + /** + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function eq(mixed $value, mixed $expect, string|callable $message = ''): mixed + { + if ($expect != $value) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value equal to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($expect) + )); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function notEq(mixed $value, mixed $expect, string|callable $message = ''): mixed + { + if ($expect == $value) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a different value than %s.', + static::valueToString($expect) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function same(mixed $value, mixed $expect, string|callable $message = ''): mixed + { + if ($expect !== $value) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value identical to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($expect) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function notSame(mixed $value, mixed $expect, string|callable $message = ''): mixed + { + if ($expect === $value) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value not identical to %s.', + static::valueToString($expect) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function greaterThan(mixed $value, mixed $limit, string|callable $message = ''): mixed + { + if ($value <= $limit) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value greater than %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function greaterThanEq(mixed $value, mixed $limit, string|callable $message = ''): mixed + { + if ($value < $limit) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value greater than or equal to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function lessThan(mixed $value, mixed $limit, string|callable $message = ''): mixed + { + if ($value >= $limit) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value less than %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function lessThanEq(mixed $value, mixed $limit, string|callable $message = ''): mixed + { + if ($value > $limit) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value less than or equal to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + + return $value; + } + + /** + * Inclusive range, so Assert::(3, 3, 5) passes. + * + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function range(mixed $value, mixed $min, mixed $max, string|callable $message = ''): mixed + { + if ($value < $min || $value > $max) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value between %2$s and %3$s. Got: %s', + static::valueToString($value), + static::valueToString($min), + static::valueToString($max) + )); + } + + return $value; + } + + /** + * A more human-readable alias of Assert::inArray(). + * + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function oneOf(mixed $value, mixed $values, string|callable $message = ''): mixed + { + static::inArray($value, $values, $message); + + return $value; + } + + /** + * Does strict comparison, so Assert::inArray(3, ['3']) does not pass the assertion. + * + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function inArray(mixed $value, mixed $values, string|callable $message = ''): mixed + { + static::isArray($values); + + if (!\in_array($value, $values, true)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected one of: %2$s. Got: %s', + static::valueToString($value), + \implode(', ', \array_map(static::valueToString(...), $values)) + )); + } + + return $value; + } + + /** + * A more human-readable alias of Assert::notInArray(). + * + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function notOneOf(mixed $value, mixed $values, string|callable $message = ''): mixed + { + static::notInArray($value, $values, $message); + + return $value; + } + + /** + * Check that a value is not present + * + * Does strict comparison, so Assert::notInArray(3, [1, 2, 3]) will not pass + * the assertion, but Assert::notInArray(3, ['3']) will. + * + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function notInArray(mixed $value, mixed $values, string|callable $message = ''): mixed + { + static::isArray($values); + + if (\in_array($value, $values, true)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: '%2$s was not expected to contain a value. Got: %s', + static::valueToString($value), + \implode(', ', \array_map(static::valueToString(...), $values)) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function contains(mixed $value, mixed $subString, string|callable $message = ''): string + { + static::string($value); + static::string($subString); + + if (!\str_contains($value, $subString)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain %2$s. Got: %s', + static::valueToString($value), + static::valueToString($subString) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function notContains(mixed $value, mixed $subString, string|callable $message = ''): string + { + static::string($value); + static::string($subString); + + if (\str_contains($value, $subString)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: '%2$s was not expected to be contained in a value. Got: %s', + static::valueToString($value), + static::valueToString($subString) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function notWhitespaceOnly(mixed $value, string|callable $message = ''): string + { + static::string($value); + + if (\preg_match('/^\s*$/', $value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a non-whitespace string. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function startsWith(mixed $value, mixed $prefix, string|callable $message = ''): string + { + static::string($value); + static::string($prefix); + + if (!\str_starts_with($value, $prefix)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to start with %2$s. Got: %s', + static::valueToString($value), + static::valueToString($prefix) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function notStartsWith(mixed $value, mixed $prefix, string|callable $message = ''): string + { + static::string($value); + static::string($prefix); + + if (\str_starts_with($value, $prefix)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value not to start with %2$s. Got: %s', + static::valueToString($value), + static::valueToString($prefix) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function startsWithLetter(mixed $value, string|callable $message = ''): string + { + static::string($value); + + $valid = isset($value[0]); + + if ($valid) { + $locale = \setlocale(LC_CTYPE, '0'); + \setlocale(LC_CTYPE, 'C'); + $valid = \ctype_alpha($value[0]); + \setlocale(LC_CTYPE, $locale); + } + + if (!$valid) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to start with a letter. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function endsWith(mixed $value, mixed $suffix, string|callable $message = ''): string + { + static::string($value); + static::string($suffix); + + if (!\str_ends_with($value, $suffix)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to end with %2$s. Got: %s', + static::valueToString($value), + static::valueToString($suffix) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function notEndsWith(mixed $value, mixed $suffix, string|callable $message = ''): string + { + static::string($value); + static::string($suffix); + + if (\str_ends_with($value, $suffix)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value not to end with %2$s. Got: %s', + static::valueToString($value), + static::valueToString($suffix) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function regex(mixed $value, mixed $pattern, string|callable $message = ''): string + { + static::string($value); + static::string($pattern); + + if (!\preg_match($pattern, $value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'The value %s does not match the expected pattern.', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function notRegex(mixed $value, mixed $pattern, string|callable $message = ''): string + { + static::string($value); + static::string($pattern); + + if (\preg_match($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'The value %s matches the pattern %s (at offset %d).', + static::valueToString($value), + static::valueToString($pattern), + $matches[0][1] + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function unicodeLetters(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + if (!\preg_match('/^\p{L}+$/u', $value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain only Unicode letters. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function alpha(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + $locale = \setlocale(LC_CTYPE, '0'); + \setlocale(LC_CTYPE, 'C'); + $valid = !\ctype_alpha($value); + \setlocale(LC_CTYPE, $locale); + + if ($valid) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain only letters. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function digits(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + $locale = \setlocale(LC_CTYPE, '0'); + \setlocale(LC_CTYPE, 'C'); + $valid = !\ctype_digit($value); + \setlocale(LC_CTYPE, $locale); + + if ($valid) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain digits only. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function alnum(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + $locale = \setlocale(LC_CTYPE, '0'); + \setlocale(LC_CTYPE, 'C'); + $valid = !\ctype_alnum($value); + \setlocale(LC_CTYPE, $locale); + + if ($valid) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain letters and digits only. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert lowercase-string $value + * + * @param string|callable():string $message + * + * @return lowercase-string + * + * @throws InvalidArgumentException + */ + public static function lower(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + $locale = \setlocale(LC_CTYPE, '0'); + \setlocale(LC_CTYPE, 'C'); + $valid = !\ctype_lower($value); + \setlocale(LC_CTYPE, $locale); + + if ($valid) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain lowercase characters only. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert string $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function upper(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + $locale = \setlocale(LC_CTYPE, '0'); + \setlocale(LC_CTYPE, 'C'); + $valid = !\ctype_upper($value); + \setlocale(LC_CTYPE, $locale); + + if ($valid) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain uppercase characters only. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function length(mixed $value, mixed $length, string|callable $message = ''): string + { + static::string($value); + static::integerish($length); + + if ($length !== static::strlen($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain %2$s characters. Got: %s', + static::valueToString($value), + $length + )); + } + + return $value; + } + + /** + * Inclusive min. + * + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function minLength(mixed $value, mixed $min, string|callable $message = ''): string + { + static::string($value); + static::integerish($min); + + if (static::strlen($value) < $min) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain at least %2$s characters. Got: %s', + static::valueToString($value), + $min + )); + } + + return $value; + } + + /** + * Inclusive max. + * + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function maxLength(mixed $value, mixed $max, string|callable $message = ''): string + { + static::string($value); + static::integerish($max); + + if (static::strlen($value) > $max) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain at most %2$s characters. Got: %s', + static::valueToString($value), + $max + )); + } + + return $value; + } + + /** + * Inclusive, so Assert::lengthBetween('asd', 3, 5); passes the assertion. + * + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function lengthBetween(mixed $value, mixed $min, mixed $max, string|callable $message = ''): string + { + static::string($value); + static::integerish($min); + static::integerish($max); + + $length = static::strlen($value); + + if ($length < $min || $length > $max) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s', + static::valueToString($value), + $min, + $max + )); + } + + return $value; + } + + /** + * Will also pass if $value is a directory, use Assert::file() instead if you need to be sure it is a file. + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function fileExists(mixed $value, string|callable $message = ''): string + { + static::string($value); + + if (!\file_exists($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'The path %s does not exist.', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function file(mixed $value, string|callable $message = ''): string + { + static::string($value); + + if (!\is_file($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'The path %s is not a file.', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function directory(mixed $value, string|callable $message = ''): string + { + static::string($value); + + if (!\is_dir($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'The path %s is not a directory.', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function readable(mixed $value, string|callable $message = ''): string + { + static::string($value); + + if (!\is_readable($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'The path %s is not readable.', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function writable(mixed $value, string|callable $message = ''): string + { + static::string($value); + + if (!\is_writable($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'The path %s is not writable.', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-assert class-string $value + * + * @param string|callable():string $message + * + * @return class-string + * + * @throws InvalidArgumentException + */ + public static function classExists(mixed $value, string|callable $message = ''): string + { + static::string($value); + + if (!\class_exists($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an existing class name. Got: %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template ExpectedType of object + * + * @psalm-assert class-string $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return class-string + * + * @throws InvalidArgumentException + */ + public static function subclassOf(mixed $value, mixed $class, string|callable $message = ''): string + { + static::string($value); + static::string($class); + + if (!\is_subclass_of($value, $class)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected a sub-class of %2$s. Got: %s', + static::valueToString($value), + static::valueToString($class) + )); + } + + return $value; + } + + /** + * @psalm-assert class-string $value + * + * @param string|callable():string $message + * + * @return class-string + * + * @throws InvalidArgumentException + */ + public static function interfaceExists(mixed $value, string|callable $message = ''): string + { + static::string($value); + + if (!\interface_exists($value)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an existing interface name. got %s', + static::valueToString($value) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template ExpectedType of object + * + * @psalm-assert class-string|ExpectedType $value + * + * @param class-string $interface + * @param string|callable():string $message + * + * @return class-string|ExpectedType + * + * @throws InvalidArgumentException + */ + public static function implementsInterface(mixed $value, mixed $interface, string|callable $message = ''): object|string + { + static::objectish($value); + + $implements = \class_implements($value); + + static::isArray($implements); + + if (!\in_array($interface, $implements, true)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an implementation of %2$s. Got: %s', + static::valueToString($value), + static::valueToString($interface) + )); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|object $classOrObject + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function propertyExists(mixed $classOrObject, mixed $property, string|callable $message = ''): object|string + { + static::objectish($classOrObject); + + if (!\property_exists($classOrObject, $property)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected the property %s to exist.', + static::valueToString($property) + )); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object + * @param T $classOrObject + * @param string|callable():string $message + * + * @return T + * @throws InvalidArgumentException + */ + public static function propertyNotExists(mixed $classOrObject, mixed $property, string|callable $message = ''): mixed + { + if (!(\is_string($classOrObject) || \is_object($classOrObject)) || \property_exists($classOrObject, $property)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected the property %s to not exist.', + static::valueToString($property) + )); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object + * @param T $classOrObject + * @param string|callable():string $message + * + * @return T + * @throws InvalidArgumentException + */ + public static function methodExists(mixed $classOrObject, mixed $method, string|callable $message = ''): object|string + { + static::objectish($classOrObject); + + if (!\method_exists($classOrObject, $method)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected the method %s to exist.', + static::valueToString($method) + )); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object + * @param T $classOrObject + * @param string|callable():string $message + * + * @return T + * @throws InvalidArgumentException + */ + public static function methodNotExists(mixed $classOrObject, mixed $method, string|callable $message = ''): mixed + { + static::objectish($classOrObject); + + if (\method_exists($classOrObject, $method)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected the method %s to not exist.', + static::valueToString($method) + )); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @param string|int $key + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function keyExists(mixed $array, string|int $key, string|callable $message = ''): array + { + static::isArray($array, $message); + + if (!(isset($array[$key]) || \array_key_exists($key, $array))) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected the key %s to exist.', + static::valueToString($key) + )); + } + + return $array; + } + + /** + * @psalm-pure + * + * @param string|int $key + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function keyNotExists(mixed $array, string|int $key, string|callable $message = ''): array + { + static::isArray($array, $message); + + if (isset($array[$key]) || \array_key_exists($key, $array)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected the key %s to not exist.', + static::valueToString($key) + )); + } + + return $array; + } + + /** + * Checks if a value is a valid array key (int or string). + * + * @psalm-pure + * + * @psalm-assert array-key $value + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function validArrayKey(mixed $value, string|callable $message = ''): string|int + { + if (!(\is_int($value) || \is_string($value))) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected string or integer. Got: %s', + static::typeToString($value) + )); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function count(mixed $array, mixed $number, string|callable $message = ''): array|Countable + { + static::isCountable($array); + static::integerish($number); + + static::eq( + \count($array), + $number, + fn () => static::resolveMessage($message) ?: \sprintf( + $message ?: 'Expected an array to contain %d elements. Got: %d.', + $number, + \count($array) + ) + ); + + return $array; + } + + /** + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function minCount(mixed $array, mixed $min, string|callable $message = ''): array|Countable + { + static::isCountable($array); + static::integerish($min); + + if (\count($array) < $min) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an array to contain at least %2$d elements. Got: %d', + \count($array), + $min + )); + } + + return $array; + } + + /** + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function maxCount(mixed $array, mixed $max, string|callable $message = ''): array|Countable + { + static::isCountable($array); + static::integerish($max); + + if (\count($array) > $max) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an array to contain at most %2$d elements. Got: %d', + \count($array), + $max + )); + } + + return $array; + } + + /** + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function countBetween(mixed $array, mixed $min, mixed $max, string|callable $message = ''): array|Countable + { + static::isCountable($array); + static::integerish($min); + static::integerish($max); + + $count = \count($array); + + if ($count < $min || $count > $max) { + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d', + $count, + $min, + $max + )); + } + + return $array; + } + + /** + * @psalm-pure + * + * @psalm-assert list $array + * + * @param string|callable():string $message + * + * @return list + * + * @throws InvalidArgumentException + */ + public static function isList(mixed $array, string|callable $message = ''): array + { + if (!\is_array($array) || !\array_is_list($array)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument( + $message ?: 'Expected list - non-associative array.' + ); + } + + return $array; + } + + /** + * @psalm-pure + * + * @psalm-assert non-empty-list $array + * + * @param string|callable():string $message + * + * @return non-empty-list + * + * @throws InvalidArgumentException + */ + public static function isNonEmptyList(mixed $array, string|callable $message = ''): array + { + static::isList($array, $message); + static::notEmpty($array, $message); + + return $array; + } + + /** + * @psalm-pure + * + * @template T + * + * @psalm-assert array $array + * + * @param mixed|array $array + * @param string|callable():string $message + * + * @return array + * + * @throws InvalidArgumentException + */ + public static function isMap(mixed $array, string|callable $message = ''): array + { + static::isArray($array, $message); + + if (\count($array) > 0 && \array_is_list($array)) { + $message = self::resolveMessage($message); + static::reportInvalidArgument( + $message ?: 'Expected map - associative array with string keys.' + ); + } + + return $array; + } + + /** + * @param callable $callable + * @param string|callable():string $message + * + * @return Closure|callable-string + * + * @throws InvalidArgumentException + */ + public static function isStatic(mixed $callable, string|callable $message = ''): Closure|string + { + static::isCallable($callable, $message); + + $callable = static::callableToClosure($callable); + + $reflection = new ReflectionFunction($callable); + + if (!$reflection->isStatic()) { + $message = self::resolveMessage($message); + static::reportInvalidArgument( + $message ?: 'Closure is not static.' + ); + } + + return $callable; + } + + /** + * @param callable $callable + * @param string|callable():string $message + * + * @return Closure|callable-string + * + * @throws InvalidArgumentException + */ + public static function notStatic(mixed $callable, string|callable $message = ''): Closure|string + { + static::isCallable($callable, $message); + + $callable = static::callableToClosure($callable); + + $reflection = new ReflectionFunction($callable); + + if ($reflection->isStatic()) { + $message = self::resolveMessage($message); + static::reportInvalidArgument( + $message ?: 'Closure is not static.' + ); + } + + return $callable; + } + + /** + * @psalm-pure + * + * @template T + * + * @psalm-assert array $array + * @psalm-assert !empty $array + * + * @param array $array + * @param string|callable():string $message + * + * @return non-empty-array + * + * @throws InvalidArgumentException + */ + public static function isNonEmptyMap(mixed $array, string|callable $message = ''): array + { + static::isMap($array, $message); + static::notEmpty($array, $message); + + return $array; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @throws InvalidArgumentException + */ + public static function uuid(mixed $value, string|callable $message = ''): string + { + static::string($value, $message); + + $uuid = '[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}'; + + // URN form as specified by RFC 9562, e.g. "urn:uuid:ff6f8cb0-...". + if (\str_starts_with($value, 'urn:uuid:') && \preg_match('/^urn:uuid:'.$uuid.'$/D', $value)) { + return $value; + } + + // "uuid:" prefix, optionally combined with the curly-braced form. + if (\str_starts_with($value, 'uuid:') && \preg_match('/^uuid:(?:'.$uuid.'|\{'.$uuid.'\})$/D', $value)) { + return $value; + } + + // Curly-braced form; the braces must be a matching pair. + if (\str_starts_with($value, '{') && \str_ends_with($value, '}') && \preg_match('/^\{'.$uuid.'\}$/D', $value)) { + return $value; + } + + // Plain form, including the nil UUID with all 128 bits set to zero. + if (\preg_match('/^'.$uuid.'$/D', $value)) { + return $value; + } + + $message = self::resolveMessage($message); + static::reportInvalidArgument(\sprintf( + $message ?: 'Value %s is not a valid UUID.', + static::valueToString($value) + )); + } + + /** + * @template T as callable + * + * @param T $expression + * @param string|callable():string $message + * @param class-string $class + * + * @return T + * + * @throws InvalidArgumentException + */ + public static function throws(mixed $expression, string $class = Throwable::class, string|callable $message = ''): callable + { + static::string($class); + static::isCallable($expression); + + $actual = 'none'; + + try { + $expression(); + } catch (Throwable $e) { + $actual = \get_class($e); + if ($e instanceof $class) { + return $expression; + } + } + + $message = self::resolveMessage($message); + + static::reportInvalidArgument($message ?: \sprintf( + 'Expected to throw "%s", got "%s"', + $class, + $actual + )); + } + + /** + * @psalm-pure + * + * @return Closure|callable-string + */ + protected static function callableToClosure(callable $callable): Closure|string + { + if (\is_string($callable) && \function_exists($callable)) { + return $callable; + } + + if ($callable instanceof Closure) { + return $callable; + } + + return $callable(...); + } + + /** + * @psalm-pure + */ + protected static function valueToString(mixed $value): string + { + if (null === $value) { + return 'null'; + } + + if (true === $value) { + return 'true'; + } + + if (false === $value) { + return 'false'; + } + + if (\is_array($value)) { + return 'array'; + } + + if (\is_object($value)) { + if (\method_exists($value, '__toString')) { + return \get_class($value).': '.self::valueToString($value->__toString()); + } + + if ($value instanceof DateTime || $value instanceof DateTimeImmutable) { + return \get_class($value).': '.self::valueToString($value->format('c')); + } + + if (\enum_exists(\get_class($value))) { + return \get_class($value).'::'.$value->name; + } + + return \get_class($value); + } + + if (\is_resource($value)) { + return 'resource'; + } + + if (\is_string($value)) { + return '"'.$value.'"'; + } + + return (string) $value; + } + + /** + * @psalm-pure + */ + protected static function typeToString(mixed $value): string + { + return \is_object($value) ? \get_class($value) : \gettype($value); + } + + protected static function strlen(string $value): int + { + if (!\function_exists('mb_detect_encoding')) { + return \strlen($value); + } + + if (false === $encoding = \mb_detect_encoding($value)) { + return \strlen($value); + } + + return \mb_strlen($value, $encoding); + } + + /** + * @psalm-pure this method is not supposed to perform side effects + * + * @throws InvalidArgumentException + */ + protected static function reportInvalidArgument(string $message): never + { + throw new InvalidArgumentException($message); + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + */ + protected static function resolveMessage(string|callable $message): string + { + return \is_callable($message) ? $message() : $message; + } + + private function __construct() + { + } +} diff --git a/tools/.phpstan/vendor/webmozart/assert/src/HasAssert.php b/tools/.phpstan/vendor/webmozart/assert/src/HasAssert.php new file mode 100644 index 0000000..11d144a --- /dev/null +++ b/tools/.phpstan/vendor/webmozart/assert/src/HasAssert.php @@ -0,0 +1,179 @@ + true, + 'nullorstring' => true, + 'allstring' => true, + 'allnullorstring' => true, + 'stringnotempty' => true, + 'nullorstringnotempty' => true, + 'allstringnotempty' => true, + 'allnullorstringnotempty' => true, + 'integer' => true, + 'nullorinteger' => true, + 'allinteger' => true, + 'allnullorinteger' => true, + 'integerish' => true, + 'nullorintegerish' => true, + 'allintegerish' => true, + 'allnullorintegerish' => true, + 'positiveinteger' => true, + 'nullorpositiveinteger' => true, + 'allpositiveinteger' => true, + 'allnullorpositiveinteger' => true, + 'notnegativeinteger' => true, + 'nullornotnegativeinteger' => true, + 'allnotnegativeinteger' => true, + 'allnullornotnegativeinteger' => true, + 'negativeinteger' => true, + 'nullornegativeinteger' => true, + 'allnegativeinteger' => true, + 'allnullornegativeinteger' => true, + 'float' => true, + 'nullorfloat' => true, + 'allfloat' => true, + 'allnullorfloat' => true, + 'numeric' => true, + 'nullornumeric' => true, + 'allnumeric' => true, + 'allnullornumeric' => true, + 'natural' => true, + 'nullornatural' => true, + 'allnatural' => true, + 'allnullornatural' => true, + 'boolean' => true, + 'nullorboolean' => true, + 'allboolean' => true, + 'allnullorboolean' => true, + 'scalar' => true, + 'nullorscalar' => true, + 'allscalar' => true, + 'allnullorscalar' => true, + 'object' => true, + 'nullorobject' => true, + 'allobject' => true, + 'allnullorobject' => true, + 'objectish' => true, + 'nullorobjectish' => true, + 'allobjectish' => true, + 'allnullorobjectish' => true, + 'resource' => true, + 'nullorresource' => true, + 'allresource' => true, + 'allnullorresource' => true, + 'iscallable' => true, + 'nulloriscallable' => true, + 'alliscallable' => true, + 'allnulloriscallable' => true, + 'isarray' => true, + 'nullorisarray' => true, + 'allisarray' => true, + 'allnullorisarray' => true, + 'isarrayaccessible' => true, + 'nullorisarrayaccessible' => true, + 'allisarrayaccessible' => true, + 'allnullorisarrayaccessible' => true, + 'iscountable' => true, + 'nulloriscountable' => true, + 'alliscountable' => true, + 'allnulloriscountable' => true, + 'isiterable' => true, + 'nullorisiterable' => true, + 'allisiterable' => true, + 'allnullorisiterable' => true, + 'isinstanceof' => true, + 'nullorisinstanceof' => true, + 'allisinstanceof' => true, + 'allnullorisinstanceof' => true, + 'notinstanceof' => true, + 'allnullornotinstanceof' => true, + 'isinstanceofany' => true, + 'nullorisinstanceofany' => true, + 'allisinstanceofany' => true, + 'allnullorisinstanceofany' => true, + 'isnotinstanceofany' => true, + 'nullorisnotinstanceofany' => true, + 'allisnotinstanceofany' => true, + 'allnullorisnotinstanceofany' => true, + 'isaof' => true, + 'nullorisaof' => true, + 'allisaof' => true, + 'allnullorisaof' => true, + 'isnota' => true, + 'allnullorisnota' => true, + 'isanyof' => true, + 'nullorisanyof' => true, + 'allisanyof' => true, + 'allnullorisanyof' => true, + 'isempty' => true, + 'nullorisempty' => true, + 'allisempty' => true, + 'allnullorisempty' => true, + 'notempty' => true, + 'allnullornotempty' => true, + 'null' => true, + 'allnull' => true, + 'notnull' => true, + 'true' => true, + 'nullortrue' => true, + 'alltrue' => true, + 'allnullortrue' => true, + 'false' => true, + 'nullorfalse' => true, + 'allfalse' => true, + 'allnullorfalse' => true, + 'notfalse' => true, + 'allnullornotfalse' => true, + 'uniquevalues' => true, + 'nulloruniquevalues' => true, + 'alluniquevalues' => true, + 'allnulloruniquevalues' => true, + 'lower' => true, + 'nullorlower' => true, + 'alllower' => true, + 'allnullorlower' => true, + 'upper' => true, + 'allnullorupper' => true, + 'classexists' => true, + 'nullorclassexists' => true, + 'allclassexists' => true, + 'allnullorclassexists' => true, + 'subclassof' => true, + 'nullorsubclassof' => true, + 'allsubclassof' => true, + 'allnullorsubclassof' => true, + 'interfaceexists' => true, + 'nullorinterfaceexists' => true, + 'allinterfaceexists' => true, + 'allnullorinterfaceexists' => true, + 'implementsinterface' => true, + 'nullorimplementsinterface' => true, + 'allimplementsinterface' => true, + 'allnullorimplementsinterface' => true, + 'validarraykey' => true, + 'nullorvalidarraykey' => true, + 'allvalidarraykey' => true, + 'allnullorvalidarraykey' => true, + 'islist' => true, + 'nullorislist' => true, + 'allislist' => true, + 'allnullorislist' => true, + 'isnonemptylist' => true, + 'nullorisnonemptylist' => true, + 'allisnonemptylist' => true, + 'allnullorisnonemptylist' => true, + 'ismap' => true, + 'nullorismap' => true, + 'allismap' => true, + 'allnullorismap' => true, + 'isnonemptymap' => true, + 'allnullorisnonemptymap' => true, + ]; +} diff --git a/tools/.phpstan/vendor/webmozart/assert/src/InvalidArgumentException.php b/tools/.phpstan/vendor/webmozart/assert/src/InvalidArgumentException.php new file mode 100644 index 0000000..fa2b223 --- /dev/null +++ b/tools/.phpstan/vendor/webmozart/assert/src/InvalidArgumentException.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Webmozart\Assert; + +class InvalidArgumentException extends \InvalidArgumentException +{ +} diff --git a/tools/.phpstan/vendor/webmozart/assert/src/Mixin.php b/tools/.phpstan/vendor/webmozart/assert/src/Mixin.php new file mode 100644 index 0000000..a714f16 --- /dev/null +++ b/tools/.phpstan/vendor/webmozart/assert/src/Mixin.php @@ -0,0 +1,5827 @@ + $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allString(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::string($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrString(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::string($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert non-empty-string|null $value + * + * @param string|callable():string $message + * + * @return non-empty-string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrStringNotEmpty(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::stringNotEmpty($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allStringNotEmpty(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::stringNotEmpty($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrStringNotEmpty(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::stringNotEmpty($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert int|null $value + * + * @param string|callable():string $message + * + * @return int|null + * + * @throws InvalidArgumentException + */ + public static function nullOrInteger(mixed $value, callable|string $message = ''): ?int + { + null === $value || static::integer($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allInteger(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::integer($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrInteger(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::integer($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert numeric|null $value + * + * @param string|callable():string $message + * + * @return numeric|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIntegerish(mixed $value, callable|string $message = ''): string|int|float|null + { + null === $value || static::integerish($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIntegerish(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::integerish($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIntegerish(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::integerish($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert positive-int|null $value + * + * @param string|callable():string $message + * + * @return positive-int|null + * + * @throws InvalidArgumentException + */ + public static function nullOrPositiveInteger(mixed $value, callable|string $message = ''): ?int + { + null === $value || static::positiveInteger($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allPositiveInteger(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::positiveInteger($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrPositiveInteger(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::positiveInteger($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert non-negative-int|null $value + * + * @param string|callable():string $message + * + * @return non-negative-int|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNotNegativeInteger(mixed $value, callable|string $message = ''): ?int + { + null === $value || static::notNegativeInteger($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotNegativeInteger(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notNegativeInteger($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotNegativeInteger(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notNegativeInteger($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert negative-int|null $value + * + * @param string|callable():string $message + * + * @return negative-int|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNegativeInteger(mixed $value, callable|string $message = ''): ?int + { + null === $value || static::negativeInteger($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNegativeInteger(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::negativeInteger($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNegativeInteger(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::negativeInteger($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert float|null $value + * + * @param string|callable():string $message + * + * @return float|null + * + * @throws InvalidArgumentException + */ + public static function nullOrFloat(mixed $value, callable|string $message = ''): ?float + { + null === $value || static::float($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allFloat(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::float($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrFloat(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::float($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert numeric|null $value + * + * @param string|callable():string $message + * + * @return numeric|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNumeric(mixed $value, callable|string $message = ''): string|int|float|null + { + null === $value || static::numeric($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNumeric(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::numeric($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNumeric(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::numeric($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert positive-int|0|null $value + * + * @param string|callable():string $message + * + * @return positive-int|0|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNatural(mixed $value, callable|string $message = ''): ?int + { + null === $value || static::natural($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNatural(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::natural($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNatural(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::natural($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert bool|null $value + * + * @param string|callable():string $message + * + * @return bool|null + * + * @throws InvalidArgumentException + */ + public static function nullOrBoolean(mixed $value, callable|string $message = ''): ?bool + { + null === $value || static::boolean($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allBoolean(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::boolean($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrBoolean(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::boolean($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert scalar|null $value + * + * @param string|callable():string $message + * + * @return scalar|null + * + * @throws InvalidArgumentException + */ + public static function nullOrScalar(mixed $value, callable|string $message = ''): string|int|float|bool|null + { + null === $value || static::scalar($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allScalar(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::scalar($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrScalar(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::scalar($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert object|null $value + * + * @param string|callable():string $message + * + * @return object|null + * + * @throws InvalidArgumentException + */ + public static function nullOrObject(mixed $value, callable|string $message = ''): ?object + { + null === $value || static::object($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allObject(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::object($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrObject(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::object($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert object|class-string|null $value + * + * @param string|callable():string $message + * + * @return object|class-string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrObjectish(mixed $value, callable|string $message = ''): object|string|null + { + null === $value || static::objectish($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allObjectish(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::objectish($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrObjectish(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::objectish($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert resource|null $value + * + * @param string|callable():string $message + * + * @see https://www.php.net/manual/en/function.get-resource-type.php + * + * @return resource|null + * + * @throws InvalidArgumentException + */ + public static function nullOrResource(mixed $value, ?string $type = null, callable|string $message = ''): mixed + { + null === $value || static::resource($value, $type, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @see https://www.php.net/manual/en/function.get-resource-type.php + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allResource(mixed $value, ?string $type = null, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::resource($entry, $type, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @see https://www.php.net/manual/en/function.get-resource-type.php + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrResource(mixed $value, ?string $type = null, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::resource($entry, $type, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert callable|null $value + * + * @param string|callable():string $message + * + * @return callable|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsCallable(mixed $value, callable|string $message = ''): ?callable + { + null === $value || static::isCallable($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsCallable(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isCallable($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsCallable(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isCallable($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert array|null $value + * + * @param string|callable():string $message + * + * @return array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsArray(mixed $value, callable|string $message = ''): ?array + { + null === $value || static::isArray($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsArray(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isArray($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsArray(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isArray($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert array|ArrayAccess|null $value + * + * @param string|callable():string $message + * + * @return array|ArrayAccess|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsArrayAccessible(mixed $value, callable|string $message = ''): ArrayAccess|array|null + { + null === $value || static::isArrayAccessible($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsArrayAccessible(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isArrayAccessible($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsArrayAccessible(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isArrayAccessible($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert countable|null $value + * + * @param string|callable():string $message + * + * @return countable|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsCountable(mixed $value, callable|string $message = ''): Countable|array|null + { + null === $value || static::isCountable($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsCountable(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isCountable($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsCountable(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isCountable($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable|null $value + * + * @param string|callable():string $message + * + * @return iterable|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsIterable(mixed $value, callable|string $message = ''): ?iterable + { + null === $value || static::isIterable($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsIterable(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isIterable($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsIterable(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isIterable($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T of object + * @psalm-assert T|null $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return T|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsInstanceOf(mixed $value, mixed $class, callable|string $message = ''): ?object + { + null === $value || static::isInstanceOf($value, $class, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @template T of object + * @psalm-assert iterable $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsInstanceOf(mixed $value, mixed $class, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isInstanceOf($entry, $class, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T of object|null + * @psalm-assert iterable $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsInstanceOf(mixed $value, mixed $class, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isInstanceOf($entry, $class, $message); + } + + return $value; + } + + /** + * @template T of object + * + * @param class-string $class + * @param string|callable():string $message + * + * @return object|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNotInstanceOf(mixed $value, mixed $class, callable|string $message = ''): ?object + { + null === $value || static::notInstanceOf($value, $class, $message); + + return $value; + } + + /** + * @template T of object + * + * @param class-string $class + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotInstanceOf(mixed $value, mixed $class, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notInstanceOf($entry, $class, $message); + } + + return $value; + } + + /** + * @template T of object|null + * @psalm-assert iterable $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotInstanceOf(mixed $value, mixed $class, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notInstanceOf($entry, $class, $message); + } + + return $value; + } + + /** + * @template T of object + * @psalm-assert T|null $value + * + * @param iterable> $classes + * @param string|callable():string $message + * + * @return T|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): ?object + { + null === $value || static::isInstanceOfAny($value, $classes, $message); + + return $value; + } + + /** + * @template T of object + * @psalm-assert iterable $value + * + * @param iterable> $classes + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isInstanceOfAny($entry, $classes, $message); + } + + return $value; + } + + /** + * @template T of object|null + * @psalm-assert iterable $value + * + * @param iterable> $classes + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isInstanceOfAny($entry, $classes, $message); + } + + return $value; + } + + /** + * @template T + * @psalm-assert object|class-string|null $value + * + * @param T|null $value + * @param string|callable():string $message + * + * @return T|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsNotInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): mixed + { + null === $value || static::isNotInstanceOfAny($value, $classes, $message); + + return $value; + } + + /** + * @template T + * @psalm-assert iterable $value + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsNotInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isNotInstanceOfAny($entry, $classes, $message); + } + + return $value; + } + + /** + * @template T + * @psalm-assert iterable $value + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsNotInstanceOfAny(mixed $value, mixed $classes, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isNotInstanceOfAny($entry, $classes, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T of object + * @psalm-assert T|class-string|null $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return T|class-string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsAOf(mixed $value, mixed $class, callable|string $message = ''): object|string|null + { + null === $value || static::isAOf($value, $class, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @template T of object + * @psalm-assert iterable> $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return iterable> + * + * @throws InvalidArgumentException + */ + public static function allIsAOf(mixed $value, mixed $class, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isAOf($entry, $class, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T of object|null + * @psalm-assert iterable|null> $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return iterable|null> + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsAOf(mixed $value, mixed $class, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isAOf($entry, $class, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T + * + * @param T|null $value + * @param string|callable():string $message + * + * @return object|class-string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsNotA(mixed $value, mixed $class, callable|string $message = ''): object|string|null + { + null === $value || static::isNotA($value, $class, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @template T + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsNotA(mixed $value, mixed $class, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isNotA($entry, $class, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T + * @psalm-assert iterable $value + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsNotA(mixed $value, mixed $class, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isNotA($entry, $class, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert T|null $value + * + * @template T as object + * + * @param array> $classes + * @param string|callable():string $message + * + * @return T|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsAnyOf(mixed $value, mixed $classes, callable|string $message = ''): object|string|null + { + null === $value || static::isAnyOf($value, $classes, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @template T as object + * + * @param array> $classes + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsAnyOf(mixed $value, mixed $classes, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isAnyOf($entry, $classes, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @template T as object|null + * + * @param array> $classes + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsAnyOf(mixed $value, mixed $classes, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isAnyOf($entry, $classes, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert empty $value + * + * @param string|callable():string $message + * + * @return empty + * + * @throws InvalidArgumentException + */ + public static function nullOrIsEmpty(mixed $value, callable|string $message = ''): mixed + { + null === $value || static::isEmpty($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsEmpty(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::isEmpty($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsEmpty(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::isEmpty($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrNotEmpty(mixed $value, callable|string $message = ''): mixed + { + null === $value || static::notEmpty($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotEmpty(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notEmpty($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotEmpty(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notEmpty($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNull(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::null($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotNull(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notNull($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert true|null $value + * + * @param string|callable():string $message + * + * @return true|null + * + * @throws InvalidArgumentException + */ + public static function nullOrTrue(mixed $value, callable|string $message = ''): ?true + { + null === $value || static::true($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allTrue(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::true($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrTrue(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::true($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert false|null $value + * + * @param string|callable():string $message + * + * @return false|null + * + * @throws InvalidArgumentException + */ + public static function nullOrFalse(mixed $value, callable|string $message = ''): ?false + { + null === $value || static::false($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allFalse(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::false($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrFalse(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::false($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template T + * + * @param T|false|null $value + * @param string|callable():string $message + * + * @return T|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNotFalse(mixed $value, callable|string $message = ''): mixed + { + null === $value || static::notFalse($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @template T + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotFalse(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notFalse($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @template T + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotFalse(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notFalse($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|null $value + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIp(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::ip($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIp(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::ip($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIp(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::ip($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|null $value + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIpv4(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::ipv4($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIpv4(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::ipv4($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIpv4(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::ipv4($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|null $value + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIpv6(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::ipv6($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIpv6(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::ipv6($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIpv6(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::ipv6($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|null $value + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrEmail(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::email($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allEmail(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::email($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param iterable $value + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrEmail(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::email($entry, $message); + } + + return $value; + } + + /** + * @psalm-assert array|null $values + * + * @param string|callable():string $message + * + * @return array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrUniqueValues(mixed $values, callable|string $message = ''): ?array + { + null === $values || static::uniqueValues($values, $message); + + return $values; + } + + /** + * @psalm-assert iterable $values + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allUniqueValues(mixed $values, callable|string $message = ''): iterable + { + static::isIterable($values); + + foreach ($values as $entry) { + static::uniqueValues($entry, $message); + } + + return $values; + } + + /** + * @psalm-assert iterable $values + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrUniqueValues(mixed $values, callable|string $message = ''): iterable + { + static::isIterable($values); + + foreach ($values as $entry) { + null === $entry || static::uniqueValues($entry, $message); + } + + return $values; + } + + /** + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrEq(mixed $value, mixed $expect, callable|string $message = ''): mixed + { + null === $value || static::eq($value, $expect, $message); + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allEq(mixed $value, mixed $expect, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::eq($entry, $expect, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrEq(mixed $value, mixed $expect, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::eq($entry, $expect, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrNotEq(mixed $value, mixed $expect, callable|string $message = ''): mixed + { + null === $value || static::notEq($value, $expect, $message); + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotEq(mixed $value, mixed $expect, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notEq($entry, $expect, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotEq(mixed $value, mixed $expect, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notEq($entry, $expect, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrSame(mixed $value, mixed $expect, callable|string $message = ''): mixed + { + null === $value || static::same($value, $expect, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allSame(mixed $value, mixed $expect, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::same($entry, $expect, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrSame(mixed $value, mixed $expect, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::same($entry, $expect, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrNotSame(mixed $value, mixed $expect, callable|string $message = ''): mixed + { + null === $value || static::notSame($value, $expect, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotSame(mixed $value, mixed $expect, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notSame($entry, $expect, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotSame(mixed $value, mixed $expect, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notSame($entry, $expect, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrGreaterThan(mixed $value, mixed $limit, callable|string $message = ''): mixed + { + null === $value || static::greaterThan($value, $limit, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allGreaterThan(mixed $value, mixed $limit, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::greaterThan($entry, $limit, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrGreaterThan(mixed $value, mixed $limit, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::greaterThan($entry, $limit, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrGreaterThanEq(mixed $value, mixed $limit, callable|string $message = ''): mixed + { + null === $value || static::greaterThanEq($value, $limit, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allGreaterThanEq(mixed $value, mixed $limit, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::greaterThanEq($entry, $limit, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrGreaterThanEq(mixed $value, mixed $limit, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::greaterThanEq($entry, $limit, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrLessThan(mixed $value, mixed $limit, callable|string $message = ''): mixed + { + null === $value || static::lessThan($value, $limit, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allLessThan(mixed $value, mixed $limit, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::lessThan($entry, $limit, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrLessThan(mixed $value, mixed $limit, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::lessThan($entry, $limit, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrLessThanEq(mixed $value, mixed $limit, callable|string $message = ''): mixed + { + null === $value || static::lessThanEq($value, $limit, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allLessThanEq(mixed $value, mixed $limit, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::lessThanEq($entry, $limit, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrLessThanEq(mixed $value, mixed $limit, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::lessThanEq($entry, $limit, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrRange(mixed $value, mixed $min, mixed $max, callable|string $message = ''): mixed + { + null === $value || static::range($value, $min, $max, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allRange(mixed $value, mixed $min, mixed $max, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::range($entry, $min, $max, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrRange(mixed $value, mixed $min, mixed $max, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::range($entry, $min, $max, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrOneOf(mixed $value, mixed $values, callable|string $message = ''): mixed + { + null === $value || static::oneOf($value, $values, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allOneOf(mixed $value, mixed $values, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::oneOf($entry, $values, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrOneOf(mixed $value, mixed $values, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::oneOf($entry, $values, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrInArray(mixed $value, mixed $values, callable|string $message = ''): mixed + { + null === $value || static::inArray($value, $values, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allInArray(mixed $value, mixed $values, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::inArray($entry, $values, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrInArray(mixed $value, mixed $values, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::inArray($entry, $values, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrNotOneOf(mixed $value, mixed $values, callable|string $message = ''): mixed + { + null === $value || static::notOneOf($value, $values, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotOneOf(mixed $value, mixed $values, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notOneOf($entry, $values, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotOneOf(mixed $value, mixed $values, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notOneOf($entry, $values, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return mixed + * + * @throws InvalidArgumentException + */ + public static function nullOrNotInArray(mixed $value, mixed $values, callable|string $message = ''): mixed + { + null === $value || static::notInArray($value, $values, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotInArray(mixed $value, mixed $values, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notInArray($entry, $values, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotInArray(mixed $value, mixed $values, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notInArray($entry, $values, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrContains(mixed $value, mixed $subString, callable|string $message = ''): ?string + { + null === $value || static::contains($value, $subString, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allContains(mixed $value, mixed $subString, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::contains($entry, $subString, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrContains(mixed $value, mixed $subString, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::contains($entry, $subString, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNotContains(mixed $value, mixed $subString, callable|string $message = ''): ?string + { + null === $value || static::notContains($value, $subString, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotContains(mixed $value, mixed $subString, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notContains($entry, $subString, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotContains(mixed $value, mixed $subString, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notContains($entry, $subString, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNotWhitespaceOnly(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::notWhitespaceOnly($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotWhitespaceOnly(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notWhitespaceOnly($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotWhitespaceOnly(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notWhitespaceOnly($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): ?string + { + null === $value || static::startsWith($value, $prefix, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::startsWith($entry, $prefix, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::startsWith($entry, $prefix, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNotStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): ?string + { + null === $value || static::notStartsWith($value, $prefix, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notStartsWith($entry, $prefix, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotStartsWith(mixed $value, mixed $prefix, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notStartsWith($entry, $prefix, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrStartsWithLetter(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::startsWithLetter($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allStartsWithLetter(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::startsWithLetter($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrStartsWithLetter(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::startsWithLetter($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): ?string + { + null === $value || static::endsWith($value, $suffix, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::endsWith($entry, $suffix, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::endsWith($entry, $suffix, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNotEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): ?string + { + null === $value || static::notEndsWith($value, $suffix, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notEndsWith($entry, $suffix, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotEndsWith(mixed $value, mixed $suffix, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notEndsWith($entry, $suffix, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrRegex(mixed $value, mixed $pattern, callable|string $message = ''): ?string + { + null === $value || static::regex($value, $pattern, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allRegex(mixed $value, mixed $pattern, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::regex($entry, $pattern, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrRegex(mixed $value, mixed $pattern, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::regex($entry, $pattern, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNotRegex(mixed $value, mixed $pattern, callable|string $message = ''): ?string + { + null === $value || static::notRegex($value, $pattern, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotRegex(mixed $value, mixed $pattern, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::notRegex($entry, $pattern, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotRegex(mixed $value, mixed $pattern, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::notRegex($entry, $pattern, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrUnicodeLetters(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::unicodeLetters($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allUnicodeLetters(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::unicodeLetters($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrUnicodeLetters(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::unicodeLetters($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrAlpha(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::alpha($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allAlpha(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::alpha($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrAlpha(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::alpha($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrDigits(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::digits($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allDigits(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::digits($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrDigits(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::digits($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrAlnum(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::alnum($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allAlnum(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::alnum($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrAlnum(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::alnum($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert lowercase-string|null $value + * + * @param string|callable():string $message + * + * @return lowercase-string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrLower(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::lower($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allLower(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::lower($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrLower(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::lower($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrUpper(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::upper($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allUpper(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::upper($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrUpper(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::upper($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrLength(mixed $value, mixed $length, callable|string $message = ''): ?string + { + null === $value || static::length($value, $length, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allLength(mixed $value, mixed $length, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::length($entry, $length, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrLength(mixed $value, mixed $length, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::length($entry, $length, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrMinLength(mixed $value, mixed $min, callable|string $message = ''): ?string + { + null === $value || static::minLength($value, $min, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allMinLength(mixed $value, mixed $min, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::minLength($entry, $min, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrMinLength(mixed $value, mixed $min, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::minLength($entry, $min, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrMaxLength(mixed $value, mixed $max, callable|string $message = ''): ?string + { + null === $value || static::maxLength($value, $max, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allMaxLength(mixed $value, mixed $max, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::maxLength($entry, $max, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrMaxLength(mixed $value, mixed $max, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::maxLength($entry, $max, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrLengthBetween(mixed $value, mixed $min, mixed $max, callable|string $message = ''): ?string + { + null === $value || static::lengthBetween($value, $min, $max, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allLengthBetween(mixed $value, mixed $min, mixed $max, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::lengthBetween($entry, $min, $max, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrLengthBetween(mixed $value, mixed $min, mixed $max, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::lengthBetween($entry, $min, $max, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrFileExists(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::fileExists($value, $message); + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allFileExists(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::fileExists($entry, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrFileExists(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::fileExists($entry, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrFile(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::file($value, $message); + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allFile(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::file($entry, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrFile(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::file($entry, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrDirectory(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::directory($value, $message); + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allDirectory(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::directory($entry, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrDirectory(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::directory($entry, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrReadable(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::readable($value, $message); + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allReadable(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::readable($entry, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrReadable(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::readable($entry, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrWritable(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::writable($value, $message); + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allWritable(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::writable($entry, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrWritable(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::writable($entry, $message); + } + + return $value; + } + + /** + * @psalm-assert class-string|null $value + * + * @param string|callable():string $message + * + * @return class-string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrClassExists(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::classExists($value, $message); + + return $value; + } + + /** + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allClassExists(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::classExists($entry, $message); + } + + return $value; + } + + /** + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrClassExists(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::classExists($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template ExpectedType of object + * @psalm-assert class-string|null $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return class-string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrSubclassOf(mixed $value, mixed $class, callable|string $message = ''): ?string + { + null === $value || static::subclassOf($value, $class, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @template ExpectedType of object + * @psalm-assert iterable> $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return iterable> + * + * @throws InvalidArgumentException + */ + public static function allSubclassOf(mixed $value, mixed $class, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::subclassOf($entry, $class, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template ExpectedType of object|null + * @psalm-assert iterable|null> $value + * + * @param class-string $class + * @param string|callable():string $message + * + * @return iterable|null> + * + * @throws InvalidArgumentException + */ + public static function allNullOrSubclassOf(mixed $value, mixed $class, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::subclassOf($entry, $class, $message); + } + + return $value; + } + + /** + * @psalm-assert class-string|null $value + * + * @param string|callable():string $message + * + * @return class-string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrInterfaceExists(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::interfaceExists($value, $message); + + return $value; + } + + /** + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allInterfaceExists(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::interfaceExists($entry, $message); + } + + return $value; + } + + /** + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrInterfaceExists(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::interfaceExists($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template ExpectedType of object + * @psalm-assert class-string|ExpectedType|null $value + * + * @param class-string $interface + * @param string|callable():string $message + * + * @return class-string|ExpectedType|null + * + * @throws InvalidArgumentException + */ + public static function nullOrImplementsInterface(mixed $value, mixed $interface, callable|string $message = ''): object|string|null + { + null === $value || static::implementsInterface($value, $interface, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @template ExpectedType of object + * @psalm-assert iterable|ExpectedType> $value + * + * @param class-string $interface + * @param string|callable():string $message + * + * @return iterable|ExpectedType> + * + * @throws InvalidArgumentException + */ + public static function allImplementsInterface(mixed $value, mixed $interface, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::implementsInterface($entry, $interface, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @template ExpectedType of object|null + * @psalm-assert iterable|ExpectedType|null> $value + * + * @param class-string $interface + * @param string|callable():string $message + * + * @return iterable|ExpectedType|null> + * + * @throws InvalidArgumentException + */ + public static function allNullOrImplementsInterface(mixed $value, mixed $interface, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::implementsInterface($entry, $interface, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|object|null $classOrObject + * @param string|callable():string $message + * + * @return string|object|null + * + * @throws InvalidArgumentException + */ + public static function nullOrPropertyExists(mixed $classOrObject, mixed $property, callable|string $message = ''): object|string|null + { + null === $classOrObject || static::propertyExists($classOrObject, $property, $message); + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @param iterable $classOrObject + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allPropertyExists(mixed $classOrObject, mixed $property, callable|string $message = ''): iterable + { + static::isIterable($classOrObject); + + foreach ($classOrObject as $entry) { + static::propertyExists($entry, $property, $message); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @param iterable $classOrObject + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrPropertyExists(mixed $classOrObject, mixed $property, callable|string $message = ''): iterable + { + static::isIterable($classOrObject); + + foreach ($classOrObject as $entry) { + null === $entry || static::propertyExists($entry, $property, $message); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object + * + * @param T|null $classOrObject + * @param string|callable():string $message + * + * @return T|null + * + * @throws InvalidArgumentException + */ + public static function nullOrPropertyNotExists(mixed $classOrObject, mixed $property, callable|string $message = ''): mixed + { + null === $classOrObject || static::propertyNotExists($classOrObject, $property, $message); + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object + * + * @param iterable $classOrObject + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allPropertyNotExists(mixed $classOrObject, mixed $property, callable|string $message = ''): iterable + { + static::isIterable($classOrObject); + + foreach ($classOrObject as $entry) { + static::propertyNotExists($entry, $property, $message); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object|null + * + * @param iterable $classOrObject + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrPropertyNotExists(mixed $classOrObject, mixed $property, callable|string $message = ''): iterable + { + static::isIterable($classOrObject); + + foreach ($classOrObject as $entry) { + null === $entry || static::propertyNotExists($entry, $property, $message); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object + * + * @param T|null $classOrObject + * @param string|callable():string $message + * + * @return T|null + * + * @throws InvalidArgumentException + */ + public static function nullOrMethodExists(mixed $classOrObject, mixed $method, callable|string $message = ''): object|string|null + { + null === $classOrObject || static::methodExists($classOrObject, $method, $message); + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object + * + * @param iterable $classOrObject + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allMethodExists(mixed $classOrObject, mixed $method, callable|string $message = ''): iterable + { + static::isIterable($classOrObject); + + foreach ($classOrObject as $entry) { + static::methodExists($entry, $method, $message); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object|null + * + * @param iterable $classOrObject + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrMethodExists(mixed $classOrObject, mixed $method, callable|string $message = ''): iterable + { + static::isIterable($classOrObject); + + foreach ($classOrObject as $entry) { + null === $entry || static::methodExists($entry, $method, $message); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object + * + * @param T|null $classOrObject + * @param string|callable():string $message + * + * @return T|null + * + * @throws InvalidArgumentException + */ + public static function nullOrMethodNotExists(mixed $classOrObject, mixed $method, callable|string $message = ''): mixed + { + null === $classOrObject || static::methodNotExists($classOrObject, $method, $message); + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object + * + * @param iterable $classOrObject + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allMethodNotExists(mixed $classOrObject, mixed $method, callable|string $message = ''): iterable + { + static::isIterable($classOrObject); + + foreach ($classOrObject as $entry) { + static::methodNotExists($entry, $method, $message); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @template T as class-string|object|null + * + * @param iterable $classOrObject + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrMethodNotExists(mixed $classOrObject, mixed $method, callable|string $message = ''): iterable + { + static::isIterable($classOrObject); + + foreach ($classOrObject as $entry) { + null === $entry || static::methodNotExists($entry, $method, $message); + } + + return $classOrObject; + } + + /** + * @psalm-pure + * + * @param string|int $key + * @param string|callable():string $message + * + * @return array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrKeyExists(mixed $array, string|int $key, callable|string $message = ''): ?array + { + null === $array || static::keyExists($array, $key, $message); + + return $array; + } + + /** + * @psalm-pure + * + * @param string|int $key + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allKeyExists(mixed $array, string|int $key, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + static::keyExists($entry, $key, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @param string|int $key + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrKeyExists(mixed $array, string|int $key, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::keyExists($entry, $key, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @param string|int $key + * @param string|callable():string $message + * + * @return array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrKeyNotExists(mixed $array, string|int $key, callable|string $message = ''): ?array + { + null === $array || static::keyNotExists($array, $key, $message); + + return $array; + } + + /** + * @psalm-pure + * + * @param string|int $key + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allKeyNotExists(mixed $array, string|int $key, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + static::keyNotExists($entry, $key, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @param string|int $key + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrKeyNotExists(mixed $array, string|int $key, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::keyNotExists($entry, $key, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @psalm-assert array-key|null $value + * + * @param string|callable():string $message + * + * @return array-key|null + * + * @throws InvalidArgumentException + */ + public static function nullOrValidArrayKey(mixed $value, callable|string $message = ''): string|int|null + { + null === $value || static::validArrayKey($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allValidArrayKey(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::validArrayKey($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable $value + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrValidArrayKey(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::validArrayKey($entry, $message); + } + + return $value; + } + + /** + * @param string|callable():string $message + * + * @return Countable|array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrCount(mixed $array, mixed $number, callable|string $message = ''): Countable|array|null + { + null === $array || static::count($array, $number, $message); + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allCount(mixed $array, mixed $number, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + static::count($entry, $number, $message); + } + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrCount(mixed $array, mixed $number, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::count($entry, $number, $message); + } + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return Countable|array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrMinCount(mixed $array, mixed $min, callable|string $message = ''): Countable|array|null + { + null === $array || static::minCount($array, $min, $message); + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allMinCount(mixed $array, mixed $min, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + static::minCount($entry, $min, $message); + } + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrMinCount(mixed $array, mixed $min, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::minCount($entry, $min, $message); + } + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return Countable|array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrMaxCount(mixed $array, mixed $max, callable|string $message = ''): Countable|array|null + { + null === $array || static::maxCount($array, $max, $message); + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allMaxCount(mixed $array, mixed $max, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + static::maxCount($entry, $max, $message); + } + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrMaxCount(mixed $array, mixed $max, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::maxCount($entry, $max, $message); + } + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return Countable|array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrCountBetween(mixed $array, mixed $min, mixed $max, callable|string $message = ''): Countable|array|null + { + null === $array || static::countBetween($array, $min, $max, $message); + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allCountBetween(mixed $array, mixed $min, mixed $max, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + static::countBetween($entry, $min, $max, $message); + } + + return $array; + } + + /** + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrCountBetween(mixed $array, mixed $min, mixed $max, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::countBetween($entry, $min, $max, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @psalm-assert list|null $array + * + * @param string|callable():string $message + * + * @return list|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsList(mixed $array, callable|string $message = ''): ?array + { + null === $array || static::isList($array, $message); + + return $array; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable> $array + * + * @param string|callable():string $message + * + * @return iterable> + * + * @throws InvalidArgumentException + */ + public static function allIsList(mixed $array, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + static::isList($entry, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable|null> $array + * + * @param string|callable():string $message + * + * @return iterable|null> + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsList(mixed $array, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::isList($entry, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @psalm-assert non-empty-list|null $array + * + * @param string|callable():string $message + * + * @return non-empty-list|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsNonEmptyList(mixed $array, callable|string $message = ''): ?array + { + null === $array || static::isNonEmptyList($array, $message); + + return $array; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable> $array + * + * @param string|callable():string $message + * + * @return iterable> + * + * @throws InvalidArgumentException + */ + public static function allIsNonEmptyList(mixed $array, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + static::isNonEmptyList($entry, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @psalm-assert iterable|null> $array + * + * @param string|callable():string $message + * + * @return iterable|null> + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsNonEmptyList(mixed $array, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::isNonEmptyList($entry, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @template T + * @psalm-assert array|null $array + * + * @param mixed|array|null $array + * @param string|callable():string $message + * + * @return array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsMap(mixed $array, callable|string $message = ''): ?array + { + null === $array || static::isMap($array, $message); + + return $array; + } + + /** + * @psalm-pure + * + * @template T + * @psalm-assert iterable> $array + * + * @param iterable> $array + * @param string|callable():string $message + * + * @return iterable> + * + * @throws InvalidArgumentException + */ + public static function allIsMap(mixed $array, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + static::isMap($entry, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @template T + * @psalm-assert iterable|null> $array + * + * @param iterable|null> $array + * @param string|callable():string $message + * + * @return iterable|null> + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsMap(mixed $array, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::isMap($entry, $message); + } + + return $array; + } + + /** + * @param callable|null $callable + * @param string|callable():string $message + * + * @return Closure|callable-string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsStatic(mixed $callable, callable|string $message = ''): Closure|string|null + { + null === $callable || static::isStatic($callable, $message); + + return $callable; + } + + /** + * @param iterable $callable + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allIsStatic(mixed $callable, callable|string $message = ''): iterable + { + static::isIterable($callable); + + foreach ($callable as $entry) { + static::isStatic($entry, $message); + } + + return $callable; + } + + /** + * @param iterable $callable + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsStatic(mixed $callable, callable|string $message = ''): iterable + { + static::isIterable($callable); + + foreach ($callable as $entry) { + null === $entry || static::isStatic($entry, $message); + } + + return $callable; + } + + /** + * @param callable|null $callable + * @param string|callable():string $message + * + * @return Closure|callable-string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrNotStatic(mixed $callable, callable|string $message = ''): Closure|string|null + { + null === $callable || static::notStatic($callable, $message); + + return $callable; + } + + /** + * @param iterable $callable + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNotStatic(mixed $callable, callable|string $message = ''): iterable + { + static::isIterable($callable); + + foreach ($callable as $entry) { + static::notStatic($entry, $message); + } + + return $callable; + } + + /** + * @param iterable $callable + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrNotStatic(mixed $callable, callable|string $message = ''): iterable + { + static::isIterable($callable); + + foreach ($callable as $entry) { + null === $entry || static::notStatic($entry, $message); + } + + return $callable; + } + + /** + * @psalm-pure + * + * @template T + * + * @param array|null $array + * @param string|callable():string $message + * + * @return non-empty-array|null + * + * @throws InvalidArgumentException + */ + public static function nullOrIsNonEmptyMap(mixed $array, callable|string $message = ''): ?array + { + null === $array || static::isNonEmptyMap($array, $message); + + return $array; + } + + /** + * @psalm-pure + * + * @template T + * + * @param iterable> $array + * @param string|callable():string $message + * + * @return iterable> + * + * @throws InvalidArgumentException + */ + public static function allIsNonEmptyMap(mixed $array, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + static::isNonEmptyMap($entry, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @template T + * @psalm-assert iterable|null> $array + * @psalm-assert iterable $array + * + * @param iterable|null> $array + * @param string|callable():string $message + * + * @return iterable|null> + * + * @throws InvalidArgumentException + */ + public static function allNullOrIsNonEmptyMap(mixed $array, callable|string $message = ''): iterable + { + static::isIterable($array); + + foreach ($array as $entry) { + null === $entry || static::isNonEmptyMap($entry, $message); + } + + return $array; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return string|null + * + * @throws InvalidArgumentException + */ + public static function nullOrUuid(mixed $value, callable|string $message = ''): ?string + { + null === $value || static::uuid($value, $message); + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allUuid(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + static::uuid($entry, $message); + } + + return $value; + } + + /** + * @psalm-pure + * + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrUuid(mixed $value, callable|string $message = ''): iterable + { + static::isIterable($value); + + foreach ($value as $entry) { + null === $entry || static::uuid($entry, $message); + } + + return $value; + } + + /** + * @template T as callable + * + * @param T|null $expression + * @param class-string $class + * @param string|callable():string $message + * + * @return T|null + * + * @throws InvalidArgumentException + */ + public static function nullOrThrows(mixed $expression, string $class = 'Throwable', callable|string $message = ''): ?callable + { + null === $expression || static::throws($expression, $class, $message); + + return $expression; + } + + /** + * @template T as callable + * + * @param iterable $expression + * @param class-string $class + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allThrows(mixed $expression, string $class = 'Throwable', callable|string $message = ''): iterable + { + static::isIterable($expression); + + foreach ($expression as $entry) { + static::throws($entry, $class, $message); + } + + return $expression; + } + + /** + * @template T as callable|null + * + * @param iterable $expression + * @param class-string $class + * @param string|callable():string $message + * + * @return iterable + * + * @throws InvalidArgumentException + */ + public static function allNullOrThrows(mixed $expression, string $class = 'Throwable', callable|string $message = ''): iterable + { + static::isIterable($expression); + + foreach ($expression as $entry) { + null === $entry || static::throws($entry, $class, $message); + } + + return $expression; + } +} diff --git a/tools/.phpstan/vendor/webmozart/assert/src/PsalmPlugin.php b/tools/.phpstan/vendor/webmozart/assert/src/PsalmPlugin.php new file mode 100644 index 0000000..1963868 --- /dev/null +++ b/tools/.phpstan/vendor/webmozart/assert/src/PsalmPlugin.php @@ -0,0 +1,50 @@ +registerHooksFromClass(self::class); + } + + public static function afterMethodCallAnalysis(AfterMethodCallAnalysisEvent $event): void + { + [$class, $method] = explode('::', $event->getAppearingMethodId()); + if ($class !== Assert::class) { + return; + } + if (!isset(HasAssert::HAS_ASSERT[$method])) { + return; + } + + $firstArg = $event->getExpr()->getArgs()[0] ?? null; + if ($firstArg === null) { + return; + } + + $varId = ExpressionIdentifier::getExtendedVarId( + $firstArg->value, + $event->getContext()->self, + $event->getStatementsSource() + ); + + if ($varId === null || !isset($event->getContext()->vars_in_scope[$varId])) { + return; + } + + $candidateType = $event->getContext()->vars_in_scope[$varId]; + + $event->setReturnTypeCandidate($candidateType); + } +}