From aebe9cdddc1bf71079ca45864d5fcc69f70d72ef Mon Sep 17 00:00:00 2001 From: Simon Mundy Date: Mon, 24 Aug 2026 16:11:10 +1000 Subject: [PATCH 1/3] Bump to PHP 8.3 minimum, migrate to Mago QA toolchain, add Pdo\Result coverage php-db/phpdb 0.6.x added getQueryResult() to Driver\ResultInterface, which Sqlite already satisfies for free via the shared core Pdo\Result class (no sqlite-owned Result class exists). Ports the core's exhaustive Pdo\Result unit test suite to cover it, matching the coverage mysql/pgsql carry for their own Result classes. Also replaces laminas-coding-standard/phpstan with the shared php-db/phpdb-qa-tools Mago toolchain, mirroring phpdb-qa-analysis-sql: mago.toml extending the shared base, the qa-tools phpunit.xml.dist template, mago-based composer scripts, and the reusable CI workflow. Pre-existing lint/analyzer findings in untouched code are captured in lint-baseline.toml/analyzer-baseline.toml rather than fixed here. --- .github/workflows/continuous-integration.yml | 32 +- .laminas-ci.json | 12 - analyzer-baseline.toml | 751 ++++++++++++ composer.json | 24 +- composer.lock | 546 +-------- lint-baseline.toml | 1021 +++++++++++++++++ mago.toml | 12 + phpcs.xml.dist | 23 - phpstan-baseline.neon | 2 - phpstan.neon.dist | 17 - phpunit.xml.dist | 33 +- test/unit/Pdo/ResultTest.php | 453 ++++++++ .../unit/Pdo/TestAsset/TemporaryResultSet.php | 9 + 13 files changed, 2332 insertions(+), 603 deletions(-) delete mode 100644 .laminas-ci.json create mode 100644 analyzer-baseline.toml create mode 100644 lint-baseline.toml create mode 100644 mago.toml delete mode 100644 phpcs.xml.dist delete mode 100644 phpstan-baseline.neon delete mode 100644 phpstan.neon.dist create mode 100644 test/unit/Pdo/ResultTest.php create mode 100644 test/unit/Pdo/TestAsset/TemporaryResultSet.php diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 25bc29b..9c67ec6 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -1,31 +1,15 @@ name: "Continuous Integration" on: - pull_request: push: - branches: - tags: + pull_request: jobs: - matrix: - name: Generate job matrix - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.matrix.outputs.matrix }} - steps: - - name: Gather CI configuration - id: matrix - uses: laminas/laminas-ci-matrix-action@v1 - qa: - name: QA Checks - needs: [matrix] - runs-on: ${{ matrix.operatingSystem }} - strategy: - fail-fast: false - matrix: ${{ fromJSON(needs.matrix.outputs.matrix) }} - steps: - - name: ${{ matrix.name }} - uses: laminas/laminas-continuous-integration-action@v1 - with: - job: ${{ matrix.job }} \ No newline at end of file + uses: php-db/phpdb-qa-tools/.github/workflows/continuous-integration.yml@0.1.x + secrets: inherit + with: + php-versions: '["8.3", "8.4", "8.5"]' + run-integration: true + enable-codecov: false + enable-infection: false diff --git a/.laminas-ci.json b/.laminas-ci.json deleted file mode 100644 index 10c530b..0000000 --- a/.laminas-ci.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "additional_checks": [ - { - "name": "PhpStan", - "job": { - "php": "8.2", - "dependencies": "latest", - "command": "composer require --dev phpstan/phpstan && vendor/bin/phpstan analyse" - } - } - ] -} \ No newline at end of file diff --git a/analyzer-baseline.toml b/analyzer-baseline.toml new file mode 100644 index 0000000..1914077 --- /dev/null +++ b/analyzer-baseline.toml @@ -0,0 +1,751 @@ +variant = "loose" + +[[issues]] +file = "src/AdapterPlatform.php" +code = "class-must-be-final" +message = 'Class `PhpDb\Sqlite\AdapterPlatform` should be declared `final`.' +count = 1 + +[[issues]] +file = "src/AdapterPlatform.php" +code = "falsable-return-statement" +message = '''Function `PhpDb\Sqlite\AdapterPlatform::quoteTrustedValue` is declared to return `null|string` but possibly returns 'false' (inferred as `false|string`).''' +count = 1 + +[[issues]] +file = "src/AdapterPlatform.php" +code = "falsable-return-statement" +message = '''Function `PhpDb\Sqlite\AdapterPlatform::quoteValue` is declared to return `string` but possibly returns 'false' (inferred as `false|string`).''' +count = 1 + +[[issues]] +file = "src/AdapterPlatform.php" +code = "invalid-return-statement" +message = 'Invalid return type for function `PhpDb\Sqlite\AdapterPlatform::quoteTrustedValue`: expected `null|string`, but found `false|string`.' +count = 1 + +[[issues]] +file = "src/AdapterPlatform.php" +code = "invalid-return-statement" +message = 'Invalid return type for function `PhpDb\Sqlite\AdapterPlatform::quoteValue`: expected `string`, but found `false|string`.' +count = 1 + +[[issues]] +file = "src/AdapterPlatform.php" +code = "missing-constant-type" +message = "Class constant `PLATFORM_NAME` is missing a type hint." +count = 1 + +[[issues]] +file = "src/AdapterPlatform.php" +code = "possibly-invalid-argument" +message = "Possible argument type mismatch for argument #1 of `PDO::quote`: expected `string`, but possibly received `bool|float|int|string`." +count = 1 + +[[issues]] +file = "src/ConfigProvider.php" +code = "imprecise-type" +message = "Type `array` in return type of `__invoke` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/ConfigProvider.php" +code = "imprecise-type" +message = "Type `array` in return type of `getConfig` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/ConfigProvider.php" +code = "imprecise-type" +message = "Type `array` in return type of `getDependencies` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Container/MetadataInterfaceFactory.php" +code = "imprecise-type" +message = "Type `array` in parameter `$options` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Container/MetadataInterfaceFactory.php" +code = "missing-constant-type" +message = "Class constant `ADAPTER_SERVICE_NAME` is missing a type hint." +count = 1 + +[[issues]] +file = "src/Container/MetadataInterfaceFactory.php" +code = "mixed-argument" +message = 'Invalid argument type for argument #1 of `PhpDb\Metadata\Source\AbstractSource::__construct`: expected `PhpDb\Adapter\AdapterInterface&PhpDb\Adapter\SchemaAwareInterface`, but found `mixed`.' +count = 1 + +[[issues]] +file = "src/Container/MetadataInterfaceFactory.php" +code = "mixed-argument" +message = 'Invalid argument type for argument #1 of `Psr\Container\ContainerInterface::get`: expected `string`, but found `nonnull`.' +count = 1 + +[[issues]] +file = "src/Container/MetadataInterfaceFactory.php" +code = "mixed-assignment" +message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." +count = 1 + +[[issues]] +file = "src/Container/MetadataInterfaceFactory.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `Psr\Container\ContainerExceptionInterface` in `PhpDb\Sqlite\Container\MetadataInterfaceFactory::__invoke`.' +count = 1 + +[[issues]] +file = "src/Container/MetadataInterfaceFactory.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `Psr\Container\NotFoundExceptionInterface` in `PhpDb\Sqlite\Container\MetadataInterfaceFactory::__invoke`.' +count = 1 + +[[issues]] +file = "src/Container/MetadataInterfaceFactory.php" +code = "unused-parameter" +message = "Parameter `$requestedName` is never used." +count = 1 + +[[issues]] +file = "src/Container/PdoConnectionFactory.php" +code = "imprecise-type" +message = "Type `array` in parameter `$options` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Container/PdoConnectionFactory.php" +code = "mixed-argument" +message = 'Invalid argument type for argument #2 of `PhpDb\Adapter\Exception\InvalidConnectionParametersException::__construct`: expected `array`, but found `nonnull`.' +count = 1 + +[[issues]] +file = "src/Container/PdoConnectionFactory.php" +code = "mixed-assignment" +message = "Assigning `nonnull` type to a variable may lead to unexpected behavior." +count = 1 + +[[issues]] +file = "src/Container/PdoConnectionFactory.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `PhpDb\Adapter\Exception\InvalidConnectionParametersException` in `PhpDb\Sqlite\Container\PdoConnectionFactory::__invoke`.' +count = 1 + +[[issues]] +file = "src/Container/PdoConnectionFactory.php" +code = "unused-parameter" +message = "Parameter `$container` is never used." +count = 1 + +[[issues]] +file = "src/Container/PdoConnectionFactory.php" +code = "unused-parameter" +message = "Parameter `$requestedName` is never used." +count = 1 + +[[issues]] +file = "src/Container/PdoDriverInterfaceFactory.php" +code = "imprecise-type" +message = "Type `array` in parameter `$options` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Container/PdoDriverInterfaceFactory.php" +code = "mixed-argument" +message = 'Invalid argument type for argument #2 of `Laminas\ServiceManager\ServiceManager::build`: expected `array|null`, but found `nonnull`.' +count = 1 + +[[issues]] +file = "src/Container/PdoDriverInterfaceFactory.php" +code = "redundant-docblock-type" +message = "Redundant docblock type for variable `$connectionInstance`." +count = 1 + +[[issues]] +file = "src/Container/PdoDriverInterfaceFactory.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `Laminas\ServiceManager\Exception\ServiceNotCreatedException` in `PhpDb\Sqlite\Container\PdoDriverInterfaceFactory::__invoke`.' +count = 1 + +[[issues]] +file = "src/Container/PdoDriverInterfaceFactory.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `Psr\Container\ContainerExceptionInterface` in `PhpDb\Sqlite\Container\PdoDriverInterfaceFactory::__invoke`.' +count = 1 + +[[issues]] +file = "src/Container/PdoDriverInterfaceFactory.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `Psr\Container\NotFoundExceptionInterface` in `PhpDb\Sqlite\Container\PdoDriverInterfaceFactory::__invoke`.' +count = 1 + +[[issues]] +file = "src/Container/PdoDriverInterfaceFactory.php" +code = "unused-parameter" +message = "Parameter `$requestedName` is never used." +count = 1 + +[[issues]] +file = "src/Container/PdoResultFactory.php" +code = "unused-parameter" +message = "Parameter `$container` is never used." +count = 1 + +[[issues]] +file = "src/Container/PdoStatementFactory.php" +code = "imprecise-type" +message = "Type `array` in parameter `$options` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Container/PdoStatementFactory.php" +code = "possibly-null-argument" +message = 'Argument #1 of method `PhpDb\Adapter\Driver\Pdo\Statement::__construct` is possibly `null`, but parameter type `array` does not accept it.' +count = 1 + +[[issues]] +file = "src/Container/PdoStatementFactory.php" +code = "unused-parameter" +message = "Parameter `$container` is never used." +count = 1 + +[[issues]] +file = "src/Container/PdoStatementFactory.php" +code = "unused-parameter" +message = "Parameter `$requestedName` is never used." +count = 1 + +[[issues]] +file = "src/Container/PlatformInterfaceFactory.php" +code = "imprecise-type" +message = "Type `array` in parameter `$options` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Container/PlatformInterfaceFactory.php" +code = "mixed-assignment" +message = "Assigning `mixed` type to a variable may lead to unexpected behavior." +count = 1 + +[[issues]] +file = "src/Container/PlatformInterfaceFactory.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `PhpDb\Exception\ContainerException` in `PhpDb\Sqlite\Container\PlatformInterfaceFactory::__invoke`.' +count = 1 + +[[issues]] +file = "src/Container/PlatformInterfaceFactory.php" +code = "unused-parameter" +message = "Parameter `$container` is never used." +count = 1 + +[[issues]] +file = "src/Container/PlatformInterfaceFactory.php" +code = "unused-parameter" +message = "Parameter `$requestedName` is never used." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "imprecise-type" +message = "Type `array` in parameter `$re` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "imprecise-type" +message = "Type `array` in return type of `fetchPragma` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "invalid-return-statement" +message = 'Invalid return type for function `PhpDb\Sqlite\Metadata\Source::getIdentifierChainRegularExpression`: expected `string`, but found `null|truthy-string`.' +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "invalid-return-statement" +message = 'Invalid return type for function `PhpDb\Sqlite\Metadata\Source::getIdentifierListRegularExpression`: expected `string`, but found `null|truthy-string`.' +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "invalid-return-statement" +message = 'Invalid return type for function `PhpDb\Sqlite\Metadata\Source::getIdentifierRegularExpression`: expected `string`, but found `null|truthy-literal-string`.' +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "less-specific-nested-argument-type" +message = "Argument type mismatch for argument #2 of `implode`: expected `array|null`, but provided type `array` is less specific." +count = 2 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-argument" +message = 'Invalid argument type for argument #1 of `PhpDb\Sqlite\Metadata\Source::parseTrigger`: expected `string`, but found `mixed`.' +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-argument" +message = 'Invalid argument type for argument #1 of `PhpDb\Sqlite\Metadata\Source::parseView`: expected `string`, but found `mixed`.' +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-argument" +message = "Invalid argument type for argument #1 of `strtoupper`: expected `string`, but found `mixed`." +count = 3 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-argument" +message = 'Invalid argument type for argument #2 of `PhpDb\Sqlite\Metadata\Source::fetchPragma`: expected `null|string`, but found `mixed`.' +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-array-access" +message = "Unsafe array access on type `mixed`." +count = 25 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-array-assignment" +message = "Unsafe array assignment on type `mixed`." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-assignment" +message = "Assigning `mixed` type to a variable may lead to unexpected behavior." +count = 10 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-operand" +message = "Casting `mixed` to `bool`." +count = 3 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-operand" +message = "Invalid left operand: type `mixed` cannot be reliably used in string concatenation." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-operand" +message = "Invalid right operand: type `mixed` cannot be reliably used in string concatenation." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-operand" +message = "Left operand in binary operation has type `mixed`." +count = 2 + +[[issues]] +file = "src/Metadata/Source.php" +code = "mixed-property-type-coercion" +message = "A value with a less specific type `array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas': array{}|non-empty-list|null, 'table_names'?: array>, 'triggers'?: array>}` is being assigned to property `$data` (array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names'?: array>, 'triggers'?: array>})." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "null-array-access" +message = "Cannot perform array access on `null`." +count = 2 + +[[issues]] +file = "src/Metadata/Source.php" +code = "null-iterator" +message = "Iterating over `null` in `foreach`." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "nullable-return-statement" +message = 'Function `PhpDb\Sqlite\Metadata\Source::getIdentifierChainRegularExpression` is declared to return `string` but possibly returns a nullable value (inferred as `null|truthy-string`).' +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "nullable-return-statement" +message = 'Function `PhpDb\Sqlite\Metadata\Source::getIdentifierListRegularExpression` is declared to return `string` but possibly returns a nullable value (inferred as `null|truthy-string`).' +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "nullable-return-statement" +message = 'Function `PhpDb\Sqlite\Metadata\Source::getIdentifierRegularExpression` is declared to return `string` but possibly returns a nullable value (inferred as `null|truthy-literal-string`).' +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "possibly-null-argument" +message = "Argument #1 of function `preg_match` is possibly `null`, but parameter type `string` does not accept it." +count = 2 + +[[issues]] +file = "src/Metadata/Source.php" +code = "possibly-null-argument" +message = "Argument #1 of function `strtoupper` is possibly `null`, but parameter type `string` does not accept it." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "possibly-null-array-index" +message = "Possibly using `null` as an array index to access element." +count = 2 + +[[issues]] +file = "src/Metadata/Source.php" +code = "possibly-null-operand" +message = "Possibly null middle operand used in string concatenation (type `null|string`)." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "possibly-undefined-string-array-index" +message = "Possibly undefined array key `string('event_manipulation')` accessed on `array`." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "possibly-undefined-string-array-index" +message = "Possibly undefined array key `string('view_definition')` accessed on `array`." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "property-type-coercion" +message = "A value of a less specific type `array{'columns': non-empty-array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': mixed}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'sqlite_columns': non-empty-list, 'table_names'?: array>, 'triggers'?: array>}` is being assigned to property `$data` (array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names'?: array>, 'triggers'?: array>})." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "property-type-coercion" +message = "A value of a less specific type `array{'columns': non-empty-array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': mixed}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names'?: array>, 'triggers'?: array>}` is being assigned to property `$data` (array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names'?: array>, 'triggers'?: array>})." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "property-type-coercion" +message = "A value of a less specific type `array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints': non-empty-array, 'constraint_name'?: mixed, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: mixed, 'referenced_table_schema'?: string, 'table_name'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names'?: array>, 'triggers'?: array>}` is being assigned to property `$data` (array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names'?: array>, 'triggers'?: array>})." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "property-type-coercion" +message = "A value of a less specific type `array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names': non-empty-array}|array{'check_option'?: null|string, 'is_updatable'?: bool|null, 'table_type': string, 'view_definition'?: null|string}>>, 'triggers'?: array>}` is being assigned to property `$data` (array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names'?: array>, 'triggers'?: array>})." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "property-type-coercion" +message = "A value of a less specific type `array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names'?: array>, 'triggers': non-empty-array}|array{'action_condition': null, 'action_order': int(0), 'action_orientation': string('ROW'), 'action_reference_new_row': string('NEW'), 'action_reference_new_table': null, 'action_reference_old_row': string('OLD'), 'action_reference_old_table': null, 'action_statement': null, 'action_timing': null, 'created': null, 'event_manipulation': null, 'event_object_catalog': null, 'event_object_schema': string, 'event_object_table': mixed, 'trigger_name': mixed}|array{'action_condition': null|string, 'action_order': string, 'action_orientation': string, 'action_reference_new_row': string, 'action_reference_new_table': null|string, 'action_reference_old_row': string, 'action_reference_old_table': null|string, 'action_statement': string, 'action_timing': string, 'created': DateTime|null, 'event_manipulation': string, 'event_object_catalog': string, 'event_object_schema': string, 'event_object_table': string}>>}` is being assigned to property `$data` (array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names'?: array>, 'triggers'?: array>})." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "redundant-condition" +message = "This condition (type `true`) will always evaluate to true." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "reference-to-undefined-variable" +message = "Reference created from a previously undefined variable `$matches`." +count = 2 + +[[issues]] +file = "src/Metadata/Source.php" +code = "undefined-string-array-index" +message = "Undefined array key 'sqlite_columns' accessed on `array{'columns'?: array, 'is_nullable': bool|null, 'numeric_precision': int|null|string, 'numeric_scale': int|null|string, 'numeric_unsigned': bool|null, 'ordinal_position': int|null|string}>>>, 'constraint_keys'?: array>, 'constraint_references'?: array>, 'constraints'?: array, 'constraint_type'?: string, 'delete_rule'?: string, 'match_option'?: string, 'referenced_columns'?: list, 'referenced_table_name'?: string, 'referenced_table_schema'?: string, 'update_rule'?: string}>>>, 'schemas'?: list, 'table_names'?: array>, 'triggers'?: array>}`." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "class-must-be-final" +message = 'Class `PhpDb\Sqlite\Pdo\Connection` should be declared `final`.' +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "imprecise-type" +message = "Type `array` in parameter `$connectionParameters` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "incompatible-parameter-type" +message = 'Parameter `$name` of `PhpDb\Sqlite\Pdo\Connection::getlastgeneratedvalue()` expects type `string` but parent `PhpDb\Adapter\Driver\ConnectionInterface::getlastgeneratedvalue()` expects type `null|string`' +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "invalid-type-cast" +message = "Casting `mixed` to `array`." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "less-specific-argument" +message = "Argument type mismatch for argument #1 of `strtolower`: expected `string`, but provided type `array-key` is less specific." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "missing-constant-type" +message = "Class constant `CURRENT_SCHEMA` is missing a type hint." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "mixed-assignment" +message = "Assigning `mixed` type to a variable may lead to unexpected behavior." +count = 3 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "mixed-return-statement" +message = 'Could not infer a precise return type for function `PhpDb\Sqlite\Pdo\Connection::getLastGeneratedValue`. Saw type `mixed`.' +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "possible-method-access-on-null" +message = "Attempting to call a method on `null`." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "redundant-condition" +message = "This condition (type `true`) will always evaluate to true." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `PhpDb\Adapter\Exception\InvalidConnectionParametersException` in `PhpDb\Sqlite\Pdo\Connection::getCurrentSchema`.' +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `PhpDb\Adapter\Exception\RuntimeException` in `PhpDb\Sqlite\Pdo\Connection::getCurrentSchema`.' +count = 1 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "class-must-be-final" +message = 'Class `PhpDb\Sqlite\Pdo\Driver` should be declared `final`.' +count = 1 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "imprecise-type" +message = "Type `array` in parameter `$features` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "invalid-method-access" +message = "Attempting to access a method on a non-object type (`resource`)." +count = 1 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "invalid-operand" +message = "Left operand in `&&` operation is an `object`." +count = 1 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "invalid-property-assignment-value" +message = 'Invalid type for property `$connection`: expected `(PhpDb\Adapter\Driver\PdoConnectionInterface&PhpDb\Adapter\Driver\AbstractConnection&PhpDb\Adapter\Driver\PdoDriverAwareInterface)|PDO`, but got `(PhpDb\Adapter\Driver\PdoConnectionInterface&PhpDb\Adapter\Driver\PdoDriverAwareInterface)|PDO`.' +count = 1 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "mixed-argument" +message = 'Invalid argument type for argument #2 of `PhpDb\Adapter\Driver\Pdo\Result::initialize`: expected `false|int|null|string`, but found `mixed`.' +count = 1 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "mixed-operand" +message = "Left operand in `>` comparison has `mixed` type." +count = 1 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "non-existent-method" +message = "Method `getlastgeneratedvalue` does not exist on type `PDO`." +count = 1 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "possibly-invalid-argument" +message = 'Possible argument type mismatch for argument #1 of `PhpDb\Adapter\Driver\Pdo\Result::initialize`: expected `PDOStatement`, but possibly received `PDOStatement|resource`.' +count = 1 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "redundant-logical-operation" +message = "Redundant `&&` operation: left operand is always truthy and right operand is evaluated." +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "class-must-be-final" +message = 'Class `PhpDb\Sqlite\Pdo\Feature\SqliteRowCounter` should be declared `final`.' +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "docblock-type-mismatch" +message = "Docblock type mismatch for variable `$pdo`." +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "invalid-method-access" +message = "Attempting to access a method on a non-object type (`false`)." +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "missing-constructor" +message = 'Class `PhpDb\Sqlite\Pdo\Feature\SqliteRowCounter` has typed properties without default values but no constructor to initialize them.' +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "mixed-array-access" +message = "Unsafe array access on type `mixed`." +count = 2 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "mixed-assignment" +message = "Assigning `mixed` type to a variable may lead to unexpected behavior." +count = 2 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "mixed-method-access" +message = "Attempting to access a method on a non-object type (`mixed`)." +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "possible-method-access-on-null" +message = "Attempting to call a method on `null`." +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "possibly-null-argument" +message = 'Argument #1 of method `PhpDb\Sqlite\Pdo\Feature\SqliteRowCounter::getCountForSql` is possibly `null`, but parameter type `string` does not accept it.' +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `PDOException` in `PhpDb\Sqlite\Pdo\Feature\SqliteRowCounter::getCountForSql`.' +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `PhpDb\Adapter\Exception\InvalidQueryException` in `PhpDb\Sqlite\Pdo\Feature\SqliteRowCounter::getCountForStatement`.' +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "unhandled-thrown-type" +message = 'Potentially unhandled exception `PhpDb\Adapter\Exception\RuntimeException` in `PhpDb\Sqlite\Pdo\Feature\SqliteRowCounter::getCountForStatement`.' +count = 1 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "imprecise-type" +message = "Type `array` in return type of `processOffset` is imprecise, equivalent to `array`." +count = 1 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "incompatible-parameter-type" +message = 'Parameter `$subject` of `PhpDb\Sqlite\Sql\SelectDecorator::setsubject()` expects type `PhpDb\Sql\Select` but parent `PhpDb\Sql\Platform\PlatformDecoratorInterface::setsubject()` expects type `PhpDb\Sql\PreparableSqlInterface|PhpDb\Sql\SqlInterface|null`' +count = 1 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "invalid-property-assignment-value" +message = "Invalid type for property `$specifications`: expected `array>|array`, but got `array{'limit': string('LIMIT 18446744073709551615'), ...|string>}`." +count = 1 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "invalid-return-statement" +message = 'Invalid return type for function `PhpDb\Sqlite\Sql\SelectDecorator::processLimit`: expected `array|array|null`, but found `list{int|string}`.' +count = 1 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "missing-override-attribute" +message = 'Missing `#[Override]` attribute on overriding method `PhpDb\Sqlite\Sql\SelectDecorator::localizeVariables`.' +count = 1 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "missing-override-attribute" +message = 'Missing `#[Override]` attribute on overriding method `PhpDb\Sqlite\Sql\SelectDecorator::processLimit`.' +count = 1 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "missing-override-attribute" +message = 'Missing `#[Override]` attribute on overriding method `PhpDb\Sqlite\Sql\SelectDecorator::processOffset`.' +count = 1 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "missing-override-attribute" +message = 'Missing `#[Override]` attribute on overriding method `PhpDb\Sqlite\Sql\SelectDecorator::setSubject`.' +count = 1 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "redundant-cast" +message = "Redundant cast to `(string)`: the expression already has this type." +count = 2 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "uninitialized-property" +message = 'Property `$subject` is not initialized in the constructor of class `PhpDb\Sqlite\Sql\SelectDecorator`.' +count = 1 + +[[issues]] +file = "vendor/php-db/phpdb/src/Adapter/Driver/AbstractConnection.php" +code = "uninitialized-property" +message = 'Property `$driverName` is not initialized in the constructor of class `PhpDb\Sqlite\Pdo\Connection`.' +count = 1 + +[[issues]] +file = "vendor/php-db/phpdb/src/Adapter/Driver/Pdo/AbstractPdo.php" +code = "uninitialized-property" +message = 'Property `$profiler` is not initialized in the constructor of class `PhpDb\Sqlite\Pdo\Driver`.' +count = 1 + +[[issues]] +file = "vendor/php-db/phpdb/src/Adapter/Driver/Pdo/AbstractPdoConnection.php" +code = "uninitialized-property" +message = 'Property `$dsn` is not initialized in the constructor of class `PhpDb\Sqlite\Pdo\Connection`.' +count = 1 diff --git a/composer.json b/composer.json index f2e6ea1..d1bc897 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "config": { "sort-packages": true, "platform": { - "php": "8.2.99" + "php": "8.3.99" }, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true @@ -30,16 +30,14 @@ } }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "php-db/phpdb": "^0.6.0", "webmozart/assert": "^2.0" }, "require-dev": { "ext-pdo": "*", "ext-pdo_sqlite": "*", - "laminas/laminas-coding-standard": "^3.0.1", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0", + "php-db/phpdb-qa-tools": "0.1.x-dev", "phpunit/phpunit": "^11.5.42" }, "suggest": { @@ -59,18 +57,22 @@ "scripts": { "check": [ "@cs-check", - "@sa", + "@static-analysis", "@test", "@test-integration" ], - "cs-check": "phpcs", - "cs-fix": "phpcbf", + "cs-check": [ + "mago format --check", + "mago lint" + ], + "cs-fix": [ + "mago format", + "mago lint --fix" + ], + "static-analysis": "mago analyze", "test": "phpunit --colors=always --testsuite \"unit test\"", "test-coverage": "phpunit --colors=always --coverage-clover clover.xml", "test-integration": "phpunit --colors=always --testsuite \"integration test\"", - "sa": "vendor/bin/phpstan analyse --memory-limit=256M", - "sa-gen-baseline": "vendor/bin/phpstan analyse --memory-limit=256M --generate-baseline", - "sa-verbose": "vendor/bin/phpstan analyse --memory-limit=256M -vv", "upload-coverage": "coveralls -v" } } diff --git a/composer.lock b/composer.lock index dd6fdc6..1a22b7b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dd8725e19686a7f5dd3dcaa1544efee1", + "content-hash": "eac5fb5580421cacc7e6da527a62a4cc", "packages": [ { "name": "brick/varexporter", @@ -201,20 +201,19 @@ }, { "name": "nikic/php-parser", - "version": "v5.7.0", + "version": "v5.8.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/044a6a392ff8ad0d61f14370a5fbbd0a0107152f", + "reference": "044a6a392ff8ad0d61f14370a5fbbd0a0107152f", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-json": "*", "ext-tokenizer": "*", "php": ">=7.4" @@ -253,9 +252,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.8.0" }, - "time": "2025-12-06T11:56:16+00:00" + "time": "2026-07-04T14:30:18+00:00" }, { "name": "php-db/phpdb", @@ -263,30 +262,29 @@ "source": { "type": "git", "url": "https://github.com/php-db/phpdb.git", - "reference": "81844a5699bf397bf044bbe36ea90be361b4a021" + "reference": "b57f549b411d0b4fe46de8a469983816395be850" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-db/phpdb/zipball/81844a5699bf397bf044bbe36ea90be361b4a021", - "reference": "81844a5699bf397bf044bbe36ea90be361b4a021", + "url": "https://api.github.com/repos/php-db/phpdb/zipball/b57f549b411d0b4fe46de8a469983816395be850", + "reference": "b57f549b411d0b4fe46de8a469983816395be850", "shasum": "" }, "require": { "laminas/laminas-servicemanager": "^3.0.0 || ^4.0.0", "laminas/laminas-stdlib": "^3.20.0", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "~8.3.0 || ~8.4.0 || ~8.5.0" }, "conflict": { "laminas/laminas-db": "*", "zendframework/zend-db": "*" }, "require-dev": { - "laminas/laminas-coding-standard": "^3.0.1", + "infection/infection": "^0.34.1", "laminas/laminas-eventmanager": "^3.14.0", "laminas/laminas-hydrator": "^4.6.0", + "php-db/phpdb-qa-tools": "0.1.x-dev", "phpbench/phpbench": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0", "phpunit/phpunit": "^11.5.42", "rector/rector": "^2.0" }, @@ -324,7 +322,7 @@ "issues": "https://github.com/php-db/phpdb/issues", "source": "https://github.com/php-db/phpdb" }, - "time": "2026-04-13T01:24:01+00:00" + "time": "2026-08-24T01:49:39+00:00" }, { "name": "psr/container", @@ -443,155 +441,6 @@ } ], "packages-dev": [ - { - "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v1.2.1", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/composer-installer.git", - "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd", - "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^2.2", - "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" - }, - "require-dev": { - "composer/composer": "^2.2", - "ext-json": "*", - "ext-zip": "*", - "php-parallel-lint/php-parallel-lint": "^1.4.0", - "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev", - "yoast/phpunit-polyfills": "^1.0" - }, - "type": "composer-plugin", - "extra": { - "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" - }, - "autoload": { - "psr-4": { - "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Franck Nijhof", - "email": "opensource@frenck.dev", - "homepage": "https://frenck.dev", - "role": "Open source developer" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "keywords": [ - "PHPCodeSniffer", - "PHP_CodeSniffer", - "code quality", - "codesniffer", - "composer", - "installer", - "phpcbf", - "phpcs", - "plugin", - "qa", - "quality", - "standard", - "standards", - "style guide", - "stylecheck", - "tests" - ], - "support": { - "issues": "https://github.com/PHPCSStandards/composer-installer/issues", - "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", - "source": "https://github.com/PHPCSStandards/composer-installer" - }, - "funding": [ - { - "url": "https://github.com/PHPCSStandards", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", - "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - }, - { - "url": "https://thanks.dev/u/gh/phpcsstandards", - "type": "thanks_dev" - } - ], - "time": "2026-05-06T08:26:05+00:00" - }, - { - "name": "laminas/laminas-coding-standard", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-coding-standard.git", - "reference": "d4412caba9ed16c93cdcf301759f5ee71f9d9aea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-coding-standard/zipball/d4412caba9ed16c93cdcf301759f5ee71f9d9aea", - "reference": "d4412caba9ed16c93cdcf301759f5ee71f9d9aea", - "shasum": "" - }, - "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", - "php": "^7.4 || ^8.0", - "slevomat/coding-standard": "^8.15.0", - "squizlabs/php_codesniffer": "^3.10", - "webimpress/coding-standard": "^1.3" - }, - "type": "phpcodesniffer-standard", - "autoload": { - "psr-4": { - "LaminasCodingStandard\\": "src/LaminasCodingStandard/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Laminas Coding Standard", - "homepage": "https://laminas.dev", - "keywords": [ - "Coding Standard", - "laminas" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-coding-standard/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-coding-standard/issues", - "rss": "https://github.com/laminas/laminas-coding-standard/releases.atom", - "source": "https://github.com/laminas/laminas-coding-standard" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2025-05-13T08:37:04+00:00" - }, { "name": "myclabs/deep-copy", "version": "1.13.4", @@ -771,160 +620,48 @@ "time": "2022-02-21T01:04:05+00:00" }, { - "name": "phpstan/phpdoc-parser", - "version": "2.3.2", + "name": "php-db/phpdb-qa-tools", + "version": "0.1.x-dev", "source": { "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", - "shasum": "" + "url": "https://github.com/php-db/phpdb-qa-tools.git", + "reference": "f2323423deac77dd719f3f2ca7a9b2a7e21c93f6" }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^5.3.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" - }, - "time": "2026-01-25T14:56:51+00:00" - }, - { - "name": "phpstan/phpstan", - "version": "2.1.55", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/9eaac3826ed5e9b8427350a43cac825eeca3f566", - "reference": "9eaac3826ed5e9b8427350a43cac825eeca3f566", + "url": "https://api.github.com/repos/php-db/phpdb-qa-tools/zipball/f2323423deac77dd719f3f2ca7a9b2a7e21c93f6", + "reference": "f2323423deac77dd719f3f2ca7a9b2a7e21c93f6", "shasum": "" }, "require": { - "php": "^7.4|^8.0" + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, - "conflict": { - "phpstan/phpstan-shim": "*" + "suggest": { + "phpunit/phpunit": "To run the shared test configuration (^11.5 || ^12.0)" }, - "bin": [ - "phpstan", - "phpstan.phar" - ], + "default-branch": true, "type": "library", - "autoload": { - "files": [ - "bootstrap.php" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "description": "PHPStan - PHP Static Analysis Tool", - "keywords": [ - "dev", - "static analysis" - ], - "support": { - "docs": "https://phpstan.org/user-guide/getting-started", - "forum": "https://github.com/phpstan/phpstan/discussions", - "issues": "https://github.com/phpstan/phpstan/issues", - "security": "https://github.com/phpstan/phpstan/security/policy", - "source": "https://github.com/phpstan/phpstan-src" - }, - "funding": [ - { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://github.com/phpstan", - "type": "github" - } - ], - "time": "2026-05-18T11:57:34+00:00" - }, - { - "name": "phpstan/phpstan-phpunit", - "version": "2.0.16", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6ab598e1bc106e6827fd346ae4a12b4a5d634c32", - "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0", - "phpstan/phpstan": "^2.1.32" - }, - "conflict": { - "phpunit/phpunit": "<7.0" - }, - "require-dev": { - "nikic/php-parser": "^5", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-deprecation-rules": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6" - }, - "type": "phpstan-extension", - "extra": { - "phpstan": { - "includes": [ - "extension.neon", - "rules.neon" - ] - } - }, - "autoload": { - "psr-4": { - "PHPStan\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" + "BSD-3-Clause" ], - "description": "PHPUnit extensions and rules for PHPStan", + "description": "Shared QA toolchain for php-db components: Mago formatting, linting and static analysis, plus the PHPUnit baseline and CI workflow.", + "homepage": "https://github.com/php-db/phpdb-qa-tools", "keywords": [ - "static analysis" + "coding-standards", + "formatter", + "linter", + "mago", + "phpdb", + "qa", + "static-analysis" ], "support": { - "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.16" + "forum": "https://github.com/php-db/phpdb-qa-tools/discussions", + "issues": "https://github.com/php-db/phpdb-qa-tools/issues", + "source": "https://github.com/php-db/phpdb-qa-tools" }, - "time": "2026-02-14T09:05:21+00:00" + "time": "2026-08-10T01:10:13+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2369,150 +2106,6 @@ ], "time": "2024-10-09T05:16:32+00:00" }, - { - "name": "slevomat/coding-standard", - "version": "8.22.1", - "source": { - "type": "git", - "url": "https://github.com/slevomat/coding-standard.git", - "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/1dd80bf3b93692bedb21a6623c496887fad05fec", - "reference": "1dd80bf3b93692bedb21a6623c496887fad05fec", - "shasum": "" - }, - "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.1.2", - "php": "^7.4 || ^8.0", - "phpstan/phpdoc-parser": "^2.3.0", - "squizlabs/php_codesniffer": "^3.13.4" - }, - "require-dev": { - "phing/phing": "3.0.1|3.1.0", - "php-parallel-lint/php-parallel-lint": "1.4.0", - "phpstan/phpstan": "2.1.24", - "phpstan/phpstan-deprecation-rules": "2.0.3", - "phpstan/phpstan-phpunit": "2.0.7", - "phpstan/phpstan-strict-rules": "2.0.6", - "phpunit/phpunit": "9.6.8|10.5.48|11.4.4|11.5.36|12.3.10" - }, - "type": "phpcodesniffer-standard", - "extra": { - "branch-alias": { - "dev-master": "8.x-dev" - } - }, - "autoload": { - "psr-4": { - "SlevomatCodingStandard\\": "SlevomatCodingStandard/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", - "keywords": [ - "dev", - "phpcs" - ], - "support": { - "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/8.22.1" - }, - "funding": [ - { - "url": "https://github.com/kukulich", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", - "type": "tidelift" - } - ], - "time": "2025-09-13T08:53:30+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.13.5", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", - "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" - }, - "bin": [ - "bin/phpcbf", - "bin/phpcs" - ], - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "Former lead" - }, - { - "name": "Juliette Reinders Folmer", - "role": "Current lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", - "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", - "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" - }, - "funding": [ - { - "url": "https://github.com/PHPCSStandards", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", - "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - }, - { - "url": "https://thanks.dev/u/gh/phpcsstandards", - "type": "thanks_dev" - } - ], - "time": "2025-11-04T16:30:35+00:00" - }, { "name": "staabm/side-effects-detector", "version": "1.0.5", @@ -2614,77 +2207,24 @@ } ], "time": "2025-11-17T20:03:58+00:00" - }, - { - "name": "webimpress/coding-standard", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/webimpress/coding-standard.git", - "reference": "6f6a1a90bd9e18fc8bee0660dd1d1ce68cf9fc53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webimpress/coding-standard/zipball/6f6a1a90bd9e18fc8bee0660dd1d1ce68cf9fc53", - "reference": "6f6a1a90bd9e18fc8bee0660dd1d1ce68cf9fc53", - "shasum": "" - }, - "require": { - "php": "^7.3 || ^8.0", - "squizlabs/php_codesniffer": "^3.10.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.6.15" - }, - "type": "phpcodesniffer-standard", - "extra": { - "dev-master": "1.2.x-dev", - "dev-develop": "1.3.x-dev" - }, - "autoload": { - "psr-4": { - "WebimpressCodingStandard\\": "src/WebimpressCodingStandard/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "description": "Webimpress Coding Standard", - "keywords": [ - "Coding Standard", - "PSR-2", - "phpcs", - "psr-12", - "webimpress" - ], - "support": { - "issues": "https://github.com/webimpress/coding-standard/issues", - "source": "https://github.com/webimpress/coding-standard/tree/1.4.0" - }, - "funding": [ - { - "url": "https://github.com/michalbundyra", - "type": "github" - } - ], - "time": "2024-10-16T06:55:17+00:00" } ], "aliases": [], "minimum-stability": "dev", - "stability-flags": {}, + "stability-flags": { + "php-db/phpdb-qa-tools": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + "php": "~8.3.0 || ~8.4.0 || ~8.5.0" }, "platform-dev": { "ext-pdo": "*", "ext-pdo_sqlite": "*" }, "platform-overrides": { - "php": "8.2.99" + "php": "8.3.99" }, "plugin-api-version": "2.9.0" } diff --git a/lint-baseline.toml b/lint-baseline.toml new file mode 100644 index 0000000..ab917dd --- /dev/null +++ b/lint-baseline.toml @@ -0,0 +1,1021 @@ +variant = "loose" + +[[issues]] +file = "src/Container/PdoConnectionFactory.php" +code = "yoda-conditions" +message = "Use Yoda condition style for safer comparisons" +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "cyclomatic-complexity" +message = "Class has high complexity." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "halstead" +message = "Method has a high halstead volume and effort" +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "inline-variable-return" +message = "Variable assignment can be inlined into the return statement." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "kan-defect" +message = "Class has a high kan defect score." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "no-array-accumulation-in-loop" +message = "`array_merge()` called inside a loop causes O(n²) array copying." +count = 2 + +[[issues]] +file = "src/Metadata/Source.php" +code = "no-else-clause" +message = "Avoid `else` clauses." +count = 3 + +[[issues]] +file = "src/Metadata/Source.php" +code = "no-empty" +message = "Use of the `empty` construct." +count = 3 + +[[issues]] +file = "src/Metadata/Source.php" +code = "no-isset" +message = "Use of the `isset` construct." +count = 5 + +[[issues]] +file = "src/Metadata/Source.php" +code = "no-multi-assignments" +message = "Avoid multiple assignments in a single statement." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "no-negated-ternary" +message = "Negated ternary condition." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "prefer-array-spread" +message = "Use the array spread operator instead of `array_merge()`." +count = 2 + +[[issues]] +file = "src/Metadata/Source.php" +code = "prefer-early-continue" +message = "Consider using early continue pattern to reduce nesting." +count = 2 + +[[issues]] +file = "src/Metadata/Source.php" +code = "string-style" +message = "String concatenation can be replaced with interpolation." +count = 9 + +[[issues]] +file = "src/Metadata/Source.php" +code = "tagged-todo" +message = "TODO should be tagged with (@username) or (#issue)." +count = 3 + +[[issues]] +file = "src/Metadata/Source.php" +code = "too-many-methods" +message = "Class has too many methods." +count = 1 + +[[issues]] +file = "src/Metadata/Source.php" +code = "yoda-conditions" +message = "Use Yoda condition style for safer comparisons" +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "assert-description" +message = "Missing description in assert function." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "no-else-clause" +message = "Avoid `elseif` clauses." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "no-empty-catch-clause" +message = "Do not use empty `catch` blocks." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "no-fully-qualified-global-class-like" +message = "Fully-qualified class-like reference detected." +count = 1 + +[[issues]] +file = "src/Pdo/Connection.php" +code = "string-style" +message = "String concatenation can be replaced with interpolation." +count = 2 + +[[issues]] +file = "src/Pdo/Driver.php" +code = "yoda-conditions" +message = "Use Yoda condition style for safer comparisons" +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "no-empty" +message = "Use of the `empty` construct." +count = 2 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "no-fully-qualified-global-class-like" +message = "Fully-qualified class-like reference detected." +count = 2 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "prefer-arrow-function" +message = "This closure can be simplified to a more concise arrow function." +count = 1 + +[[issues]] +file = "src/Pdo/Feature/SqliteRowCounter.php" +code = "string-style" +message = "String concatenation can be replaced with interpolation." +count = 2 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "no-negated-ternary" +message = "Negated ternary condition." +count = 1 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "string-style" +message = "String concatenation can be replaced with interpolation." +count = 4 + +[[issues]] +file = "src/Sql/SelectDecorator.php" +code = "yoda-conditions" +message = "Use Yoda condition style for safer comparisons" +count = 7 + +[[issues]] +file = "test/integration/AdapterPlatformTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 1 + +[[issues]] +file = "test/integration/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testQuoteValueWithPdoSqlite`." +count = 1 + +[[issues]] +file = "test/integration/AdapterPlatformTest.php" +code = "strict-assertions" +message = "Use strict assertions in PHPUnit tests." +count = 1 + +[[issues]] +file = "test/integration/Container/PdoConnectionFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 3 + +[[issues]] +file = "test/integration/Container/PdoConnectionFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsPdoConnection`." +count = 1 + +[[issues]] +file = "test/integration/Container/PdoDriverInterfaceFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 2 + +[[issues]] +file = "test/integration/Container/PdoDriverInterfaceFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsPdoDriver`." +count = 1 + +[[issues]] +file = "test/integration/Container/PdoResultFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 2 + +[[issues]] +file = "test/integration/Container/PdoResultFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsPdoResult`." +count = 1 + +[[issues]] +file = "test/integration/Container/PdoStatementFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 2 + +[[issues]] +file = "test/integration/Container/PdoStatementFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsPdoStatement`." +count = 1 + +[[issues]] +file = "test/integration/Container/SqliteMetadataFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 2 + +[[issues]] +file = "test/integration/Container/SqliteMetadataFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testFactoryReturnsMysqlMetadata`." +count = 1 + +[[issues]] +file = "test/integration/Container/TestAsset/SetupTrait.php" +code = "yoda-conditions" +message = "Use Yoda condition style for safer comparisons" +count = 1 + +[[issues]] +file = "test/integration/Pdo/AbstractAdapterTestCase.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 9 + +[[issues]] +file = "test/integration/Pdo/AbstractAdapterTestCase.php" +code = "braced-string-interpolation" +message = "Unbraced variable in string interpolation." +count = 1 + +[[issues]] +file = "test/integration/Pdo/AbstractAdapterTestCase.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConnection`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/AbstractAdapterTestCase.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testDriverDisconnectAfterQuoteWithPlatform`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/AbstractAdapterTestCase.php" +code = "yoda-conditions" +message = "Use Yoda condition style for safer comparisons" +count = 1 + +[[issues]] +file = "test/integration/Pdo/ConnectionIntegrationTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 11 + +[[issues]] +file = "test/integration/Pdo/ConnectionIntegrationTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConnectReturnsConnectionWhenResourceSet`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/ConnectionIntegrationTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConnect`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/ConnectionIntegrationTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testDisconnect`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/ConnectionIntegrationTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetCurrentSchema`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/ConnectionIntegrationTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetResource`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/ConnectionIntegrationTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testIsConnected`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/StatementTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 7 + +[[issues]] +file = "test/integration/Pdo/StatementTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testExecute`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/StatementTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetResource`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/StatementTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetSql`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/StatementTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testIsPrepared`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/StatementTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testPrepare`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/StatementTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetSql`." +count = 1 + +[[issues]] +file = "test/integration/Pdo/StatementTest.php" +code = "strict-assertions" +message = "Use strict assertions in PHPUnit tests." +count = 2 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 27 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "no-error-control-operator" +message = "Unsafe use of error control operator `@`." +count = 4 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructWithPdoDriver`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructWithPdo`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetIdentifierSeparator`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetNameReturnsSqlite`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetName`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetQuoteIdentifierSymbol`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetQuoteValueSymbol`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetSqlPlatformDecorator`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testPlatformNameConstant`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testQuoteIdentifierChain`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testQuoteIdentifierInFragment`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testQuoteIdentifier`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testQuoteTrustedValue`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testQuoteValueList`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testQuoteValueThrowsExeceptionWithoutDriverSupport`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testQuoteValue`." +count = 1 + +[[issues]] +file = "test/unit/AdapterPlatformTest.php" +code = "strict-assertions" +message = "Use strict assertions in PHPUnit tests." +count = 21 + +[[issues]] +file = "test/unit/ConfigProviderTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 13 + +[[issues]] +file = "test/unit/ConfigProviderTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetDependenciesContainsExpectedAliases`." +count = 1 + +[[issues]] +file = "test/unit/ConfigProviderTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetDependenciesContainsMetadataAlias`." +count = 1 + +[[issues]] +file = "test/unit/ConfigProviderTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetDependenciesContainsMetadataFactory`." +count = 1 + +[[issues]] +file = "test/unit/ConfigProviderTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetDependenciesReturnsCorrectStructure`." +count = 1 + +[[issues]] +file = "test/unit/ConfigProviderTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsCorrectStructure`." +count = 1 + +[[issues]] +file = "test/unit/ConfigProviderTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsExpectedStructure`." +count = 1 + +[[issues]] +file = "test/unit/Container/MetadataInterfaceFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 1 + +[[issues]] +file = "test/unit/Container/MetadataInterfaceFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsMetadata`." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoConnectionFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoConnectionFactoryTest.php" +code = "literal-named-argument" +message = "Literal argument `null` should be passed as a named argument for clarity." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoConnectionFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsConnection`." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoConnectionFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeWithNullOptionsThrows`." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoConnectionFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeWithoutConnectionConfigThrows`." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoDriverInterfaceFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoDriverInterfaceFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testFactoryExists`." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoResultFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoResultFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsResult`." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoStatementFactoryTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 2 + +[[issues]] +file = "test/unit/Container/PdoStatementFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsStatementWithEmptyOptions`." +count = 1 + +[[issues]] +file = "test/unit/Container/PdoStatementFactoryTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInvokeReturnsStatementWithOptions`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ConnectionTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 3 + +[[issues]] +file = "test/unit/Pdo/ConnectionTest.php" +code = "no-empty-catch-clause" +message = "Do not use empty `catch` blocks." +count = 2 + +[[issues]] +file = "test/unit/Pdo/ConnectionTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testArrayOfConnectionParametersCreatesCorrectDsn`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ConnectionTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetDsn`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ConnectionTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testResource`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/DriverTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 1 + +[[issues]] +file = "test/unit/Pdo/DriverTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testFormatParameterNameWithInvalidCharacters`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/DriverTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testFormatParameterName`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/Feature/SqliteRowCounterTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 1 + +[[issues]] +file = "test/unit/Pdo/Feature/SqliteRowCounterTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRowCounterExists`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "assert-description" +message = "Missing description in assert function." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 39 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-first-class-callable" +message = "Use first-class callable syntax `...` instead of a arrow function." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testBufferIsCallableWithNoEffect`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCountCachesResultFromClosure`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCountCachesResultFromStatementRowCount`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCountWithClosureInvokesClosureAndReturnsValue`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCountWithIntReturnsProvidedValue`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCountWithNoRowCountFallsBackToStatementRowCount`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testCurrentReturnsSameDataOnConsecutiveCalls`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testFetchModeAcceptsNamedMode`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testFetchModeObjReturnsStdClass`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetAffectedRowsDelegatesToRowCount`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetFetchModeDefaultIsAssoc`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetFieldCountDelegatesToColumnCount`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetGeneratedValueReturnsInitializedValue`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetGeneratedValueReturnsNullByDefault`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetQueryResultClonesGivenPrototypeRatherThanMutatingIt`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetQueryResultInitializesReturnedResultSetWithThisResult`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetQueryResultReturnsDefaultResultSetPrototypeWhenNoneGiven`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetQueryResultThrowsWhenResultIsNotAQueryResult`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testGetResourceReturnsPdoStatement`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testInitializeStoresResourceAndValues`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testIsBufferedReturnsFalse`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testIsQueryResultReturnsFalseWhenNoColumns`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testIsQueryResultReturnsTrueWhenColumnsExist`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testNextAdvancesPositionAndFetchesData`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRewindResetsIterationToStart`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testRewindThrowsExceptionOnForwardOnlyAfterAdvancing`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetFetchModeStoresValidMode`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetFetchModeThrowsOnInvalidFetchMode`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetFetchModeThrowsOnInvalidMode`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetStatementModeThrowsOnInvalidMode`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetStatementModeToForward`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetStatementModeToScrollable`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testValidReturnsFalseWhenCurrentDataIsFalse`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testValidReturnsTrueWhenCurrentDataExists`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/ResultTest.php" +code = "strict-assertions" +message = "Use strict assertions in PHPUnit tests." +count = 6 + +[[issues]] +file = "test/unit/Pdo/StatementIntegrationTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 12 + +[[issues]] +file = "test/unit/Pdo/StatementIntegrationTest.php" +code = "no-assign-in-argument" +message = "Avoid assignments in function call arguments." +count = 1 + +[[issues]] +file = "test/unit/Pdo/StatementIntegrationTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testStatementExecuteWillConvertPhpBoolToPdoBoolWhenBinding`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/StatementIntegrationTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testStatementExecuteWillUsePdoIntForIntWhenBinding`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/StatementIntegrationTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testStatementExecuteWillUsePdoStrByDefaultWhenBinding`." +count = 1 + +[[issues]] +file = "test/unit/Pdo/StatementIntegrationTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testStatementExecuteWillUsePdoStrForStringIntegerWhenBinding`." +count = 1 + +[[issues]] +file = "test/unit/Sql/PlatformTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 3 + +[[issues]] +file = "test/unit/Sql/PlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testConstructorSetsTypeDecorators`." +count = 1 + +[[issues]] +file = "test/unit/Sql/PlatformTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSelectDecoratorIsRegistered`." +count = 1 + +[[issues]] +file = "test/unit/Sql/SelectDecoratorTest.php" +code = "assertion-style" +message = "Inconsistent assertions style." +count = 13 + +[[issues]] +file = "test/unit/Sql/SelectDecoratorTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testProcessLimitWithLimitAndParameterContainer`." +count = 1 + +[[issues]] +file = "test/unit/Sql/SelectDecoratorTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testProcessLimitWithLimit`." +count = 1 + +[[issues]] +file = "test/unit/Sql/SelectDecoratorTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testProcessLimitWithoutLimitAndWithoutOffset`." +count = 1 + +[[issues]] +file = "test/unit/Sql/SelectDecoratorTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testProcessLimitWithoutLimitButWithOffset`." +count = 1 + +[[issues]] +file = "test/unit/Sql/SelectDecoratorTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testProcessOffsetWithOffsetAndParameterContainer`." +count = 1 + +[[issues]] +file = "test/unit/Sql/SelectDecoratorTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testProcessOffsetWithOffset`." +count = 1 + +[[issues]] +file = "test/unit/Sql/SelectDecoratorTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testProcessOffsetWithoutOffset`." +count = 1 + +[[issues]] +file = "test/unit/Sql/SelectDecoratorTest.php" +code = "prefer-test-attribute" +message = "Use `#[Test]` attribute instead of `test` prefix on method `testSetSubject`." +count = 1 diff --git a/mago.toml b/mago.toml new file mode 100644 index 0000000..ad23c4d --- /dev/null +++ b/mago.toml @@ -0,0 +1,12 @@ +extends = "vendor/php-db/phpdb-qa-tools/mago.toml" +php-version = "8.3.0" + +[source] +paths = ["src", "test"] +includes = ["vendor"] + +[linter] +baseline = "lint-baseline.toml" + +[analyzer] +baseline = "analyzer-baseline.toml" diff --git a/phpcs.xml.dist b/phpcs.xml.dist deleted file mode 100644 index 57d45ca..0000000 --- a/phpcs.xml.dist +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - src - test - - - - - - diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon deleted file mode 100644 index 364905f..0000000 --- a/phpstan-baseline.neon +++ /dev/null @@ -1,2 +0,0 @@ -parameters: - ignoreErrors: diff --git a/phpstan.neon.dist b/phpstan.neon.dist deleted file mode 100644 index 4b4e3f6..0000000 --- a/phpstan.neon.dist +++ /dev/null @@ -1,17 +0,0 @@ -includes: - - phpstan-baseline.neon -parameters: - level: 5 - paths: - - src - - test - universalObjectCratesClasses: - - Laminas\Stdlib\ArrayObject - stubFiles: - - stubs/Laminas/ServiceManager/Factory/AbstractFactoryInterface.stub - - stubs/Laminas/ServiceManager/Factory/DelegatorFactoryInterface.stub - - stubs/Laminas/ServiceManager/Factory/FactoryInterface.stub - - stubs/Laminas/ServiceManager/Factory/InvokableFactory.stub - - stubs/Laminas/ServiceManager/Initializer/InitializerInterface.stub - - stubs/Psr/Container/ContainerInterface.stub - treatPhpDocTypesAsCertain: false \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1d24c33..0e7051d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,15 +1,22 @@ - +> ./test/unit @@ -18,9 +25,13 @@ ./test/integration - + + + + + - ./src + ./src - \ No newline at end of file + diff --git a/test/unit/Pdo/ResultTest.php b/test/unit/Pdo/ResultTest.php new file mode 100644 index 0000000..84ffd89 --- /dev/null +++ b/test/unit/Pdo/ResultTest.php @@ -0,0 +1,453 @@ +buffer(); + + self::assertFalse($result->isBuffered()); + } + + public function testCountCachesResultFromClosure(): void + { + $callCount = 0; + $rowCount = static function () use (&$callCount): int { + $callCount++; + return 7; + }; + + $stub = $this->getMockBuilder(PDOStatement::class)->getMock(); + + $result = new Result(); + $result->initialize($stub, null, $rowCount); + + $result->count(); + $result->count(); + + self::assertSame(1, $callCount); + } + + public function testCountCachesResultFromStatementRowCount(): void + { + $stub = $this->getMockBuilder(PDOStatement::class)->getMock(); + $stub->expects($this->once())->method('rowCount')->willReturn(3); + + $result = new Result(); + $result->initialize($stub, null); + + $result->count(); + $result->count(); + + self::assertSame(3, $result->count()); + } + + public function testCountWithClosureInvokesClosureAndReturnsValue(): void + { + $stub = $this->getMockBuilder(PDOStatement::class)->getMock(); + $stub->expects($this->never())->method('rowCount'); + + $rowCount = static fn(): int => 42; + + $result = new Result(); + $result->initialize($stub, null, $rowCount); + + self::assertSame(42, $result->count()); + } + + public function testCountWithIntReturnsProvidedValue(): void + { + $stub = $this->getMockBuilder(PDOStatement::class)->getMock(); + $stub->expects($this->never())->method('rowCount'); + + $result = new Result(); + $result->initialize($stub, null, 10); + + self::assertSame(10, $result->count()); + } + + public function testCountWithNoRowCountFallsBackToStatementRowCount(): void + { + $stub = $this->getMockBuilder(PDOStatement::class)->getMock(); + $stub->expects($this->once()) + ->method('rowCount') + ->willReturn(5); + + $result = new Result(); + $result->initialize($stub, null); + + self::assertSame(5, $result->count()); + } + + /** + * Tests current method returns same data on consecutive calls. + */ + public function testCurrentReturnsSameDataOnConsecutiveCalls(): void + { + $stub = $this->getMockBuilder('PDOStatement')->getMock(); + $stub->expects($this->any()) + ->method('fetch') + ->willReturnCallback(static fn() => uniqid()); + + $result = new Result(); + $result->initialize($stub, null); + + self::assertEquals($result->current(), $result->current()); + } + + /** + * Tests whether the fetch mode has a broader range + */ + public function testFetchModeAcceptsNamedMode(): void + { + $stub = $this->getMockBuilder('PDOStatement')->getMock(); + $stub->expects($this->any()) + ->method('fetch') + ->willReturnCallback(static fn() => new stdClass()); + $result = new Result(); + $result->initialize($stub, null); + $result->setFetchMode(PDO::FETCH_NAMED); + self::assertEquals(11, $result->getFetchMode()); + self::assertInstanceOf('stdClass', $result->current()); + } + + /** + * Tests whether the fetch mode was set properly and + */ + public function testFetchModeObjReturnsStdClass(): void + { + $stub = $this->getMockBuilder('PDOStatement')->getMock(); + $stub->expects($this->any()) + ->method('fetch') + ->willReturnCallback(static fn() => new stdClass()); + + $result = new Result(); + $result->initialize($stub, null); + $result->setFetchMode(PDO::FETCH_OBJ); + + self::assertEquals(5, $result->getFetchMode()); + self::assertInstanceOf('stdClass', $result->current()); + } + + public function testGetAffectedRowsDelegatesToRowCount(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('rowCount')->willReturn(5); + + $result = new Result(); + $result->initialize($stub, null); + + self::assertSame(5, $result->getAffectedRows()); + } + + public function testGetFetchModeDefaultIsAssoc(): void + { + $result = new Result(); + + self::assertSame(PDO::FETCH_ASSOC, $result->getFetchMode()); + } + + public function testGetFieldCountDelegatesToColumnCount(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('columnCount')->willReturn(3); + + $result = new Result(); + $result->initialize($stub, null); + + self::assertSame(3, $result->getFieldCount()); + } + + public function testGetGeneratedValueReturnsInitializedValue(): void + { + $stub = $this->createMock(PDOStatement::class); + $result = new Result(); + $result->initialize($stub, 42); + + self::assertSame(42, $result->getGeneratedValue()); + } + + public function testGetGeneratedValueReturnsNullByDefault(): void + { + $result = new Result(); + + self::assertNull($result->getGeneratedValue()); + } + + public function testGetQueryResultClonesGivenPrototypeRatherThanMutatingIt(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('columnCount')->willReturn(3); + + $result = new Result(); + $result->initialize($stub, null); + $prototype = new TemporaryResultSet(); + + $returned = $result->getQueryResult($prototype); + + self::assertInstanceOf(TemporaryResultSet::class, $returned); + self::assertNotSame($prototype, $returned); + } + + public function testGetQueryResultInitializesReturnedResultSetWithThisResult(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('columnCount')->willReturn(3); + + $result = new Result(); + $result->initialize($stub, null); + + self::assertSame($result->getFieldCount(), $result->getQueryResult()->getFieldCount()); + } + + public function testGetQueryResultReturnsDefaultResultSetPrototypeWhenNoneGiven(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('columnCount')->willReturn(3); + + $result = new Result(); + $result->initialize($stub, null); + + self::assertInstanceOf(ResultSet::class, $result->getQueryResult()); + } + + public function testGetQueryResultThrowsWhenResultIsNotAQueryResult(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('columnCount')->willReturn(0); + + $result = new Result(); + $result->initialize($stub, null); + + $this->expectException(RuntimeException::class); + $result->getQueryResult(); + } + + public function testGetResourceReturnsPdoStatement(): void + { + $stub = $this->createMock(PDOStatement::class); + $result = new Result(); + $result->initialize($stub, null); + + self::assertSame($stub, $result->getResource()); + } + + public function testInitializeStoresResourceAndValues(): void + { + $stub = $this->createMock(PDOStatement::class); + $result = new Result(); + + $result->initialize($stub, 42, 5); + + self::assertSame(42, $result->getGeneratedValue()); + self::assertSame(5, $result->count()); + } + + public function testIsBufferedReturnsFalse(): void + { + $result = new Result(); + + self::assertFalse($result->isBuffered()); + } + + public function testIsQueryResultReturnsFalseWhenNoColumns(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('columnCount')->willReturn(0); + + $result = new Result(); + $result->initialize($stub, null); + + self::assertFalse($result->isQueryResult()); + } + + public function testIsQueryResultReturnsTrueWhenColumnsExist(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('columnCount')->willReturn(3); + + $result = new Result(); + $result->initialize($stub, null); + + self::assertTrue($result->isQueryResult()); + } + + public function testNextAdvancesPositionAndFetchesData(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('fetch')->willReturn(['name' => 'test']); + + $result = new Result(); + $result->initialize($stub, null); + + $result->rewind(); + self::assertSame(0, $result->key()); + + $result->next(); + self::assertSame(1, $result->key()); + } + + public function testRewindResetsIterationToStart(): void + { + $data = [ + ['test' => 1], + ['test' => 2], + ]; + $position = 0; + + $stub = $this->getMockBuilder('PDOStatement')->getMock(); + assert($stub instanceof PDOStatement); // to suppress IDE type warnings + $stub->expects($this->any()) + ->method('fetch') + ->willReturnCallback(static function () use ($data, &$position) { + return $data[$position++]; + }); + $result = new Result(); + $result->initialize($stub, null); + + $result->rewind(); + $result->rewind(); + + $this->assertEquals(0, $result->key()); + $this->assertEquals(1, $position); + $this->assertEquals($data[0], $result->current()); + + $result->next(); + $this->assertEquals(1, $result->key()); + $this->assertEquals(2, $position); + $this->assertEquals($data[1], $result->current()); + } + + public function testRewindThrowsExceptionOnForwardOnlyAfterAdvancing(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('fetch')->willReturn(['id' => 1]); + + $result = new Result(); + $result->initialize($stub, null); + $result->setStatementMode(Result::STATEMENT_MODE_FORWARD); + + $result->rewind(); + $result->next(); + + $this->expectException(RuntimeException::class); + $result->rewind(); + } + + public function testSetFetchModeStoresValidMode(): void + { + $result = new Result(); + $result->setFetchMode(PDO::FETCH_NUM); + + self::assertSame(PDO::FETCH_NUM, $result->getFetchMode()); + } + + public function testSetFetchModeThrowsOnInvalidFetchMode(): void + { + $result = new Result(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The fetch mode must be one of the PDO::FETCH_* constants.'); + + $result->setFetchMode(9999); + } + + public function testSetFetchModeThrowsOnInvalidMode(): void + { + $result = new Result(); + + $this->expectException(InvalidArgumentException::class); + $result->setFetchMode(13); + } + + public function testSetStatementModeThrowsOnInvalidMode(): void + { + $result = new Result(); + + $this->expectException(InvalidArgumentException::class); + $result->setStatementMode('invalid'); + } + + public function testSetStatementModeToForward(): void + { + $result = new Result(); + + $result->setStatementMode(Result::STATEMENT_MODE_FORWARD); + + self::assertSame(Result::STATEMENT_MODE_FORWARD, $result->getStatementMode()); + } + + public function testSetStatementModeToScrollable(): void + { + $result = new Result(); + + $result->setStatementMode(Result::STATEMENT_MODE_SCROLLABLE); + + self::assertSame(Result::STATEMENT_MODE_SCROLLABLE, $result->getStatementMode()); + } + + public function testValidReturnsFalseWhenCurrentDataIsFalse(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('fetch')->willReturn(false); + + $result = new Result(); + $result->initialize($stub, null); + $result->rewind(); + + self::assertFalse($result->valid()); + } + + public function testValidReturnsTrueWhenCurrentDataExists(): void + { + $stub = $this->createMock(PDOStatement::class); + $stub->method('fetch')->willReturn(['id' => 1]); + + $result = new Result(); + $result->initialize($stub, null); + $result->rewind(); + + self::assertTrue($result->valid()); + } +} diff --git a/test/unit/Pdo/TestAsset/TemporaryResultSet.php b/test/unit/Pdo/TestAsset/TemporaryResultSet.php new file mode 100644 index 0000000..e25ca57 --- /dev/null +++ b/test/unit/Pdo/TestAsset/TemporaryResultSet.php @@ -0,0 +1,9 @@ + Date: Mon, 24 Aug 2026 16:11:19 +1000 Subject: [PATCH 2/3] Apply mago formatting --- src/AdapterPlatform.php | 50 +-- src/ConfigProvider.php | 16 +- src/Container/MetadataInterfaceFactory.php | 3 +- src/Container/PdoConnectionFactory.php | 4 +- src/Container/PdoDriverInterfaceFactory.php | 6 +- src/Container/PdoStatementFactory.php | 2 +- src/Container/PlatformInterfaceFactory.php | 4 +- src/Metadata/Source.php | 305 +++++++++--------- src/Pdo/Connection.php | 43 +-- src/Pdo/Feature/SqliteRowCounter.php | 28 +- src/Sql/SelectDecorator.php | 16 +- .../PdoDriverInterfaceFactoryTest.php | 2 +- .../Container/TestAsset/SetupTrait.php | 20 +- .../Pdo/ConnectionIntegrationTest.php | 58 ++-- test/integration/Pdo/StatementTest.php | 38 +-- test/unit/AdapterPlatformTest.php | 156 ++++----- test/unit/ConfigProviderTest.php | 59 ++-- .../Container/PdoConnectionFactoryTest.php | 8 +- .../Container/PdoStatementFactoryTest.php | 8 +- test/unit/Pdo/ConnectionTest.php | 56 ++-- test/unit/Pdo/DriverTest.php | 80 ++--- .../unit/Pdo/Feature/SqliteRowCounterTest.php | 8 +- test/unit/Pdo/StatementIntegrationTest.php | 72 +++-- test/unit/Pdo/TestAsset/CtorlessPdo.php | 6 +- test/unit/Sql/PlatformTest.php | 10 +- test/unit/Sql/SelectDecoratorTest.php | 76 ++--- 26 files changed, 579 insertions(+), 555 deletions(-) diff --git a/src/AdapterPlatform.php b/src/AdapterPlatform.php index 220c020..fc64afd 100644 --- a/src/AdapterPlatform.php +++ b/src/AdapterPlatform.php @@ -13,7 +13,8 @@ class AdapterPlatform extends AbstractPlatform { - public final const PLATFORM_NAME = 'SQLite'; + final public const PLATFORM_NAME = 'SQLite'; + /** @var string[] */ protected array $quoteIdentifier = ['"', '"']; @@ -27,27 +28,25 @@ class AdapterPlatform extends AbstractPlatform protected string $quoteIdentifierTo = '\''; public function __construct( - protected readonly PdoDriverInterface|PDO|null $driver = null - ) { - } + protected readonly PdoDriverInterface|PDO|null $driver = null, + ) {} /** * {@inheritDoc} */ #[Override] - public function quoteValue(string $value): string + public function getName(): string { - $resource = $this->resource; - - if ($resource instanceof PdoDriverInterface) { - $resource = $resource->getConnection()->getResource(); - } - - if ($resource instanceof PDO) { - return $resource->quote($value); - } + return self::PLATFORM_NAME; + } - return parent::quoteValue($value); + /** + * {@inheritDoc} + */ + #[Override] + public function getSqlPlatformDecorator(): PlatformDecoratorInterface + { + return new Platform(); } /** @@ -73,17 +72,18 @@ public function quoteTrustedValue(int|float|string|bool $value): ?string * {@inheritDoc} */ #[Override] - public function getName(): string + public function quoteValue(string $value): string { - return self::PLATFORM_NAME; - } + $resource = $this->resource; - /** - * {@inheritDoc} - */ - #[Override] - public function getSqlPlatformDecorator(): PlatformDecoratorInterface - { - return new Platform(); + if ($resource instanceof PdoDriverInterface) { + $resource = $resource->getConnection()->getResource(); + } + + if ($resource instanceof PDO) { + return $resource->quote($value); + } + + return parent::quoteValue($value); } } diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 2101a4d..b240998 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -15,14 +15,6 @@ final class ConfigProvider { - public function __invoke(): array - { - return [ - 'dependencies' => $this->getDependencies(), - //AdapterInterface::class => $this->getConfig(), - ]; - } - public function getConfig(): array { return [ @@ -61,4 +53,12 @@ public function getDependencies(): array ], ]; } + + public function __invoke(): array + { + return [ + 'dependencies' => $this->getDependencies(), + //AdapterInterface::class => $this->getConfig(), + ]; + } } diff --git a/src/Container/MetadataInterfaceFactory.php b/src/Container/MetadataInterfaceFactory.php index bd4f615..4557a03 100644 --- a/src/Container/MetadataInterfaceFactory.php +++ b/src/Container/MetadataInterfaceFactory.php @@ -12,10 +12,11 @@ final class MetadataInterfaceFactory { public const ADAPTER_SERVICE_NAME = 'adapter_service_name'; + public function __invoke( ContainerInterface $container, string $requestedName, - ?array $options = null + ?array $options = null, ): MetadataInterface&Metadata\Source { $adapterServiceName = $options[self::ADAPTER_SERVICE_NAME] ?? AdapterInterface::class; diff --git a/src/Container/PdoConnectionFactory.php b/src/Container/PdoConnectionFactory.php index 7957e26..6fc6972 100644 --- a/src/Container/PdoConnectionFactory.php +++ b/src/Container/PdoConnectionFactory.php @@ -16,13 +16,13 @@ final class PdoConnectionFactory public function __invoke( ContainerInterface $container, string $requestedName, - ?array $options = null + ?array $options = null, ): PdoConnectionInterface&Connection { $conn = $options['connection'] ?? []; if (! is_array($conn) || $conn === []) { throw new InvalidConnectionParametersException( 'Connection configuration must be an array of parameters passed via $options["connection"]', - $conn + $conn, ); } diff --git a/src/Container/PdoDriverInterfaceFactory.php b/src/Container/PdoDriverInterfaceFactory.php index f32715b..bfd5366 100644 --- a/src/Container/PdoDriverInterfaceFactory.php +++ b/src/Container/PdoDriverInterfaceFactory.php @@ -17,12 +17,12 @@ final class PdoDriverInterfaceFactory public function __invoke( ContainerInterface&ServiceManager $container, string $requestedName, - ?array $options = null + ?array $options = null, ): PdoDriverInterface&Pdo\Driver { /** @var Pdo\Connection $connectionInstance */ $connectionInstance = $container->build( Pdo\Connection::class, - $options + $options, ); /** @var ResultInterface&Result $resultInstance */ @@ -31,7 +31,7 @@ public function __invoke( : new Result(); return new Pdo\Driver( - connection:$connectionInstance, + connection: $connectionInstance, statementPrototype: $container->build(Statement::class, $options['options'] ?? []), resultPrototype: $resultInstance, features: [new Pdo\Feature\SqliteRowCounter()], diff --git a/src/Container/PdoStatementFactory.php b/src/Container/PdoStatementFactory.php index 270fea3..690cd7a 100644 --- a/src/Container/PdoStatementFactory.php +++ b/src/Container/PdoStatementFactory.php @@ -13,7 +13,7 @@ final class PdoStatementFactory public function __invoke( ContainerInterface $container, string $requestedName, - ?array $options = null + ?array $options = null, ): StatementInterface&Statement { return new Statement(options: $options); } diff --git a/src/Container/PlatformInterfaceFactory.php b/src/Container/PlatformInterfaceFactory.php index 9407dc0..593735e 100644 --- a/src/Container/PlatformInterfaceFactory.php +++ b/src/Container/PlatformInterfaceFactory.php @@ -15,14 +15,14 @@ final class PlatformInterfaceFactory public function __invoke( ContainerInterface $container, string $requestedName, - ?array $options = null + ?array $options = null, ): PlatformInterface&AdapterPlatform { $driverInstance = $options['driver'] ?? null; if (! $driverInstance instanceof Driver) { throw ContainerException::forService( AdapterPlatform::class, self::class, - 'Invalid or missing driver provided recieved: ' + 'Invalid or missing driver provided recieved: ', ); } return new AdapterPlatform($driverInstance); diff --git a/src/Metadata/Source.php b/src/Metadata/Source.php index 0f905b2..9f1cbd5 100644 --- a/src/Metadata/Source.php +++ b/src/Metadata/Source.php @@ -19,65 +19,78 @@ final class Source extends AbstractSource { - #[Override] - protected function loadSchemaData(): void + protected function buildRegularExpression(array $re): string { - if (isset($this->data['schemas'])) { - return; - } - $this->prepareDataHierarchy('schemas'); - - $results = $this->fetchPragma('database_list'); - // proposed phpstan fix - $schemas = $results !== [] ? [] : null; - // end proposed fix - foreach ($results as $row) { - $schemas[] = $row['name']; + foreach ($re as &$value) { + if (is_array($value)) { + $value = '(?:' . implode('\\s*+', $value) . '\\s*+)?'; + } else { + $value .= '\\s*+'; + } } - $this->data['schemas'] = $schemas; + unset($value); + $re = '/^' . implode('\\s*+', $re) . '$/'; + return $re; } - #[Override] - protected function loadTableNameData(string $schema): void + protected function fetchPragma(string $name, ?string $value = null, ?string $schema = null): array { - if (isset($this->data['table_names'][$schema])) { - return; - } - $this->prepareDataHierarchy('table_names', $schema); + $p = $this->adapter->getPlatform(); - // FEATURE: Filename? + $sql = 'PRAGMA '; - $p = $this->adapter->getPlatform(); + if (null !== $schema) { + $sql .= $p->quoteIdentifier($schema) . '.'; + } + $sql .= $name; - $sql = 'SELECT "name", "type", "sql" FROM ' . $p->quoteIdentifierChain([$schema, 'sqlite_master']) - . ' WHERE "type" IN (\'table\',\'view\') AND "name" NOT LIKE \'sqlite_%\''; + if (null !== $value) { + $sql .= '(' . $p->quoteTrustedValue($value) . ')'; + } /** @var ResultSet $results */ $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE); - $tables = []; - foreach ($results->toArray() as $row) { - if ('table' === $row['type']) { - $table = [ - 'table_type' => 'BASE TABLE', - 'view_definition' => null, // VIEW only - 'check_option' => null, // VIEW only - 'is_updatable' => null, // VIEW only - ]; - } else { - $table = [ - 'table_type' => 'VIEW', - 'view_definition' => null, - 'check_option' => 'NONE', - 'is_updatable' => false, - ]; + if ($results instanceof ResultSetInterface) { + return $results->toArray(); + } + return []; + } - // Parse out extra data - if (null !== ($data = $this->parseView($row['sql']))) { - $table = array_merge($table, $data); - } - } - $tables[$row['name']] = $table; + protected function getIdentifierChainRegularExpression(): string + { + static $re = null; + if (null === $re) { + $identifier = $this->getIdentifierRegularExpression(); + $re = $identifier . '(?:\\s*\\.\\s*' . $identifier . ')*+'; } - $this->data['table_names'][$schema] = $tables; + return $re; + } + + protected function getIdentifierListRegularExpression(): string + { + static $re = null; + if (null === $re) { + $identifier = $this->getIdentifierRegularExpression(); + $re = $identifier . '(?:\\s*,\\s*' . $identifier . ')*+'; + } + return $re; + } + + protected function getIdentifierRegularExpression(): string + { + static $re = null; + if (null === $re) { + $re = + '(?:' + . implode('|', [ + '"(?:[^"\\\\]++|\\\\.)*+"', + '`(?:[^`]++|``)*+`', + '\\[[^\\]]+\\]', + '[^\\s\\.]+', + ]) + . ')'; + } + + return $re; } #[Override] @@ -107,6 +120,7 @@ protected function loadColumnData(string $table, string $schema): void 'numeric_unsigned' => null, 'erratas' => [], ]; + // TODO: populate character_ and numeric_values with correct info } @@ -199,6 +213,69 @@ protected function loadConstraintData(string $table, string $schema): void $this->data['constraints'][$schema][$table] = $constraints; } + #[Override] + protected function loadSchemaData(): void + { + if (isset($this->data['schemas'])) { + return; + } + $this->prepareDataHierarchy('schemas'); + + $results = $this->fetchPragma('database_list'); + // proposed phpstan fix + $schemas = $results !== [] ? [] : null; + // end proposed fix + foreach ($results as $row) { + $schemas[] = $row['name']; + } + $this->data['schemas'] = $schemas; + } + + #[Override] + protected function loadTableNameData(string $schema): void + { + if (isset($this->data['table_names'][$schema])) { + return; + } + $this->prepareDataHierarchy('table_names', $schema); + + // FEATURE: Filename? + + $p = $this->adapter->getPlatform(); + + $sql = + 'SELECT "name", "type", "sql" FROM ' + . $p->quoteIdentifierChain([$schema, 'sqlite_master']) + . ' WHERE "type" IN (\'table\',\'view\') AND "name" NOT LIKE \'sqlite_%\''; + /** @var ResultSet $results */ + $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE); + $tables = []; + foreach ($results->toArray() as $row) { + if ('table' === $row['type']) { + $table = [ + 'table_type' => 'BASE TABLE', + 'view_definition' => null, // VIEW only + 'check_option' => null, // VIEW only + 'is_updatable' => null, // VIEW only + ]; + } else { + $table = [ + 'table_type' => 'VIEW', + 'view_definition' => null, + 'check_option' => 'NONE', + 'is_updatable' => false, + ]; + + // Parse out extra data + if (null !== ($data = $this->parseView($row['sql']))) { + $table = array_merge($table, $data); + } + } + $tables[$row['name']] = $table; + } + $this->data['table_names'][$schema] = $tables; + } + #[Override] protected function loadTriggerData(string $schema): void { @@ -210,24 +287,25 @@ protected function loadTriggerData(string $schema): void $p = $this->adapter->getPlatform(); - $sql = 'SELECT "name", "tbl_name", "sql" FROM ' - . $p->quoteIdentifierChain([$schema, 'sqlite_master']) - . ' WHERE "type" = \'trigger\''; + $sql = + 'SELECT "name", "tbl_name", "sql" FROM ' + . $p->quoteIdentifierChain([$schema, 'sqlite_master']) + . ' WHERE "type" = \'trigger\''; /** @var ResultSet $results */ $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE); $triggers = []; foreach ($results->toArray() as $row) { $trigger = [ - 'trigger_name' => $row['name'], - 'event_manipulation' => null, // in $row['sql'] - 'event_object_catalog' => null, - 'event_object_schema' => $schema, - 'event_object_table' => $row['tbl_name'], - 'action_order' => 0, - 'action_condition' => null, // in $row['sql'] - 'action_statement' => null, // in $row['sql'] - 'action_orientation' => 'ROW', - 'action_timing' => null, // in $row['sql'] + 'trigger_name' => $row['name'], + 'event_manipulation' => null, // in $row['sql'] + 'event_object_catalog' => null, + 'event_object_schema' => $schema, + 'event_object_table' => $row['tbl_name'], + 'action_order' => 0, + 'action_condition' => null, // in $row['sql'] + 'action_statement' => null, // in $row['sql'] + 'action_orientation' => 'ROW', + 'action_timing' => null, // in $row['sql'] 'action_reference_old_table' => null, 'action_reference_new_table' => null, 'action_reference_old_row' => 'OLD', @@ -245,55 +323,6 @@ protected function loadTriggerData(string $schema): void $this->data['triggers'][$schema] = $triggers; } - protected function fetchPragma(string $name, ?string $value = null, ?string $schema = null): array - { - $p = $this->adapter->getPlatform(); - - $sql = 'PRAGMA '; - - if (null !== $schema) { - $sql .= $p->quoteIdentifier($schema) . '.'; - } - $sql .= $name; - - if (null !== $value) { - $sql .= '(' . $p->quoteTrustedValue($value) . ')'; - } - /** @var ResultSet $results */ - $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE); - if ($results instanceof ResultSetInterface) { - return $results->toArray(); - } - return []; - } - - /** @return null|array */ - protected function parseView(string $sql): ?array - { - static $re = null; - if (null === $re) { - $identifierChain = $this->getIdentifierChainRegularExpression(); - $re = $this->buildRegularExpression([ - 'CREATE', - ['TEMP|TEMPORARY'], - 'VIEW', - ['IF', 'NOT', 'EXISTS'], - $identifierChain, - 'AS', - '(?.+)', - [';'], - ]); - } - - if (! preg_match($re, $sql, $matches)) { - return null; - } - - return [ - 'view_definition' => $matches['view_definition'], - ]; - } - /** @return null|array */ protected function parseTrigger(string $sql): ?array { @@ -353,52 +382,30 @@ protected function parseTrigger(string $sql): ?array return $data; } - protected function buildRegularExpression(array $re): string - { - foreach ($re as &$value) { - if (is_array($value)) { - $value = '(?:' . implode('\\s*+', $value) . '\\s*+)?'; - } else { - $value .= '\\s*+'; - } - } - unset($value); - $re = '/^' . implode('\\s*+', $re) . '$/'; - return $re; - } - - protected function getIdentifierRegularExpression(): string + /** @return null|array */ + protected function parseView(string $sql): ?array { static $re = null; if (null === $re) { - $re = '(?:' . implode('|', [ - '"(?:[^"\\\\]++|\\\\.)*+"', - '`(?:[^`]++|``)*+`', - '\\[[^\\]]+\\]', - '[^\\s\\.]+', - ]) . ')'; + $identifierChain = $this->getIdentifierChainRegularExpression(); + $re = $this->buildRegularExpression([ + 'CREATE', + ['TEMP|TEMPORARY'], + 'VIEW', + ['IF', 'NOT', 'EXISTS'], + $identifierChain, + 'AS', + '(?.+)', + [';'], + ]); } - return $re; - } - - protected function getIdentifierChainRegularExpression(): string - { - static $re = null; - if (null === $re) { - $identifier = $this->getIdentifierRegularExpression(); - $re = $identifier . '(?:\\s*\\.\\s*' . $identifier . ')*+'; + if (! preg_match($re, $sql, $matches)) { + return null; } - return $re; - } - protected function getIdentifierListRegularExpression(): string - { - static $re = null; - if (null === $re) { - $identifier = $this->getIdentifierRegularExpression(); - $re = $identifier . '(?:\\s*,\\s*' . $identifier . ')*+'; - } - return $re; + return [ + 'view_definition' => $matches['view_definition'], + ]; } } diff --git a/src/Pdo/Connection.php b/src/Pdo/Connection.php index 1442d47..f63ac1c 100644 --- a/src/Pdo/Connection.php +++ b/src/Pdo/Connection.php @@ -22,10 +22,10 @@ class Connection extends AbstractPdoConnection { - public final const CURRENT_SCHEMA = 'main'; + final public const CURRENT_SCHEMA = 'main'; public function __construct( - PDO|array $connectionParameters + PDO|array $connectionParameters, ) { if (is_array($connectionParameters)) { $this->setConnectionParameters($connectionParameters); @@ -34,19 +34,6 @@ public function __construct( } } - /** - * {@inheritDoc} - */ - #[Override] - public function getCurrentSchema(): string|false - { - if (! $this->isConnected()) { - $this->connect(); - } - - return self::CURRENT_SCHEMA; - } - /** * {@inheritDoc} * @@ -85,22 +72,22 @@ public function connect(): ConnectionInterface if (! is_string($dsn)) { throw new Exception\InvalidConnectionParametersException( 'A dsn was not provided', - $this->connectionParameters + $this->connectionParameters, ); } if (! str_starts_with($dsn, 'sqlite:')) { Assert::fileExists( $dsn, - 'The provided DSN does not point to a valid file.' + 'The provided DSN does not point to a valid file.', ); Assert::readable( $dsn, - 'The provided DSN does not point to a readable file.' + 'The provided DSN does not point to a readable file.', ); Assert::writable( $dsn, - 'The provided DSN does not point to a writable file.' + 'The provided DSN does not point to a writable file.', ); $dsn = 'sqlite:' . $dsn; } @@ -108,7 +95,10 @@ public function connect(): ConnectionInterface $this->dsn = $dsn; try { - $this->resource = new PDO(dsn: $dsn, options: $options); + $this->resource = new PDO( + dsn: $dsn, + options: $options, + ); $this->resource->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $driverName = $this->resource->getAttribute(PDO::ATTR_DRIVER_NAME); assert(is_string($driverName)); @@ -124,6 +114,19 @@ public function connect(): ConnectionInterface return $this; } + /** + * {@inheritDoc} + */ + #[Override] + public function getCurrentSchema(): string|false + { + if (! $this->isConnected()) { + $this->connect(); + } + + return self::CURRENT_SCHEMA; + } + /** * {@inheritDoc} * diff --git a/src/Pdo/Feature/SqliteRowCounter.php b/src/Pdo/Feature/SqliteRowCounter.php index 9b9238f..ce2ee98 100644 --- a/src/Pdo/Feature/SqliteRowCounter.php +++ b/src/Pdo/Feature/SqliteRowCounter.php @@ -17,32 +17,32 @@ */ class SqliteRowCounter extends AbstractFeature { - public function getCountForStatement(Pdo\Statement $statement): int + public function getCountForSql(string $sql): int { - $countStmt = clone $statement; - $sql = $statement->getSql(); if (empty($sql) || ! str_contains(strtolower($sql), 'select')) { return 0; } - $countSql = 'SELECT COUNT(*) as "count" FROM (' . $sql . ')'; - $countStmt->prepare($countSql); - $result = $countStmt->execute(); - $countRow = $result->getResource()->fetch(\PDO::FETCH_ASSOC); - unset($statement, $result); + $countSql = 'SELECT COUNT(*) as count FROM (' . $sql . ')'; + /** @var \PDO $pdo */ + $pdo = $this->driver->getConnection()->getResource(); + $result = $pdo->query($countSql); + $countRow = $result->fetch(\PDO::FETCH_ASSOC); return (int) $countRow['count']; } - public function getCountForSql(string $sql): int + public function getCountForStatement(Pdo\Statement $statement): int { + $countStmt = clone $statement; + $sql = $statement->getSql(); if (empty($sql) || ! str_contains(strtolower($sql), 'select')) { return 0; } - $countSql = 'SELECT COUNT(*) as count FROM (' . $sql . ')'; - /** @var \PDO $pdo */ - $pdo = $this->driver->getConnection()->getResource(); - $result = $pdo->query($countSql); - $countRow = $result->fetch(\PDO::FETCH_ASSOC); + $countSql = 'SELECT COUNT(*) as "count" FROM (' . $sql . ')'; + $countStmt->prepare($countSql); + $result = $countStmt->execute(); + $countRow = $result->getResource()->fetch(\PDO::FETCH_ASSOC); + unset($statement, $result); return (int) $countRow['count']; } diff --git a/src/Sql/SelectDecorator.php b/src/Sql/SelectDecorator.php index 6013cd0..5c9e21d 100644 --- a/src/Sql/SelectDecorator.php +++ b/src/Sql/SelectDecorator.php @@ -37,8 +37,8 @@ protected function localizeVariables(): void /** @return null|string[]|int[] */ protected function processLimit( PlatformInterface $platform, - DriverInterface|null $driver = null, - ParameterContainer|null $parameterContainer = null + ?DriverInterface $driver = null, + ?ParameterContainer $parameterContainer = null, ): ?array { if ($this->limit === null && $this->offset !== null) { return ['']; @@ -49,9 +49,11 @@ protected function processLimit( if ($parameterContainer) { $paramPrefix = (string) $this->processInfo['paramPrefix']; $parameterContainer->offsetSet($paramPrefix . 'limit', $this->limit, ParameterContainer::TYPE_INTEGER); - return $driver !== null - ? [$driver->formatParameterName($paramPrefix . 'limit')] - : null; + return ( + $driver !== null + ? [$driver->formatParameterName($paramPrefix . 'limit')] + : null + ); } return [$this->limit]; @@ -59,8 +61,8 @@ protected function processLimit( protected function processOffset( PlatformInterface $platform, - DriverInterface|null $driver = null, - ParameterContainer|null $parameterContainer = null + ?DriverInterface $driver = null, + ?ParameterContainer $parameterContainer = null, ): ?array { if ($this->offset === null) { return null; diff --git a/test/integration/Container/PdoDriverInterfaceFactoryTest.php b/test/integration/Container/PdoDriverInterfaceFactoryTest.php index bbdea71..cddf092 100644 --- a/test/integration/Container/PdoDriverInterfaceFactoryTest.php +++ b/test/integration/Container/PdoDriverInterfaceFactoryTest.php @@ -27,7 +27,7 @@ public function testInvokeReturnsPdoDriver(): void $instance = $factory( $this->container, Driver::class, - $this->container->get('config')[AdapterInterface::class] + $this->container->get('config')[AdapterInterface::class], ); self::assertInstanceOf(PdoDriverInterface::class, $instance); diff --git a/test/integration/Container/TestAsset/SetupTrait.php b/test/integration/Container/TestAsset/SetupTrait.php index 7c1fe68..ddc9b89 100644 --- a/test/integration/Container/TestAsset/SetupTrait.php +++ b/test/integration/Container/TestAsset/SetupTrait.php @@ -34,13 +34,6 @@ trait SetupTrait protected DriverInterface|string|null $driver; - #[Override] - protected function setUp(): void - { - $this->getAdapter(); - parent::setUp(); - } - protected function getAdapter(array $config = []): AdapterInterface { $connectionConfig = [ @@ -60,12 +53,12 @@ protected function getAdapter(array $config = []): AdapterInterface // merge service config from both PhpDb and PhpDb\Sqlite $serviceManagerConfig = ArrayUtils::merge( (new LaminasDbConfigProvider())()['dependencies'], - (new ConfigProvider())()['dependencies'] + (new ConfigProvider())()['dependencies'], ); $serviceManagerConfig = ArrayUtils::merge( $serviceManagerConfig, - $connectionConfig + $connectionConfig, ); // prefer passed config over environment variables @@ -79,7 +72,7 @@ protected function getAdapter(array $config = []): AdapterInterface 'services' => [ 'config' => $serviceManagerConfig, ], - ] + ], ); $this->config = $serviceManagerConfig; @@ -98,4 +91,11 @@ protected function getHostname(): string { return $this->getConfig()['db']['connection']['hostname']; } + + #[Override] + protected function setUp(): void + { + $this->getAdapter(); + parent::setUp(); + } } diff --git a/test/integration/Pdo/ConnectionIntegrationTest.php b/test/integration/Pdo/ConnectionIntegrationTest.php index 7b08555..5815f15 100644 --- a/test/integration/Pdo/ConnectionIntegrationTest.php +++ b/test/integration/Pdo/ConnectionIntegrationTest.php @@ -30,64 +30,65 @@ final class ConnectionIntegrationTest extends TestCase { use SetupTrait; - public function testGetCurrentSchema(): void + public function testConnect(): void { $connection = $this->getAdapter()->getDriver()->getConnection(); - self::assertIsString($connection->getCurrentSchema()); + self::assertSame($connection, $connection->connect()); + self::assertTrue($connection->isConnected()); + + $connection->disconnect(); } - public function testGetResource(): void + public function testConnectReturnsConnectionWhenResourceSet(): void { + /** @var PDO $resource */ + $resource = $this->getAdapter()->getDriver()->getConnection()->getResource(); + /** @var PdoConnectionInterface&Connection $connection */ $connection = $this->getAdapter()->getDriver()->getConnection(); - $connection->connect(); - - self::assertInstanceOf(PDO::class, $connection->getResource()); + self::assertInstanceOf(PdoConnectionInterface::class, $connection); + $connection->setResource($resource); + self::assertSame($connection, $connection->connect()); $connection->disconnect(); + unset($connection); + unset($resource); } - public function testConnect(): void + public function testDisconnect(): void { $connection = $this->getAdapter()->getDriver()->getConnection(); - self::assertSame($connection, $connection->connect()); + $connection->connect(); self::assertTrue($connection->isConnected()); - $connection->disconnect(); + self::assertFalse($connection->isConnected()); } - public function testIsConnected(): void + public function testGetCurrentSchema(): void { $connection = $this->getAdapter()->getDriver()->getConnection(); - self::assertFalse($connection->isConnected()); - self::assertSame($connection, $connection->connect()); - self::assertTrue($connection->isConnected()); - - $connection->disconnect(); - //unset($connection); + self::assertIsString($connection->getCurrentSchema()); } - public function testDisconnect(): void + public function testGetResource(): void { $connection = $this->getAdapter()->getDriver()->getConnection(); $connection->connect(); - self::assertTrue($connection->isConnected()); + + self::assertInstanceOf(PDO::class, $connection->getResource()); + $connection->disconnect(); - self::assertFalse($connection->isConnected()); } - public function testConnectReturnsConnectionWhenResourceSet(): void + public function testIsConnected(): void { - /** @var PDO $resource */ - $resource = $this->getAdapter()->getDriver()->getConnection()->getResource(); - /** @var PdoConnectionInterface&Connection $connection */ $connection = $this->getAdapter()->getDriver()->getConnection(); - self::assertInstanceOf(PdoConnectionInterface::class, $connection); - $connection->setResource($resource); + self::assertFalse($connection->isConnected()); self::assertSame($connection, $connection->connect()); + self::assertTrue($connection->isConnected()); $connection->disconnect(); - unset($connection); - unset($resource); + + //unset($connection); } // public function testBeginTransaction(): never @@ -97,7 +98,6 @@ public function testConnectReturnsConnectionWhenResourceSet(): void // 'This test has not been implemented yet.' // ); // } - // public function testCommit(): never // { // // Remove the following lines when you implement this test. @@ -105,7 +105,6 @@ public function testConnectReturnsConnectionWhenResourceSet(): void // 'This test has not been implemented yet.' // ); // } - // public function testRollback(): never // { // // Remove the following lines when you implement this test. @@ -113,7 +112,6 @@ public function testConnectReturnsConnectionWhenResourceSet(): void // 'This test has not been implemented yet.' // ); // } - // public function testGetLastGeneratedValue(): never // { // $this->markTestIncomplete('Need to create a temporary sequence.'); diff --git a/test/integration/Pdo/StatementTest.php b/test/integration/Pdo/StatementTest.php index db86498..74bf89a 100644 --- a/test/integration/Pdo/StatementTest.php +++ b/test/integration/Pdo/StatementTest.php @@ -26,6 +26,15 @@ final class StatementTest extends TestCase { use SetupTrait; + public function testExecute(): void + { + /** @var StatementInterface&Statement $statement */ + $statement = $this->getAdapter()->getDriver()->createStatement(); + //$statement->initialize($pdo); + $statement->prepare('SELECT 1'); + self::assertInstanceOf(Result::class, $statement->execute()); + } + public function testGetResource(): void { /** @var PDO $pdo */ @@ -39,19 +48,21 @@ public function testGetResource(): void self::assertSame($stmt, $statement->getResource()); } - public function testSetSql(): void + public function testGetSql(): void { - /** @var StatementInterface&Statement $statement */ $statement = $this->getAdapter()->getDriver()->createStatement(); $statement->setSql('SELECT 1'); self::assertEquals('SELECT 1', $statement->getSql()); } - public function testGetSql(): void + public function testIsPrepared(): void { + /** @var StatementInterface&Statement $statement */ $statement = $this->getAdapter()->getDriver()->createStatement(); - $statement->setSql('SELECT 1'); - self::assertEquals('SELECT 1', $statement->getSql()); + self::assertFalse($statement->isPrepared()); + //$statement->initialize($resource); + $statement->prepare('SELECT 1'); + self::assertTrue($statement->isPrepared()); } /** @@ -64,22 +75,11 @@ public function testPrepare(): void self::assertInstanceOf(StatementInterface::class, $statement->prepare('SELECT 1')); } - public function testIsPrepared(): void - { - /** @var StatementInterface&Statement $statement */ - $statement = $this->getAdapter()->getDriver()->createStatement(); - self::assertFalse($statement->isPrepared()); - //$statement->initialize($resource); - $statement->prepare('SELECT 1'); - self::assertTrue($statement->isPrepared()); - } - - public function testExecute(): void + public function testSetSql(): void { /** @var StatementInterface&Statement $statement */ $statement = $this->getAdapter()->getDriver()->createStatement(); - //$statement->initialize($pdo); - $statement->prepare('SELECT 1'); - self::assertInstanceOf(Result::class, $statement->execute()); + $statement->setSql('SELECT 1'); + self::assertEquals('SELECT 1', $statement->getSql()); } } diff --git a/test/unit/AdapterPlatformTest.php b/test/unit/AdapterPlatformTest.php index 6d0c2d0..d23469d 100644 --- a/test/unit/AdapterPlatformTest.php +++ b/test/unit/AdapterPlatformTest.php @@ -17,39 +17,48 @@ final class AdapterPlatformTest extends TestCase { private AdapterPlatform $platform; - protected function setUp(): void + public function testConstructWithPdo(): void { - $pdoMock = $this->createMock(PDO::class); - $this->platform = new AdapterPlatform($pdoMock); + $pdoMock = $this->createMock(PDO::class); + $platform = new AdapterPlatform($pdoMock); + + self::assertInstanceOf(AdapterPlatform::class, $platform); } - public function testGetNameReturnsSqlite(): void + public function testConstructWithPdoDriver(): void { - $pdoMock = $this->createMock(PDO::class); - $platform = new AdapterPlatform($pdoMock); + $driverMock = $this->createMock(PdoDriverInterface::class); + $platform = new AdapterPlatform($driverMock); - self::assertSame('SQLite', $platform->getName()); + self::assertInstanceOf(AdapterPlatform::class, $platform); } - public function testPlatformNameConstant(): void + public function testGetIdentifierSeparator(): void { - self::assertSame('SQLite', AdapterPlatform::PLATFORM_NAME); + self::assertEquals('.', $this->platform->getIdentifierSeparator()); } - public function testConstructWithPdo(): void + public function testGetName(): void + { + self::assertEquals('SQLite', $this->platform->getName()); + } + + public function testGetNameReturnsSqlite(): void { $pdoMock = $this->createMock(PDO::class); $platform = new AdapterPlatform($pdoMock); - self::assertInstanceOf(AdapterPlatform::class, $platform); + self::assertSame('SQLite', $platform->getName()); } - public function testConstructWithPdoDriver(): void + public function testGetQuoteIdentifierSymbol(): void { - $driverMock = $this->createMock(PdoDriverInterface::class); - $platform = new AdapterPlatform($driverMock); + self::assertEquals('"', $this->platform->getQuoteIdentifierSymbol()); + } - self::assertInstanceOf(AdapterPlatform::class, $platform); + public function testGetQuoteValueSymbol(): void + { + self::assertEquals("'", $this->platform->getQuoteValueSymbol()); } public function testGetSqlPlatformDecorator(): void @@ -62,14 +71,9 @@ public function testGetSqlPlatformDecorator(): void self::assertInstanceOf(SqlPlatformDecorator::class, $decorator); } - public function testGetName(): void - { - self::assertEquals('SQLite', $this->platform->getName()); - } - - public function testGetQuoteIdentifierSymbol(): void + public function testPlatformNameConstant(): void { - self::assertEquals('"', $this->platform->getQuoteIdentifierSymbol()); + self::assertSame('SQLite', AdapterPlatform::PLATFORM_NAME); } public function testQuoteIdentifier(): void @@ -84,39 +88,33 @@ public function testQuoteIdentifierChain(): void self::assertEquals('"schema"."identifier"', $this->platform->quoteIdentifierChain(['schema', 'identifier'])); } - public function testGetQuoteValueSymbol(): void - { - self::assertEquals("'", $this->platform->getQuoteValueSymbol()); - } - - public function testQuoteValueThrowsExeceptionWithoutDriverSupport(): void + public function testQuoteIdentifierInFragment(): void { - $platform = new AdapterPlatform(); - //$this->expectNotToPerformAssertions(); - $this->expectException(VunerablePlatformQuoteException::class); - $platform->quoteValue('value'); - } + self::assertEquals('"foo"."bar"', $this->platform->quoteIdentifierInFragment('foo.bar')); + self::assertEquals('"foo" as "bar"', $this->platform->quoteIdentifierInFragment('foo as bar')); - public function testQuoteValueList(): void - { - $expected = "'Foo O\\'Bar'"; - $platform = new AdapterPlatform(); - $this->expectException(VunerablePlatformQuoteException::class); - $actual = $platform->quoteValueList("Foo O'Bar"); - self::assertEquals($expected, $actual); - } + // single char words + self::assertEquals( + '("foo"."bar" = "boo"."baz")', + $this->platform->quoteIdentifierInFragment('(foo.bar = boo.baz)', ['(', ')', '=']), + ); - public function testQuoteValue(): void - { - self::assertEquals("'value'", @$this->platform->quoteValue('value')); - self::assertEquals("'Foo O\\'Bar'", @$this->platform->quoteValue("Foo O'Bar")); + // case insensitive safe words self::assertEquals( - '\'\\\'; DELETE FROM some_table; -- \'', - @$this->platform->quoteValue('\'; DELETE FROM some_table; -- ') + '("foo"."bar" = "boo"."baz") AND ("foo"."baz" = "boo"."baz")', + $this->platform->quoteIdentifierInFragment( + '(foo.bar = boo.baz) AND (foo.baz = boo.baz)', + ['(', ')', '=', 'and'], + ), ); + + // case insensitive safe words in field self::assertEquals( - "'\\\\\\'; DELETE FROM some_table; -- '", - @$this->platform->quoteValue('\\\'; DELETE FROM some_table; -- ') + '("foo"."bar" = "boo".baz) AND ("foo".baz = "boo".baz)', + $this->platform->quoteIdentifierInFragment( + '(foo.bar = boo.baz) AND (foo.baz = boo.baz)', + ['(', ')', '=', 'and', 'bAz'], + ), ); } @@ -126,48 +124,50 @@ public function testQuoteTrustedValue(): void self::assertEquals("'Foo O\\'Bar'", $this->platform->quoteTrustedValue("Foo O'Bar")); self::assertEquals( '\'\\\'; DELETE FROM some_table; -- \'', - $this->platform->quoteTrustedValue('\'; DELETE FROM some_table; -- ') + $this->platform->quoteTrustedValue('\'; DELETE FROM some_table; -- '), ); // '\\\'; DELETE FROM some_table; -- ' <- actual below self::assertEquals( "'\\\\\\'; DELETE FROM some_table; -- '", - $this->platform->quoteTrustedValue('\\\'; DELETE FROM some_table; -- ') + $this->platform->quoteTrustedValue('\\\'; DELETE FROM some_table; -- '), ); } - public function testGetIdentifierSeparator(): void - { - self::assertEquals('.', $this->platform->getIdentifierSeparator()); - } - - public function testQuoteIdentifierInFragment(): void + public function testQuoteValue(): void { - self::assertEquals('"foo"."bar"', $this->platform->quoteIdentifierInFragment('foo.bar')); - self::assertEquals('"foo" as "bar"', $this->platform->quoteIdentifierInFragment('foo as bar')); - - // single char words + self::assertEquals("'value'", @$this->platform->quoteValue('value')); + self::assertEquals("'Foo O\\'Bar'", @$this->platform->quoteValue("Foo O'Bar")); self::assertEquals( - '("foo"."bar" = "boo"."baz")', - $this->platform->quoteIdentifierInFragment('(foo.bar = boo.baz)', ['(', ')', '=']) + '\'\\\'; DELETE FROM some_table; -- \'', + @$this->platform->quoteValue('\'; DELETE FROM some_table; -- '), ); - - // case insensitive safe words self::assertEquals( - '("foo"."bar" = "boo"."baz") AND ("foo"."baz" = "boo"."baz")', - $this->platform->quoteIdentifierInFragment( - '(foo.bar = boo.baz) AND (foo.baz = boo.baz)', - ['(', ')', '=', 'and'] - ) + "'\\\\\\'; DELETE FROM some_table; -- '", + @$this->platform->quoteValue('\\\'; DELETE FROM some_table; -- '), ); + } - // case insensitive safe words in field - self::assertEquals( - '("foo"."bar" = "boo".baz) AND ("foo".baz = "boo".baz)', - $this->platform->quoteIdentifierInFragment( - '(foo.bar = boo.baz) AND (foo.baz = boo.baz)', - ['(', ')', '=', 'and', 'bAz'] - ) - ); + public function testQuoteValueList(): void + { + $expected = "'Foo O\\'Bar'"; + $platform = new AdapterPlatform(); + $this->expectException(VunerablePlatformQuoteException::class); + $actual = $platform->quoteValueList("Foo O'Bar"); + self::assertEquals($expected, $actual); + } + + public function testQuoteValueThrowsExeceptionWithoutDriverSupport(): void + { + $platform = new AdapterPlatform(); + //$this->expectNotToPerformAssertions(); + $this->expectException(VunerablePlatformQuoteException::class); + $platform->quoteValue('value'); + } + + protected function setUp(): void + { + $pdoMock = $this->createMock(PDO::class); + $this->platform = new AdapterPlatform($pdoMock); } } diff --git a/test/unit/ConfigProviderTest.php b/test/unit/ConfigProviderTest.php index 993ddae..268c969 100644 --- a/test/unit/ConfigProviderTest.php +++ b/test/unit/ConfigProviderTest.php @@ -38,27 +38,35 @@ final class ConfigProviderTest extends TestCase StatementInterface::class => Statement::class, MetadataInterface::class => Metadata\Source::class, ]; + private ConfigProvider $configProvider; - protected function setUp(): void + public function testGetDependenciesContainsExpectedAliases(): void { - $this->configProvider = new ConfigProvider(); + $config = $this->configProvider->getDependencies(); + self::assertEquals(self::EXPECTED_ALIASES, $config['aliases']); } - public function testInvokeReturnsExpectedStructure(): void + public function testGetDependenciesContainsMetadataAlias(): void { - $config = ($this->configProvider)(); + $dependencies = $this->configProvider->getDependencies(); - self::assertNotEmpty($config); - self::assertArrayHasKey('dependencies', $config); + self::assertArrayHasKey(MetadataInterface::class, $dependencies['aliases']); + self::assertSame( + Metadata\Source::class, + $dependencies['aliases'][MetadataInterface::class], + ); } - public function testInvokeReturnsCorrectStructure(): void + public function testGetDependenciesContainsMetadataFactory(): void { - $config = (new ConfigProvider())(); - self::assertArrayHasKey('dependencies', $config); - self::assertArrayHasKey('aliases', $config['dependencies']); - self::assertArrayHasKey('factories', $config['dependencies']); + $dependencies = $this->configProvider->getDependencies(); + + self::assertArrayHasKey(Metadata\Source::class, $dependencies['factories']); + self::assertSame( + Container\MetadataInterfaceFactory::class, + $dependencies['factories'][Metadata\Source::class], + ); } public function testGetDependenciesReturnsCorrectStructure(): void @@ -70,31 +78,24 @@ public function testGetDependenciesReturnsCorrectStructure(): void self::assertArrayHasKey('factories', $dependencies); } - public function testGetDependenciesContainsMetadataAlias(): void + public function testInvokeReturnsCorrectStructure(): void { - $dependencies = $this->configProvider->getDependencies(); - - self::assertArrayHasKey(MetadataInterface::class, $dependencies['aliases']); - self::assertSame( - Metadata\Source::class, - $dependencies['aliases'][MetadataInterface::class] - ); + $config = (new ConfigProvider())(); + self::assertArrayHasKey('dependencies', $config); + self::assertArrayHasKey('aliases', $config['dependencies']); + self::assertArrayHasKey('factories', $config['dependencies']); } - public function testGetDependenciesContainsMetadataFactory(): void + public function testInvokeReturnsExpectedStructure(): void { - $dependencies = $this->configProvider->getDependencies(); + $config = ($this->configProvider)(); - self::assertArrayHasKey(Metadata\Source::class, $dependencies['factories']); - self::assertSame( - Container\MetadataInterfaceFactory::class, - $dependencies['factories'][Metadata\Source::class] - ); + self::assertNotEmpty($config); + self::assertArrayHasKey('dependencies', $config); } - public function testGetDependenciesContainsExpectedAliases(): void + protected function setUp(): void { - $config = $this->configProvider->getDependencies(); - self::assertEquals(self::EXPECTED_ALIASES, $config['aliases']); + $this->configProvider = new ConfigProvider(); } } diff --git a/test/unit/Container/PdoConnectionFactoryTest.php b/test/unit/Container/PdoConnectionFactoryTest.php index 7da08cb..d04f047 100644 --- a/test/unit/Container/PdoConnectionFactoryTest.php +++ b/test/unit/Container/PdoConnectionFactoryTest.php @@ -24,23 +24,23 @@ public function testInvokeReturnsConnection(): void self::assertInstanceOf(Connection::class, $connection); } - public function testInvokeWithoutConnectionConfigThrows(): void + public function testInvokeWithNullOptionsThrows(): void { $containerMock = $this->createMock(ContainerInterface::class); $this->expectException(InvalidConnectionParametersException::class); $factory = new PdoConnectionFactory(); - $factory($containerMock, Connection::class, []); + $factory($containerMock, Connection::class, null); } - public function testInvokeWithNullOptionsThrows(): void + public function testInvokeWithoutConnectionConfigThrows(): void { $containerMock = $this->createMock(ContainerInterface::class); $this->expectException(InvalidConnectionParametersException::class); $factory = new PdoConnectionFactory(); - $factory($containerMock, Connection::class, null); + $factory($containerMock, Connection::class, []); } } diff --git a/test/unit/Container/PdoStatementFactoryTest.php b/test/unit/Container/PdoStatementFactoryTest.php index 4c11e5e..2b148c4 100644 --- a/test/unit/Container/PdoStatementFactoryTest.php +++ b/test/unit/Container/PdoStatementFactoryTest.php @@ -13,22 +13,22 @@ #[CoversClass(PdoStatementFactory::class)] final class PdoStatementFactoryTest extends TestCase { - public function testInvokeReturnsStatementWithOptions(): void + public function testInvokeReturnsStatementWithEmptyOptions(): void { $containerMock = $this->createMock(ContainerInterface::class); $factory = new PdoStatementFactory(); - $statement = $factory($containerMock, Statement::class, ['key' => 'value']); + $statement = $factory($containerMock, Statement::class, []); self::assertInstanceOf(Statement::class, $statement); } - public function testInvokeReturnsStatementWithEmptyOptions(): void + public function testInvokeReturnsStatementWithOptions(): void { $containerMock = $this->createMock(ContainerInterface::class); $factory = new PdoStatementFactory(); - $statement = $factory($containerMock, Statement::class, []); + $statement = $factory($containerMock, Statement::class, ['key' => 'value']); self::assertInstanceOf(Statement::class, $statement); } diff --git a/test/unit/Pdo/ConnectionTest.php b/test/unit/Pdo/ConnectionTest.php index 9ee377c..29f8278 100644 --- a/test/unit/Pdo/ConnectionTest.php +++ b/test/unit/Pdo/ConnectionTest.php @@ -19,25 +19,20 @@ final class ConnectionTest extends TestCase //protected string $dsn = 'sqlite::memory:'; protected string $dsn = 'sqlite::memory:'; - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - #[Override] - protected function setUp(): void + #[Group('2622')] + public function testArrayOfConnectionParametersCreatesCorrectDsn(): void { - $this->connection = new Connection([ - 'dsn' => $this->dsn, + $this->connection->setConnectionParameters([ + 'dsn' => 'sqlite::memory:', ]); - } + try { + $this->connection->connect(); + } catch (Exception) { + } + $responseString = $this->connection->getDsn(); - /** - * Test getResource method tries to connect to the database, it should never return null - */ - public function testResource(): void - { - $this->expectNotToPerformAssertions(); - $this->connection->getResource(); + self::assertStringStartsWith('sqlite:', $responseString); + self::assertStringContainsString('memory', $responseString); } /** @@ -55,19 +50,24 @@ public function testGetDsn(): void self::assertEquals($this->dsn, $responseString); } - #[Group('2622')] - public function testArrayOfConnectionParametersCreatesCorrectDsn(): void + /** + * Test getResource method tries to connect to the database, it should never return null + */ + public function testResource(): void { - $this->connection->setConnectionParameters([ - 'dsn' => 'sqlite::memory:', - ]); - try { - $this->connection->connect(); - } catch (Exception) { - } - $responseString = $this->connection->getDsn(); + $this->expectNotToPerformAssertions(); + $this->connection->getResource(); + } - self::assertStringStartsWith('sqlite:', $responseString); - self::assertStringContainsString('memory', $responseString); + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + #[Override] + protected function setUp(): void + { + $this->connection = new Connection([ + 'dsn' => $this->dsn, + ]); } } diff --git a/test/unit/Pdo/DriverTest.php b/test/unit/Pdo/DriverTest.php index fb0fcc9..4776dc3 100644 --- a/test/unit/Pdo/DriverTest.php +++ b/test/unit/Pdo/DriverTest.php @@ -22,43 +22,32 @@ final class DriverTest extends TestCase { protected Driver $pdo; - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - #[Override] - protected function setUp(): void + /** @psalm-return array */ + public static function getInvalidParamName(): array { - $connection = new Connection(['dsn' => ':memory:']); - - /** @var StatementInterface&PdoDriverAwareInterface&MockObject $statementPrototype */ - $statementPrototype = $this->createMockForIntersectionOfInterfaces([ - StatementInterface::class, - PdoDriverAwareInterface::class, - ]); - - $this->pdo = new Driver( - $connection, - $statementPrototype, - $this->createMock(ResultInterface::class), - ); + return [ + ['foo%'], + ['foo-'], + ['foo$'], + ['foo0!'], + ]; } /** @psalm-return array */ public static function getParamsAndType(): array { return [ - ['foo', null, ':foo'], - ['foo_bar', null, ':foo_bar'], - ['123foo', null, ':123foo'], - [1, null, '?'], - ['1', null, '?'], - ['foo', PdoDriverInterface::PARAMETERIZATION_NAMED, ':foo'], + ['foo', null, ':foo'], + ['foo_bar', null, ':foo_bar'], + ['123foo', null, ':123foo'], + [1, null, '?'], + ['1', null, '?'], + ['foo', PdoDriverInterface::PARAMETERIZATION_NAMED, ':foo'], ['foo_bar', PdoDriverInterface::PARAMETERIZATION_NAMED, ':foo_bar'], - ['123foo', PdoDriverInterface::PARAMETERIZATION_NAMED, ':123foo'], - [1, PdoDriverInterface::PARAMETERIZATION_NAMED, ':1'], - ['1', PdoDriverInterface::PARAMETERIZATION_NAMED, ':1'], - [':foo', null, ':foo'], + ['123foo', PdoDriverInterface::PARAMETERIZATION_NAMED, ':123foo'], + [1, PdoDriverInterface::PARAMETERIZATION_NAMED, ':1'], + ['1', PdoDriverInterface::PARAMETERIZATION_NAMED, ':1'], + [':foo', null, ':foo'], ]; } @@ -69,21 +58,32 @@ public function testFormatParameterName(int|string $name, ?string $type, string $this->assertEquals($expected, $result); } - /** @psalm-return array */ - public static function getInvalidParamName(): array - { - return [ - ['foo%'], - ['foo-'], - ['foo$'], - ['foo0!'], - ]; - } - #[DataProvider('getInvalidParamName')] public function testFormatParameterNameWithInvalidCharacters(string $name): void { $this->expectException(RuntimeException::class); $this->pdo->formatParameterName($name); } + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + #[Override] + protected function setUp(): void + { + $connection = new Connection(['dsn' => ':memory:']); + + /** @var StatementInterface&PdoDriverAwareInterface&MockObject $statementPrototype */ + $statementPrototype = $this->createMockForIntersectionOfInterfaces([ + StatementInterface::class, + PdoDriverAwareInterface::class, + ]); + + $this->pdo = new Driver( + $connection, + $statementPrototype, + $this->createMock(ResultInterface::class), + ); + } } diff --git a/test/unit/Pdo/Feature/SqliteRowCounterTest.php b/test/unit/Pdo/Feature/SqliteRowCounterTest.php index 4377b87..ac332d8 100644 --- a/test/unit/Pdo/Feature/SqliteRowCounterTest.php +++ b/test/unit/Pdo/Feature/SqliteRowCounterTest.php @@ -13,13 +13,13 @@ final class SqliteRowCounterTest extends TestCase { private SqliteRowCounter $rowCounter; - protected function setUp(): void + public function testRowCounterExists(): void { - $this->rowCounter = new SqliteRowCounter(); + self::assertInstanceOf(SqliteRowCounter::class, $this->rowCounter); } - public function testRowCounterExists(): void + protected function setUp(): void { - self::assertInstanceOf(SqliteRowCounter::class, $this->rowCounter); + $this->rowCounter = new SqliteRowCounter(); } } diff --git a/test/unit/Pdo/StatementIntegrationTest.php b/test/unit/Pdo/StatementIntegrationTest.php index 5c8fc56..4c56f70 100644 --- a/test/unit/Pdo/StatementIntegrationTest.php +++ b/test/unit/Pdo/StatementIntegrationTest.php @@ -23,44 +23,56 @@ final class StatementIntegrationTest extends TestCase public function testStatementExecuteWillConvertPhpBoolToPdoBoolWhenBinding(): void { - $this->pdoStatementMock->expects($this->any())->method('bindParam')->with( - $this->equalTo(':foo'), - $this->equalTo(false), - $this->equalTo(PDO::PARAM_BOOL) - ); + $this->pdoStatementMock + ->expects($this->any()) + ->method('bindParam') + ->with( + $this->equalTo(':foo'), + $this->equalTo(false), + $this->equalTo(PDO::PARAM_BOOL), + ); $this->statement->execute(['foo' => false]); } + public function testStatementExecuteWillUsePdoIntForIntWhenBinding(): void + { + $this->pdoStatementMock + ->expects($this->any()) + ->method('bindParam') + ->with( + $this->equalTo(':foo'), + $this->equalTo(123), + $this->equalTo(PDO::PARAM_INT), + ); + $this->statement->execute(['foo' => 123]); + } + public function testStatementExecuteWillUsePdoStrByDefaultWhenBinding(): void { - $this->pdoStatementMock->expects($this->any())->method('bindParam')->with( - $this->equalTo(':foo'), - $this->equalTo('bar'), - $this->equalTo(PDO::PARAM_STR) - ); + $this->pdoStatementMock + ->expects($this->any()) + ->method('bindParam') + ->with( + $this->equalTo(':foo'), + $this->equalTo('bar'), + $this->equalTo(PDO::PARAM_STR), + ); $this->statement->execute(['foo' => 'bar']); } public function testStatementExecuteWillUsePdoStrForStringIntegerWhenBinding(): void { - $this->pdoStatementMock->expects($this->any())->method('bindParam')->with( - $this->equalTo(':foo'), - $this->equalTo('123'), - $this->equalTo(PDO::PARAM_STR) - ); + $this->pdoStatementMock + ->expects($this->any()) + ->method('bindParam') + ->with( + $this->equalTo(':foo'), + $this->equalTo('123'), + $this->equalTo(PDO::PARAM_STR), + ); $this->statement->execute(['foo' => '123']); } - public function testStatementExecuteWillUsePdoIntForIntWhenBinding(): void - { - $this->pdoStatementMock->expects($this->any())->method('bindParam')->with( - $this->equalTo(':foo'), - $this->equalTo(123), - $this->equalTo(PDO::PARAM_INT) - ); - $this->statement->execute(['foo' => 123]); - } - /** * Sets up the fixture, for example, opens a network connection. * This method is called before a test is executed. @@ -69,16 +81,16 @@ public function testStatementExecuteWillUsePdoIntForIntWhenBinding(): void protected function setUp(): void { $driver = $this->getMockBuilder(Driver::class) - ->onlyMethods(['createResult']) - ->disableOriginalConstructor() - ->getMock(); + ->onlyMethods(['createResult']) + ->disableOriginalConstructor() + ->getMock(); $this->statement = new Statement(); $this->statement->setDriver($driver); $this->statement->initialize(new CtorlessPdo( $this->pdoStatementMock = $this->getMockBuilder(PDOStatement::class) - ->onlyMethods(['execute', 'bindParam']) - ->getMock() + ->onlyMethods(['execute', 'bindParam']) + ->getMock(), )); } } diff --git a/test/unit/Pdo/TestAsset/CtorlessPdo.php b/test/unit/Pdo/TestAsset/CtorlessPdo.php index 8c16ad1..b4ce191 100644 --- a/test/unit/Pdo/TestAsset/CtorlessPdo.php +++ b/test/unit/Pdo/TestAsset/CtorlessPdo.php @@ -11,9 +11,9 @@ final class CtorlessPdo extends PDO { - public function __construct(protected PDOStatement&MockObject $mockStatement) - { - } + public function __construct( + protected PDOStatement&MockObject $mockStatement, + ) {} /** * @param array $options diff --git a/test/unit/Sql/PlatformTest.php b/test/unit/Sql/PlatformTest.php index 2e1a2f0..b53f00d 100644 --- a/test/unit/Sql/PlatformTest.php +++ b/test/unit/Sql/PlatformTest.php @@ -16,11 +16,6 @@ final class PlatformTest extends TestCase { private Platform $platform; - protected function setUp(): void - { - $this->platform = new Platform(); - } - public function testConstructorSetsTypeDecorators(): void { self::assertInstanceOf(Platform::class, $this->platform); @@ -35,4 +30,9 @@ public function testSelectDecoratorIsRegistered(): void self::assertArrayHasKey(Select::class, $decorators); self::assertInstanceOf(SelectDecorator::class, $decorators[Select::class]); } + + protected function setUp(): void + { + $this->platform = new Platform(); + } } diff --git a/test/unit/Sql/SelectDecoratorTest.php b/test/unit/Sql/SelectDecoratorTest.php index 118eeef..950fbc8 100644 --- a/test/unit/Sql/SelectDecoratorTest.php +++ b/test/unit/Sql/SelectDecoratorTest.php @@ -18,37 +18,6 @@ final class SelectDecoratorTest extends TestCase { private SelectDecorator $decorator; - protected function setUp(): void - { - $this->decorator = new SelectDecorator(); - } - - public function testSetSubject(): void - { - $select = new Select(); - $result = $this->decorator->setSubject($select); - - self::assertSame($this->decorator, $result); - - $reflection = new ReflectionClass($this->decorator); - $subjectProperty = $reflection->getProperty('subject'); - $subject = $subjectProperty->getValue($this->decorator); - - self::assertSame($select, $subject); - } - - public function testProcessLimitWithoutLimitAndWithoutOffset(): void - { - $platformMock = $this->createMock(PlatformInterface::class); - - $reflection = new ReflectionClass($this->decorator); - $method = $reflection->getMethod('processLimit'); - - $result = $method->invoke($this->decorator, $platformMock); - - self::assertNull($result); - } - public function testProcessLimitWithLimit(): void { $this->decorator->limit(10); @@ -88,10 +57,8 @@ public function testProcessLimitWithLimitAndParameterContainer(): void self::assertSame(15, $parameterContainer->offsetGet('limit')); } - public function testProcessLimitWithoutLimitButWithOffset(): void + public function testProcessLimitWithoutLimitAndWithoutOffset(): void { - $this->decorator->offset(5); - $platformMock = $this->createMock(PlatformInterface::class); $reflection = new ReflectionClass($this->decorator); @@ -99,19 +66,21 @@ public function testProcessLimitWithoutLimitButWithOffset(): void $result = $method->invoke($this->decorator, $platformMock); - self::assertSame([''], $result); + self::assertNull($result); } - public function testProcessOffsetWithoutOffset(): void + public function testProcessLimitWithoutLimitButWithOffset(): void { + $this->decorator->offset(5); + $platformMock = $this->createMock(PlatformInterface::class); $reflection = new ReflectionClass($this->decorator); - $method = $reflection->getMethod('processOffset'); + $method = $reflection->getMethod('processLimit'); $result = $method->invoke($this->decorator, $platformMock); - self::assertNull($result); + self::assertSame([''], $result); } public function testProcessOffsetWithOffset(): void @@ -152,4 +121,35 @@ public function testProcessOffsetWithOffsetAndParameterContainer(): void self::assertTrue($parameterContainer->offsetExists('offset')); self::assertSame(25, $parameterContainer->offsetGet('offset')); } + + public function testProcessOffsetWithoutOffset(): void + { + $platformMock = $this->createMock(PlatformInterface::class); + + $reflection = new ReflectionClass($this->decorator); + $method = $reflection->getMethod('processOffset'); + + $result = $method->invoke($this->decorator, $platformMock); + + self::assertNull($result); + } + + public function testSetSubject(): void + { + $select = new Select(); + $result = $this->decorator->setSubject($select); + + self::assertSame($this->decorator, $result); + + $reflection = new ReflectionClass($this->decorator); + $subjectProperty = $reflection->getProperty('subject'); + $subject = $subjectProperty->getValue($this->decorator); + + self::assertSame($select, $subject); + } + + protected function setUp(): void + { + $this->decorator = new SelectDecorator(); + } } From af8d723c69587d4378d14ab7e00354b318d43510 Mon Sep 17 00:00:00 2001 From: Simon Mundy Date: Mon, 24 Aug 2026 16:11:28 +1000 Subject: [PATCH 3/3] Ignore mago reformat in git blame --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..2c34e73 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +c20d173529037a293036b1c8dd51fd0b757ccb62