Learn To extract();
January 18th, 2006UPDATE I stand corrected: I am hard pressed to find any real reason why one would want to use extract to surmount the security and coding concerns, as it really doesn’t gain you anything. Sure, it might be less to type out ($Variable vs. $Array[’Variable’]), but is it really worth all the confusion? My new opinion? Nope.
The days of explicitly declaring variables passed in from a form for use - or (gasp) using $_GET['varName'] in your code is no longer needed! Clear as well as clean code is the discussion for variable setting today, and we get to explore how easy it is to extract(), Typically you would have call the variable and then set it.
[code lang="php"]
$username = $_GET['username'];
$password = $_GET['password'];
[/code]
The world is changing. While that is not bad for small forms or simple tasks but what if your project is larger or lets just say after reading this article you abondon gathering all the $_GET data from your simple form? Wouldn’t it be nice if PHP could automate it for you? (and easily in one line of code)
[code lang="php"]
extract($_GET);
//Or best to protect it with an if
if($_GET) extract($_GET);
if($_POST) extract($_POST);
[/code]
That function, which is a built-in PHP function by the way is extracting all the query string variables and sets them equal to their respective values. So now instead of having to set the variables, you can use the same name that you called the field element in your php app as a variable. But wait it gets even cooler.
The developers of php knew that you might already be using a variable name and we wouldn’t want to overwrite that. So what to do? Well thankfully they saw this coming and gave us extract_type! And this is what they do
- EXTR_OVERWRITE: If there is a collision, overwrite the existing variable.
- EXTR_SKIP: If there is a collision, don’t overwrite the existing variable.
- EXTR_PREFIX_SAME: If there is a collision, prefix the variable name with prefix.
- EXTR_PREFIX_ALL: Prefix all variable names with prefix. Beginning with PHP 4.0.5, this includes numeric variables as well.
- EXTR_PREFIX_INVALID: Only prefix invalid/numeric variable names with prefix. This flag was added in PHP 4.0.5.
- EXTR_IF_EXISTS: Only overwrite the variable if it already exists in the current symbol table, otherwise do nothing. This is useful for defining a list of valid variables and then extracting only those variables you have defined out of $_REQUEST, for example. This flag was added in PHP 4.2.0.
- EXTR_PREFIX_IF_EXISTS: Only create prefixed variable names if the non-prefixed version of the same variable exists in the current symbol table. This flag was added in PHP 4.2.0.
- EXTR_REFS: Extracts variables as references. This effectively means that the values of the imported variables are still referencing the values of the var_array parameter. You can use this flag on its own or combine it with any other flag by OR’ing the extract_type. This flag was added in PHP 4.3.0.
Note: If the prefixed result is not a valid variable name, it is not imported into the symbol table. Prefixes are automatically separated from the array key by an underscore character. If you’re not sure what variables you’ve created through extraction, you can call get_defined_vars() to see all defined variables in the current scope.
I noticed a post on the extract() page of a possible Zend2 Engine bug, but they also showed how bad programming practices can produce this error. I also have found that if any of the assigned values are null it causes the engine to do all sorts of incredibly whacky stuff like certifiably lose track of variables in an incredibly inconsistent way. Fortuantly I read the post and was able to trace the problem down to the EXTR_REFS flag as well. As noted it’s probably ok to assume that in PHP’s internal variable storage or reference counting mechanism, trying to extract null references makes it lose track or count of something or rather. In a nutshell, if you start getting wierd behavior when using extract() make sure that the array or object you are trying to get variables out of doesn’t contain null keys or values! So I recommend a method to loop through before extracting to check for blank variables - ok so I tricked you into thinking it was a oneliner, get over it - it is still much simplier than your way, hell it’s cool to do right?
January 19th, 2006 at 9:10 am
Hi Tim,
According to the PHP Manual:
“Warning: Do not use extract() on untrusted data, like user-input ($_GET, …).”
A better way would be to use the function import_request_variables(string type,prefix) with string type being G for $_GET vars, P for $_POST vars and C for $_COOKIE vars.
Example:
import_request_variables(”P”, “form1_”);
would return
$form1_var1, $form1_var2, etc…
Danny
January 19th, 2006 at 9:25 am
I agree with Danny on not trusting “unknown” data. But don’t discount extract quite yet, per the manual on import_request_variables: “If you’re interested in importing other variables into the global scope, such as SERVER, consider using extract().”
Thanks again Danny for a great insight
January 19th, 2006 at 9:25 am
there’s also a reverse function - compact()
January 19th, 2006 at 10:18 am
I’d also like to caution users from using extract too liberally, especially on $_GET or $_POST arrays. My primary concern is that you are extracting ALL the variables from the array into the local scope, when you may only need a small number of them.
Also, extract can be a maintenance nightmare when you come back to your code at a later date, or give it to someone else to read/understand. With extract, you have variables popping into existence seemingly out of nowhere.
Typically, you’ll also want to validate and sanitize any values that come in from those arrays so this doesn’t save you that much coding.
January 19th, 2006 at 10:36 am
Point taken Oscar. Well put about using extract in large devel environment, currently I find this at my current job using a far less complicated language. I have found that in smaller personal projects that this helps in minimizing variable declaration if you aren’t overly worried about the values coming in. Also it helps using extract in a class where you can use it in the method of sanatizing your values. My 2 cent
January 19th, 2006 at 10:49 am
I agree with Oscar (disclaimer, we work together and have discussed this issue). You really don’t want to be minimizing your declaration of variables, as this is the number one way that security exploits happen in PHP.
Programming is habit-forming: when you do something one way once, you’re likely to do it the same way again. If you aren’t anal about declaring your variables in small projects, that habit can turn around and bite you on big projects…or if you reuse code from a smaller project.
Thus we have an explicit policy never to use extract(), because the potential downsides far outweigh any benefits.
January 19th, 2006 at 10:52 pm
This is a bad idea.
“The days of explicitly declaring variables passed in from a form for use - or (gasp) using $_GET[’varName’] in your code is no longer needed!”
Those days are already come and gone. They called it “register_globals,” and it wasn’t a good idea then, either. Some basic research of any secure PHP coding practices would reveal why. Start at http://phpsec.org.
January 19th, 2006 at 11:58 pm
This is one of the most terrible ideas ever. You should ALWAYS use $_GET and $_POST for various security, debugging and clarity (as in: where did this variable come from?) reasons. If you wish to learn more about the security risks, check out http://www.sitepoint.com/blogs/2005/08/15/the-problem-with-extract/
Why PHPDeveloper.org linked to this I’ll never know…. no offense, but people (most likely novice php’ers) who don’t know any better are going to read this and think its a good idea; this will leave their website in a highly vulnerable position.
January 20th, 2006 at 8:21 am
Thank you all for these contributions it has helped me to reevaluate the uses of extract. Any further comments on this subject please leave at http://www.designbytim.com/2006/01/20/rethinking-extract-by-convention/
January 20th, 2006 at 8:23 am
[...] Since posting Learn To extract(); I have gotten a lot of great criticism that has helped me revise my thinking of the uses for extract(). Along with reading The Problem With ‘extract’ on SitePoint - here is my conclusion to when/where to use extract. Thanks to Danny, Oscar, Sandy, Ed, and Mitchell for their insights on the proper use of extract(). [...]