Well Formed Errors Initiative

Last updated on
6 October 2020

The Goal

Even long time programmers can be at a loss when a large program throws an error from a corner of it they've never heard before. This isn't unique to Drupal, or even to PHP, but Drupal's error messages can be vastly improved upon from where they are now. That is the goal of this initiative - to take as much mystery as possible out of situations when something goes wrong.

There is overlap between this initiative and others, and it can be argued that this issue is entirely contained within the D8DX initiative, but this one has a more clearly defined focus - to improve upon the error mechanisms of Drupal core. In particular:

  • No more white screens of doom, at least not in a development environment with error reporting turned on. Something useful should always be returned.
  • Begin using assert statements throughout the program. Assertion is a powerful but often misunderstood programming feature that, properly pursued, can remove hours and even days out of a bug hunt.
  • Use of trigger_error when appropriate Exceptions are the preferred norm, but there are corner cases where trigger_error is appropriate as will be discussed below.
  • Include resolution instructions for commonly occuring errors.

What this is not about.

This initiative is not about error and exception handlers so long as they can conform to the error handling requirements discussed below. Instead it is concerned with the method in which the errors are actually thrown by the system - changing it from semi-random strings to a unified schema that allows for more detailed error messages.

Proposed Implementation

PHP has an error message length limit that is relatively short, which makes it impossible to go into too much detail with in code error strings. Further, such long messages in code would quickly become a nightmare to maintain.

Proposed is that all Drupal error and exception messages begin their life as an error code. That code is

NamespacePath.Class.Method.Identifier

For the few functions that aren't class bound the string is

NamespacePath.Function.Identifier

Identifier is necessary since more than one exception, assertion or error may exist in the same method, especially if it's large.

The internal error handlers would be rewritten to match this string against yml files provided in the modules. and also one core yml error file(s) for errors in core. Any module can comment about any error though, so contributed modules could expand the documentation on errors if desired. This is especially true if the module uses hooks that could cause a failure in the core system.

A patch file containing a prototype of this concept for assertions only is here. This initiative calls for this coding to be expanded to the exceptions.

Introducing Assert

One of the most powerful weapons against bugs available is the assert statement, which is sadly ignored by Drupal. This needs to change. Assertions are not exceptions - The key differences are:

Purpose

Assert is for debugging only - exceptions are for error handling. While all bugs are errors, not all errors are bugs. Errors can be caused by users, by system hardware failure, the misbehavior of other programs or a whole other host of reasons external to the program. The program must expect these "exceptional" circumstances and resolve them somehow. If a condition can be expected to arise in the normal operation of the program, however rarely, then an exception is called for.

By contrast, Assertions check for conditions that not only must be true for the program to continue, but also are impossible not to be true unless bad code or configuration files are present. Note when I say configuration files I here refer to the yml files that configure modules - not the configuration options an end user can compose. Note however installing a module is not composing a configuration for the purposes of assert. The incoming module's behavior should have already been tested by its developer.

Documentation

In a way Assertions are a form of code self documentation. By reading them a programmer can know what the original programmer expects to always be true at the start of the method, and sometimes at the end (such as asserting that a return is going to be of the type desired). If one of those conditions isn't true then there is, at the least, code behavior the original developer did not account for or else he wouldn't have asserted that the condition was impossible.

This gives the programmers who follow some amount of insight into the intent and expectations of the original code, which sometimes can be invaluable even when the assertion isn't failing or even turned on.

Non-resolvable

Exceptions can be caught by try/catch. Assertions, like errors, cannot. Once triggered they go out as a warning or fatal error, or to a designated assertion handler which is usually different from the error handler. If an assertion only throws a php warning then code execution will resume at the next line.

Removable

Finally, unique to assertions is the ability to be disabled system wide. The theory is that once the configuration has been checked over, the code has been debugged and everything is working the assertion code is no longer necessary. Rather than hunt it down and delete it (which introduces the possibility of errors since editing is occurring) the assertions can be turned off. In this state they are for all intents and purposes comment text.

trigger_error has its place

Before there were exceptions there was the trigger_error function. With the arrival of exceptions this function has largely fell by the wayside, but there are still two circumstances where it is of use. Both of these uses are predicated on the following feature of errors - unless the triggered error is fatal, program execution resumes at the line immediately following the error. This isn't possible with exceptions short of recalling the throwing function and even then it's tricky and some tasks will likely be redone.

E_USER_DEPRECATED

At a minimum the trigger_error function should be used t to throw E_USER_DEPRECATED notices at the start of methods that are pending removal at some point in the future. This makes such plans by the core developers immediately and unambiguously clear.

E_USER_NOTICE

Subject to sharper debate is using E_USER_NOTICE to alert developers about things they really shouldn't be doing but the program can tolerate them doing, usually by assuming a value is falsey. This mirror's PHP itself by being forgiving to new programmers, but there is an argument to be made that such is damaging to them and Drupal in the long term.

Exceptions...

...handle everything else.

The Project

The project itself divides into three parts. First, rewriting the error class to decode the error strings and resolve them to the long form messages contained in the yml files. Second placement of asserts and changing exceptions to use those strings throughout the code, which will be a long term process guaranteed to run the life of the project. Third, the composition of those long form error messages.

Those messages should explain.

  • Where the error is really occurring. PHP already does this to some extent
  • What the most likely causes of the error are.
  • How to resolve the error if it is one of the more likely causes, and where further documentation can be found.
  • Why and When for deprecation notices, specifically why whatever it is has been deprecated, what replaces it, and when the deprecation will be followed up for removal if that has been scheduled.

These files exist for assertion failures, errors and uncaught exceptions.

  • As mentioned before, this initiative should be viewed as a subset of the Drupal 8 Developer Experience Improvement Initiative (D8DX), focusing on a specific aspect of the developer experience, error reporting.
  • By its very nature this is one of the Testing Initiatives, although this project focuses on dev time testing where the testing initiative is focused on the automated testing. The nature and disposition of the errors and exceptions, and the introduction of assertions, is an area of overlap.
  • The abstraction of error strings was done to get around PHP's very restrictive 1K limit on error message strings. There is a very helpful by product though - it should be trivial to instruct the error system to load verbose error reports in different languages, which is a part of the Multilingual Initiative
  • Simplifying the error messages and reducing research time to resolve errors should, in theory, lower the learning bar, which is part of the Learnability Initiative.

In short, The handling of errors touches directly and indirectly a large amount of the project.

The following projects are related to this initiative

  • Assertion Handling This project will introduce an Assertion Handler and a baby version of the Error System to Drupal 8.0.x. At this late stage in the dev cycle that's all that can be done. The assertion statements this project introduces are limited to the DrupalKernel class, though related projects will introduce assertion statements to other areas of the system on a by need basis, with priority given to those components and modules most vulnerable to disruption by miswritten module yml files.
  • Template AssertionsSpeaking of which, the various twig classes are very easily disrupted by bad service.yml setups and the errors they raise are cryptic. This child of the assertion handling ticket addresses the template component specifically.
  • Use Whoops for exception handling Whoops is a separate system for formatting error messages, but as written it has the major drawback of not being lazy loading. Given the performance requirements on Drupal I don't know if it can be included, but at the very least the project can be analyzed and appraised for pieces that might be added to an error handling system specific to Drupal
  • Well Formed Errors System - this is the 8.1.x heir to the Assertion Handling project, which expands that project to include exception, error, and register shutdown handlers (which can even capture parse errors outside the index.php and ErrorSystem itself). It also replaces Drupal's current disparate collection of error handlers and loggers and should expose an API for Devel and similar modules to expand upon and provide greater error management.

Help improve this page

Page status: No known problems

You can: