diff --git a/rector.php b/rector.php index 0a8995c7b..a51182ee7 100644 --- a/rector.php +++ b/rector.php @@ -3,7 +3,7 @@ use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\ClassLike\RemoveAnnotationRector; use Rector\Php70\Rector\StmtsAwareInterface\IfIssetToCoalescingRector; -use Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector; +use Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector; use Rector\Set\ValueObject\LevelSetList; return static function (RectorConfig $rectorConfig): void { @@ -17,13 +17,14 @@ // Modernize code $rectorConfig->sets([LevelSetList::UP_TO_PHP_74]); + $rectorConfig->rule(ChangeSwitchToMatchRector::class); + // phpcs:disable Squiz.Arrays.ArrayDeclaration.KeySpecified $rectorConfig->skip([ // Do not use ternaries extensively IfIssetToCoalescingRector::class, - // Not necessary in documentation examples - JsonThrowOnErrorRector::class => [ - __DIR__ . '/tests/DocumentationExamplesTest.php', + ChangeSwitchToMatchRector::class => [ + __DIR__ . '/tests/SpecTests/Operation.php', ], ]); // phpcs:enable diff --git a/src/Builder/Encoder/OperatorEncoder.php b/src/Builder/Encoder/OperatorEncoder.php index f52ad392a..69ef27c24 100644 --- a/src/Builder/Encoder/OperatorEncoder.php +++ b/src/Builder/Encoder/OperatorEncoder.php @@ -38,27 +38,14 @@ public function encode(mixed $value): stdClass throw UnsupportedValueException::invalidEncodableValue($value); } - switch ($value::ENCODE) { - case Encode::Single: - return $this->encodeAsSingle($value); - - case Encode::Array: - return $this->encodeAsArray($value); - - case Encode::Object: - case Encode::FlatObject: - return $this->encodeAsObject($value); - - case Encode::DollarObject: - return $this->encodeAsDollarObject($value); - - case Encode::Group: - assert($value instanceof GroupStage); - - return $this->encodeAsGroup($value); - } - - throw new LogicException(sprintf('Class "%s" does not have a valid ENCODE constant.', $value::class)); + return match ($value::ENCODE) { + Encode::Single => $this->encodeAsSingle($value), + Encode::Array => $this->encodeAsArray($value), + Encode::Object, Encode::FlatObject => $this->encodeAsObject($value), + Encode::DollarObject => $this->encodeAsDollarObject($value), + Encode::Group => $this->encodeAsGroup($value), + default => throw new LogicException(sprintf('Class "%s" does not have a valid ENCODE constant.', $value::class)), + }; } /** @@ -111,8 +98,10 @@ private function encodeAsDollarObject(OperatorInterface $value): stdClass /** * $group stage have a specific encoding because the _id argument is required and others are variadic */ - private function encodeAsGroup(GroupStage $value): stdClass + private function encodeAsGroup(OperatorInterface $value): stdClass { + assert($value instanceof GroupStage); + $result = new stdClass(); $result->_id = $this->recursiveEncode($value->_id); diff --git a/tests/SpecTests/ClientSideEncryption/Prose22_RangeExplicitEncryptionTest.php b/tests/SpecTests/ClientSideEncryption/Prose22_RangeExplicitEncryptionTest.php index aebfa6303..0c80cf2e0 100644 --- a/tests/SpecTests/ClientSideEncryption/Prose22_RangeExplicitEncryptionTest.php +++ b/tests/SpecTests/ClientSideEncryption/Prose22_RangeExplicitEncryptionTest.php @@ -451,26 +451,13 @@ private function assertMultipleDocumentsMatch(array $expectedDocuments, Iterator private static function getCastCallableForType(string $type): callable { - switch ($type) { - case 'DecimalNoPrecision': - case 'DecimalPrecision': - return fn (int $value) => new Decimal128((string) $value); - - case 'DoubleNoPrecision': - case 'DoublePrecision': - return fn (int $value) => (double) $value; - - case 'Date': - return fn (int $value) => new UTCDateTime($value); - - case 'Int': - return fn (int $value) => $value; - - case 'Long': - return fn (int $value) => new Int64($value); - - default: - throw new LogicException('Unsupported type: ' . $type); - } + return match ($type) { + 'DecimalNoPrecision', 'DecimalPrecision' => fn (int $value) => new Decimal128((string) $value), + 'DoubleNoPrecision', 'DoublePrecision' => fn (int $value) => (double) $value, + 'Date' => fn (int $value) => new UTCDateTime($value), + 'Int' => fn (int $value) => $value, + 'Long' => fn (int $value) => new Int64($value), + default => throw new LogicException('Unsupported type: ' . $type), + }; } } diff --git a/tests/SpecTests/CommandExpectations.php b/tests/SpecTests/CommandExpectations.php index 7c9a946f0..8adc8cd5c 100644 --- a/tests/SpecTests/CommandExpectations.php +++ b/tests/SpecTests/CommandExpectations.php @@ -40,25 +40,15 @@ class CommandExpectations implements CommandSubscriber private function __construct(private Client $observedClient, array $events) { foreach ($events as $event) { - switch (key((array) $event)) { - case 'command_failed_event': - // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $this->expectedEvents[] = [$event->command_failed_event, CommandFailedEvent::class]; - break; - - case 'command_started_event': - // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $this->expectedEvents[] = [$event->command_started_event, CommandStartedEvent::class]; - break; - - case 'command_succeeded_event': - // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $this->expectedEvents[] = [$event->command_succeeded_event, CommandSucceededEvent::class]; - break; - - default: - throw new LogicException('Unsupported event type: ' . key($event)); - } + $this->expectedEvents[] = match (key((array) $event)) { + // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + 'command_failed_event' => [$event->command_failed_event, CommandFailedEvent::class], + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + 'command_started_event' => [$event->command_started_event, CommandStartedEvent::class], + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + 'command_succeeded_event' => [$event->command_succeeded_event, CommandSucceededEvent::class], + default => throw new LogicException('Unsupported event type: ' . key($event)), + }; } } diff --git a/tests/SpecTests/Context.php b/tests/SpecTests/Context.php index 1341e65da..c3704900f 100644 --- a/tests/SpecTests/Context.php +++ b/tests/SpecTests/Context.php @@ -358,18 +358,11 @@ public function replaceArgumentSessionPlaceholder(array &$args): void return; } - switch ($args['session']) { - case 'session0': - $args['session'] = $this->session0; - break; - - case 'session1': - $args['session'] = $this->session1; - break; - - default: - throw new LogicException('Unsupported session placeholder: ' . $args['session']); - } + $args['session'] = match ($args['session']) { + 'session0' => $this->session0, + 'session1' => $this->session1, + default => throw new LogicException('Unsupported session placeholder: ' . $args['session']), + }; } /** @@ -386,18 +379,11 @@ public function replaceCommandSessionPlaceholder(stdClass $command): void return; } - switch ($command->lsid) { - case 'session0': - $command->lsid = $this->session0Lsid; - break; - - case 'session1': - $command->lsid = $this->session1Lsid; - break; - - default: - throw new LogicException('Unsupported session placeholder: ' . $command->lsid); - } + $command->lsid = match ($command->lsid) { + 'session0' => $this->session0Lsid, + 'session1' => $this->session1Lsid, + default => throw new LogicException('Unsupported session placeholder: ' . $command->lsid), + }; } public function selectCollection($databaseName, $collectionName, array $collectionOptions = [], array $databaseOptions = []) diff --git a/tests/SpecTests/FunctionalTestCase.php b/tests/SpecTests/FunctionalTestCase.php index cd02f5908..b4ca35a9e 100644 --- a/tests/SpecTests/FunctionalTestCase.php +++ b/tests/SpecTests/FunctionalTestCase.php @@ -126,18 +126,11 @@ protected function assertOutcomeCollectionData(array $expectedDocuments, int $re $this->assertNotNull($expectedDocument); $this->assertNotNull($actualDocument); - switch ($resultExpectation) { - case ResultExpectation::ASSERT_SAME_DOCUMENT: - $this->assertSameDocument($expectedDocument, $actualDocument); - break; - - case ResultExpectation::ASSERT_DOCUMENTS_MATCH: - $this->assertDocumentsMatch($expectedDocument, $actualDocument); - break; - - default: - $this->fail(sprintf('Invalid result expectation "%d" for %s', $resultExpectation, __METHOD__)); - } + match ($resultExpectation) { + ResultExpectation::ASSERT_SAME_DOCUMENT => $this->assertSameDocument($expectedDocument, $actualDocument), + ResultExpectation::ASSERT_DOCUMENTS_MATCH => $this->assertDocumentsMatch($expectedDocument, $actualDocument), + default => $this->fail(sprintf('Invalid result expectation "%d" for %s', $resultExpectation, __METHOD__)), + }; } } @@ -284,18 +277,12 @@ private function isServerlessRequirementSatisfied(?string $serverlessMode): bool return true; } - switch ($serverlessMode) { - case self::SERVERLESS_ALLOW: - return true; - - case self::SERVERLESS_FORBID: - return ! static::isServerless(); - - case self::SERVERLESS_REQUIRE: - return static::isServerless(); - } - - throw new UnexpectedValueException(sprintf('Invalid serverless requirement "%s" found.', $serverlessMode)); + return match ($serverlessMode) { + self::SERVERLESS_ALLOW => true, + self::SERVERLESS_FORBID => ! static::isServerless(), + self::SERVERLESS_REQUIRE => static::isServerless(), + default => throw new UnexpectedValueException(sprintf('Invalid serverless requirement "%s" found.', $serverlessMode)), + }; } /** diff --git a/tests/UnifiedSpecTests/Constraint/IsBsonType.php b/tests/UnifiedSpecTests/Constraint/IsBsonType.php index e2f73e807..7d17a9af7 100644 --- a/tests/UnifiedSpecTests/Constraint/IsBsonType.php +++ b/tests/UnifiedSpecTests/Constraint/IsBsonType.php @@ -90,77 +90,32 @@ public static function anyOf(string ...$types): Constraint private function doMatches($other): bool { - switch ($this->type) { - case 'double': - return is_float($other); - - case 'string': - return is_string($other); - - case 'object': - return self::isObject($other); - - case 'array': - return self::isArray($other); - - case 'binData': - return $other instanceof BinaryInterface; - - case 'undefined': - return $other instanceof Undefined; - - case 'objectId': - return $other instanceof ObjectIdInterface; - - case 'bool': - return is_bool($other); - - case 'date': - return $other instanceof UTCDateTimeInterface; - - case 'null': - return $other === null; - - case 'regex': - return $other instanceof RegexInterface; - - case 'dbPointer': - return $other instanceof DBPointer; - - case 'javascript': - return $other instanceof JavascriptInterface && $other->getScope() === null; - - case 'symbol': - return $other instanceof Symbol; - - case 'javascriptWithScope': - return $other instanceof JavascriptInterface && $other->getScope() !== null; - - case 'int': - return is_int($other); - - case 'timestamp': - return $other instanceof TimestampInterface; - - case 'long': - return is_int($other) || $other instanceof Int64; - - case 'decimal': - return $other instanceof Decimal128Interface; - - case 'minKey': - return $other instanceof MinKeyInterface; - - case 'maxKey': - return $other instanceof MaxKeyInterface; - - case 'number': - return is_int($other) || $other instanceof Int64 || is_float($other) || $other instanceof Decimal128Interface; - - default: - // This should already have been caught in the constructor - throw new LogicException('Unsupported type: ' . $this->type); - } + return match ($this->type) { + 'double' => is_float($other), + 'string' => is_string($other), + 'object' => self::isObject($other), + 'array' => self::isArray($other), + 'binData' => $other instanceof BinaryInterface, + 'undefined' => $other instanceof Undefined, + 'objectId' => $other instanceof ObjectIdInterface, + 'bool' => is_bool($other), + 'date' => $other instanceof UTCDateTimeInterface, + 'null' => $other === null, + 'regex' => $other instanceof RegexInterface, + 'dbPointer' => $other instanceof DBPointer, + 'javascript' => $other instanceof JavascriptInterface && $other->getScope() === null, + 'symbol' => $other instanceof Symbol, + 'javascriptWithScope' => $other instanceof JavascriptInterface && $other->getScope() !== null, + 'int' => is_int($other), + 'timestamp' => $other instanceof TimestampInterface, + 'long' => is_int($other) || $other instanceof Int64, + 'decimal' => $other instanceof Decimal128Interface, + 'minKey' => $other instanceof MinKeyInterface, + 'maxKey' => $other instanceof MaxKeyInterface, + 'number' => is_int($other) || $other instanceof Int64 || is_float($other) || $other instanceof Decimal128Interface, + // This should already have been caught in the constructor + default => throw new LogicException('Unsupported type: ' . $this->type), + }; } private function doToString(): string diff --git a/tests/UnifiedSpecTests/Context.php b/tests/UnifiedSpecTests/Context.php index 2210cad75..56f68adc0 100644 --- a/tests/UnifiedSpecTests/Context.php +++ b/tests/UnifiedSpecTests/Context.php @@ -95,34 +95,15 @@ public function createEntities(array $entities): void $id = $def->id ?? null; assertIsString($id); - switch ($type) { - case 'client': - $this->createClient($id, $def); - break; - - case 'clientEncryption': - $this->createClientEncryption($id, $def); - break; - - case 'database': - $this->createDatabase($id, $def); - break; - - case 'collection': - $this->createCollection($id, $def); - break; - - case 'session': - $this->createSession($id, $def); - break; - - case 'bucket': - $this->createBucket($id, $def); - break; - - default: - throw new LogicException('Unsupported entity type: ' . $type); - } + match ($type) { + 'client' => $this->createClient($id, $def), + 'clientEncryption' => $this->createClientEncryption($id, $def), + 'database' => $this->createDatabase($id, $def), + 'collection' => $this->createCollection($id, $def), + 'session' => $this->createSession($id, $def), + 'bucket' => $this->createBucket($id, $def), + default => throw new LogicException('Unsupported entity type: ' . $type), + }; } } diff --git a/tests/UnifiedSpecTests/UnifiedTestRunner.php b/tests/UnifiedSpecTests/UnifiedTestRunner.php index 605eb2cac..39ea3832d 100644 --- a/tests/UnifiedSpecTests/UnifiedTestRunner.php +++ b/tests/UnifiedSpecTests/UnifiedTestRunner.php @@ -299,25 +299,16 @@ private function getServerVersion(): string */ private function getTopology(): string { - switch ($this->getPrimaryServer()->getType()) { - case Server::TYPE_STANDALONE: - return RunOnRequirement::TOPOLOGY_SINGLE; - - case Server::TYPE_RS_PRIMARY: - return RunOnRequirement::TOPOLOGY_REPLICASET; - - case Server::TYPE_MONGOS: - /* Since MongoDB 3.6, all sharded clusters use replica sets. The - * unified test format deprecated use of "sharded-replicaset" in - * tests but we should still identify as such. */ - return RunOnRequirement::TOPOLOGY_SHARDED_REPLICASET; - - case Server::TYPE_LOAD_BALANCER: - return RunOnRequirement::TOPOLOGY_LOAD_BALANCED; - - default: - throw new UnexpectedValueException('Topology is neither single nor RS nor sharded'); - } + return match ($this->getPrimaryServer()->getType()) { + Server::TYPE_STANDALONE => RunOnRequirement::TOPOLOGY_SINGLE, + Server::TYPE_RS_PRIMARY => RunOnRequirement::TOPOLOGY_REPLICASET, + /* Since MongoDB 3.6, all sharded clusters use replica sets. The + * unified test format deprecated use of "sharded-replicaset" in + * tests but we should still identify as such. */ + Server::TYPE_MONGOS => RunOnRequirement::TOPOLOGY_SHARDED_REPLICASET, + Server::TYPE_LOAD_BALANCER => RunOnRequirement::TOPOLOGY_LOAD_BALANCED, + default => throw new UnexpectedValueException('Topology is neither single nor RS nor sharded'), + }; } private function isAtlasDataLake(): bool