Problem/Motivation

When writing code to catch DatabaseException, intellisense doesn't see the existence of any exception-related methods.

Steps to reproduce

Write something like this:

namespace Drupal\Core\Database;

try {
  throw new DatabaseExceptionWrapper('Error message.');
}
catch (DatabaseException $e) {
  \Drupal::logger()->error($e->getMessage());
}

Then, since DatabaseException is just an empty interface and isn't guaranteed to have a getMessage() function, an IDE with intellisense will complain.

A workaround can be to either put /** @var \Throwable $e */ inside the catch, or to catch every specific class that implements the interface.

Proposed resolution

interface DatabaseException extends \Throwable { }

This still works with all the other existing code, and it makes it obvious to development tools that what you're catching is something that is a \Throwable.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

CommentFileSizeAuthor
#4 3340077-4.patch501 bytesankitsingh0188

Comments

solideogloria created an issue. See original summary.

solideogloria’s picture

Issue summary: View changes
solideogloria’s picture

Issue summary: View changes
ankitsingh0188’s picture

Status: Active » Needs review
StatusFileSize
new501 bytes

I've created the patch with the new CR.

mondrake’s picture

Maybe \Throwable is too high in the inheritance tree? In the end aren't we facing a kind of \RuntimeException when the database fails?

ankitsingh0188’s picture

What if we get the \DatabaseException?

In that case $e->getMessage() will not work as mentioned in Steps to reproduce section.

I think the exception interface should extend the \Throwable with respect to what you're catching is something that is a \Throwable.

Maybe \Throwable is too high in the inheritance tree? In the end aren't we facing a kind of \RuntimeException when the database fails?

Please clarify why there's need of \DatabaseException? If we will handle everything in \RuntimeException?

And even \RuntimeException implements the \Throwable interface.

class RuntimeException extends Exception implements Throwable, Stringable

solideogloria’s picture

Not every implementer of DatabaseException extends \RuntimeException, though. See \InvalidQueryException.

class InvalidQueryException extends \InvalidArgumentException implements DatabaseException {}

It could be \Exception instead of \Throwable, but no lower in the tree. Except that \Exception is a class, not an interface. So if you do that, then you have to change every single exception class to extend rather than implement DatabaseException, which might break third-party exception classes that implement DatabaseException.

solideogloria’s picture

Status: Needs review » Reviewed & tested by the community

The proposed patch is guaranteed to work with existing code, and I think it's the best solution.

Edit: The change record draft also looks good to me.

catch credited longwave.

catch’s picture

Status: Reviewed & tested by the community » Fixed

Overall I feel like this shouldn't be necessary, however to make it unnecessary I think we'd have to change the database API's InvalidArgumentExceptions to assertions. Also discussed briefly with @longwave who pointed out that Guzzle has the same pattern.

So.. Committed 87d90f9 and pushed to 10.1.x. Thanks!

  • catch committed 87d90f94 on 10.1.x
    Issue #3340077 by ankitsingh0188, solideogloria, longwave: Make...
catch’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

quietone’s picture

Published the CR.