Skip to content

Route the Adapter throw sites through named constructors and remove VunerablePlatformQuoteException - #194

Merged
simon-mundy merged 3 commits into
php-db:0.6.xfrom
simon-mundy:exception/pr-5-adapter
Sep 29, 2026
Merged

simon-mundy merged 3 commits into
php-db:0.6.xfrom
simon-mundy:exception/pr-5-adapter

Conversation

@simon-mundy

Copy link
Copy Markdown
Member

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • QA improvement (unit/integration tests, CS fixes, etc.) that does not change code behavior

Checklist:

  • An issue has been created for this pull request.
  • I have added tests for my changes.
  • All new and existing tests passed.
  • Documentation has been updated accordingly.
  • This pull request targets the correct branch.

Description

PR 5 of 6 from #189: all 26 throw sites in src/Adapter, and the removal of VunerablePlatformQuoteException.

Stacked on #190. GitHub cannot base a cross-fork PR on a fork branch, so this targets 0.6.x and shows both commits. Only the second, 9870e985, belongs to this PR. Merge #190 first; I will rebase after. It does not depend on #191, #192 or #193.

Message text is byte-identical, verified template by template against the original literals.

  • 13 templates and factories on Adapter\Exception\RuntimeException, 6 on its InvalidArgumentException, 2 on InvalidQueryException.
  • The drifted PDO param message converges on the quoted form, per decision 3 on [RFC]: Named exception constructors and complete per-component exception namespaces #189. The two variants differed only in the quotes around %s.
  • The four 'Data does not exist for this name/position' copies in ParameterContainer collapse onto one template.

VunerablePlatformQuoteException is removed

It held no state, implemented no external contract, and reported a fail-fast setup error a caller cannot recover from, so it met none of the three tests for a condition earning its own class. Its two throw sites now call RuntimeException::forVulnerablePlatformQuote(), which renders the identical message, and the misspelling goes with the class.

That also retires the one exemption in ExceptionHierarchyTest, which existed solely for this class. No exception class is exempt from the constant naming rule any more.

There is no BC shim, deliberately. The class was final, so nobody can have subclassed it, but a class_alias() to Adapter\Exception\RuntimeException would be worse than the break: catch (VunerablePlatformQuoteException) would silently start catching every adapter runtime failure. It already extended Adapter\Exception\RuntimeException, so anything catching that is unaffected.

The driver-error passthroughs

Pdo\Statement and Pdo\AbstractPdoConnection throw with PDO's own error text as the whole message. Both now use a DRIVER_ERROR template of '%s', documented on the constant as carrying the driver's wording verbatim. It is a thin template, but it keeps the throw site free of message assembly and keeps every factory under the naming guard.

One deviation from the plan on #189

InvalidConnectionParametersException keeps its constructor rather than gaining a named constructor. Its message is supplied by the adapter that throws it, so there is no template for core to own — an adapter-specific message does not belong in a core constant. It earns its own class on the extra state it carries.

Motivation and Context

Refs #189. Depends on #190.

Downstream work this creates. phpdb-pgsql and phpdb-sqlite each have one test file naming the removed class (3 and 2 references). Those need a one-line import swap and expectException(RuntimeException::class) in their own repositories. Both already require php-db/phpdb: ^0.6.0, which is unreleased, so those changes cannot go green until 0.6.0 ships — they should follow this merge, not precede it. phpdb-mysql has no references.

How has this been tested?

mago format --check, mago lint and mago analyze all exit 0. Full unit suite passes: 1681 tests, 3581 assertions.

Every factory is covered: 13/13, 6/6 and 2/2. All 21 templates verified byte-identical against the original literals by a throwaway script before the call sites were touched, including a check that forVulnerablePlatformQuote() reproduces the removed class's message exactly.

Baseline and configuration changes

Three baselined entries are re-attributed, not added to, because the same findings moved rather than appeared:

Was Now
mixed-argument on Exception::__construct in AbstractPdoConnection on InvalidQueryException::forDriverError
unhandled-thrown-type VunerablePlatformQuoteException in Sql92::quoteValue RuntimeException
unhandled-thrown-type VunerablePlatformQuoteException in AbstractPlatform::quoteValue RuntimeException

@simon-mundy simon-mundy added the enhancement New feature or request label Sep 28, 2026
@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@simon-mundy
simon-mundy force-pushed the exception/pr-5-adapter branch from 9870e98 to be6922d Compare September 28, 2026 06:56
@simon-mundy
simon-mundy force-pushed the exception/pr-5-adapter branch from be6922d to db83a9b Compare September 28, 2026 22:52
simon-mundy added a commit to simon-mundy/phpdb that referenced this pull request Sep 28, 2026
… constructors

The last 5 throw sites outside the components covered by php-db#191 to php-db#194. Message
text is byte-identical, verified template by template against the originals.

- Add ResultSet\Exception\RuntimeException::forUnbufferedIteration()
- Add forNonAdapterAwareDelegate() to Exception\ContainerException, and give
  the three reasons that AdapterInterfaceFactory and
  AbstractAdapterInterfaceFactory passed as inline strings their own factories:
  forMissingConfigService(), forMissingConfiguration() and forMissingDriver(),
  each composing the reason into the SERVICE template
