New API Functions That Rock in Drupal 7

03 Dec in d7, drupal, drupal-7, planet-drupal
Printer-friendly version

For Drupal module developers, hooks get all the glory, and for good reason. Hooks make our lives much easier when it comes to figuring out how to implement a client's complex business requirement.

But the unsung heroes in Drupal's API are its rich suite of utility functions. With each iteration of Drupal, developer feedback and community effort have added to that list functions that increase our efficiency and help make Drupal a better CMS framework.

I have been a Drupal developer since 2005 and I can remember several instances where I've said to myself - elbow deep in code - "Man, I wish Drupal just had a function that did this for me." Oftentimes, developers in the field create utility modules that contain reusable helper functions that address those needs. Sometimes we're lucky and those developers contribute back and we all benefit.

I remember how giddy I was when I saw the introduction of drupal_alter() and drupal_write_record() in D6. Or how I can't imagine life without module_load_include() now. All good things indeed that make the coder in me warm and fuzzy.

In this article, I'm going to show off a few new utility functions shipping with D7 that have stoked my interest. The first function is one that I'm sure nearly every module developer has implemented in some fashion themselves at one time or another: debug().

Let's see debug()'s function signature which can be found in common.inc:

<?php
/**
 * Debug function used for outputting debug information.
 *
 * The debug information is passed on to trigger_error() after being converted
 * to a string using _drupal_debug_message().
 *
 * @param $data
 *   Data to be output.
 * @param $label
 *   Label to prefix the data.
 * @param $print_r
 *   Flag to switch between print_r() and var_export() for data conversion to
 *   string. Set $print_r to TRUE when dealing with a recursive data structure
 *   as var_export() will generate an error.
 */
function debug($data, $label = NULL, $print_r = FALSE) {
  // Print $data contents to string.
  $string = check_plain($print_r ? print_r($data, TRUE) : var_export($data, TRUE));

  // Display values with pre-formatting to increase readability.
  $string = '<pre>' . $string . '</pre>';

  trigger_error(trim($label ? "$label: $string" : $string));
}
?>

This function simply takes a PHP variable and outputs useful information about that variable and what it contains. Let's try it out to get a picture of what it outputs. A very common debugging practice for module developers is to print out the contents of a $node or $user object or some property of those objects. Let's spin up a quick module that will allow us to do that.

First, our module's .info file:

; $Id$
name = Britesparkz
description = "Module for demonstrating various features in D7."
package = Development
core = 7.x
files[] = britesparkz.module
version = "7.x-1.x-dev"

Let's assume, for the purposes of this example, that we have node content that requires some modification and we want to know exactly how that content is being assembled for our nodes. We can implement hook_node_view() in our module and use D7's debug() function to print out our node object to see what we're working with. In our britesparkz.module file:

<?php
/**
 * Implements hook_node_view().
 */
function britesparkz_node_view($node, $view_mode, $langcode) {
  debug($node);
}
?>

The output looks like the following when I view a node on my D7 test site:

Whoa. That's doesn't look helpful at all. What's going on? Well, referring to the docblock comment for the debug() function, we see that by default it uses the PHP function var_export() for outputting available data for a variable. However, the debug() function's documentation also states that if we are attempting to output a recursive data structure, that we should set debug()'s third parameter $print_r to TRUE so debug() will use the PHP function print_r() instead of var_export(). This is because print_r() can handle outputting recursive structures where var_export() will throw an error.

So, let's make some modifications real quick and see if anything gets better:

<?php
/**
 * Implements hook_node_view().
 */
function britesparkz_node_view($node, $view_mode, $langcode) {
  debug($node, 'hook_node_view()', TRUE);
}
?>

And the output of this looks like:

Much better. This time we also used debug()'s second parameter to give our output a descriptive label. This comes in handy when you need to use multiple calls to debug() and you want to keep track of what's happening where.

As you can see in the screenshot above, this output is certainly easier on the eyes and - more importantly - didn't generate an error. That's because our node object is in fact a recursive structure and var_export() chokes on it. Got it? Good.

For posterity, let's see what a variable that var_export() can output looks like:

<?php
/**
 * Implements hook_node_view().
 */
function britesparkz_node_view($node, $view_mode, $langcode) {
  debug($node->body);
}
?>

Not much difference in the presentation of output, but it serves to illustrate when you should use debug() with $print_r enabled and when you shouldn't.

The next two functions to look at are related and work quite powerfully together. I've evangelized all the good things to come for module developers in D7, but I haven't written much about D7's improved theme layer (with the minor exception of the article I wrote about hook_page_alter()). Well, these next two functions should make all my Drupal themers out there very happy campers.

The functions I'm referring to are hide() and render() and they basically equate to slow-simmering your .tpl files in awesome sauce. For themers, they are a long-awaited boon from the Drupal gods/core-contributors. Working in conjuction with ANY renderable Drupal array (e.g. $page, $form, etc...), they are essentially an on/off switch for any piece of a page's content that results in HTML that has passed through Drupal's theme layer.

I'll let that sink in for a second. No more having to add custom CSS or jQuery to add classes to elements. No more having to write a convoluted theme function in template.php that adds the show/hide class attribute to elements. In D7, we only have to know the parent key in a renderable array that corresponds to what content we want to show or hide, and then pass that key off to render() or hide(). The following node.tpl.php example snippet from the drupal.org handbook page on converting D6 themes to D7 should make this clearer:

<div class="content">
  <?php
    // We hide the comments and links now so that we can render them later.
    hide($content['comments']);
    hide($content['links']);
    print render($content);
  ?>
</div>

<?php print render($content['links']); ?>

<?php print render($content['comments']); ?>

The above example will first hide the node's comments and links, then display the main node content, and finally display the node's links and comments. A trivial example, but it serves its purpose of demonstrating how easy it is now to shift the ordering and display of template elements.

D7's refactored theme layer is very exciting and should intrigue themers, module developers, and project managers alike as so much work has been done on it to increase our productivity and simplify implementing complex business requirements. With tools available to us like hook_page_build(), hook_page_alter(), and the helpers hide() and render() - D7 sites are going to not only be easier to theme, but a pleasure to theme.

That wraps up today's article. In the next article in my D7 series, we are going to get our hands dirty and be up to our elbows in Fields, Entities, and Bundles. The introduction of CCK into core as D7's Field API is arguably the biggest addition/change to core yet. We will create a small module that demonstrates how developers can implement D7's Field API and start taking advantage of this incredible new API.

Until next time, happy coding

Comments

dpm?

What's the benefit of the new debug() over devel.module and dpm()/dprint_r()?
-Chris

You can use debug() when you

You can use debug() when you are writing simpletests. If you use dpm() here it gets outputted in the test installation and you never see it. If you use debug() it triggers a PHP error and so gets shown in the test results.

Bob Marchman's picture

Not much

Very little beyond not having to have a dependency on a contrib module for printing debug output. I know most developers usually have Devel installed and use dpm()/dprint_r() religiously, but I found it cool that core introduced its own function. It's very handy for those that develop for core and for those times where you want to debug in an environment with no contrib modules. Personally, I would have loved to have seen Krumo make it into core, or something comparable. Or, for that matter, just go ahead and get Devel in core. Seriously.

This will help make you feel better