Tracing & Debugging Drupal Errors.. Backwards!
Have you ever encountered ambiguous Drupal errors? I bet you have! Errors like those which happen in Drupal core files can be really tricky! They usually occur based on an invalid input or a parameter sent by one of the contributed or custom modules, but PHP errors don't tell you that; they just tell you where the error happened not who caused it. Errors like:
Fatal error: Unsupported operand types in form.inc on line...
Fatal error: Call to undefined function function_name() in /sites/modules/php/php.module(80) : eval()'d code on line...
Fatal error: EntityMetadataWrapperException: Invalid data value given. Be sure it matches the required data type and format. in EntityMetadataWrapper->set()
Although I'm familiar with many types of errors nowadays because I've seen them a lot in my life, I still get challenging bugs every now and then. I usually use Xdebug and trace errors using breakpoints around the suspected places where the bug is happening. The problem with the traditional way of debugging is that it starts from the very beginning and goes forward. This is cool, but sometimes the code logic is just too long and complex for that.
I recently started using this function:
$options
= DEBUG_BACKTRACE_PROVIDE_OBJECT [, int $limit
= 0 ]] )Which allows you to debug errors backwards! It is much more efficient to do so in some cases. The concept is very simple; you just place that function where the PHP error is happening, and it returns a list of the function calls that led to this error, along with their context arguments. So for instance, when a function in the core receives an invalid argument, you go back to the previous caller function and see what kind of argument did it pass to it, and that would give you a much better understanding of why the problem is happening.
Below is a quick real life example of how to debug code backwards.
I created a custom module with a form function:
function custom_init() { drupal_get_form('custom_form'); } function custom_form() { $form = array(); $form['fullname'] = array( '#type' => 'textfield', '#title' => 'Full name', 'description' => 'Enter your full name', '#required' => TRUE, ); return $form; }
After enabling this module, it would immediately throw the following fatal error:
Fatal error: Unsupported operand types in /www/sandbox/includes/form.inc on line 1808
Now let's use the backtrace function exactly after the error line: which would look something like this:
// Assign basic defaults common for all form elements. $element += array( '#required' => FALSE, '#attributes' => array(), '#title_display' => 'before', ); print_r(debug_backtrace()); die();
This will print an array of the function calls which led to this place. I dumped the output in an external link for reference. You can view it here: http://codebin.org/view/be08bb15
Looking at the very first element in the debug array you will understand that the error occurred in the form_builder function while building the a form called custom_form. Also, looking at the arguments passed - if you're familiar enough with Drupal form API - you will be able to spot the faulty "description" property missing its preceding hashtag.
If you scroll down to the 3rd element in the array, you will end up at the custom.module hook_init which has triggered this whole form building.
I hope this helps some developer out there and saves them some time :)
If you have any questions, leave them in the comments down below.