- Retype the delegator's non-AdapterAware guard from the root
  Exception\RuntimeException to ContainerException, which extends it, so the
  failure carries PSR-11's ContainerExceptionInterface as a container failure
  should
- Own the "not found in container" wording as ContainerException::MISSING_SERVICE
  while still throwing the ServiceManager's own ServiceNotFoundException. Laminas
  annotates that class @Final, so this package cannot own it; the throw site
  renders the template itself, which docs/book/exceptions.md now records as the
  single documented exception to the convention
- Derive the message assertions in the six test files the earlier PRs do not
  touch from the templates rather than restating them

One literal assertion is deliberately left in
test/unit/Container/TableIdentifierFactoryFactoryTest.php: it asserts
Sql\Exception\InvalidArgumentException::EMPTY_SEPARATOR, a constant php-db#192
introduces, so it can only be migrated once that has landed.

Signed-off-by: Simon Mundy <simon.mundy@peptolab.com>
…unerablePlatformQuoteException

All 26 throw sites in src/Adapter now call a named constructor. Message text is
byte-identical, verified template by template against the originals.

- Add 13 templates and factories to Adapter\Exception\RuntimeException, 6 to
  its InvalidArgumentException and 2 to InvalidQueryException
- Converge the drifted PDO param message on the quoted form, per php-db#189
- Collapse the four "Data does not exist for this name/position" copies in
  ParameterContainer onto one template
- Remove VunerablePlatformQuoteException. It held no state, implemented no
  external contract and reported a fail-fast setup error, so it met none of the
  three tests for a condition earning its own class. Its two throw sites now
  call RuntimeException::forVulnerablePlatformQuote(), which renders the same
  message, and the misspelling goes with the class
- Drop the class from ExceptionHierarchyTest's EXCEPTION_CLASSES map and retire
  the guard's exemption, which existed only for that class, so no exception class
  is exempt from the constant naming rule any more
- Give the two driver-error passthroughs a DRIVER_ERROR template of '%s',
  documented as carrying the driver's own wording verbatim
- Cover every factory with a template test, and derive the 16 Adapter message
  assertions from the templates rather than restating them
- Declare CoversMethod for Adapter::executeQuery(), which four tests exercise
  but none declared, so PHPUnit recorded no coverage for the method
- Re-attribute three baselined entries to the factories and class that now
  carry them, exclude src/Adapter/Exception/ from too-many-methods, and
  baseline the halstead reading Pdo\Statement shifts

InvalidConnectionParametersException keeps its constructor rather than gaining a
named constructor: its message is supplied by the adapter that throws it, so
there is no template for core to own. It earns its own class on the extra state
it carries.

BC: removing VunerablePlatformQuoteException breaks any catch of that exact
type. It extended Adapter\Exception\RuntimeException, so a catch of that is
unaffected. The tests in phpdb-pgsql and phpdb-sqlite that name the class need
updating in their own repositories.

Signed-off-by: Simon Mundy <simon.mundy@peptolab.com>
@simon-mundy
simon-mundy force-pushed the exception/pr-5-adapter branch from db83a9b to 583033b Compare September 29, 2026 03:35
simon-mundy added a commit to simon-mundy/phpdb that referenced this pull request Sep 29, 2026
… constructors

The last 5 throw sites outside the components covered by php-db#191 to php-db#194. Message
text is byte-identical, verified template by template against the originals.

- Add ResultSet\Exception\RuntimeException::forUnbufferedIteration()
- Add forNonAdapterAwareDelegate() to Exception\ContainerException, and give
  the three reasons that AdapterInterfaceFactory and
  AbstractAdapterInterfaceFactory passed as inline strings their own factories:
  forMissingConfigService(), forMissingConfiguration() and forMissingDriver(),
  each composing the reason into the SERVICE template
- Retype the delegator's non-AdapterAware guard from the root
  Exception\RuntimeException to ContainerException, which extends it, so the
  failure carries PSR-11's ContainerExceptionInterface as a container failure
  should
- Own the "not found in container" wording as ContainerException::MISSING_SERVICE
  while still throwing the ServiceManager's own ServiceNotFoundException. Laminas
  annotates that class @Final, so this package cannot own it; the throw site
  renders the template itself, which docs/book/exceptions.md now records as the
  single documented exception to the convention
- Derive the message assertions in the six test files the earlier PRs do not
  touch from the templates rather than restating them

One literal assertion is deliberately left in
test/unit/Container/TableIdentifierFactoryFactoryTest.php: it asserts
Sql\Exception\InvalidArgumentException::EMPTY_SEPARATOR, a constant php-db#192
introduces, so it can only be migrated once that has landed.

Signed-off-by: Simon Mundy <simon.mundy@peptolab.com>
@simon-mundy simon-mundy added documentation Improvements or additions to documentation qa Improvements in quality assurance of the project RFC RFC issue or PR labels Sep 29, 2026
@github-project-automation github-project-automation Bot moved this to Todo in @phpdb Sep 29, 2026
@simon-mundy simon-mundy added this to the 0.6.0 milestone Sep 29, 2026
@simon-mundy simon-mundy self-assigned this Sep 29, 2026
@simon-mundy
simon-mundy requested a review from tyrsson September 29, 2026 03:46
@simon-mundy
simon-mundy merged commit 45d4ade into php-db:0.6.x Sep 29, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request qa Improvements in quality assurance of the project RFC RFC issue or PR

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants