diff --git a/analyzer-baseline.toml b/analyzer-baseline.toml index 26938f84..90717a3d 100644 --- a/analyzer-baseline.toml +++ b/analyzer-baseline.toml @@ -1482,12 +1482,6 @@ code = "missing-api-or-internal" message = 'Interface `PhpDb\Exception\ExceptionInterface` is missing an `@api` or `@internal` annotation.' count = 1 -[[issues]] -file = "src/Feature/FeatureInterface.php" -code = "missing-api-or-internal" -message = 'Interface `PhpDb\Feature\FeatureInterface` is missing an `@api` or `@internal` annotation.' -count = 1 - [[issues]] file = "src/Metadata/Object/ConstraintKeyObject.php" code = "missing-constant-type" @@ -2082,102 +2076,6 @@ code = "class-must-be-final" message = 'Class `PhpDb\RowGateway\Exception\RuntimeException` should be declared `final`.' count = 1 -[[issues]] -file = "src/RowGateway/Feature/AbstractFeature.php" -code = "imprecise-type" -message = "Type `array` in property `$sharedData` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/AbstractFeature.php" -code = "missing-api-or-internal" -message = 'Abstract class `PhpDb\RowGateway\Feature\AbstractFeature` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/FeatureInterface.php" -code = "missing-api-or-internal" -message = 'Interface `PhpDb\RowGateway\Feature\FeatureInterface` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "imprecise-type" -message = "Type `array` in parameter `$args` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "imprecise-type" -message = "Type `array` in parameter `$arguments` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "imprecise-type" -message = "Type `array` in parameter `$features` is imprecise, equivalent to `array`." -count = 2 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "imprecise-type" -message = "Type `array` in property `$magicSpecifications` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "missing-constant-type" -message = "Class constant `APPLY_HALT` is missing a type hint." -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `PhpDb\RowGateway\Feature\FeatureSet::addFeature`: expected `PhpDb\RowGateway\Feature\FeatureInterface`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 2 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "string-member-selector" -message = "This member selector uses a non-literal string type (`string`); its specific value cannot be statically determined." -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "unused-parameter" -message = "Parameter `$arguments` is never used." -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "unused-parameter" -message = "Parameter `$method` is never used." -count = 2 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "unused-parameter" -message = "Parameter `$property` is never used." -count = 4 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "unused-parameter" -message = "Parameter `$value` is never used." -count = 1 - -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "unused-property" -message = "Property `$magicSpecifications` is never used." -count = 1 - [[issues]] file = "src/RowGateway/RowGateway.php" code = "class-must-be-final" @@ -5394,12 +5292,6 @@ code = "imprecise-type" message = "Type `array` in return type of `getTable` is imprecise, equivalent to `array`." count = 1 -[[issues]] -file = "src/TableGateway/AbstractTableGateway.php" -code = "invalid-array-access" -message = 'Cannot access array index on object `PhpDb\Sql\TableIdentifier` that does not implement `ArrayAccess`.' -count = 2 - [[issues]] file = "src/TableGateway/AbstractTableGateway.php" code = "invalid-clone" @@ -5604,12 +5496,6 @@ code = "possibly-invalid-clone" message = 'Expression of type `PhpDb\Sql\Sql|null` might not be a cloneable object.' count = 1 -[[issues]] -file = "src/TableGateway/AbstractTableGateway.php" -code = "possibly-undefined-string-array-index" -message = "Possibly undefined array key `string('table')` accessed on `array`." -count = 2 - [[issues]] file = "src/TableGateway/AbstractTableGateway.php" code = "redundant-comparison" @@ -5640,510 +5526,6 @@ code = "class-must-be-final" message = 'Class `PhpDb\TableGateway\Exception\RuntimeException` should be declared `final`.' count = 1 -[[issues]] -file = "src/TableGateway/Feature/AbstractFeature.php" -code = "imprecise-type" -message = "Type `array` in property `$sharedData` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/AbstractFeature.php" -code = "missing-api-or-internal" -message = 'Abstract class `PhpDb\TableGateway\Feature\AbstractFeature` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/AbstractFeature.php" -code = "uninitialized-property" -message = 'Property `$tableGateway` is not initialized in the constructor of class `PhpDb\TableGateway\Feature\EventFeature`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/AbstractFeature.php" -code = "uninitialized-property" -message = 'Property `$tableGateway` is not initialized in the constructor of class `PhpDb\TableGateway\Feature\MasterSlaveFeature`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/AbstractFeature.php" -code = "uninitialized-property" -message = 'Property `$tableGateway` is not initialized in the constructor of class `PhpDb\TableGateway\Feature\MetadataFeature`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/AbstractFeature.php" -code = "uninitialized-property" -message = 'Property `$tableGateway` is not initialized in the constructor of class `PhpDb\TableGateway\Feature\RowGatewayFeature`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/AbstractFeature.php" -code = "uninitialized-property" -message = 'Property `$tableGateway` is not initialized in the constructor of class `PhpDb\TableGateway\Feature\SequenceFeature`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature.php" -code = "class-must-be-final" -message = 'Class `PhpDb\TableGateway\Feature\EventFeature` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature.php" -code = "invalid-return-statement" -message = 'Invalid return type for function `PhpDb\TableGateway\Feature\EventFeature::getEvent`: expected `PhpDb\TableGateway\Feature\EventFeature\TableGatewayEvent`, but found `PhpDb\TableGateway\Feature\EventFeature\TableGatewayEvent|null`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature.php" -code = "nullable-return-statement" -message = 'Function `PhpDb\TableGateway\Feature\EventFeature::getEvent` is declared to return `PhpDb\TableGateway\Feature\EventFeature\TableGatewayEvent` but possibly returns a nullable value (inferred as `PhpDb\TableGateway\Feature\EventFeature\TableGatewayEvent|null`).' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature.php" -code = "possible-method-access-on-null" -message = "Attempting to call a method on `null`." -count = 19 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature.php" -code = "possibly-null-argument" -message = 'Argument #1 of method `Laminas\EventManager\EventManagerInterface::triggerEvent` is possibly `null`, but parameter type `Laminas\EventManager\EventInterface|array|object>` does not accept it.' -count = 10 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature/TableGatewayEvent.php" -code = "class-must-be-final" -message = 'Class `PhpDb\TableGateway\Feature\EventFeature\TableGatewayEvent` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature/TableGatewayEvent.php" -code = "imprecise-type" -message = "Type `array` in property `$params` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature/TableGatewayEvent.php" -code = "missing-template-parameter" -message = 'Too few template arguments for `Laminas\EventManager\EventInterface`: expected at least 2, but found 0.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature/TableGatewayEvent.php" -code = "possibly-invalid-array-access" -message = "Cannot perform array access on value of type `object`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature/TableGatewayEvent.php" -code = "property-type-coercion" -message = 'A value of a less specific type `null|object|string` is being assigned to property `$target` (PhpDb\TableGateway\AbstractTableGateway|null).' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature/TableGatewayEvent.php" -code = "unused-template-parameter" -message = 'Template parameter `NewTParams` is never used in method `PhpDb\TableGateway\Feature\EventFeature\TableGatewayEvent::setParams`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature/TableGatewayEvent.php" -code = "unused-template-parameter" -message = 'Template parameter `NewTTarget` is never used in method `PhpDb\TableGateway\Feature\EventFeature\TableGatewayEvent::setTarget`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-api-or-internal" -message = 'Interface `PhpDb\TableGateway\Feature\EventFeatureEventsInterface` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-constant-type" -message = "Class constant `EVENT_POST_DELETE` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-constant-type" -message = "Class constant `EVENT_POST_INITIALIZE` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-constant-type" -message = "Class constant `EVENT_POST_INSERT` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-constant-type" -message = "Class constant `EVENT_POST_SELECT` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-constant-type" -message = "Class constant `EVENT_POST_UPDATE` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-constant-type" -message = "Class constant `EVENT_PRE_DELETE` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-constant-type" -message = "Class constant `EVENT_PRE_INITIALIZE` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-constant-type" -message = "Class constant `EVENT_PRE_INSERT` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-constant-type" -message = "Class constant `EVENT_PRE_SELECT` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeatureEventsInterface.php" -code = "missing-constant-type" -message = "Class constant `EVENT_PRE_UPDATE` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/FeatureInterface.php" -code = "missing-api-or-internal" -message = 'Interface `PhpDb\TableGateway\Feature\FeatureInterface` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "class-must-be-final" -message = 'Class `PhpDb\TableGateway\Feature\FeatureSet` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "imprecise-type" -message = "Type `array` in parameter `$args` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "imprecise-type" -message = "Type `array` in parameter `$arguments` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "imprecise-type" -message = "Type `array` in parameter `$features` is imprecise, equivalent to `array`." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "imprecise-type" -message = "Type `array` in property `$magicSpecifications` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "missing-constant-type" -message = "Class constant `APPLY_HALT` is missing a type hint." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `PhpDb\TableGateway\Feature\FeatureSet::addFeature`: expected `PhpDb\TableGateway\Feature\FeatureInterface`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "string-member-selector" -message = "This member selector uses a non-literal string type (`string`); its specific value cannot be statically determined." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "unused-parameter" -message = "Parameter `$property` is never used." -count = 4 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "unused-parameter" -message = "Parameter `$value` is never used." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/GlobalAdapterFeature.php" -code = "class-must-be-final" -message = 'Class `PhpDb\TableGateway\Feature\GlobalAdapterFeature` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/GlobalAdapterFeature.php" -code = "invalid-return-statement" -message = 'Invalid return type for function `PhpDb\TableGateway\Feature\GlobalAdapterFeature::getStaticAdapter`: expected `PhpDb\Adapter\AdapterInterface`, but found `PhpDb\Adapter\AdapterInterface|null`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/GlobalAdapterFeature.php" -code = "missing-constructor" -message = 'Class `PhpDb\TableGateway\Feature\GlobalAdapterFeature` has typed properties without default values but no constructor to initialize them.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/GlobalAdapterFeature.php" -code = "nullable-return-statement" -message = 'Function `PhpDb\TableGateway\Feature\GlobalAdapterFeature::getStaticAdapter` is declared to return `PhpDb\Adapter\AdapterInterface` but possibly returns a nullable value (inferred as `PhpDb\Adapter\AdapterInterface|null`).' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/GlobalAdapterFeature.php" -code = "possibly-undefined-string-array-index" -message = '''Possibly undefined array key `class-string('PhpDb\TableGateway\Feature\GlobalAdapterFeature')` accessed on `array`.''' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MasterSlaveFeature.php" -code = "class-must-be-final" -message = 'Class `PhpDb\TableGateway\Feature\MasterSlaveFeature` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MasterSlaveFeature.php" -code = "invalid-property-assignment-value" -message = 'Invalid type for property `$masterSql`: expected `PhpDb\Sql\Sql`, but got `PhpDb\Sql\Sql|null`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MasterSlaveFeature.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `PhpDb\Sql\Sql::__construct`: expected `PhpDb\Sql\TableIdentifier|array|null|string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MasterSlaveFeature.php" -code = "possible-method-access-on-null" -message = "Attempting to call a method on `null`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MasterSlaveFeature.php" -code = "uninitialized-property" -message = 'Property `$masterSql` is not initialized in the constructor of class `PhpDb\TableGateway\Feature\MasterSlaveFeature`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "class-must-be-final" -message = 'Class `PhpDb\TableGateway\Feature\MetadataFeature` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "impossible-condition" -message = "Redundant ternary operator: condition is always falsy." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "impossible-condition" -message = "This condition (type `false`) will always evaluate to false." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "impossible-type-comparison" -message = "Impossible type assertion: `$t->table` of type `string` can never be `array`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `PhpDb\Metadata\MetadataInterface::getColumnNames`: expected `string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `PhpDb\Metadata\MetadataInterface::getConstraints`: expected `string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `PhpDb\Metadata\MetadataInterface::getTable`: expected `string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `PhpDb\Metadata\MetadataInterface::getColumnNames`: expected `null|string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `PhpDb\Metadata\MetadataInterface::getConstraints`: expected `null|string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `PhpDb\Metadata\MetadataInterface::getTable`: expected `null|string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "mixed-array-assignment" -message = "Unsafe array assignment on type `mixed`." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "no-value" -message = "Argument #1 passed to function `current` has type `never`, meaning it cannot produce a value." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "possibly-undefined-int-array-index" -message = "Possibly undefined array key `int(0)` accessed on `non-empty-array`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "class-must-be-final" -message = 'Class `PhpDb\TableGateway\Feature\RowGatewayFeature` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "imprecise-type" -message = "Type `array` in property `$constructorArguments` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `PhpDb\RowGateway\RowGateway::__construct`: expected `array|null|string`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "mixed-array-access" -message = "Unsafe array access on type `nonnull`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "possible-method-access-on-null" -message = "Attempting to call a method on `null`." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "class-must-be-final" -message = 'Class `PhpDb\TableGateway\Feature\SequenceFeature` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "invalid-array-access" -message = 'Cannot access array index on object `PhpDb\Sql\TableIdentifier` that does not implement `ArrayAccess`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "mixed-array-access" -message = "Unsafe array access on type `mixed`." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "mixed-property-type-coercion" -message = "A value with a less specific type `mixed` is being assigned to property `$sequenceValue` (int|null)." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "mixed-return-statement" -message = 'Could not infer a precise return type for function `PhpDb\TableGateway\Feature\SequenceFeature::lastSequenceId`. Saw type `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "mixed-return-statement" -message = 'Could not infer a precise return type for function `PhpDb\TableGateway\Feature\SequenceFeature::nextSequenceId`. Saw type `mixed`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "possible-method-access-on-null" -message = "Attempting to call a method on `null`." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "possibly-invalid-argument" -message = 'Possible argument type mismatch for argument #2 of `array_search`: expected `array`, but possibly received `PhpDb\Sql\TableIdentifier|array|string`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "unhandled-thrown-type" -message = 'Potentially unhandled exception `PhpDb\Exception\RuntimeException` in `PhpDb\TableGateway\Feature\SequenceFeature::preInsert`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "unused-parameter" -message = "Parameter `$result` is never used." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "unused-parameter" -message = "Parameter `$statement` is never used." -count = 1 - [[issues]] file = "src/TableGateway/TableGateway.php" code = "class-must-be-final" @@ -6185,3 +5567,28 @@ file = "src/TableGateway/TableGatewayInterface.php" code = "missing-api-or-internal" message = 'Interface `PhpDb\TableGateway\TableGatewayInterface` is missing an `@api` or `@internal` annotation.' count = 1 + +[[issues]] +file = "src/TableGateway/AbstractTableGateway.php" +code = "nullable-return-statement" +message = 'Function `PhpDb\TableGateway\AbstractTableGateway::getTable` is declared to return `PhpDb\Sql\TableIdentifier|array|string` but possibly returns a nullable value (inferred as `PhpDb\Sql\TableIdentifier|array|null|string`).' +count = 1 + +[[issues]] +file = "src/TableGateway/AbstractTableGateway.php" +code = "invalid-return-statement" +message = 'Invalid return type for function `PhpDb\TableGateway\AbstractTableGateway::getTable`: expected `PhpDb\Sql\TableIdentifier|array|string`, but found `PhpDb\Sql\TableIdentifier|array|null|string`.' +count = 1 + + +[[issues]] +file = "src/TableGateway/AbstractTableGateway.php" +code = "invalid-array-access" +message = 'Cannot access array index on object `PhpDb\Sql\TableIdentifier` that does not implement `ArrayAccess`.' +count = 3 + +[[issues]] +file = "src/TableGateway/AbstractTableGateway.php" +code = "possibly-undefined-string-array-index" +message = "Possibly undefined array key `string('table')` accessed on `array`." +count = 3 diff --git a/lint-baseline.toml b/lint-baseline.toml index d4aa363a..ff32f48d 100644 --- a/lint-baseline.toml +++ b/lint-baseline.toml @@ -414,12 +414,6 @@ code = "yoda-conditions" message = "Use Yoda condition style for safer comparisons" count = 6 -[[issues]] -file = "src/RowGateway/Feature/FeatureSet.php" -code = "too-many-methods" -message = "Class has too many methods." -count = 1 - [[issues]] file = "src/RowGateway/RowGateway.php" code = "no-else-clause" @@ -972,90 +966,6 @@ code = "too-many-methods" message = "Class has too many methods." count = 1 -[[issues]] -file = "src/TableGateway/Feature/EventFeature.php" -code = "no-shorthand-ternary" -message = "Use of the shorthand ternary operator." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/EventFeature.php" -code = "too-many-methods" -message = "Class has too many methods." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "kan-defect" -message = "Class has a high kan defect score." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/FeatureSet.php" -code = "too-many-methods" -message = "Class has too many methods." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/GlobalAdapterFeature.php" -code = "no-isset" -message = "Use of the `isset` construct." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/MetadataFeature.php" -code = "no-else-clause" -message = "Avoid `else` clauses." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "no-else-clause" -message = "Avoid `else` clauses." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "no-else-clause" -message = "Avoid `elseif` clauses." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "no-isset" -message = "Use of the `isset` construct." -count = 2 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "no-redundant-use" -message = 'Redundant import: `MetadataFeature` is already in the current namespace `PhpDb\TableGateway\Feature`.' -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/RowGatewayFeature.php" -code = "yoda-conditions" -message = "Use Yoda condition style for safer comparisons" -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "strict-behavior" -message = "Call to `array_search` must enforce strict comparison." -count = 1 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "string-style" -message = "String concatenation can be replaced with interpolation." -count = 3 - -[[issues]] -file = "src/TableGateway/Feature/SequenceFeature.php" -code = "tagged-todo" -message = "TODO should be tagged with (@username) or (#issue)." -count = 1 - [[issues]] file = "src/TableGateway/TableGateway.php" code = "no-shorthand-ternary" @@ -3660,144 +3570,6 @@ code = "strict-assertions" message = "Use strict assertions in PHPUnit tests." count = 21 -[[issues]] -file = "test/unit/RowGateway/Feature/AbstractFeatureTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 5 - -[[issues]] -file = "test/unit/RowGateway/Feature/AbstractFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetMagicMethodSpecificationsReturnsEmptyArray`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/AbstractFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetNameReturnsClassName`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/AbstractFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testInitializeThrowsRuntimeException`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/AbstractFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetRowGateway`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 20 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testAddFeatureCallsSetRowGatewayWhenRowGatewayIsSet`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testAddFeature`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testAddFeatures`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testApplyCallsMethodOnFeatures`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testApplyHaltsWhenFeatureReturnsHalt`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testApplySkipsFeatureWithoutMethod`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallMagicCallReturnsNull`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallMagicGetReturnsNull`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallMagicSetReturnsNull`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCanCallMagicCallReturnsFalse`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCanCallMagicGetReturnsFalse`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCanCallMagicSetReturnsFalse`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithEmptyArray`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithFeatures`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetFeatureByClassNameReturnsFeature`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetFeatureByClassNameReturnsNullWhenNotFound`." -count = 1 - -[[issues]] -file = "test/unit/RowGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetRowGateway`." -count = 1 - [[issues]] file = "test/unit/RowGateway/RowGatewayTest.php" code = "assertion-style" @@ -8623,697 +8395,115 @@ message = "Use strict assertions in PHPUnit tests." count = 16 [[issues]] -file = "test/unit/TableGateway/Feature/AbstractFeatureTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 4 - -[[issues]] -file = "test/unit/TableGateway/Feature/AbstractFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetMagicMethodSpecificationsReturnsEmptyArray`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/AbstractFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetNameReturnsClassName`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/AbstractFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testInitializeDoesNothing`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/AbstractFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetTableGateway`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "assertion-style" message = "Inconsistent assertions style." -count = 13 +count = 32 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetParamWithDefault`." -count = 1 +file = "test/unit/TableGateway/TableGatewayTest.php" +code = "no-assign-in-argument" +message = "Avoid assignments in function call arguments." +count = 3 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPropagationIsStoppedAlwaysReturnsFalse`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorThrowsExceptionWhenSqlTableDoesNotMatch`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetNameAndGetName`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithArrayOfFeatures`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetParamAndGetParam`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithCustomResultSetPrototype`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetParamsAndGetParams`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithFeatureSet`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetParamsWithObject`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithSingleFeature`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetTargetAndGetTarget`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructor`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testStopPropagation`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testDeleteShouldResetTableToUnaliasedTable`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php" -code = "strict-assertions" -message = "Use strict assertions in PHPUnit tests." -count = 4 - -[[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 49 - -[[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "no-assign-in-argument" -message = "Avoid assignments in function call arguments." -count = 13 - -[[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithDefaults`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInsertShouldResetTableToUnaliasedTable`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetEventManager`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testTableAsAliasedTableIdentifierObject`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetEvent`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testTableAsString`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostDelete`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testTableAsTableIdentifierObject`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" +file = "test/unit/TableGateway/TableGatewayTest.php" code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitialize`." +message = "Use `#[Test]` attribute instead of `test` prefix on method `testUpdateShouldResetTableToUnaliasedTable`." count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInsert`." -count = 1 +file = "test/unit/TableGateway/TableGatewayTest.php" +code = "strict-assertions" +message = "Use strict assertions in PHPUnit tests." +count = 2 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostSelect`." +file = "src/RowGateway/Feature/FeatureSet.php" +code = "too-many-methods" +message = 'Class has too many methods.' count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostUpdate`." +file = "src/TableGateway/Feature/FeatureSet.php" +code = "kan-defect" +message = 'Class has a high kan defect score.' count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreDelete`." +file = "src/TableGateway/Feature/FeatureSet.php" +code = "too-many-methods" +message = 'Class has too many methods.' count = 1 [[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreInitializeAddsIdentifiersForCustomTableGatewayClass`." +file = "src/TableGateway/Feature/EventFeature.php" +code = "too-many-methods" +message = 'Class has too many methods.' count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreInitialize`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreInsert`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreSelect`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/EventFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreUpdate`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 22 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testAddFeatureThatFeatureDoesNotHaveTableGatewayButFeatureSetHas`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testAddFeatureThatFeatureHasTableGatewayButFeatureSetDoesNotHave`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testAddFeaturesReturnsFluentInterface`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testApplyCallsAllFeaturesWhenNoHalt`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testApplyCallsMethodOnFeatures`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testApplyHaltsWhenFeatureReturnsHalt`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testApplyPassesArgumentsToFeatures`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testApplySkipsFeatureWithoutMethod`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallMagicCallReturnsNullWhenNoFeatureHasMethod`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallMagicCallSucceedsForValidMethodOfAddedFeature`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallMagicGetReturnsNull`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCallMagicSetReturnsNull`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCanCallMagicCallReturnsFalseForAddedMethodOfAddedFeature`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCanCallMagicCallReturnsFalseWhenNoFeaturesHaveBeenAdded`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCanCallMagicCallReturnsTrueForAddedMethodOfAddedFeature`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCanCallMagicGetReturnsFalse`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testCanCallMagicSetReturnsFalse`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithFeatures`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetFeatureByClassNameReturnsNullWhenNotFound`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetTableGateway`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/FeatureSetTest.php" -code = "strict-assertions" -message = "Use strict assertions in PHPUnit tests." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 6 - -[[issues]] -file = "test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetStaticAdapterReturnsDefaultAdapterWhenClassSpecificNotSet`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetStaticAdapterThrowsExceptionWhenNoAdapterSet`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreInitializeSetsAdapterOnTableGateway`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetStaticAdapter`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSubclassCanSetAndGetOwnAdapter`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSubclassFallsBackToDefaultAdapterWhenNoSpecificAdapterSet`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testSubclassThrowsExceptionWhenNoAdaptersSet`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 5 - -[[issues]] -file = "test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithSlaveSql`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetSlaveAdapter`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeWithProvidedSlaveSql`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitialize`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostSelect`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreSelect`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MetadataFeatureTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 12 - -[[issues]] -file = "test/unit/TableGateway/Feature/MetadataFeatureTest.php" -code = "no-isset" -message = "Use of the `isset` construct." -count = 2 - -[[issues]] -file = "test/unit/TableGateway/Feature/MetadataFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorSetsInitialSharedData`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MetadataFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeRecordsListOfColumnsInPrimaryKeyToSharedMetadata`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MetadataFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeRecordsPrimaryKeyColumnToSharedMetadata`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MetadataFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeSkipsPrimaryKeyCheckIfNotTable`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MetadataFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeThrowsExceptionWhenNoPrimaryKeyFound`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MetadataFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeWithArrayTable`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/MetadataFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeWithTableIdentifier`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 6 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorStoresArguments`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorStoresRowGatewayInstance`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithNoArguments`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeThrowsExceptionForNonResultSet`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeThrowsExceptionWhenMetadataHasNoMetadataKey`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeThrowsExceptionWhenNoMetadataAndNoPrimaryKey`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeWithMetadataFeature`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeWithRowGatewayInstance`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInitializeWithStringPrimaryKey`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/RowGatewayFeatureTest.php" -code = "yoda-conditions" -message = "Use Yoda condition style for safer comparisons" -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 13 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testLastSequenceIdThrowsExceptionForUnsupportedPlatform`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testLastSequenceId`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testNextSequenceIdThrowsExceptionForUnsupportedPlatform`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testNextSequenceId`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInsertDoesNotSetLastInsertValueWhenSequenceValueIsNull`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPostInsertSetsLastInsertValue`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreInsertGeneratesSequenceWhenPrimaryKeyNotInValues`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreInsertReturnsEarlyWhenNextSequenceIdReturnsNull`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreInsertWhenPrimaryKeyAlreadyInValues`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testPreInsertWithPrimaryKeyColumnButNullValue`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/Feature/SequenceFeatureTest.php" -code = "strict-assertions" -message = "Use strict assertions in PHPUnit tests." -count = 5 - -[[issues]] -file = "test/unit/TableGateway/Feature/TestAsset/TestTableGatewayFeature.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testMethod`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "assertion-style" -message = "Inconsistent assertions style." -count = 32 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "no-assign-in-argument" -message = "Avoid assignments in function call arguments." -count = 3 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorThrowsExceptionWhenSqlTableDoesNotMatch`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithArrayOfFeatures`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithCustomResultSetPrototype`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithFeatureSet`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorWithSingleFeature`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructor`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testDeleteShouldResetTableToUnaliasedTable`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testInsertShouldResetTableToUnaliasedTable`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testTableAsAliasedTableIdentifierObject`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testTableAsString`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testTableAsTableIdentifierObject`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "prefer-test-attribute" -message = "Use `#[Test]` attribute instead of `test` prefix on method `testUpdateShouldResetTableToUnaliasedTable`." -count = 1 - -[[issues]] -file = "test/unit/TableGateway/TableGatewayTest.php" -code = "strict-assertions" -message = "Use strict assertions in PHPUnit tests." -count = 2 diff --git a/mago.toml b/mago.toml index ae9cf5c7..5ef92248 100644 --- a/mago.toml +++ b/mago.toml @@ -15,4 +15,6 @@ baseline = "lint-baseline.toml" baseline = "analyzer-baseline.toml" class-initializers = [ "PhpDb\\ResultSet\\AbstractResultSet::initialize", + "PhpDb\\TableGateway\\Feature\\AbstractFeature::setTableGateway", + "PhpDb\\RowGateway\\Feature\\AbstractFeature::setRowGateway", ] diff --git a/src/Feature/FeatureInterface.php b/src/Feature/FeatureInterface.php index 241a9121..4fcf01da 100644 --- a/src/Feature/FeatureInterface.php +++ b/src/Feature/FeatureInterface.php @@ -4,6 +4,9 @@ namespace PhpDb\Feature; +/** + * @api + */ interface FeatureInterface { /** @return array */ diff --git a/src/RowGateway/Feature/AbstractFeature.php b/src/RowGateway/Feature/AbstractFeature.php index fbf92656..2e4539db 100644 --- a/src/RowGateway/Feature/AbstractFeature.php +++ b/src/RowGateway/Feature/AbstractFeature.php @@ -9,10 +9,14 @@ use PhpDb\RowGateway\Exception; use PhpDb\RowGateway\Exception\RuntimeException; +/** + * @api + */ abstract class AbstractFeature extends AbstractRowGateway implements FeatureInterface { protected AbstractRowGateway $rowGateway; + /** @var array */ protected array $sharedData = []; /** @return array */ diff --git a/src/RowGateway/Feature/FeatureInterface.php b/src/RowGateway/Feature/FeatureInterface.php index d6931334..637c7701 100644 --- a/src/RowGateway/Feature/FeatureInterface.php +++ b/src/RowGateway/Feature/FeatureInterface.php @@ -7,6 +7,9 @@ use PhpDb\Feature\FeatureInterface as BaseFeatureInterface; use PhpDb\RowGateway\AbstractRowGateway; +/** + * @api + */ interface FeatureInterface extends BaseFeatureInterface { public function setRowGateway(AbstractRowGateway $rowGateway): void; diff --git a/src/RowGateway/Feature/FeatureSet.php b/src/RowGateway/Feature/FeatureSet.php index 213e1b58..1e567d23 100644 --- a/src/RowGateway/Feature/FeatureSet.php +++ b/src/RowGateway/Feature/FeatureSet.php @@ -13,15 +13,17 @@ */ class FeatureSet { - final public const APPLY_HALT = 'halt'; + final public const string APPLY_HALT = 'halt'; protected ?AbstractRowGateway $rowGateway = null; /** @var FeatureInterface[] */ protected array $features = []; + /** @var array */ protected array $magicSpecifications = []; + /** @param FeatureInterface[] $features */ public function __construct(array $features = []) { if ([] !== $features) { @@ -38,6 +40,7 @@ public function addFeature(FeatureInterface $feature): static return $this; } + /** @param FeatureInterface[] $features */ public function addFeatures(array $features): static { foreach ($features as $feature) { @@ -46,6 +49,12 @@ public function addFeatures(array $features): static return $this; } + /** + * @param array $args + * + * @mago-expect analysis:string-member-selector + * @mago-expect analysis:mixed-assignment + */ public function apply(string $method, array $args): void { foreach ($this->features as $feature) { @@ -60,31 +69,51 @@ public function apply(string $method, array $args): void } } + /** + * @mago-expect analysis:unused-parameter + * + * @param array $arguments + */ public function callMagicCall(string $method, array $arguments): mixed { return null; } + /** + * @mago-expect analysis:unused-parameter + */ public function callMagicGet(string $property): mixed { return null; } + /** + * @mago-expect analysis:unused-parameter + */ public function callMagicSet(string $property, mixed $value): mixed { return null; } + /** + * @mago-expect analysis:unused-parameter + */ public function canCallMagicCall(string $method): bool { return false; } + /** + * @mago-expect analysis:unused-parameter + */ public function canCallMagicGet(string $property): false { return false; } + /** + * @mago-expect analysis:unused-parameter + */ public function canCallMagicSet(string $property): false { return false; diff --git a/src/TableGateway/AbstractTableGateway.php b/src/TableGateway/AbstractTableGateway.php index 808a1a72..6c0b7da7 100644 --- a/src/TableGateway/AbstractTableGateway.php +++ b/src/TableGateway/AbstractTableGateway.php @@ -31,7 +31,7 @@ /** * @property AdapterInterface $adapter * @property int $lastInsertValue - * @property string $table + * @property TableIdentifier|string|array|null $table */ abstract class AbstractTableGateway implements TableGatewayInterface { diff --git a/src/TableGateway/Feature/AbstractFeature.php b/src/TableGateway/Feature/AbstractFeature.php index 310dd89e..29de0696 100644 --- a/src/TableGateway/Feature/AbstractFeature.php +++ b/src/TableGateway/Feature/AbstractFeature.php @@ -7,10 +7,14 @@ use Override; use PhpDb\TableGateway\AbstractTableGateway; +/** + * @api + */ abstract class AbstractFeature extends AbstractTableGateway implements FeatureInterface { protected AbstractTableGateway $tableGateway; + /** @var array */ protected array $sharedData = []; /** @return array */ diff --git a/src/TableGateway/Feature/EventFeature.php b/src/TableGateway/Feature/EventFeature.php index cac06cd6..b153d9e0 100644 --- a/src/TableGateway/Feature/EventFeature.php +++ b/src/TableGateway/Feature/EventFeature.php @@ -19,11 +19,14 @@ use function get_class; +/** + * @api + */ class EventFeature extends AbstractFeature implements EventFeatureEventsInterface, EventsCapableInterface { protected EventManagerInterface $eventManager; - protected ?EventFeature\TableGatewayEvent $event; + protected EventFeature\TableGatewayEvent $event; public function __construct( ?EventManagerInterface $eventManager = null, @@ -37,7 +40,9 @@ public function __construct( TableGateway::class, ]); - $this->event = $tableGatewayEvent ?: new EventFeature\TableGatewayEvent(); + $this->event = $tableGatewayEvent instanceof EventFeature\TableGatewayEvent + ? $tableGatewayEvent + : new EventFeature\TableGatewayEvent(); } /** diff --git a/src/TableGateway/Feature/EventFeature/TableGatewayEvent.php b/src/TableGateway/Feature/EventFeature/TableGatewayEvent.php index f76a374c..df747c1d 100644 --- a/src/TableGateway/Feature/EventFeature/TableGatewayEvent.php +++ b/src/TableGateway/Feature/EventFeature/TableGatewayEvent.php @@ -8,12 +8,16 @@ use Override; use PhpDb\TableGateway\AbstractTableGateway; -class TableGatewayEvent implements EventInterface +/** + * @implements EventInterface|object> + */ +final class TableGatewayEvent implements EventInterface { protected ?AbstractTableGateway $target = null; protected ?string $name = null; + /** @var array|object */ protected array|object $params = []; #[Override] @@ -77,6 +81,8 @@ public function setName($name): void * * @param string|int $name * @param mixed $value + * + * @mago-expect analysis:possibly-invalid-array-access */ #[Override] public function setParam($name, $value): void @@ -87,8 +93,10 @@ public function setParam($name, $value): void /** * Set event parameters * - * @param array|object $params + * @param array|object $params * @phpstan-ignore selfOut.type + * + * @mago-expect analysis:unused-template-parameter */ #[Override] public function setParams($params): void @@ -101,6 +109,9 @@ public function setParams($params): void * * @param object|string|null $target * @phpstan-ignore selfOut.type + * + * @mago-expect analysis:unused-template-parameter + * @mago-expect analysis:property-type-coercion */ #[Override] public function setTarget($target): void diff --git a/src/TableGateway/Feature/EventFeatureEventsInterface.php b/src/TableGateway/Feature/EventFeatureEventsInterface.php index 166f0950..26778c4b 100644 --- a/src/TableGateway/Feature/EventFeatureEventsInterface.php +++ b/src/TableGateway/Feature/EventFeatureEventsInterface.php @@ -11,21 +11,23 @@ * into a separate interface that EventFeature implements; the change keeps * backwards compatibility, while simultaneously removing the need to add * another hard dependency to the component. + * + * @api */ interface EventFeatureEventsInterface { - public const EVENT_PRE_INITIALIZE = 'preInitialize'; - public const EVENT_POST_INITIALIZE = 'postInitialize'; + public const string EVENT_PRE_INITIALIZE = 'preInitialize'; + public const string EVENT_POST_INITIALIZE = 'postInitialize'; - public const EVENT_PRE_SELECT = 'preSelect'; - public const EVENT_POST_SELECT = 'postSelect'; + public const string EVENT_PRE_SELECT = 'preSelect'; + public const string EVENT_POST_SELECT = 'postSelect'; - public const EVENT_PRE_INSERT = 'preInsert'; - public const EVENT_POST_INSERT = 'postInsert'; + public const string EVENT_PRE_INSERT = 'preInsert'; + public const string EVENT_POST_INSERT = 'postInsert'; - public const EVENT_PRE_DELETE = 'preDelete'; - public const EVENT_POST_DELETE = 'postDelete'; + public const string EVENT_PRE_DELETE = 'preDelete'; + public const string EVENT_POST_DELETE = 'postDelete'; - public const EVENT_PRE_UPDATE = 'preUpdate'; - public const EVENT_POST_UPDATE = 'postUpdate'; + public const string EVENT_PRE_UPDATE = 'preUpdate'; + public const string EVENT_POST_UPDATE = 'postUpdate'; } diff --git a/src/TableGateway/Feature/FeatureInterface.php b/src/TableGateway/Feature/FeatureInterface.php index 29507148..3959d918 100644 --- a/src/TableGateway/Feature/FeatureInterface.php +++ b/src/TableGateway/Feature/FeatureInterface.php @@ -7,6 +7,9 @@ use PhpDb\Feature\FeatureInterface as BaseFeatureInterface; use PhpDb\TableGateway\AbstractTableGateway; +/** + * @api + */ interface FeatureInterface extends BaseFeatureInterface { public function setTableGateway(AbstractTableGateway $tableGateway): void; diff --git a/src/TableGateway/Feature/FeatureSet.php b/src/TableGateway/Feature/FeatureSet.php index efef7c2d..01c5c8d8 100644 --- a/src/TableGateway/Feature/FeatureSet.php +++ b/src/TableGateway/Feature/FeatureSet.php @@ -9,17 +9,22 @@ use function method_exists; +/** + * @api + */ class FeatureSet { - public const APPLY_HALT = 'halt'; + public const string APPLY_HALT = 'halt'; protected ?AbstractTableGateway $tableGateway = null; /** @var FeatureInterface[] */ protected array $features = []; + /** @var array */ protected array $magicSpecifications = []; + /** @param FeatureInterface[] $features */ public function __construct(array $features = []) { if ([] !== $features) { @@ -36,6 +41,7 @@ public function addFeature(FeatureInterface $feature): static return $this; } + /** @param FeatureInterface[] $features */ public function addFeatures(array $features): static { foreach ($features as $feature) { @@ -44,6 +50,12 @@ public function addFeatures(array $features): static return $this; } + /** + * @param array $args + * + * @mago-expect analysis:string-member-selector + * @mago-expect analysis:mixed-assignment + */ public function apply(string $method, array $args): void { foreach ($this->features as $feature) { @@ -60,6 +72,10 @@ public function apply(string $method, array $args): void /** * Call method of on added feature as though it were a local method + * + * @param array $arguments + * + * @mago-expect analysis:string-member-selector */ public function callMagicCall(string $method, array $arguments): mixed { @@ -72,11 +88,17 @@ public function callMagicCall(string $method, array $arguments): mixed return null; } + /** + * @mago-expect analysis:unused-parameter + */ public function callMagicGet(string $property): mixed { return null; } + /** + * @mago-expect analysis:unused-parameter + */ public function callMagicSet(string $property, mixed $value): mixed { return null; @@ -97,11 +119,17 @@ public function canCallMagicCall(string $method): bool return false; } + /** + * @mago-expect analysis:unused-parameter + */ public function canCallMagicGet(string $property): bool { return false; } + /** + * @mago-expect analysis:unused-parameter + */ public function canCallMagicSet(string $property): bool { return false; diff --git a/src/TableGateway/Feature/GlobalAdapterFeature.php b/src/TableGateway/Feature/GlobalAdapterFeature.php index de5fadeb..2e2f8eb0 100644 --- a/src/TableGateway/Feature/GlobalAdapterFeature.php +++ b/src/TableGateway/Feature/GlobalAdapterFeature.php @@ -7,6 +7,9 @@ use PhpDb\Adapter\AdapterInterface; use PhpDb\TableGateway\Exception; +/** + * @api + */ class GlobalAdapterFeature extends AbstractFeature { /** @var AdapterInterface[] */ @@ -21,17 +24,13 @@ public static function getStaticAdapter(): AdapterInterface { $class = static::class; - // class specific adapter - if (isset(static::$staticAdapters[$class])) { - return static::$staticAdapters[$class]; - } + $adapter = static::$staticAdapters[$class] ?? static::$staticAdapters[self::class] ?? null; - // default adapter - if (isset(static::$staticAdapters[self::class])) { - return static::$staticAdapters[self::class]; + if (! $adapter instanceof AdapterInterface) { + throw new Exception\RuntimeException('No database adapter was found in the static registry.'); } - throw new Exception\RuntimeException('No database adapter was found in the static registry.'); + return $adapter; } /** diff --git a/src/TableGateway/Feature/MasterSlaveFeature.php b/src/TableGateway/Feature/MasterSlaveFeature.php index 500e56e6..e07eac52 100644 --- a/src/TableGateway/Feature/MasterSlaveFeature.php +++ b/src/TableGateway/Feature/MasterSlaveFeature.php @@ -6,12 +6,13 @@ use PhpDb\Adapter\AdapterInterface; use PhpDb\Sql\Sql; +use PhpDb\TableGateway\Exception; -class MasterSlaveFeature extends AbstractFeature +final class MasterSlaveFeature extends AbstractFeature { protected AdapterInterface $slaveAdapter; - protected Sql $masterSql; + protected ?Sql $masterSql = null; protected ?Sql $slaveSql = null; @@ -35,14 +36,23 @@ public function getSlaveSql(): ?Sql /** * after initialization, retrieve the original adapter as "master" + * + * @throws Exception\RuntimeException */ public function postInitialize(): void { - $this->masterSql = $this->tableGateway->sql; + $masterSql = $this->tableGateway->sql; + if (! $masterSql instanceof Sql) { + throw new Exception\RuntimeException( + 'The table gateway must be initialized with a Sql instance before this feature is applied.', + ); + } + + $this->masterSql = $masterSql; if (null === $this->slaveSql) { $this->slaveSql = new Sql( $this->slaveAdapter, - $this->tableGateway->sql->getTable(), + $masterSql->getTable(), ); } } @@ -50,9 +60,17 @@ public function postInitialize(): void /** * postSelect() * Ensure to return to the master adapter + * + * @throws Exception\RuntimeException */ public function postSelect(): void { + if (! $this->masterSql instanceof Sql) { + throw new Exception\RuntimeException( + 'The master Sql instance is not available; postInitialize() has not been run.', + ); + } + $this->tableGateway->sql = $this->masterSql; } diff --git a/src/TableGateway/Feature/MetadataFeature.php b/src/TableGateway/Feature/MetadataFeature.php index ac4c2d69..2bc5423b 100644 --- a/src/TableGateway/Feature/MetadataFeature.php +++ b/src/TableGateway/Feature/MetadataFeature.php @@ -12,7 +12,12 @@ use function count; use function current; use function is_array; +use function is_string; +use function reset; +/** + * @api + */ class MetadataFeature extends AbstractFeature { /** @@ -27,28 +32,43 @@ public function __construct( ]; } + /** + * @throws Exception\RuntimeException + */ public function postInitialize(): void { // localize variable for brevity $t = $this->tableGateway; $m = $this->metadata; - $tableGatewayTable = is_array($t->table) ? current($t->table) : $t->table; + $tableGatewayTable = $t->getTable(); + if (is_array($tableGatewayTable)) { + $tableGatewayTable = current($tableGatewayTable); + } - if ($tableGatewayTable instanceof TableIdentifier) { - $table = $tableGatewayTable->getTable(); - $schema = $tableGatewayTable->getSchema(); - } else { - $table = $tableGatewayTable; - $schema = null; + if (! $tableGatewayTable instanceof TableIdentifier && ! is_string($tableGatewayTable)) { + throw new Exception\RuntimeException( + 'The table gateway must reference a named table before metadata can be resolved.', + ); } + $table = $tableGatewayTable instanceof TableIdentifier + ? $tableGatewayTable->getTable() + : $tableGatewayTable; + + $schema = $tableGatewayTable instanceof TableIdentifier + ? $tableGatewayTable->getSchema() + : null; + // get column named $columns = $m->getColumnNames($table, $schema); $t->columns = $columns; // set locally - $this->sharedData['metadata']['columns'] = $columns; + $metadata = $this->sharedData['metadata'] ?? []; + $metadata = is_array($metadata) ? $metadata : []; + $metadata['columns'] = $columns; + $this->sharedData['metadata'] = $metadata; // process primary key only if table is a table; there are no PK constraints on views if (! $m->getTable($table, $schema) instanceof TableObject) { @@ -71,12 +91,9 @@ public function postInitialize(): void } $pkcColumns = $pkc->getColumns(); - if (count($pkcColumns) === 1) { - $primaryKey = $pkcColumns[0]; - } else { - $primaryKey = $pkcColumns; - } + $primaryKey = 1 === count($pkcColumns) ? reset($pkcColumns) : $pkcColumns; - $this->sharedData['metadata']['primaryKey'] = $primaryKey; + $metadata['primaryKey'] = $primaryKey; + $this->sharedData['metadata'] = $metadata; } } diff --git a/src/TableGateway/Feature/RowGatewayFeature.php b/src/TableGateway/Feature/RowGatewayFeature.php index 7a224145..11351d6d 100644 --- a/src/TableGateway/Feature/RowGatewayFeature.php +++ b/src/TableGateway/Feature/RowGatewayFeature.php @@ -7,13 +7,15 @@ use PhpDb\ResultSet\RowPrototypeResultSet; use PhpDb\RowGateway\RowGateway; use PhpDb\RowGateway\RowGatewayInterface; +use PhpDb\Sql\TableIdentifier; use PhpDb\TableGateway\Exception; -use PhpDb\TableGateway\Feature\MetadataFeature; +use function is_array; use function is_string; -class RowGatewayFeature extends AbstractFeature +final class RowGatewayFeature extends AbstractFeature { + /** @var array */ protected array $constructorArguments = []; public function __construct(mixed ...$constructorArguments) @@ -21,14 +23,13 @@ public function __construct(mixed ...$constructorArguments) $this->constructorArguments = $constructorArguments; } + /** + * @throws Exception\RuntimeException + */ public function postInitialize(): void { - $args = $this->constructorArguments; - - /** @var RowPrototypeResultSet $resultSetPrototype */ $resultSetPrototype = $this->tableGateway->resultSetPrototype; - - if (! $this->tableGateway->resultSetPrototype instanceof RowPrototypeResultSet) { + if (! $resultSetPrototype instanceof RowPrototypeResultSet) { throw new Exception\RuntimeException( 'This feature ' . self::class @@ -37,37 +38,66 @@ public function postInitialize(): void ); } - if (isset($args[0])) { - if (is_string($args[0])) { - $primaryKey = $args[0]; - $rowGatewayPrototype = new RowGateway( - $primaryKey, - $this->tableGateway->table, - $this->tableGateway->adapter, - ); - $resultSetPrototype->setRowPrototype($rowGatewayPrototype); - } elseif ($args[0] instanceof RowGatewayInterface) { - $rowGatewayPrototype = $args[0]; - $resultSetPrototype->setRowPrototype($rowGatewayPrototype); - } - } else { - // get from metadata feature - $metadata = $this->tableGateway->featureSet->getFeatureByClassName( - MetadataFeature::class, + $firstArgument = $this->constructorArguments[0] ?? null; + + if ($firstArgument instanceof RowGatewayInterface) { + $resultSetPrototype->setRowPrototype($firstArgument); + return; + } + + if (null !== $firstArgument && ! is_string($firstArgument)) { + return; + } + + $primaryKey = $firstArgument ?? $this->primaryKeyFromMetadata(); + + $table = $this->tableGateway->table; + if (! is_string($table) && ! $table instanceof TableIdentifier) { + throw new Exception\RuntimeException( + 'The table gateway must reference a named table before a RowGateway prototype can be created.', + ); + } + + $resultSetPrototype->setRowPrototype(new RowGateway( + $primaryKey, + $table, + $this->tableGateway->adapter, + )); + } + + /** + * @return string|array + * + * @throws Exception\RuntimeException + * + * @mago-expect analysis:mixed-assignment + */ + private function primaryKeyFromMetadata(): string|array + { + $metadata = $this->tableGateway->featureSet?->getFeatureByClassName(MetadataFeature::class); + + $metadataData = $metadata instanceof MetadataFeature + ? $metadata->sharedData['metadata'] ?? null + : null; + + if (null === $metadataData) { + throw new Exception\RuntimeException( + 'No information was provided to the RowGatewayFeature and/or no MetadataFeature could be consulted ' + . 'to find the primary key necessary for RowGateway object creation.', ); - if ($metadata === null || ! isset($metadata->sharedData['metadata'])) { - throw new Exception\RuntimeException( - 'No information was provided to the RowGatewayFeature and/or no MetadataFeature could be consulted ' - . 'to find the primary key necessary for RowGateway object creation.', - ); - } - $primaryKey = $metadata->sharedData['metadata']['primaryKey']; - $rowGatewayPrototype = new RowGateway( - $primaryKey, - $this->tableGateway->table, - $this->tableGateway->adapter, + } + + if (! is_array($metadataData)) { + throw new Exception\RuntimeException('The MetadataFeature did not expose its metadata as an array.'); + } + + $primaryKey = $metadataData['primaryKey'] ?? null; + if (! is_string($primaryKey) && ! is_array($primaryKey)) { + throw new Exception\RuntimeException( + 'The MetadataFeature did not expose a usable primary key for RowGateway object creation.', ); - $resultSetPrototype->setRowPrototype($rowGatewayPrototype); } + + return $primaryKey; } } diff --git a/src/TableGateway/Feature/SequenceFeature.php b/src/TableGateway/Feature/SequenceFeature.php index 4cbe0091..df4d4e91 100644 --- a/src/TableGateway/Feature/SequenceFeature.php +++ b/src/TableGateway/Feature/SequenceFeature.php @@ -10,7 +10,12 @@ use PhpDb\Sql\Insert; use function array_search; +use function is_array; +use function is_int; +/** + * @api + */ class SequenceFeature extends AbstractFeature { protected string $primaryKeyField; @@ -29,26 +34,35 @@ public function __construct(string $primaryKeyField, string $sequenceName) * Return the most recent value from the specified sequence in the database. * * @throws RuntimeException + * + * @mago-expect analysis:mixed-assignment */ public function lastSequenceId(): int { $platform = $this->tableGateway->adapter->getPlatform(); $platformName = $platform->getName(); - // todo: Remove string usage + // TODO(@simon-mundy): Remove string usage $sql = match ($platformName) { - 'Oracle' => 'SELECT ' - . $platform->quoteIdentifier($this->sequenceName) - . '.CURRVAL as "currval" FROM dual', + 'Oracle' => "SELECT {$platform->quoteIdentifier($this->sequenceName)}.CURRVAL as \"currval\" FROM dual", 'PostgreSQL' => 'SELECT LAST_INSERT_ROWID() as "currval"', default => throw new RuntimeException('Unsupported platform for retrieving last sequence id'), }; $statement = $this->tableGateway->adapter->createStatement(); $statement->prepare($sql); - $result = $statement->execute(); + $result = $statement->execute(); + if (! $result instanceof ResultInterface) { + throw new RuntimeException('The sequence statement did not produce a result.'); + } + $sequence = $result->current(); unset($statement, $result); + + if (! is_array($sequence) || ! is_int($sequence['currval'] ?? null)) { + throw new RuntimeException('The sequence did not return a current value.'); + } + return $sequence['currval']; } @@ -56,6 +70,8 @@ public function lastSequenceId(): int * Generate a new value from the specified sequence in the database, and return it. * * @throws RuntimeException + * + * @mago-expect analysis:mixed-assignment */ public function nextSequenceId(): ?int { @@ -63,21 +79,33 @@ public function nextSequenceId(): ?int $platformName = $platform->getName(); $sql = match ($platformName) { - 'Oracle' => 'SELECT ' - . $platform->quoteIdentifier($this->sequenceName) - . '.NEXTVAL as "nextval" FROM dual', - 'PostgreSQL' => 'SELECT NEXTVAL(\'"' . $this->sequenceName . '"\')', + 'Oracle' => "SELECT {$platform->quoteIdentifier($this->sequenceName)}.NEXTVAL as \"nextval\" FROM dual", + 'PostgreSQL' => "SELECT NEXTVAL('\"{$this->sequenceName}\"')", default => throw new RuntimeException('Unsupported platform for retrieving next sequence id'), }; $statement = $this->tableGateway->adapter->createStatement(); $statement->prepare($sql); - $result = $statement->execute(); + $result = $statement->execute(); + if (! $result instanceof ResultInterface) { + throw new RuntimeException('The sequence statement did not produce a result.'); + } + $sequence = $result->current(); unset($statement, $result); - return $sequence['nextval']; + + if (! is_array($sequence)) { + throw new RuntimeException('The sequence did not return a next value.'); + } + + $nextValue = $sequence['nextval'] ?? null; + + return is_int($nextValue) ? $nextValue : null; } + /** + * @mago-expect analysis:unused-parameter + */ public function postInsert(StatementInterface $statement, ResultInterface $result): void { if (null !== $this->sequenceValue) { @@ -85,13 +113,22 @@ public function postInsert(StatementInterface $statement, ResultInterface $resul } } + /** + * @throws RuntimeException + */ public function preInsert(Insert $insert): Insert { $columns = $insert->getRawState('columns'); $values = $insert->getRawState('values'); - $key = array_search($this->primaryKeyField, $columns); + + if (! is_array($columns) || ! is_array($values)) { + throw new RuntimeException('The insert does not expose columns and values as arrays.'); + } + + $key = array_search($this->primaryKeyField, $columns, strict: true); if (false !== $key) { - $this->sequenceValue = $values[$key] ?? null; + $sequenceValue = $values[$key] ?? null; + $this->sequenceValue = is_int($sequenceValue) ? $sequenceValue : null; return $insert; } diff --git a/src/TableGateway/TableGateway.php b/src/TableGateway/TableGateway.php index c26604be..d2024437 100644 --- a/src/TableGateway/TableGateway.php +++ b/src/TableGateway/TableGateway.php @@ -15,6 +15,8 @@ class TableGateway extends AbstractTableGateway { /** + * @param Feature\FeatureSet|Feature\FeatureInterface|Feature\FeatureInterface[]|null $features + * * @throws Exception\InvalidArgumentException */ public function __construct( diff --git a/test/unit/RowGateway/Feature/AbstractFeatureTest.php b/test/unit/RowGateway/Feature/AbstractFeatureTest.php index ef16e46d..ef552b06 100644 --- a/test/unit/RowGateway/Feature/AbstractFeatureTest.php +++ b/test/unit/RowGateway/Feature/AbstractFeatureTest.php @@ -7,6 +7,7 @@ use PhpDb\RowGateway\AbstractRowGateway; use PhpDb\RowGateway\Exception\RuntimeException; use PhpDb\RowGateway\Feature\AbstractFeature; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use ReflectionProperty; @@ -15,26 +16,29 @@ class AbstractFeatureTest extends TestCase { private AbstractFeature&MockObject $feature; - public function testGetMagicMethodSpecificationsReturnsEmptyArray(): void + #[Test] + public function getMagicMethodSpecificationsReturnsEmptyArray(): void { $result = $this->feature->getMagicMethodSpecifications(); /** @phpstan-ignore staticMethod.alreadyNarrowedType */ - self::assertIsArray($result); - self::assertEmpty($result); + static::assertIsArray($result); + static::assertEmpty($result); } - public function testGetNameReturnsClassName(): void + #[Test] + public function getNameReturnsClassName(): void { $name = $this->feature->getName(); // The mock class name will contain the class name /** @phpstan-ignore staticMethod.alreadyNarrowedType */ - self::assertIsString($name); - self::assertNotEmpty($name); + static::assertIsString($name); + static::assertNotEmpty($name); } - public function testInitializeThrowsRuntimeException(): void + #[Test] + public function initializeThrowsRuntimeException(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('This method is not intended to be called on this object.'); @@ -42,7 +46,8 @@ public function testInitializeThrowsRuntimeException(): void $this->feature->initialize(); } - public function testSetRowGateway(): void + #[Test] + public function setRowGateway(): void { /** @var AbstractRowGateway&MockObject $rowGateway */ $rowGateway = $this->getMockBuilder(AbstractRowGateway::class) @@ -55,7 +60,7 @@ public function testSetRowGateway(): void $reflection = new ReflectionProperty(AbstractFeature::class, 'rowGateway'); $value = $reflection->getValue($this->feature); - self::assertSame($rowGateway, $value); + static::assertSame($rowGateway, $value); } protected function setUp(): void diff --git a/test/unit/RowGateway/Feature/FeatureSetTest.php b/test/unit/RowGateway/Feature/FeatureSetTest.php index cc7bc384..4387224d 100644 --- a/test/unit/RowGateway/Feature/FeatureSetTest.php +++ b/test/unit/RowGateway/Feature/FeatureSetTest.php @@ -6,25 +6,29 @@ use PhpDb\RowGateway\AbstractRowGateway; use PhpDb\RowGateway\Feature\AbstractFeature; +use PhpDb\RowGateway\Feature\FeatureInterface; use PhpDb\RowGateway\Feature\FeatureSet; use PhpDbTest\RowGateway\Feature\TestAsset\TestRowGatewayFeature; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; class FeatureSetTest extends TestCase { - public function testAddFeature(): void + #[Test] + public function addFeature(): void { $feature = $this->createMock(AbstractFeature::class); $featureSet = new FeatureSet(); $result = $featureSet->addFeature($feature); - self::assertSame($featureSet, $result); - self::assertSame($feature, $featureSet->getFeatureByClassName(AbstractFeature::class)); + static::assertSame($featureSet, $result); + static::assertSame($feature, $featureSet->getFeatureByClassName(AbstractFeature::class)); } - public function testAddFeatureCallsSetRowGatewayWhenRowGatewayIsSet(): void + #[Test] + public function addFeatureCallsSetRowGatewayWhenRowGatewayIsSet(): void { /** @var AbstractRowGateway&MockObject $rowGateway */ $rowGateway = $this->getMockBuilder(AbstractRowGateway::class) @@ -41,7 +45,8 @@ public function testAddFeatureCallsSetRowGatewayWhenRowGatewayIsSet(): void $featureSet->addFeature($feature); } - public function testAddFeatures(): void + #[Test] + public function addFeatures(): void { $feature1 = $this->createMock(AbstractFeature::class); $feature2 = $this->createMock(AbstractFeature::class); @@ -49,22 +54,24 @@ public function testAddFeatures(): void $featureSet = new FeatureSet(); $result = $featureSet->addFeatures([$feature1, $feature2]); - self::assertSame($featureSet, $result); - self::assertSame($feature1, $featureSet->getFeatureByClassName(AbstractFeature::class)); + static::assertSame($featureSet, $result); + static::assertSame($feature1, $featureSet->getFeatureByClassName(AbstractFeature::class)); } - public function testApplyCallsMethodOnFeatures(): void + #[Test] + public function applyCallsMethodOnFeatures(): void { $feature = new TestRowGatewayFeature(); $featureSet = new FeatureSet([$feature]); $featureSet->apply('preInitialize', ['arg1', 'arg2']); - self::assertTrue($feature->called); - self::assertEquals(['arg1', 'arg2'], $feature->receivedArgs); + static::assertTrue($feature->called); + static::assertEquals(['arg1', 'arg2'], $feature->receivedArgs); } - public function testApplyHaltsWhenFeatureReturnsHalt(): void + #[Test] + public function applyHaltsWhenFeatureReturnsHalt(): void { $feature1 = new TestRowGatewayFeature(); $feature1->returnValue = FeatureSet::APPLY_HALT; @@ -74,11 +81,12 @@ public function testApplyHaltsWhenFeatureReturnsHalt(): void $featureSet = new FeatureSet([$feature1, $feature2]); $featureSet->apply('preInitialize', []); - self::assertTrue($feature1->called); - self::assertFalse($feature2->called); + static::assertTrue($feature1->called); + static::assertFalse($feature2->called); } - public function testApplySkipsFeatureWithoutMethod(): void + #[Test] + public function applySkipsFeatureWithoutMethod(): void { $feature = $this->createMock(AbstractFeature::class); @@ -86,80 +94,102 @@ public function testApplySkipsFeatureWithoutMethod(): void $featureSet->apply('nonExistentMethod', []); /** @phpstan-ignore staticMethod.alreadyNarrowedType */ - self::assertTrue(true); + static::assertTrue(true); } - public function testCallMagicCallReturnsNull(): void + #[Test] + public function callMagicCallReturnsNull(): void { $featureSet = new FeatureSet(); - self::assertNull($featureSet->callMagicCall('method', [])); + static::assertNull($featureSet->callMagicCall('method', [])); } - public function testCallMagicGetReturnsNull(): void + #[Test] + public function callMagicGetReturnsNull(): void { $featureSet = new FeatureSet(); - self::assertNull($featureSet->callMagicGet('property')); + static::assertNull($featureSet->callMagicGet('property')); } - public function testCallMagicSetReturnsNull(): void + #[Test] + public function callMagicSetReturnsNull(): void { $featureSet = new FeatureSet(); - self::assertNull($featureSet->callMagicSet('property', 'value')); + static::assertNull($featureSet->callMagicSet('property', 'value')); } - public function testCanCallMagicCallReturnsFalse(): void + #[Test] + public function canCallMagicCallReturnsFalse(): void { $featureSet = new FeatureSet(); - self::assertFalse($featureSet->canCallMagicCall('method')); + static::assertFalse($featureSet->canCallMagicCall('method')); } - public function testCanCallMagicGetReturnsFalse(): void + #[Test] + public function canCallMagicGetReturnsFalse(): void { $featureSet = new FeatureSet(); /** @phpstan-ignore staticMethod.impossibleType */ - self::assertFalse($featureSet->canCallMagicGet('property')); + static::assertFalse($featureSet->canCallMagicGet('property')); } - public function testCanCallMagicSetReturnsFalse(): void + #[Test] + public function canCallMagicSetReturnsFalse(): void { $featureSet = new FeatureSet(); /** @phpstan-ignore staticMethod.impossibleType */ - self::assertFalse($featureSet->canCallMagicSet('property')); + static::assertFalse($featureSet->canCallMagicSet('property')); } - public function testConstructorWithEmptyArray(): void + #[Test] + public function constructorWithEmptyArray(): void { $featureSet = new FeatureSet(); - self::assertInstanceOf(FeatureSet::class, $featureSet); + static::assertInstanceOf(FeatureSet::class, $featureSet); } - public function testConstructorWithFeatures(): void + #[Test] + public function constructorWithFeatures(): void { $feature = $this->createMock(AbstractFeature::class); $featureSet = new FeatureSet([$feature]); - self::assertInstanceOf(FeatureSet::class, $featureSet); + static::assertInstanceOf(FeatureSet::class, $featureSet); } - public function testGetFeatureByClassNameReturnsFeature(): void + #[Test] + public function getFeatureByClassNameReturnsFeature(): void { $feature = $this->createMock(AbstractFeature::class); $featureSet = new FeatureSet([$feature]); $result = $featureSet->getFeatureByClassName(AbstractFeature::class); - self::assertSame($feature, $result); + static::assertSame($feature, $result); } - public function testGetFeatureByClassNameReturnsNullWhenNotFound(): void + #[Test] + public function getFeatureByClassNameReturnsNullWhenNotFound(): void { $featureSet = new FeatureSet(); $result = $featureSet->getFeatureByClassName(AbstractFeature::class); - self::assertNull($result); + static::assertNull($result); } - public function testSetRowGateway(): void + #[Test] + public function getFeatureByClassNameSkipsFeaturesOfAnotherClass(): void + { + $other = $this->createMock(FeatureInterface::class); + $wanted = $this->createMock(AbstractFeature::class); + + $featureSet = new FeatureSet([$other, $wanted]); + + static::assertSame($wanted, $featureSet->getFeatureByClassName(AbstractFeature::class)); + } + + #[Test] + public function setRowGateway(): void { /** @var AbstractRowGateway&MockObject $rowGateway */ $rowGateway = $this->getMockBuilder(AbstractRowGateway::class) @@ -174,6 +204,6 @@ public function testSetRowGateway(): void $featureSet = new FeatureSet([$feature]); $result = $featureSet->setRowGateway($rowGateway); - self::assertSame($featureSet, $result); + static::assertSame($featureSet, $result); } } diff --git a/test/unit/TableGateway/Feature/AbstractFeatureTest.php b/test/unit/TableGateway/Feature/AbstractFeatureTest.php index fe896a97..b028d18d 100644 --- a/test/unit/TableGateway/Feature/AbstractFeatureTest.php +++ b/test/unit/TableGateway/Feature/AbstractFeatureTest.php @@ -6,6 +6,7 @@ use PhpDb\TableGateway\AbstractTableGateway; use PhpDb\TableGateway\Feature\AbstractFeature; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use ReflectionProperty; @@ -14,30 +15,34 @@ class AbstractFeatureTest extends TestCase { private AbstractFeature&MockObject $feature; - public function testGetMagicMethodSpecificationsReturnsEmptyArray(): void + #[Test] + public function getMagicMethodSpecificationsReturnsEmptyArray(): void { $result = $this->feature->getMagicMethodSpecifications(); - self::assertEmpty($result); + static::assertEmpty($result); } - public function testGetNameReturnsClassName(): void + #[Test] + public function getNameReturnsClassName(): void { $name = $this->feature->getName(); - self::assertNotEmpty($name); + static::assertNotEmpty($name); } - public function testInitializeDoesNothing(): void + #[Test] + public function initializeDoesNothing(): void { // initialize() is a no-op, just verify it doesn't throw $this->feature->initialize(); /** @phpstan-ignore staticMethod.alreadyNarrowedType */ - self::assertTrue(true); + static::assertTrue(true); } - public function testSetTableGateway(): void + #[Test] + public function setTableGateway(): void { /** @var AbstractTableGateway&MockObject $tableGateway */ $tableGateway = $this->getMockBuilder(AbstractTableGateway::class) @@ -49,7 +54,7 @@ public function testSetTableGateway(): void $reflection = new ReflectionProperty(AbstractFeature::class, 'tableGateway'); $value = $reflection->getValue($this->feature); - self::assertSame($tableGateway, $value); + static::assertSame($tableGateway, $value); } protected function setUp(): void diff --git a/test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php b/test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php index 62d93496..7b87f52a 100644 --- a/test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php +++ b/test/unit/TableGateway/Feature/EventFeature/TableGatewayEventTest.php @@ -6,6 +6,7 @@ use PhpDb\TableGateway\AbstractTableGateway; use PhpDb\TableGateway\Feature\EventFeature\TableGatewayEvent; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use stdClass; @@ -14,65 +15,72 @@ class TableGatewayEventTest extends TestCase { private TableGatewayEvent $event; - public function testGetParamWithDefault(): void + #[Test] + public function getParamWithDefault(): void { $result = $this->event->getParam('nonExistent', 'defaultValue'); - self::assertEquals('defaultValue', $result); + static::assertSame('defaultValue', $result); } - public function testPropagationIsStoppedAlwaysReturnsFalse(): void + #[Test] + public function propagationIsStoppedAlwaysReturnsFalse(): void { /** @phpstan-ignore staticMethod.impossibleType */ - self::assertFalse($this->event->propagationIsStopped()); + static::assertFalse($this->event->propagationIsStopped()); $this->event->stopPropagation(true); // Still returns false as per implementation /** @phpstan-ignore staticMethod.impossibleType */ - self::assertFalse($this->event->propagationIsStopped()); + static::assertFalse($this->event->propagationIsStopped()); } - public function testSetNameAndGetName(): void + #[Test] + public function setNameAndGetName(): void { - self::assertNull($this->event->getName()); + static::assertNull($this->event->getName()); $this->event->setName('test.event'); - self::assertEquals('test.event', $this->event->getName()); + static::assertSame('test.event', $this->event->getName()); } - public function testSetParamAndGetParam(): void + #[Test] + public function setParamAndGetParam(): void { - self::assertNull($this->event->getParam('unknown')); - self::assertEquals('default', $this->event->getParam('unknown', 'default')); + static::assertNull($this->event->getParam('unknown')); + static::assertSame('default', $this->event->getParam('unknown', 'default')); $this->event->setParam('myParam', 'myValue'); - self::assertEquals('myValue', $this->event->getParam('myParam')); + static::assertSame('myValue', $this->event->getParam('myParam')); } - public function testSetParamsAndGetParams(): void + #[Test] + public function setParamsAndGetParams(): void { - self::assertEquals([], $this->event->getParams()); + static::assertEquals([], $this->event->getParams()); $params = ['key1' => 'value1', 'key2' => 'value2']; $this->event->setParams($params); - self::assertEquals($params, $this->event->getParams()); + static::assertEquals($params, $this->event->getParams()); } - public function testSetParamsWithObject(): void + #[Test] + public function setParamsWithObject(): void { $params = new stdClass(); $params->key = 'value'; $this->event->setParams($params); - self::assertSame($params, $this->event->getParams()); + static::assertSame($params, $this->event->getParams()); } - public function testSetTargetAndGetTarget(): void + #[Test] + public function setTargetAndGetTarget(): void { /** @var AbstractTableGateway&MockObject $tableGateway */ $tableGateway = $this->getMockBuilder(AbstractTableGateway::class) @@ -81,17 +89,18 @@ public function testSetTargetAndGetTarget(): void $this->event->setTarget($tableGateway); - self::assertSame($tableGateway, $this->event->getTarget()); + static::assertSame($tableGateway, $this->event->getTarget()); } - public function testStopPropagation(): void + #[Test] + public function stopPropagation(): void { // stopPropagation should do nothing, just ensure it doesn't throw $this->event->stopPropagation(true); $this->event->stopPropagation(false); /** @phpstan-ignore staticMethod.impossibleType */ - self::assertFalse($this->event->propagationIsStopped()); + static::assertFalse($this->event->propagationIsStopped()); } protected function setUp(): void diff --git a/test/unit/TableGateway/Feature/EventFeatureTest.php b/test/unit/TableGateway/Feature/EventFeatureTest.php index d47ded98..105a39c2 100644 --- a/test/unit/TableGateway/Feature/EventFeatureTest.php +++ b/test/unit/TableGateway/Feature/EventFeatureTest.php @@ -18,6 +18,7 @@ use PhpDb\TableGateway\Feature\EventFeatureEventsInterface; use PhpDb\TableGateway\TableGateway; use PhpDbTest\TableGateway\Feature\TestAsset\TestTableGateway; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -32,25 +33,29 @@ final class EventFeatureTest extends TestCase protected TableGateway&MockObject $tableGateway; - public function testConstructorWithDefaults(): void + #[Test] + public function constructorWithDefaults(): void { $feature = new EventFeature(); - self::assertInstanceOf(EventManagerInterface::class, $feature->getEventManager()); - self::assertInstanceOf(EventFeature\TableGatewayEvent::class, $feature->getEvent()); + static::assertInstanceOf(EventManagerInterface::class, $feature->getEventManager()); + static::assertInstanceOf(EventFeature\TableGatewayEvent::class, $feature->getEvent()); } - public function testGetEvent(): void + #[Test] + public function getEvent(): void { - self::assertSame($this->event, $this->feature->getEvent()); + static::assertSame($this->event, $this->feature->getEvent()); } - public function testGetEventManager(): void + #[Test] + public function getEventManager(): void { - self::assertSame($this->eventManager, $this->feature->getEventManager()); + static::assertSame($this->eventManager, $this->feature->getEventManager()); } - public function testPostDelete(): void + #[Test] + public function postDelete(): void { $closureHasRun = false; @@ -64,18 +69,19 @@ static function (EventFeature\TableGatewayEvent $e) use (&$closureHasRun, &$even }, ); - $this->feature->postDelete( - $stmt = $this->getMockBuilder(StatementInterface::class)->getMock(), - $result = $this->getMockBuilder(ResultInterface::class)->getMock(), - ); - self::assertTrue($closureHasRun); - self::assertInstanceOf(TableGateway::class, $event->getTarget()); - self::assertEquals(EventFeatureEventsInterface::EVENT_POST_DELETE, $event->getName()); - self::assertSame($stmt, $event->getParam('statement')); - self::assertSame($result, $event->getParam('result')); + $stmt = $this->getMockBuilder(StatementInterface::class)->getMock(); + $result = $this->getMockBuilder(ResultInterface::class)->getMock(); + + $this->feature->postDelete($stmt, $result); + static::assertTrue($closureHasRun); + static::assertInstanceOf(TableGateway::class, $event->getTarget()); + static::assertEquals(EventFeatureEventsInterface::EVENT_POST_DELETE, $event->getName()); + static::assertSame($stmt, $event->getParam('statement')); + static::assertSame($result, $event->getParam('result')); } - public function testPostInitialize(): void + #[Test] + public function postInitialize(): void { $closureHasRun = false; @@ -90,12 +96,13 @@ static function (EventFeature\TableGatewayEvent $e) use (&$closureHasRun, &$even ); $this->feature->postInitialize(); - self::assertTrue($closureHasRun); - self::assertInstanceOf(TableGateway::class, $event->getTarget()); - self::assertEquals(EventFeatureEventsInterface::EVENT_POST_INITIALIZE, $event->getName()); + static::assertTrue($closureHasRun); + static::assertInstanceOf(TableGateway::class, $event->getTarget()); + static::assertEquals(EventFeatureEventsInterface::EVENT_POST_INITIALIZE, $event->getName()); } - public function testPostInsert(): void + #[Test] + public function postInsert(): void { $closureHasRun = false; @@ -109,18 +116,19 @@ static function (EventFeature\TableGatewayEvent $e) use (&$closureHasRun, &$even }, ); - $this->feature->postInsert( - $stmt = $this->getMockBuilder(StatementInterface::class)->getMock(), - $result = $this->getMockBuilder(ResultInterface::class)->getMock(), - ); - self::assertTrue($closureHasRun); - self::assertInstanceOf(TableGateway::class, $event->getTarget()); - self::assertEquals(EventFeatureEventsInterface::EVENT_POST_INSERT, $event->getName()); - self::assertSame($stmt, $event->getParam('statement')); - self::assertSame($result, $event->getParam('result')); + $stmt = $this->getMockBuilder(StatementInterface::class)->getMock(); + $result = $this->getMockBuilder(ResultInterface::class)->getMock(); + + $this->feature->postInsert($stmt, $result); + static::assertTrue($closureHasRun); + static::assertInstanceOf(TableGateway::class, $event->getTarget()); + static::assertEquals(EventFeatureEventsInterface::EVENT_POST_INSERT, $event->getName()); + static::assertSame($stmt, $event->getParam('statement')); + static::assertSame($result, $event->getParam('result')); } - public function testPostSelect(): void + #[Test] + public function postSelect(): void { $closureHasRun = false; @@ -134,20 +142,21 @@ static function (EventFeature\TableGatewayEvent $e) use (&$closureHasRun, &$even }, ); - $this->feature->postSelect( - $stmt = $this->getMockBuilder(StatementInterface::class)->getMock(), - $result = $this->getMockBuilder(ResultInterface::class)->getMock(), - $resultset = $this->getMockBuilder(ResultSet::class)->getMock(), - ); - self::assertTrue($closureHasRun); - self::assertInstanceOf(TableGateway::class, $event->getTarget()); - self::assertEquals(EventFeatureEventsInterface::EVENT_POST_SELECT, $event->getName()); - self::assertSame($stmt, $event->getParam('statement')); - self::assertSame($result, $event->getParam('result')); - self::assertSame($resultset, $event->getParam('result_set')); + $stmt = $this->getMockBuilder(StatementInterface::class)->getMock(); + $result = $this->getMockBuilder(ResultInterface::class)->getMock(); + $resultset = $this->getMockBuilder(ResultSet::class)->getMock(); + + $this->feature->postSelect($stmt, $result, $resultset); + static::assertTrue($closureHasRun); + static::assertInstanceOf(TableGateway::class, $event->getTarget()); + static::assertEquals(EventFeatureEventsInterface::EVENT_POST_SELECT, $event->getName()); + static::assertSame($stmt, $event->getParam('statement')); + static::assertSame($result, $event->getParam('result')); + static::assertSame($resultset, $event->getParam('result_set')); } - public function testPostUpdate(): void + #[Test] + public function postUpdate(): void { $closureHasRun = false; @@ -161,18 +170,19 @@ static function (EventFeature\TableGatewayEvent $e) use (&$closureHasRun, &$even }, ); - $this->feature->postUpdate( - $stmt = $this->getMockBuilder(StatementInterface::class)->getMock(), - $result = $this->getMockBuilder(ResultInterface::class)->getMock(), - ); - self::assertTrue($closureHasRun); - self::assertInstanceOf(TableGateway::class, $event->getTarget()); - self::assertEquals(EventFeatureEventsInterface::EVENT_POST_UPDATE, $event->getName()); - self::assertSame($stmt, $event->getParam('statement')); - self::assertSame($result, $event->getParam('result')); + $stmt = $this->getMockBuilder(StatementInterface::class)->getMock(); + $result = $this->getMockBuilder(ResultInterface::class)->getMock(); + + $this->feature->postUpdate($stmt, $result); + static::assertTrue($closureHasRun); + static::assertInstanceOf(TableGateway::class, $event->getTarget()); + static::assertEquals(EventFeatureEventsInterface::EVENT_POST_UPDATE, $event->getName()); + static::assertSame($stmt, $event->getParam('statement')); + static::assertSame($result, $event->getParam('result')); } - public function testPreDelete(): void + #[Test] + public function preDelete(): void { $closureHasRun = false; @@ -186,14 +196,17 @@ static function (EventFeature\TableGatewayEvent $e) use (&$closureHasRun, &$even }, ); - $this->feature->preDelete($delete = $this->getMockBuilder(Delete::class)->getMock()); - self::assertTrue($closureHasRun); - self::assertInstanceOf(TableGateway::class, $event->getTarget()); - self::assertEquals(EventFeatureEventsInterface::EVENT_PRE_DELETE, $event->getName()); - self::assertSame($delete, $event->getParam('delete')); + $delete = $this->getMockBuilder(Delete::class)->getMock(); + + $this->feature->preDelete($delete); + static::assertTrue($closureHasRun); + static::assertInstanceOf(TableGateway::class, $event->getTarget()); + static::assertEquals(EventFeatureEventsInterface::EVENT_PRE_DELETE, $event->getName()); + static::assertSame($delete, $event->getParam('delete')); } - public function testPreInitialize(): void + #[Test] + public function preInitialize(): void { $closureHasRun = false; @@ -208,15 +221,16 @@ static function (EventFeature\TableGatewayEvent $e) use (&$closureHasRun, &$even ); $this->feature->preInitialize(); - self::assertTrue($closureHasRun); - self::assertInstanceOf(TableGateway::class, $event->getTarget()); - self::assertEquals(EventFeatureEventsInterface::EVENT_PRE_INITIALIZE, $event->getName()); + static::assertTrue($closureHasRun); + static::assertInstanceOf(TableGateway::class, $event->getTarget()); + static::assertEquals(EventFeatureEventsInterface::EVENT_PRE_INITIALIZE, $event->getName()); } /** * @throws Exception */ - public function testPreInitializeAddsIdentifiersForCustomTableGatewayClass(): void + #[Test] + public function preInitializeAddsIdentifiersForCustomTableGatewayClass(): void { // Create a custom subclass of TableGateway (using anonymous class) $customTableGateway = new TestTableGateway(); @@ -232,11 +246,12 @@ public function testPreInitializeAddsIdentifiersForCustomTableGatewayClass(): vo $identifiers = $eventManager->getIdentifiers(); // Should contain both TableGateway::class and the anonymous class name - self::assertContains(TableGateway::class, $identifiers); - self::assertContains($customTableGateway::class, $identifiers); + static::assertContains(TableGateway::class, $identifiers); + static::assertContains($customTableGateway::class, $identifiers); } - public function testPreInsert(): void + #[Test] + public function preInsert(): void { $closureHasRun = false; @@ -250,14 +265,17 @@ static function (EventFeature\TableGatewayEvent $e) use (&$closureHasRun, &$even }, ); - $this->feature->preInsert($insert = $this->getMockBuilder(Insert::class)->getMock()); - self::assertTrue($closureHasRun); - self::assertInstanceOf(TableGateway::class, $event->getTarget()); - self::assertEquals(EventFeatureEventsInterface::EVENT_PRE_INSERT, $event->getName()); - self::assertSame($insert, $event->getParam('insert')); + $insert = $this->getMockBuilder(Insert::class)->getMock(); + + $this->feature->preInsert($insert); + static::assertTrue($closureHasRun); + static::assertInstanceOf(TableGateway::class, $event->getTarget()); + static::assertEquals(EventFeatureEventsInterface::EVENT_PRE_INSERT, $event->getName()); + static::assertSame($insert, $event->getParam('insert')); } - public function testPreSelect(): void + #[Test] + public function preSelect(): void { $closureHasRun = false; @@ -271,14 +289,17 @@ static function (EventFeature\TableGatewayEvent $e) use (&$closureHasRun, &$even }, ); - $this->feature->preSelect($select = $this->getMockBuilder(Select::class)->getMock()); - self::assertTrue($closureHasRun); - self::assertInstanceOf(TableGateway::class, $event->getTarget()); - self::assertEquals(EventFeatureEventsInterface::EVENT_PRE_SELECT, $event->getName()); - self::assertSame($select, $event->getParam('select')); + $select = $this->getMockBuilder(Select::class)->getMock(); + + $this->feature->preSelect($select); + static::assertTrue($closureHasRun); + static::assertInstanceOf(TableGateway::class, $event->getTarget()); + static::assertEquals(EventFeatureEventsInterface::EVENT_PRE_SELECT, $event->getName()); + static::assertSame($select, $event->getParam('select')); } - public function testPreUpdate(): void + #[Test] + public function preUpdate(): void { $closureHasRun = false; @@ -292,11 +313,13 @@ static function (EventFeature\TableGatewayEvent $e) use (&$closureHasRun, &$even }, ); - $this->feature->preUpdate($update = $this->getMockBuilder(Update::class)->getMock()); - self::assertTrue($closureHasRun); - self::assertInstanceOf(TableGateway::class, $event->getTarget()); - self::assertEquals(EventFeatureEventsInterface::EVENT_PRE_UPDATE, $event->getName()); - self::assertSame($update, $event->getParam('update')); + $update = $this->getMockBuilder(Update::class)->getMock(); + + $this->feature->preUpdate($update); + static::assertTrue($closureHasRun); + static::assertInstanceOf(TableGateway::class, $event->getTarget()); + static::assertEquals(EventFeatureEventsInterface::EVENT_PRE_UPDATE, $event->getName()); + static::assertSame($update, $event->getParam('update')); } /** diff --git a/test/unit/TableGateway/Feature/FeatureSetTest.php b/test/unit/TableGateway/Feature/FeatureSetTest.php index ac2fd43a..a4cd6c4d 100644 --- a/test/unit/TableGateway/Feature/FeatureSetTest.php +++ b/test/unit/TableGateway/Feature/FeatureSetTest.php @@ -20,6 +20,7 @@ use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\Attributes\RequiresPhp; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\TestCase; @@ -39,7 +40,8 @@ #[CoversMethod(FeatureSet::class, 'callMagicCall')] class FeatureSetTest extends TestCase { - public function testAddFeaturesReturnsFluentInterface(): void + #[Test] + public function addFeaturesReturnsFluentInterface(): void { $feature1 = new SequenceFeature('id', 'seq1'); $feature2 = new SequenceFeature('id', 'seq2'); @@ -47,15 +49,16 @@ public function testAddFeaturesReturnsFluentInterface(): void $featureSet = new FeatureSet(); $result = $featureSet->addFeatures([$feature1, $feature2]); - self::assertSame($featureSet, $result); + static::assertSame($featureSet, $result); } /** * @cover FeatureSet::addFeature * @throws Exception */ + #[Test] #[Group('Laminas-4993')] - public function testAddFeatureThatFeatureDoesNotHaveTableGatewayButFeatureSetHas(): void + public function addFeatureThatFeatureDoesNotHaveTableGatewayButFeatureSetHas(): void { $mockMasterAdapter = $this->getMockBuilder(AdapterInterface::class)->getMock(); @@ -80,15 +83,16 @@ public function testAddFeatureThatFeatureDoesNotHaveTableGatewayButFeatureSetHas $featureSet = new FeatureSet(); $featureSet->setTableGateway($tableGatewayMock); - self::assertInstanceOf(FeatureSet::class, $featureSet->addFeature($feature)); + static::assertInstanceOf(FeatureSet::class, $featureSet->addFeature($feature)); } /** * @cover FeatureSet::addFeature * @throws Exception */ + #[Test] #[Group('Laminas-4993')] - public function testAddFeatureThatFeatureHasTableGatewayButFeatureSetDoesNotHave(): void + public function addFeatureThatFeatureHasTableGatewayButFeatureSetDoesNotHave(): void { $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class)->onlyMethods([])->getMock(); @@ -105,22 +109,24 @@ public function testAddFeatureThatFeatureHasTableGatewayButFeatureSetDoesNotHave $feature->setTableGateway($tableGatewayMock); $featureSet = new FeatureSet(); - self::assertInstanceOf(FeatureSet::class, $featureSet->addFeature($feature)); + static::assertInstanceOf(FeatureSet::class, $featureSet->addFeature($feature)); } - public function testApplyCallsAllFeaturesWhenNoHalt(): void + #[Test] + public function applyCallsAllFeaturesWhenNoHalt(): void { $feature1 = new TestTableGatewayFeature(); $feature2 = new TestTableGatewayFeature(); $featureSet = new FeatureSet([$feature1, $feature2]); - $featureSet->apply('testMethod', []); + $featureSet->apply('recordCall', []); - self::assertTrue($feature1->called); - self::assertTrue($feature2->called); + static::assertTrue($feature1->called); + static::assertTrue($feature2->called); } - public function testApplyCallsMethodOnFeatures(): void + #[Test] + public function applyCallsMethodOnFeatures(): void { $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class) ->disableOriginalConstructor() @@ -136,10 +142,11 @@ public function testApplyCallsMethodOnFeatures(): void $featureSet->apply('preSelect', []); /** @phpstan-ignore staticMethod.alreadyNarrowedType */ - self::assertTrue(true); + static::assertTrue(true); } - public function testApplyHaltsWhenFeatureReturnsHalt(): void + #[Test] + public function applyHaltsWhenFeatureReturnsHalt(): void { $feature1 = new TestTableGatewayFeature(); $feature1->returnValue = FeatureSet::APPLY_HALT; @@ -147,23 +154,25 @@ public function testApplyHaltsWhenFeatureReturnsHalt(): void $feature2 = new TestTableGatewayFeature(); $featureSet = new FeatureSet([$feature1, $feature2]); - $featureSet->apply('testMethod', []); + $featureSet->apply('recordCall', []); - self::assertTrue($feature1->called); - self::assertFalse($feature2->called); + static::assertTrue($feature1->called); + static::assertFalse($feature2->called); } - public function testApplyPassesArgumentsToFeatures(): void + #[Test] + public function applyPassesArgumentsToFeatures(): void { $feature = new TestTableGatewayFeature(); $featureSet = new FeatureSet([$feature]); - $featureSet->apply('testMethod', ['test value']); + $featureSet->apply('recordCall', ['test value']); - self::assertEquals(['test value'], $feature->receivedArgs); + static::assertEquals(['test value'], $feature->receivedArgs); } - public function testApplySkipsFeatureWithoutMethod(): void + #[Test] + public function applySkipsFeatureWithoutMethod(): void { $feature = new SequenceFeature('id', 'table_sequence'); $featureSet = new FeatureSet([$feature]); @@ -171,17 +180,19 @@ public function testApplySkipsFeatureWithoutMethod(): void $featureSet->apply('nonExistentMethod', []); /** @phpstan-ignore staticMethod.alreadyNarrowedType */ - self::assertTrue(true); + static::assertTrue(true); } - public function testCallMagicCallReturnsNullWhenNoFeatureHasMethod(): void + #[Test] + public function callMagicCallReturnsNullWhenNoFeatureHasMethod(): void { $featureSet = new FeatureSet(); - self::assertNull($featureSet->callMagicCall('nonExistentMethod', [])); + static::assertNull($featureSet->callMagicCall('nonExistentMethod', [])); } - public function testCallMagicCallSucceedsForValidMethodOfAddedFeature(): void + #[Test] + public function callMagicCallSucceedsForValidMethodOfAddedFeature(): void { $feature = new TestTableGatewayFeature(); @@ -190,87 +201,108 @@ public function testCallMagicCallSucceedsForValidMethodOfAddedFeature(): void $result = $featureSet->callMagicCall('customMethod', ['test_value']); - self::assertEquals('result: test_value', $result); + static::assertSame('result: test_value', $result); } - public function testCallMagicGetReturnsNull(): void + #[Test] + public function callMagicGetReturnsNull(): void { $featureSet = new FeatureSet(); - self::assertNull($featureSet->callMagicGet('property')); + static::assertNull($featureSet->callMagicGet('property')); } - public function testCallMagicSetReturnsNull(): void + #[Test] + public function callMagicSetReturnsNull(): void { $featureSet = new FeatureSet(); - self::assertNull($featureSet->callMagicSet('property', 'value')); + static::assertNull($featureSet->callMagicSet('property', 'value')); } - public function testCanCallMagicCallReturnsFalseForAddedMethodOfAddedFeature(): void + #[Test] + public function canCallMagicCallReturnsFalseForAddedMethodOfAddedFeature(): void { $feature = new SequenceFeature('id', 'table_sequence'); $featureSet = new FeatureSet(); $featureSet->addFeature($feature); - self::assertFalse( + static::assertFalse( $featureSet->canCallMagicCall('postInitialize'), 'Should have been able to call postInitialize from the MetaData Feature', ); } - public function testCanCallMagicCallReturnsFalseWhenNoFeaturesHaveBeenAdded(): void + #[Test] + public function canCallMagicCallReturnsFalseWhenNoFeaturesHaveBeenAdded(): void { $featureSet = new FeatureSet(); - self::assertFalse( + static::assertFalse( $featureSet->canCallMagicCall('lastSequenceId'), ); } - public function testCanCallMagicCallReturnsTrueForAddedMethodOfAddedFeature(): void + #[Test] + public function canCallMagicCallReturnsTrueForAddedMethodOfAddedFeature(): void { $feature = new SequenceFeature('id', 'table_sequence'); $featureSet = new FeatureSet(); $featureSet->addFeature($feature); - self::assertTrue( + static::assertTrue( $featureSet->canCallMagicCall('lastSequenceId'), 'Should have been able to call lastSequenceId from the Sequence Feature', ); } - public function testCanCallMagicGetReturnsFalse(): void + #[Test] + public function canCallMagicGetReturnsFalse(): void { $featureSet = new FeatureSet(); - self::assertFalse($featureSet->canCallMagicGet('property')); + static::assertFalse($featureSet->canCallMagicGet('property')); } - public function testCanCallMagicSetReturnsFalse(): void + #[Test] + public function canCallMagicSetReturnsFalse(): void { $featureSet = new FeatureSet(); - self::assertFalse($featureSet->canCallMagicSet('property')); + static::assertFalse($featureSet->canCallMagicSet('property')); } - public function testConstructorWithFeatures(): void + #[Test] + public function constructorWithFeatures(): void { $feature = new SequenceFeature('id', 'table_sequence'); $featureSet = new FeatureSet([$feature]); - self::assertSame($feature, $featureSet->getFeatureByClassName(SequenceFeature::class)); + static::assertSame($feature, $featureSet->getFeatureByClassName(SequenceFeature::class)); } - public function testGetFeatureByClassNameReturnsNullWhenNotFound(): void + #[Test] + public function getFeatureByClassNameReturnsNullWhenNotFound(): void { $featureSet = new FeatureSet(); $result = $featureSet->getFeatureByClassName(SequenceFeature::class); - self::assertNull($result); + static::assertNull($result); } - public function testSetTableGateway(): void + #[Test] + public function getFeatureByClassNameSkipsFeaturesOfAnotherClass(): void + { + $other = new TestTableGatewayFeature(); + $wanted = new SequenceFeature('id', 'table_sequence'); + + $featureSet = new FeatureSet([$other, $wanted]); + + static::assertSame($wanted, $featureSet->getFeatureByClassName(SequenceFeature::class)); + } + + #[Test] + public function setTableGateway(): void { $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class) ->disableOriginalConstructor() @@ -281,6 +313,6 @@ public function testSetTableGateway(): void $result = $featureSet->setTableGateway($tableGatewayMock); - self::assertSame($featureSet, $result); + static::assertSame($featureSet, $result); } } diff --git a/test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php b/test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php index de9b5306..c3ff8252 100644 --- a/test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php +++ b/test/unit/TableGateway/Feature/GlobalAdapterFeatureTest.php @@ -9,13 +9,15 @@ use PhpDb\TableGateway\Exception\RuntimeException; use PhpDb\TableGateway\Feature\GlobalAdapterFeature; use PhpDbTest\TableGateway\Feature\TestAsset\TestGlobalAdapterFeatureSubclass; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use ReflectionProperty; class GlobalAdapterFeatureTest extends TestCase { - public function testGetStaticAdapterReturnsDefaultAdapterWhenClassSpecificNotSet(): void + #[Test] + public function getStaticAdapterReturnsDefaultAdapterWhenClassSpecificNotSet(): void { $adapter = $this->createMock(AdapterInterface::class); @@ -25,10 +27,11 @@ public function testGetStaticAdapterReturnsDefaultAdapterWhenClassSpecificNotSet // Get adapter should return the default adapter $result = GlobalAdapterFeature::getStaticAdapter(); - self::assertSame($adapter, $result); + static::assertSame($adapter, $result); } - public function testGetStaticAdapterThrowsExceptionWhenNoAdapterSet(): void + #[Test] + public function getStaticAdapterThrowsExceptionWhenNoAdapterSet(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('No database adapter was found in the static registry.'); @@ -36,7 +39,8 @@ public function testGetStaticAdapterThrowsExceptionWhenNoAdapterSet(): void GlobalAdapterFeature::getStaticAdapter(); } - public function testPreInitializeSetsAdapterOnTableGateway(): void + #[Test] + public function preInitializeSetsAdapterOnTableGateway(): void { $adapter = $this->createMock(AdapterInterface::class); GlobalAdapterFeature::setStaticAdapter($adapter); @@ -55,20 +59,22 @@ public function testPreInitializeSetsAdapterOnTableGateway(): void $reflection = new ReflectionProperty(AbstractTableGateway::class, 'adapter'); $result = $reflection->getValue($tableGatewayMock); - self::assertSame($adapter, $result); + static::assertSame($adapter, $result); } - public function testSetStaticAdapter(): void + #[Test] + public function setStaticAdapter(): void { $adapter = $this->createMock(AdapterInterface::class); GlobalAdapterFeature::setStaticAdapter($adapter); $result = GlobalAdapterFeature::getStaticAdapter(); - self::assertSame($adapter, $result); + static::assertSame($adapter, $result); } - public function testSubclassCanSetAndGetOwnAdapter(): void + #[Test] + public function subclassCanSetAndGetOwnAdapter(): void { $baseAdapter = $this->createMock(AdapterInterface::class); $subclassAdapter = $this->createMock(AdapterInterface::class); @@ -80,13 +86,14 @@ public function testSubclassCanSetAndGetOwnAdapter(): void TestGlobalAdapterFeatureSubclass::setStaticAdapter($subclassAdapter); // Base class should return base adapter - self::assertSame($baseAdapter, GlobalAdapterFeature::getStaticAdapter()); + static::assertSame($baseAdapter, GlobalAdapterFeature::getStaticAdapter()); // Subclass should return its own adapter - self::assertSame($subclassAdapter, TestGlobalAdapterFeatureSubclass::getStaticAdapter()); + static::assertSame($subclassAdapter, TestGlobalAdapterFeatureSubclass::getStaticAdapter()); } - public function testSubclassFallsBackToDefaultAdapterWhenNoSpecificAdapterSet(): void + #[Test] + public function subclassFallsBackToDefaultAdapterWhenNoSpecificAdapterSet(): void { $defaultAdapter = $this->createMock(AdapterInterface::class); @@ -96,10 +103,11 @@ public function testSubclassFallsBackToDefaultAdapterWhenNoSpecificAdapterSet(): // Subclass should fall back to default adapter $result = TestGlobalAdapterFeatureSubclass::getStaticAdapter(); - self::assertSame($defaultAdapter, $result); + static::assertSame($defaultAdapter, $result); } - public function testSubclassThrowsExceptionWhenNoAdaptersSet(): void + #[Test] + public function subclassThrowsExceptionWhenNoAdaptersSet(): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('No database adapter was found in the static registry.'); diff --git a/test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php b/test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php index efc845a9..3bf2103a 100644 --- a/test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php +++ b/test/unit/TableGateway/Feature/MasterSlaveFeatureTest.php @@ -11,8 +11,11 @@ use PhpDb\Adapter\Driver\StatementInterface; use PhpDb\Adapter\Platform\Sql92; use PhpDb\Sql\Sql; +use PhpDb\TableGateway\AbstractTableGateway; +use PhpDb\TableGateway\Exception\RuntimeException; use PhpDb\TableGateway\Feature\MasterSlaveFeature; use PhpDb\TableGateway\TableGateway; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -28,36 +31,59 @@ final class MasterSlaveFeatureTest extends TestCase /** * @throws Exception */ - public function testConstructorWithSlaveSql(): void + #[Test] + public function constructorWithSlaveSql(): void { $slaveSql = new Sql($this->mockSlaveAdapter, 'foo'); $feature = new MasterSlaveFeature($this->mockSlaveAdapter, $slaveSql); - self::assertSame($slaveSql, $feature->getSlaveSql()); + static::assertSame($slaveSql, $feature->getSlaveSql()); } - public function testGetSlaveAdapter(): void + #[Test] + public function getSlaveAdapter(): void { - self::assertSame($this->mockSlaveAdapter, $this->feature->getSlaveAdapter()); + static::assertSame($this->mockSlaveAdapter, $this->feature->getSlaveAdapter()); } /** * @throws Exception */ - public function testPostInitialize(): void + #[Test] + public function postInitialize(): void { $this->getMockBuilder(TableGateway::class) ->setConstructorArgs(['foo', $this->mockMasterAdapter, $this->feature]) ->onlyMethods([]) ->getMock(); // postInitialize is run - self::assertSame($this->mockSlaveAdapter, $this->feature->getSlaveSql()->getAdapter()); + static::assertSame($this->mockSlaveAdapter, $this->feature->getSlaveSql()->getAdapter()); } /** * @throws Exception */ - public function testPostInitializeWithProvidedSlaveSql(): void + #[Test] + public function postInitializeThrowsWhenTableGatewayHasNoSql(): void + { + $tableGateway = $this->getMockBuilder(AbstractTableGateway::class)->onlyMethods([])->getMock(); + + $feature = new MasterSlaveFeature($this->mockSlaveAdapter); + $feature->setTableGateway($tableGateway); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage( + 'The table gateway must be initialized with a Sql instance before this feature is applied.', + ); + + $feature->postInitialize(); + } + + /** + * @throws Exception + */ + #[Test] + public function postInitializeWithProvidedSlaveSql(): void { $slaveSql = new Sql($this->mockSlaveAdapter, 'foo'); $feature = new MasterSlaveFeature($this->mockSlaveAdapter, $slaveSql); @@ -68,13 +94,14 @@ public function testPostInitializeWithProvidedSlaveSql(): void ->getMock(); // The provided slaveSql should be used instead of creating a new one - self::assertSame($slaveSql, $feature->getSlaveSql()); + static::assertSame($slaveSql, $feature->getSlaveSql()); } /** * @throws Exception */ - public function testPostSelect(): void + #[Test] + public function postSelect(): void { $table = $this->getMockBuilder(TableGateway::class) ->setConstructorArgs(['foo', $this->mockMasterAdapter, $this->feature]) @@ -98,13 +125,25 @@ public function testPostSelect(): void $table->select('foo = bar'); // test that the sql object is restored - self::assertSame($masterSql, $table->getSql()); + static::assertSame($masterSql, $table->getSql()); + } + + #[Test] + public function postSelectThrowsWhenPostInitializeHasNotRun(): void + { + $feature = new MasterSlaveFeature($this->mockSlaveAdapter); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The master Sql instance is not available; postInitialize() has not been run.'); + + $feature->postSelect(); } /** * @throws Exception */ - public function testPreSelect(): void + #[Test] + public function preSelect(): void { $this->expectNotToPerformAssertions(); diff --git a/test/unit/TableGateway/Feature/MetadataFeatureTest.php b/test/unit/TableGateway/Feature/MetadataFeatureTest.php index 71443c45..01ecad78 100644 --- a/test/unit/TableGateway/Feature/MetadataFeatureTest.php +++ b/test/unit/TableGateway/Feature/MetadataFeatureTest.php @@ -14,6 +14,7 @@ use PhpDb\TableGateway\Feature\MetadataFeature; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\Attributes\RequiresPhp; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -23,7 +24,8 @@ #[RequiresPhp('<= 8.6')] class MetadataFeatureTest extends TestCase { - public function testConstructorSetsInitialSharedData(): void + #[Test] + public function constructorSetsInitialSharedData(): void { $metadataMock = $this->getMockBuilder(MetadataInterface::class)->getMock(); $feature = new MetadataFeature($metadataMock); @@ -31,17 +33,18 @@ public function testConstructorSetsInitialSharedData(): void $r = new ReflectionProperty(MetadataFeature::class, 'sharedData'); $sharedData = $r->getValue($feature); - self::assertIsArray($sharedData); - self::assertArrayHasKey('metadata', $sharedData); - self::assertNull($sharedData['metadata']['primaryKey']); - self::assertEquals([], $sharedData['metadata']['columns']); + static::assertIsArray($sharedData); + static::assertArrayHasKey('metadata', $sharedData); + static::assertNull($sharedData['metadata']['primaryKey']); + static::assertEquals([], $sharedData['metadata']['columns']); } /** * @throws Exception * @throws \Exception */ - public function testPostInitializeRecordsListOfColumnsInPrimaryKeyToSharedMetadata(): void + #[Test] + public function postInitializeRecordsListOfColumnsInPrimaryKeyToSharedMetadata(): void { /** @var AbstractTableGateway&MockObject $tableGatewayMock */ $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class)->onlyMethods([])->getMock(); @@ -69,12 +72,11 @@ public function testPostInitializeRecordsListOfColumnsInPrimaryKeyToSharedMetada $r = new ReflectionProperty(MetadataFeature::class, 'sharedData'); $sharedData = $r->getValue($feature); - self::assertIsArray($sharedData); - self::assertTrue( - isset($sharedData['metadata']['primaryKey']), - 'Shared data must have metadata entry for primary key', - ); - self::assertEquals(['composite', 'id'], $sharedData['metadata']['primaryKey']); + static::assertIsArray($sharedData); + static::assertArrayHasKey('metadata', $sharedData); + static::assertIsArray($sharedData['metadata']); + static::assertArrayHasKey('primaryKey', $sharedData['metadata']); + static::assertEquals(['composite', 'id'], $sharedData['metadata']['primaryKey']); } /** @@ -85,7 +87,8 @@ public function testPostInitializeRecordsListOfColumnsInPrimaryKeyToSharedMetada * @throws Exception * @throws \Exception */ - public function testPostInitializeRecordsPrimaryKeyColumnToSharedMetadata(): void + #[Test] + public function postInitializeRecordsPrimaryKeyColumnToSharedMetadata(): void { /** @var AbstractTableGateway&MockObject $tableGatewayMock */ $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class)->onlyMethods([])->getMock(); @@ -113,19 +116,62 @@ public function testPostInitializeRecordsPrimaryKeyColumnToSharedMetadata(): voi $r = new ReflectionProperty(MetadataFeature::class, 'sharedData'); $sharedData = $r->getValue($feature); - self::assertIsArray($sharedData); - self::assertTrue( - isset($sharedData['metadata']['primaryKey']), - 'Shared data must have metadata entry for primary key', - ); - self::assertSame('id', $sharedData['metadata']['primaryKey']); + static::assertIsArray($sharedData); + static::assertArrayHasKey('metadata', $sharedData); + static::assertIsArray($sharedData['metadata']); + static::assertArrayHasKey('primaryKey', $sharedData['metadata']); + static::assertSame('id', $sharedData['metadata']['primaryKey']); + } + + /** + * @throws Exception + * @throws \Exception + */ + #[Test] + public function postInitializeSkipsConstraintsThatAreNotPrimaryKeys(): void + { + /** @var AbstractTableGateway&MockObject $tableGatewayMock */ + $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class)->onlyMethods([])->getMock(); + + $tableProperty = new ReflectionProperty(AbstractTableGateway::class, 'table'); + $tableProperty->setValue($tableGatewayMock, 'foo'); + + $metadataMock = $this->getMockBuilder(MetadataInterface::class)->getMock(); + $metadataMock->expects($this->any())->method('getColumnNames')->willReturn(['id', 'name']); + $metadataMock->expects($this->any()) + ->method('getTable') + ->willReturn(new TableObject('foo')); + + $unique = new ConstraintObject('name_unique', 'foo'); + $unique->setColumns(['name']); + $unique->setType('UNIQUE'); + + $primary = new ConstraintObject('id_pk', 'foo'); + $primary->setColumns(['id']); + $primary->setType('PRIMARY KEY'); + + $metadataMock->expects($this->any())->method('getConstraints')->willReturn([$unique, $primary]); + + $feature = new MetadataFeature($metadataMock); + $feature->setTableGateway($tableGatewayMock); + $feature->postInitialize(); + + $r = new ReflectionProperty(MetadataFeature::class, 'sharedData'); + $sharedData = $r->getValue($feature); + + static::assertIsArray($sharedData); + static::assertArrayHasKey('metadata', $sharedData); + static::assertIsArray($sharedData['metadata']); + static::assertArrayHasKey('primaryKey', $sharedData['metadata']); + static::assertSame('id', $sharedData['metadata']['primaryKey']); } /** * @throws Exception * @throws \Exception */ - public function testPostInitializeSkipsPrimaryKeyCheckIfNotTable(): void + #[Test] + public function postInitializeSkipsPrimaryKeyCheckIfNotTable(): void { /** @var AbstractTableGateway&MockObject $tableGatewayMock */ $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class)->onlyMethods([])->getMock(); @@ -151,7 +197,8 @@ public function testPostInitializeSkipsPrimaryKeyCheckIfNotTable(): void * @throws Exception * @throws \Exception */ - public function testPostInitializeThrowsExceptionWhenNoPrimaryKeyFound(): void + #[Test] + public function postInitializeThrowsExceptionWhenNoPrimaryKeyFound(): void { /** @var AbstractTableGateway&MockObject $tableGatewayMock */ $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class)->onlyMethods([])->getMock(); @@ -178,11 +225,35 @@ public function testPostInitializeThrowsExceptionWhenNoPrimaryKeyFound(): void $feature->postInitialize(); } + /** + * @throws Exception + */ + #[Test] + public function postInitializeThrowsWhenTableIsNotNamed(): void + { + $metadataMock = $this->getMockBuilder(MetadataInterface::class)->getMock(); + $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class)->onlyMethods([])->getMock(); + + $tableProperty = new ReflectionProperty(AbstractTableGateway::class, 'table'); + $tableProperty->setValue($tableGatewayMock, [42]); + + $feature = new MetadataFeature($metadataMock); + $feature->setTableGateway($tableGatewayMock); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage( + 'The table gateway must reference a named table before metadata can be resolved.', + ); + + $feature->postInitialize(); + } + /** * @throws Exception * @throws \Exception */ - public function testPostInitializeWithArrayTable(): void + #[Test] + public function postInitializeWithArrayTable(): void { /** @var AbstractTableGateway&MockObject $tableGatewayMock */ $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class)->onlyMethods([])->getMock(); @@ -213,14 +284,15 @@ public function testPostInitializeWithArrayTable(): void $r = new ReflectionProperty(MetadataFeature::class, 'sharedData'); $sharedData = $r->getValue($feature); - self::assertSame('id', $sharedData['metadata']['primaryKey']); + static::assertSame('id', $sharedData['metadata']['primaryKey']); } /** * @throws Exception * @throws \Exception */ - public function testPostInitializeWithTableIdentifier(): void + #[Test] + public function postInitializeWithTableIdentifier(): void { /** @var AbstractTableGateway&MockObject $tableGatewayMock */ $tableGatewayMock = $this->getMockBuilder(AbstractTableGateway::class)->onlyMethods([])->getMock(); @@ -256,6 +328,6 @@ public function testPostInitializeWithTableIdentifier(): void $r = new ReflectionProperty(MetadataFeature::class, 'sharedData'); $sharedData = $r->getValue($feature); - self::assertSame('id', $sharedData['metadata']['primaryKey']); + static::assertSame('id', $sharedData['metadata']['primaryKey']); } } diff --git a/test/unit/TableGateway/Feature/RowGatewayFeatureTest.php b/test/unit/TableGateway/Feature/RowGatewayFeatureTest.php index d7f690a1..3bbc8234 100644 --- a/test/unit/TableGateway/Feature/RowGatewayFeatureTest.php +++ b/test/unit/TableGateway/Feature/RowGatewayFeatureTest.php @@ -13,13 +13,15 @@ use PhpDb\TableGateway\Feature\FeatureSet; use PhpDb\TableGateway\Feature\MetadataFeature; use PhpDb\TableGateway\Feature\RowGatewayFeature; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use ReflectionProperty; class RowGatewayFeatureTest extends TestCase { - public function testConstructorStoresArguments(): void + #[Test] + public function constructorStoresArguments(): void { $feature = new RowGatewayFeature('id'); @@ -27,10 +29,11 @@ public function testConstructorStoresArguments(): void $property = new ReflectionProperty(RowGatewayFeature::class, 'constructorArguments'); $args = $property->getValue($feature); - self::assertEquals(['id'], $args); + static::assertEquals(['id'], $args); } - public function testConstructorStoresRowGatewayInstance(): void + #[Test] + public function constructorStoresRowGatewayInstance(): void { /** @var RowGatewayInterface&MockObject $rowGateway */ $rowGateway = $this->createMock(RowGatewayInterface::class); @@ -41,10 +44,11 @@ public function testConstructorStoresRowGatewayInstance(): void $property = new ReflectionProperty(RowGatewayFeature::class, 'constructorArguments'); $args = $property->getValue($feature); - self::assertSame($rowGateway, $args[0]); + static::assertSame($rowGateway, $args[0]); } - public function testConstructorWithNoArguments(): void + #[Test] + public function constructorWithNoArguments(): void { $feature = new RowGatewayFeature(); @@ -52,10 +56,26 @@ public function testConstructorWithNoArguments(): void $property = new ReflectionProperty(RowGatewayFeature::class, 'constructorArguments'); $args = $property->getValue($feature); - self::assertEquals([], $args); + static::assertEquals([], $args); } - public function testPostInitializeThrowsExceptionForNonResultSet(): void + #[Test] + public function postInitializeIgnoresAnArgumentThatIsNeitherStringNorRowGateway(): void + { + $resultSet = $this->createInitialResultSet(); + $tableGateway = $this->createTableGatewayMock($resultSet); + $original = $resultSet->getRowPrototype(); + + $feature = new RowGatewayFeature(42); + $feature->setTableGateway($tableGateway); + + $feature->postInitialize(); + + static::assertSame($original, $resultSet->getRowPrototype()); + } + + #[Test] + public function postInitializeThrowsExceptionForNonResultSet(): void { $resultSet = $this->createMock(ResultSetInterface::class); $tableGateway = $this->createTableGatewayMock($resultSet); @@ -69,7 +89,8 @@ public function testPostInitializeThrowsExceptionForNonResultSet(): void $feature->postInitialize(); } - public function testPostInitializeThrowsExceptionWhenMetadataHasNoMetadataKey(): void + #[Test] + public function postInitializeThrowsExceptionWhenMetadataHasNoMetadataKey(): void { $resultSet = $this->createInitialResultSet(); @@ -99,7 +120,8 @@ public function testPostInitializeThrowsExceptionWhenMetadataHasNoMetadataKey(): $feature->postInitialize(); } - public function testPostInitializeThrowsExceptionWhenNoMetadataAndNoPrimaryKey(): void + #[Test] + public function postInitializeThrowsExceptionWhenNoMetadataAndNoPrimaryKey(): void { $resultSet = $this->createInitialResultSet(); @@ -120,7 +142,104 @@ public function testPostInitializeThrowsExceptionWhenNoMetadataAndNoPrimaryKey() $feature->postInitialize(); } - public function testPostInitializeWithMetadataFeature(): void + #[Test] + public function postInitializeThrowsWhenMetadataIsNotAnArray(): void + { + $resultSet = $this->createInitialResultSet(); + + $metadataFeature = $this->getMockBuilder(MetadataFeature::class) + ->disableOriginalConstructor() + ->getMock(); + + $sharedDataProperty = new ReflectionProperty(MetadataFeature::class, 'sharedData'); + $sharedDataProperty->setValue($metadataFeature, ['metadata' => 'not-an-array']); + + $featureSet = $this->createMock(FeatureSet::class); + $featureSet->expects($this->once()) + ->method('getFeatureByClassName') + ->with(MetadataFeature::class) + ->willReturn($metadataFeature); + + $feature = new RowGatewayFeature(); + $feature->setTableGateway($this->createTableGatewayMock($resultSet, $featureSet)); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The MetadataFeature did not expose its metadata as an array.'); + + $feature->postInitialize(); + } + + #[Test] + public function postInitializeThrowsWhenMetadataPrimaryKeyIsUnusable(): void + { + $resultSet = $this->createInitialResultSet(); + + $metadataFeature = $this->getMockBuilder(MetadataFeature::class) + ->disableOriginalConstructor() + ->getMock(); + + $sharedDataProperty = new ReflectionProperty(MetadataFeature::class, 'sharedData'); + $sharedDataProperty->setValue($metadataFeature, [ + 'metadata' => ['primaryKey' => 42], + ]); + + $featureSet = $this->createMock(FeatureSet::class); + $featureSet->expects($this->once()) + ->method('getFeatureByClassName') + ->with(MetadataFeature::class) + ->willReturn($metadataFeature); + + $feature = new RowGatewayFeature(); + $feature->setTableGateway($this->createTableGatewayMock($resultSet, $featureSet)); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage( + 'The MetadataFeature did not expose a usable primary key for RowGateway object creation.', + ); + + $feature->postInitialize(); + } + + #[Test] + public function postInitializeThrowsWhenTableGatewayHasNoFeatureSet(): void + { + $resultSet = $this->createInitialResultSet(); + $tableGateway = $this->createTableGatewayMock($resultSet); + + $featureSetProperty = new ReflectionProperty(AbstractTableGateway::class, 'featureSet'); + $featureSetProperty->setValue($tableGateway, null); + + $feature = new RowGatewayFeature(); + $feature->setTableGateway($tableGateway); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('No information was provided to the RowGatewayFeature'); + + $feature->postInitialize(); + } + + #[Test] + public function postInitializeThrowsWhenTableIsNotNamed(): void + { + $resultSet = $this->createInitialResultSet(); + $tableGateway = $this->createTableGatewayMock($resultSet); + + $tableProperty = new ReflectionProperty(AbstractTableGateway::class, 'table'); + $tableProperty->setValue($tableGateway, [42]); + + $feature = new RowGatewayFeature('id'); + $feature->setTableGateway($tableGateway); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage( + 'The table gateway must reference a named table before a RowGateway prototype can be created.', + ); + + $feature->postInitialize(); + } + + #[Test] + public function postInitializeWithMetadataFeature(): void { $resultSet = $this->createInitialResultSet(); @@ -149,10 +268,11 @@ public function testPostInitializeWithMetadataFeature(): void $feature->postInitialize(); $prototype = $resultSet->getRowPrototype(); - self::assertInstanceOf(RowGatewayInterface::class, $prototype); + static::assertInstanceOf(RowGatewayInterface::class, $prototype); } - public function testPostInitializeWithRowGatewayInstance(): void + #[Test] + public function postInitializeWithRowGatewayInstance(): void { $resultSet = $this->createInitialResultSet(); @@ -166,10 +286,11 @@ public function testPostInitializeWithRowGatewayInstance(): void $feature->postInitialize(); - self::assertSame($rowGateway, $resultSet->getRowPrototype()); + static::assertSame($rowGateway, $resultSet->getRowPrototype()); } - public function testPostInitializeWithStringPrimaryKey(): void + #[Test] + public function postInitializeWithStringPrimaryKey(): void { $resultSet = $this->createInitialResultSet(); $tableGateway = $this->createTableGatewayMock($resultSet); @@ -180,7 +301,7 @@ public function testPostInitializeWithStringPrimaryKey(): void $feature->postInitialize(); $prototype = $resultSet->getRowPrototype(); - self::assertInstanceOf(RowGatewayInterface::class, $prototype); + static::assertInstanceOf(RowGatewayInterface::class, $prototype); } /** @@ -211,7 +332,7 @@ private function createTableGatewayMock( $resultSetProperty = new ReflectionProperty(AbstractTableGateway::class, 'resultSetPrototype'); $resultSetProperty->setValue($tableGateway, $resultSetPrototype); - if ($featureSet !== null) { + if (null !== $featureSet) { $featureSetProperty = new ReflectionProperty(AbstractTableGateway::class, 'featureSet'); $featureSetProperty->setValue($tableGateway, $featureSet); } diff --git a/test/unit/TableGateway/Feature/SequenceFeatureTest.php b/test/unit/TableGateway/Feature/SequenceFeatureTest.php index b90262fd..538b1b84 100644 --- a/test/unit/TableGateway/Feature/SequenceFeatureTest.php +++ b/test/unit/TableGateway/Feature/SequenceFeatureTest.php @@ -15,6 +15,7 @@ use PhpDb\TableGateway\Feature\SequenceFeature; use PhpDb\TableGateway\TableGateway; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -50,18 +51,20 @@ public static function nextSequenceIdProvider(): array ]; } + #[Test] #[DataProvider('lastSequenceIdProvider')] - public function testLastSequenceId(string $platformName): void + public function lastSequenceId(string $platformName): void { $tableGateway = $this->createTableGatewayWithPlatform($platformName, 55); $this->feature->setTableGateway($tableGateway); $result = $this->feature->lastSequenceId(); - self::assertEquals(55, $result); + static::assertSame(55, $result); } - public function testLastSequenceIdThrowsExceptionForUnsupportedPlatform(): void + #[Test] + public function lastSequenceIdThrowsExceptionForUnsupportedPlatform(): void { $tableGateway = $this->createTableGatewayWithPlatform('MySQL'); $this->feature->setTableGateway($tableGateway); @@ -72,11 +75,34 @@ public function testLastSequenceIdThrowsExceptionForUnsupportedPlatform(): void $this->feature->lastSequenceId(); } + #[Test] + public function lastSequenceIdThrowsWhenSequenceHasNoCurrentValue(): void + { + $this->feature->setTableGateway($this->createTableGatewayReturning('Oracle', [])); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The sequence did not return a current value.'); + + $this->feature->lastSequenceId(); + } + + #[Test] + public function lastSequenceIdThrowsWhenStatementProducesNoResult(): void + { + $this->feature->setTableGateway($this->createTableGatewayReturning('Oracle', null)); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The sequence statement did not produce a result.'); + + $this->feature->lastSequenceId(); + } + /** * @throws Exception */ + #[Test] #[DataProvider('nextSequenceIdProvider')] - public function testNextSequenceId(string $platformName, string $statementSql): void + public function nextSequenceId(string $platformName, string $statementSql): void { $platform = $this->createMock(PlatformInterface::class); $platform->expects($this->any()) @@ -114,7 +140,16 @@ public function testNextSequenceId(string $platformName, string $statementSql): $this->feature->nextSequenceId(); } - public function testNextSequenceIdThrowsExceptionForUnsupportedPlatform(): void + #[Test] + public function nextSequenceIdReturnsNullWhenSequenceValueIsNotAnInteger(): void + { + $this->feature->setTableGateway($this->createTableGatewayReturning('Oracle', ['nextval' => 'abc'])); + + static::assertNull($this->feature->nextSequenceId()); + } + + #[Test] + public function nextSequenceIdThrowsExceptionForUnsupportedPlatform(): void { $tableGateway = $this->createTableGatewayWithPlatform('MySQL'); $this->feature->setTableGateway($tableGateway); @@ -125,7 +160,30 @@ public function testNextSequenceIdThrowsExceptionForUnsupportedPlatform(): void $this->feature->nextSequenceId(); } - public function testPostInsertDoesNotSetLastInsertValueWhenSequenceValueIsNull(): void + #[Test] + public function nextSequenceIdThrowsWhenSequenceReturnsNoRow(): void + { + $this->feature->setTableGateway($this->createTableGatewayReturning('Oracle', 'not-an-array')); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The sequence did not return a next value.'); + + $this->feature->nextSequenceId(); + } + + #[Test] + public function nextSequenceIdThrowsWhenStatementProducesNoResult(): void + { + $this->feature->setTableGateway($this->createTableGatewayReturning('Oracle', null)); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The sequence statement did not produce a result.'); + + $this->feature->nextSequenceId(); + } + + #[Test] + public function postInsertDoesNotSetLastInsertValueWhenSequenceValueIsNull(): void { $tableGateway = $this->createTableGatewayWithPlatform('PostgreSQL'); $this->feature->setTableGateway($tableGateway); @@ -138,10 +196,11 @@ public function testPostInsertDoesNotSetLastInsertValueWhenSequenceValueIsNull() $this->feature->postInsert($statement, $result); - self::assertEquals(999, $lastInsertValueProp->getValue($tableGateway)); + static::assertSame(999, $lastInsertValueProp->getValue($tableGateway)); } - public function testPostInsertSetsLastInsertValue(): void + #[Test] + public function postInsertSetsLastInsertValue(): void { $tableGateway = $this->createTableGatewayWithPlatform('PostgreSQL', 123); $this->feature->setTableGateway($tableGateway); @@ -156,10 +215,11 @@ public function testPostInsertSetsLastInsertValue(): void $this->feature->postInsert($statement, $result); - self::assertEquals(123, $tableGateway->lastInsertValue); + static::assertSame(123, $tableGateway->lastInsertValue); } - public function testPreInsertGeneratesSequenceWhenPrimaryKeyNotInValues(): void + #[Test] + public function preInsertGeneratesSequenceWhenPrimaryKeyNotInValues(): void { $tableGateway = $this->createTableGatewayWithPlatform('PostgreSQL', 99); $this->feature->setTableGateway($tableGateway); @@ -170,16 +230,17 @@ public function testPreInsertGeneratesSequenceWhenPrimaryKeyNotInValues(): void $result = $this->feature->preInsert($insert); - self::assertSame($insert, $result); + static::assertSame($insert, $result); $sequenceValueProp = new ReflectionProperty(SequenceFeature::class, 'sequenceValue'); - self::assertEquals(99, $sequenceValueProp->getValue($this->feature)); + static::assertSame(99, $sequenceValueProp->getValue($this->feature)); $rawState = $insert->getRawState(); - self::assertContains('id', $rawState['columns']); + static::assertContains('id', $rawState['columns']); } - public function testPreInsertReturnsEarlyWhenNextSequenceIdReturnsNull(): void + #[Test] + public function preInsertReturnsEarlyWhenNextSequenceIdReturnsNull(): void { $tableGateway = $this->createTableGatewayWithPlatform('PostgreSQL'); @@ -200,16 +261,31 @@ public function testPreInsertReturnsEarlyWhenNextSequenceIdReturnsNull(): void $result = $feature->preInsert($insert); - self::assertSame($insert, $result); + static::assertSame($insert, $result); $sequenceValueProp = new ReflectionProperty(SequenceFeature::class, 'sequenceValue'); - self::assertNull($sequenceValueProp->getValue($feature)); + static::assertNull($sequenceValueProp->getValue($feature)); $rawState = $insert->getRawState(); - self::assertNotContains('id', $rawState['columns']); + static::assertNotContains('id', $rawState['columns']); } - public function testPreInsertWhenPrimaryKeyAlreadyInValues(): void + #[Test] + public function preInsertThrowsWhenInsertDoesNotExposeArrays(): void + { + $insert = $this->createMock(Insert::class); + $insert->expects($this->any()) + ->method('getRawState') + ->willReturn('not-an-array'); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('The insert does not expose columns and values as arrays.'); + + $this->feature->preInsert($insert); + } + + #[Test] + public function preInsertWhenPrimaryKeyAlreadyInValues(): void { $tableGateway = $this->createTableGatewayWithPlatform('PostgreSQL'); $this->feature->setTableGateway($tableGateway); @@ -220,13 +296,14 @@ public function testPreInsertWhenPrimaryKeyAlreadyInValues(): void $result = $this->feature->preInsert($insert); - self::assertSame($insert, $result); + static::assertSame($insert, $result); $sequenceValueProp = new ReflectionProperty(SequenceFeature::class, 'sequenceValue'); - self::assertEquals(42, $sequenceValueProp->getValue($this->feature)); + static::assertSame(42, $sequenceValueProp->getValue($this->feature)); } - public function testPreInsertWithPrimaryKeyColumnButNullValue(): void + #[Test] + public function preInsertWithPrimaryKeyColumnButNullValue(): void { $tableGateway = $this->createTableGatewayWithPlatform('PostgreSQL'); $this->feature->setTableGateway($tableGateway); @@ -237,10 +314,10 @@ public function testPreInsertWithPrimaryKeyColumnButNullValue(): void $result = $this->feature->preInsert($insert); - self::assertSame($insert, $result); + static::assertSame($insert, $result); $sequenceValueProp = new ReflectionProperty(SequenceFeature::class, 'sequenceValue'); - self::assertNull($sequenceValueProp->getValue($this->feature)); + static::assertNull($sequenceValueProp->getValue($this->feature)); } #[Override] @@ -249,6 +326,49 @@ protected function setUp(): void $this->feature = new SequenceFeature($this->primaryKeyField, self::$sequenceName); } + private function createTableGatewayReturning( + string $platformName, + mixed $current, + ): AbstractTableGateway&MockObject { + $platform = $this->createMock(PlatformInterface::class); + $platform->expects($this->any()) + ->method('getName') + ->willReturn($platformName); + $platform->expects($this->any()) + ->method('quoteIdentifier') + ->willReturnCallback(static fn($name) => $name); + + $result = null; + if (null !== $current) { + $result = $this->createMock(ResultInterface::class); + $result->expects($this->any()) + ->method('current') + ->willReturn($current); + } + + $statement = $this->createMock(StatementInterface::class); + $statement->expects($this->any()) + ->method('execute') + ->willReturn($result); + + $adapter = $this->getMockBuilder(Adapter::class) + ->onlyMethods(['getPlatform', 'createStatement']) + ->disableOriginalConstructor() + ->getMock(); + $adapter->expects($this->any()) + ->method('getPlatform') + ->willReturn($platform); + $adapter->expects($this->any()) + ->method('createStatement') + ->willReturn($statement); + + /** @var AbstractTableGateway&MockObject $tableGateway */ + return $this->getMockBuilder(TableGateway::class) + ->setConstructorArgs(['table', $adapter]) + ->onlyMethods([]) + ->getMock(); + } + private function createTableGatewayWithPlatform( string $platformName, int $sequenceValue = 2, diff --git a/test/unit/TableGateway/Feature/TestAsset/TestTableGatewayFeature.php b/test/unit/TableGateway/Feature/TestAsset/TestTableGatewayFeature.php index d297ef04..df8926ae 100644 --- a/test/unit/TableGateway/Feature/TestAsset/TestTableGatewayFeature.php +++ b/test/unit/TableGateway/Feature/TestAsset/TestTableGatewayFeature.php @@ -31,7 +31,7 @@ public function getMagicMethodSpecifications(): array return $this->magicMethodSpecs; } - public function testMethod(mixed ...$args): mixed + public function recordCall(mixed ...$args): mixed { $this->called = true; $this->receivedArgs = $args;