Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough

Implementing drupal_static on function with dynamic variables

Parent Feed: 

Using drupal_static() on your helper functions is a great habit to get into when developing your modules. Drupal static ensures that your function will only run through its logic once during the bootstrap process. Depending on your functions logic, this can start saving lots of processing time if your function is being called multiple times during bootstrap or page load.

Implementing drupal_static

Let's take the following function for example. Here is helper function takes a url string from the ELMSLN network and breaks it up into identifiable parts.


/**
 * Helper function to break an elmsln url into
 * identifiable parts. 
 * 
 * @param  string $url
 * @return array()
 *         - protocol
 *         - path
 *         - subdomain
 *         - domain
 */
function _cis_connector_url_get_properties($url) {
    // split the url at the protocol
    $url_array = preg_split('/(:\/\/)/', $url);
    $properties['protocol'] = $url_array[0];
    // delete the protocol
    array_shift($url_array);
    // split the domain at the first backslash
    $url_array = preg_split('/\//', $url_array[0], 2);
    $properties['path'] = $url_array[1];
    // split the url into subdomain and domain
    $url_array = preg_split('/\./', $url_array[0], 2);
    $properties['subdomain'] = $url_array[0];
    $properties['domain'] = $url_array[1];

    return $properties;
}

By itself the function doesn't have a ton of processing time invested into breaking the url string into an array but until we look at the function that is utilizing it. In our module we are calling it from the very dangerous `hook_url_outbound_alter()`. Depending on what page we are rendering, this function has the potential of being called a ton of times during the bootstrap process. It would be great if this function could just instantly return the value from the last time I called it. This is where drupal_static comes into play. Drupal static will check to see if the function has run already, and if it has, it will immediately return it's value saving you a lot of processing time. Let's look at how to implement drupal_static on this function.


function _cis_connector_url_get_properties($url) {
  $properties = &drupal_static(__FUNCTION__);
  
  if (!isset($properties)) {
    // split the url at the protocol
    $url_array = preg_split('/(:\/\/)/', $url);
    $properties['protocol'] = $url_array[0];
    // delete the protocol
    array_shift($url_array);
    // split the domain at the first backslash
    $url_array = preg_split('/\//', $url_array[0], 2);
    $properties['path'] = $url_array[1];
    // split the url into subdomain and domain
    $url_array = preg_split('/\./', $url_array[0], 2);
    $properties['subdomain'] = $url_array[0];
    $properties['domain'] = $url_array[1];
  }

  return $properties;
}

It's as easy as that! We first set the return value `$properties` to the drupal_static function. Then we check to see if the drupal_static function has the properties value. If it doesn't then it runs through its logic. If it does, we just immediately return the `$properties` value. Pretty cool. Let's test to make sure this is working.


function _cis_connector_url_get_properties($url) {
  $properties = &drupal_static(__FUNCTION__);
  
  if (!isset($properties)) {
    // split the url at the protocol
    $url_array = preg_split('/(:\/\/)/', $url);
    $properties['protocol'] = $url_array[0];
    // delete the protocol
    array_shift($url_array);
    // split the domain at the first backslash
    $url_array = preg_split('/\//', $url_array[0], 2);
    $properties['path'] = $url_array[1];
    // split the url into subdomain and domain
    $url_array = preg_split('/\./', $url_array[0], 2);
    $properties['subdomain'] = $url_array[0];
    $properties['domain'] = $url_array[1];

    drush_print_r('Processed the following url: ' $url);
  }

  return $properties;
}

Then we'll use Drush to execute some example calls:


_cis_connector_url_get_properties('http://courses.elmsln.local/sing100');
_cis_connector_url_get_properties('http://courses.elmsln.local/sing100');
_cis_connector_url_get_properties('http://courses.elmsln.local/sing100');

# Result
Processed the following url: http://courses.elmsln.local/sing100

It works! It only processed the url one time!

Implementing drupal_static on a function with dynamic variables

But what happens if we run a bunch of different urls through the function? Let's try:

_cis_connector_url_get_properties('http://courses.elmsln.local/sing100');
_cis_connector_url_get_properties('http://blog.elmsln.local/sing100');
_cis_connector_url_get_properties('http://studio.elmsln.local/sing100');

# Result
Processed the following url: http://courses.elmsln.local/sing100

Ugh-oh. It only processed the first url. That's because of our drupal_static name. Drupal static calls need to have unique names passed to them so that the caching layer will know what cached values belong to which functions. An easy way of doing this is simply using the name of the parent function by giving it the name unique name of `&drupal_static(__FUNCTION__)`. However, this only works if your variables being passed into the parent function are static; meaning they will always be the same during the bootstrap process. In our case, the variable $url, has multiple values.

The simple fix is to create a unique name based on the function name AND the incoming variables. To do this, I like run them through 'drupal_hash_base64()'. This makes a really nice hash value even if you have long string or arrays coming in as variables. Let's take a look at what this will look like:


function _cis_connector_url_get_properties($url) {
  $function_id = drupal_hash_base64(__FUNCTION__ . $url);
  $properties = &drupal_static($function_id);
  
  if (!isset($properties)) {
    // split the url at the protocol
    $url_array = preg_split('/(:\/\/)/', $url);
    $properties['protocol'] = $url_array[0];
    // delete the protocol
    array_shift($url_array);
    // split the domain at the first backslash
    $url_array = preg_split('/\//', $url_array[0], 2);
    $properties['path'] = $url_array[1];
    // split the url into subdomain and domain
    $url_array = preg_split('/\./', $url_array[0], 2);
    $properties['subdomain'] = $url_array[0];
    $properties['domain'] = $url_array[1];

    drush_print_r('Processed the following url: ' . $url)
  }

  return $properties;
}

Now let's test it:

_cis_connector_url_get_properties('http://courses.elmsln.local/sing100');
_cis_connector_url_get_properties('http://courses.elmsln.local/sing100');
_cis_connector_url_get_properties('http://courses.elmsln.local/sing100');
_cis_connector_url_get_properties('http://blog.elmsln.local/sing100');
_cis_connector_url_get_properties('http://blog.elmsln.local/sing100');
_cis_connector_url_get_properties('http://studio.elmsln.local/sing100');
_cis_connector_url_get_properties('http://studio.elmsln.local/sing100');

# Result
Processed the following url: http://courses.elmsln.local/sing100
Processed the following url: http://blog.elmsln.local/sing100
Processed the following url: http://studio.elmsln.local/sing100

As you can see, even though we sent the function seven urls, it only ran through the process three times. Success!

Conclusion

Drupal static is an extremely easy way of making your functions more performant. Just make sure that if your functions variables are going to be different, you need to create a custom name to pass to drupal_static().

Update

In the comments below, Panagiotis Moutsopoulos shows an alternative way that you can handle this which I like better.  Instead of putting the variable inside of the drupal_static function id, you can simply segment your properties under the value of the function variable. In your if statement you then don't just check for 'properties' you would check for '$properties[$variable]'.


function _cis_connector_url_get_properties($url) {
  $properties = &drupal_static(__FUNCTION__);
  if (!isset($properties[$url])) {
    // split the url at the protocol
    $url_array = preg_split('/(:\/\/)/', $url);
    $properties[$url]['protocol'] = $url_array[0];
    // delete the protocol
    array_shift($url_array);
    // split the domain at the first backslash
    $url_array = preg_split('/\//', $url_array[0], 2);
    $properties[$url]['path'] = $url_array[1];
    // split the url into subdomain and domain
    $url_array = preg_split('/\./', $url_array[0], 2);
    $properties[$url]['subdomain'] = $url_array[0];
    $properties[$url]['domain'] = $url_array[1];

    drush_print_r('Processed the following url: ' . $url);
  }

  return $properties[$url];
}
Author: 
Original Post: 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web