Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Backtraces for DatabaseExceptionWrappers, lacking a previous Exception, cause a Fatal Error, due to a method call on a non-object.
Fatal error: Call to a member function getTrace() on a non-object in ../core/lib/Drupal/Core/Controller/ExceptionController.php on line 332
The method, ExceptionController::decodeException(), has a refactoring @todo, but I can't find anything else addressing it currently.
Comment | File | Size | Author |
---|---|---|---|
#21 | 2003654-chained-exceptions-21.patch | 8.54 KB | mgifford |
#19 | 2003654-chained-exceptions-19.patch | 6.38 KB | mgifford |
#17 | 2003654-chained-exceptions-17.patch | 8.54 KB | mgifford |
#9 | 2003654-chained-exceptions-9.patch | 9.24 KB | xtfer |
#9 | interdiff.txt | 3.57 KB | xtfer |
Comments
Comment #1
xtfer CreditAttribution: xtfer commentedSimple wrap in is_object().
Comment #2
xtfer CreditAttribution: xtfer commentedComment #4
glennpratt CreditAttribution: glennpratt commented#1: 2003654-1-fatal-error-for-DatabaseExceptionWrapper.diff queued for re-testing.
+1
https://gist.github.com/glennpratt/5666118
Comment #5
larowlanMinor nit
Should wrap at 80 chars
Any way you can replicate this in a test?
Comment #6
xtfer CreditAttribution: xtfer commentedFurther investigation has shown that this is caused by \Drupal\views\Plugin\views\query\Sql() not properly passing the Exception through to the DatabaseExceptionWrapper, but not passing the original Exception. This is an implementation detail where the caller doesn't have sufficient information to pass the parameters correctly.
This suggest the fix is two-fold:
- Fix \Drupal\views\Plugin\views\query\Sql() to pass the $e correctly
- Refactor DatabaseExceptionWrapper so that it contains the correct logic to avoid this situation in future.
Comment #7
xtfer CreditAttribution: xtfer commentedThis patch does the following:
- Tidies up ExceptionController::decodeException().
- Adds a new exception type, ExceptionWrapper, which forces the previous Exception to be injected.
- Bases DatabaseExceptionWrapper of ExceptionWrapper
- Includes a test for ExceptionWrapper to ensure the dependency is set.
- Updates \Drupal\views\Plugin\views\query\Sql() to set the previous Exception correctly.
In addition, I corrected a spelling mistake in DatabaseExceptionWrapperTest.
The potential issue I can see currently is that ExceptionController never gets the actual Exception, just a flattened Symfony FlattenException object, which seems a bit odd to me. This makes it hard to check what type of Exception you've actually got. There seem to be two potential solutions to this problem, mocking a new exception object in decodeException(), which is what Ive done, or adding the original exception back into a new DrupalFlattenException object (which seems to defeat the purpose, but should be doable).
Comment #8
larowlanI like this approach
Some minor phpcs nitpicks
new syntax is Contains \Drupal\Core\Utility\WrapperException
no need to use root-level objects, use \RuntimeException instead
\RuntimeException once use statement is gone
needs leading \
I think the answer might be no, but I know the question will be asked - can we use UnitTestCase instead of UnitTestBase aka Php unit here instead for the test? Would require mock objects.
Comment #9
xtfer CreditAttribution: xtfer commentedUpdated as per cs standards, and changed the test to PHPUnit.
Comment #10
larowlanis it worth making it new \Exception('Something went wrong') and then adding another assert such as
?
Other than that RTBC imo
Comment #11
larowlanis there a reason why we can just go
$stub = new WrapperException('Test Exception', 0, new \Exception');
?Would need a use at the top of the file obviously - I don't think we need the mock in this case because its a simple class?
Comment #12
xtfer CreditAttribution: xtfer commentedIts not worth checking the message since all we want to know is that the Exception has been set. What might be useful is a test which fails, i.e. where the Exception is not set.
As for the $stub object, it appears there is no need for it. I was just following on the examples you pointed me at. ;)
It works as it stands though, so as those two changes don't appear to be deal breakers I'd like to leave them as they are.
Comment #14
larowlanI know you're probably getting tired of my feedback........but
Is there a reason why we need the extra class (WrapperException) can't we just make the $previous argument required on DatabaseExceptionWrapper? Then we can write a unit test for decodeException method, mocking FlattenException - but that would require changing use of check_plain something else.
Comment #15
xtfer CreditAttribution: xtfer commentedIt seemed to make more sense to me to create a base for chained exceptions rather than simply put that functionality into the DatabaseExceptionWrapper. The final result is effectively the same, however.
The archicture of the so-called "ExceptionController" is problematic, though. Its actually a Logger, not a controller, which is why it gets the FlattenedException in the first place. All a bit screwy if you ask me. Its impossible to sub-class it, so we end up having to play "guess the exception class" inside of it in decodeException.
So I agree a test for decodeException would be good, but thats separate to whether we have a WrapperException or not.
Comment #16
xtfer CreditAttribution: xtfer commentedComment #17
mgiffordI was just mucking about with Views trying to track down an exposed filter problem when I got:
PHP Fatal error: Call to a member function getTrace() on a non-object in /DRUPAL8/core/lib/Drupal/Core/Controller/ExceptionController.php on line 451
By trying to access /admin/content
Really, this should never happen with a D8 Core install. You should have to extend it somehow before seeing stuff like this. That's why I'm marking this major.
This is just a re-roll, but it didn't seem to resolve the problem, just seemed to change it.
PHP Fatal error: Class 'Drupal\Core\Database\WrapperException' not found in /DRUPAL8/core/lib/Drupal/Core/Database/DatabaseExceptionWrapper.php on line 15
Comment #19
mgiffordcore/lib/Drupal/Core/Utility/WrapperException.php already exists.
Other than that just a re-roll
Comment #21
mgiffordhmm...
Comment #23
jhedstromThe ExceptionController was removed/refactored in #2323759: Modularize kernel exception handling, not sure if this is still relevant.
Comment #31
pameeela CreditAttribution: pameeela commentedThanks everyone who contributed to this issue.
Based on #2323759: Modularize kernel exception handling and after consulting with xtfer we have determined this issue is no longer relevant.