Categories
Posts

I Don’t Like PHP’s extract() Function

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. This makes backtracking variable information a real pain. Here’s a basic example to give you an idea:

function say_hello( $person_info ) {
    extract( $person_info );

    //  ... imagine another 75 lines of code here

    print "Hello {$name}!n";
}

If I was looking to find out more about the $name variable there’s no obvious information. A simple search for the variable name shows it only ever being used once. No hint as to where it came from or how it got there in the first place. It isn’t until you catch the extract() call that you realize that it came from the $person_info function argument, but there’s nothing that an easy search operation will do to tell you that. If there is more than one extract() call in the function then you have to start adding debug code and running it just to determine which extract() created the variable.

I like to be able to easily tell where variables came from. Operations that magically insert variables into the current symbol table make that task a lot harder. And there’s no reason that you’d ever have to use extract(), it’s purely for convenience. A convenience that can make life harder for others and for yourself when debugging code you haven’t looked at in years.

There’s a companion function to extract() called compact, which does the opposite. It takes a list of variable names and creates an array. I don’t have any problem with this because it’s not hiding where the data for the new array came from.

26 replies on “I Don’t Like PHP’s extract() Function”

I agree!

The first time I’ve seen the use of extract() and compact() was while I was tracing through WordPress source code. I find it makes code less readable. I think open source software would try to be as readable as possible and the use of these functions don’t help. You also run the risk of including variables in your extract that you did not intend to be their.

I personally think the associative array is the most powerful feature in PHP and should be used in place of extract() calls when ever possible. It’s readable and keeps values well organized. Why not abandon the extract() and compact() calls and just deal with the array directly?

e.g.

print “Hello {$person_info[‘name’]}!n”;

extract() is great when used appropriately. For example, variables in an html template. You can assign data to a view object, the object can hold that data in an associative array, you render the view object and it extracts the associative array and includes an html template.

Without extract:

With extract:

I’ll try again.

Without extract:
…some html… …some html…
With extract:
…some html… …some html…

Yeah, I don’t like extract. I don’t get where stuff comes from.

I also don’t like how it’s used in WordPress shortcodes examples, where it looks like the array is being initialized with values that are overwritten during the actual “extract” operation.

Rick, I don’t quite get your example, but I’m willing to trust that it’s a good reason for using extract. For other cases, definitely not. Maybe one of those seemingly good ideas that was slightly misimplemented, that is, unforeseen side effects crop up.

Back to the salt mine…

I agree that extract() should only be used when rightfully necessary. For example in a template manager as Rick mentions. I’m actually working on my own Bloging platform and I use extract to “export” variables into the template. So in the PHP code I would call $page->addVars(array("varName" => "value"); and within the template I can access it as $varName. This lets me know where this variable comes from. Also, in regards to extract and security I implement sanity/error checks to stop any potential security issues.

I have a template system and in it, classes of what i call “modules” are dynamically loaded.

all object generated are stocked in an associative array in the “template” class and i wanted to access these “module” objects in my template with : $module1->function() ,instead of $template->module_array[object]->function() …

Moreover, it seamed that by including and setting a “module” classe inside the template class,it wasn’t accessible as $module1 directly even when put to the global scope (this is wierd, i know i need to review my app design skills).

By simply extracting the module array ($template->module_array) right before including a template (like a body.tpl.php file for exemple) all this became possible.

I don’t really need to know what the object name is from because it is extracted from the filename of the file containing the class definition,like module1.php .

I don’t know if i’m clear enough, but it was the only way for me and i will use extract only for this extreme case.

Thanks for this article which i came to read and made me try this solution.

The trick to using extract cleanly is to always use the EXTR_IF_EXISTS flag. This allows you to predefine the only variables you are willing to let extract populate, and also means that if you search for a mystery variable name it will take you right to the lines where the extract happened.


list($name, $title, $email, $date) = array_fill(0, 4, null);
extract($data, EXTR_IF_EXISTS);

This is the only way I ever use extract, and although a little strange looking at first it quickly because really easy to recognize what is going on. A slight variation also lets you assign default values to variables which may or may not exist in the array.


list($name, $title, $email, $date) = array('Anonymous', '', 'example@example.com', null);
extract($data, EXTR_IF_EXISTS);

I’ve used extract in normal code before (non-template systems, as well as template systems), and the way I got around the “not knowing where things come from” issue is to add a comment to the extract stating what variables were created by the function.


extract($data); // creates $name, $title, $email, and $date

Now when I’m running a search for a variable, it will find the extract line, and I’ll know what’s going on.

It is confusing when the comment is missing, though, or when any other method of letting the reader know what variables are being created is missing.

I also like Mr. Brewer’s method… keeps unwanted variables out (because who knows what’s going on elsewhere in the code, and what other elements are being added to the $data array) and it also lets the reader know what variables are being created by the function. Very nice.

I’ve used extract() in order to create some pretty nifty de-serialization of incoming $_POST data where I have potentially hundreds of variables that could be extracted. The trick to avoiding the problem you mention above is to use the EXTR_PREFIX_ALL parameter. Thus I use extract( $_POST, EXTR_PREFIX_ALL, “POST” ), or even better, extract( $_REQUEST, EXTR_PREFIX_ALL, “POST”), which gives me variables named $POST_name, etc. Now it’s really easy for me to see which variables come from regular programming (e.g. $name) and which come from some extracted data (e.g. $POST_name). Similarly when getting lines from a database I can use $row = mysql_fetch_array( $result ); extract( $row, EXTR_PREFIX_ALL, “DATA” ) and then use variables like $DATA_name, $DATA_address, etc. By judicious use of prefixes I also avoid naming conflicts (e.g. $POST_name is different than $DATA_name which are both different than a $name you might have created on the fly)

These are definitely ways to make extract() less annoying, but I still don’t see how that would be better than just referencing $POST[‘name’], $row[‘name’] and $name.

Ditto this approach. I do this all the time, without the array_fill magic:

$name_first = $name_last = $name_middle = $name_suffix = $name_prefix = null;
extract($person, EXTR_IF_EXISTS);

The ones which do it without pre-declaring the vars it feel a little scary to me.

Hi guys, I am getting this error in a log file in my cache folder which is created each time a page is loaded:

PHP Warning: extract() [function.extract]: First argument should be an array in ……

Has anybody any ideas, as I am stumped. Not a programmer so you know.
Cheers

Hi, very good point. I was used to:

$secured = array();
$secured = secure_input($_POST);
extract($secured, EXTR_PREFIX_ALL, 'post');

Because after that came the insertion into DB. But after reading your opinions it looks to me now really unnecessary to extract. Keep posting such best practices 😉

Interested to know if this method of using list() can be used with shortcode_atts().

This is the most common place I find extract() and where it becomes a annoyance.

Thanks for the insight so far, very helpful.

What about when passing an array of objects?

It’s less readable to do:

$args[“object_four”]->getInfo();

Than to do:

Extract($args);

$object_four->getInfo();

I think that when we’re using MVC, instead of using extract we can call our array $controller and pass it to the template.

When someone is reading our code, if we use MVC that person must know that we use MVC. So when he reads he knows that everything related to $controller came from the controller that called to that template. And that’s it.

Great point why cause more friction!

I feel if there’s a NEED to use extract() through either non-understanding or it’s the current practice of the codebase you’re working on then at least come up with a convention to name its resulting variables like $extVariable so it’s not completely magic especially if it’s used hundreds of lines later.

Leave a Reply

Your email address will not be published. Required fields are marked *