Skip to content

Instantly share code, notes, and snippets.

@dotherightthing
Created August 15, 2019 05:51
Show Gist options
  • Save dotherightthing/a1bde197a6ff5a9fddb886b0eb17ac79 to your computer and use it in GitHub Desktop.
Save dotherightthing/a1bde197a6ff5a9fddb886b0eb17ac79 to your computer and use it in GitHub Desktop.
[extract()] PHP's extract() function dynamically creates variables, making debugging harder. #php #wordpress

extract()

Created: 2017.04.19

PHP's extract() function dynamically creates variables, making debugging harder.

extract() creates a variable for each key in an associative array, checking that it is a valid variable name, and that it does not collide with any existing variables.

Using extract()

Some WordPress tutorials make use of extract(), to minimise the code required to reference array items in HTML templating.

For example, in WordPress there is a WP_Widget class. It has several methods including widget, which is passed a standard array of $args. These arguments allow users to customise the widget title, and the HTML that wraps the widget.

Without extract()

function widget( $args, $instance ) {

  echo $args['before_widget'];

  echo $args['before_title'] . $args['title'] . $args['after_title'];

  echo "<p>My widget content.</p>";

  echo $args['after_widget'];
}

With extract()

function widget( $args, $instance ) {

  extract( $args );

  echo $before_widget;

  echo $before_title . $title . $after_title;

  echo "<p>My widget content.</p>";

  echo after_widget;
}

Easier for the developer, harder for the developer

An unwritten rule of development life is: laziness on your part usually results in extra work for someone else.

Here, the extracted variable names are much easier to write, but as they're not declared anywhere, it is very difficult to figure out where they came from:

function widget( $args, $instance ) {

  extract( $args );

  require('inc/views/front-end-widget.php');
}
<!--front-end-widget.php-->

echo $before_widget; // ??

echo $before_title . $title . $after_title; // ??

echo "<p>My widget content.</p>";

echo after_widget; // ??

Searching the codebase for $before_widget won't show any results, because it was dynamically created. A developer just needs to know that WP_Widget::widget is passed an array of $args - which always contains before_widget.

extract() is a terrible function that makes code harder to debug and harder to understand. We should discourage it’s [sic] use and remove all of our uses of it.

When looking at PHP code that I’m not familiar with a common task is back tracking what operations were done to a variable for it to end up in it’s current state. More often than not this means looking through dozens of lines of code in a function or method. First on my list is often figuring out how a variable came to be in the function in the first place. Was it a global, function argument, class variable or a return value from another function? Knowing this helps me determine if I need to start looking outside the function.

Then there are the times that I can’t find any reference to where a variable came from at all. It’s just suddenly being used. One way that happens is through the extract function. You pass it an array and it injects the array items into the current symbol table.

Easy for everybody

Predeclaring the variables that extract() will dynamically create, provides a way to trace these back to the extract function.

In addition, using EXTR_IF_EXISTS will only overwrite the variables that we've created. This means that if we don't predeclare a variable, it won't be available in our HTML templating.

function widget( $args, $instance ) {

  // predeclare the variables
  $before_widget = $before_title = $title = $after_title = $after_widget = null;

  // only overwrite the predeclared variables
  extract($args, EXTR_IF_EXISTS);

  require('inc/views/front-end-widget.php');
}

Adding a little extra code upfront, goes a long way towards keeping the code readable and maintainable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment