What's really bad about extract() is that the default behavior is to overwrite existing variables. This is just a recipe for disaster, no less than the infamous register_globals.
I once wrote a framework where the controller would pass variables to the view as $view->currentUser and the template would access it as $currentUser instead of the more verbose $this->currentUser. This was implemented with extract(), but I went to great lengths to make sure that the scope was clean.
PHP probably ain't gonna get rid of extract() any time soon, but at least they should change the default to EXTR_SKIP.
function qux() {
return compact('foo', 'bar', 'baz');
}
And in the other function: extract(qux());
You can have one "preparer" function that works on the data and sets up the variables, then several others that work on it, and all call the same preparer. Or the reverse.Obviously you can do the same thing by just passing arrays but sometimes a simple variable is easier and less cumbersome.
Wow. This is the anti-Scheme. It's like they took everything good in language design and decided to do the exact opposite. "Let's make a function that has the side effect of introducing variables into scope. That's a great idea."
Oh, Bog....
And ``let'' is? ;-)
Doing a (let [request] (check-auth ...)) would be equally dumb. Let's not blame the tools.
My point is that trusting user input is the error, not having the ability to play with the scope.
Since when is Scheme the arbiter of what is good or bad?
PHP lets you do this, or not do this, your choice. Like C, PHP doesn't tell you what to do, it's up to you.
That's why people actually use it.
Use it or not, if you don't like it you can smash stuff with the triblade screw driver. Or the 7 point socket wrench.
PHP provides a wealth of very useful tools that you can choose to use or not. I don't know why people think my 7 point socket wrench is dumb, it works quite well to round the edges of 6 sided bolts and saves a lot of money on buying those special bolts that can't be removed once tightened.
Your post might be [slightly] humorous, but as an argument against PHP language constructs it fails miserably.
Features that have limited real benefits with lots of risk get abused all the time. They are the retarded tools of the world creating technical debt for everybody else and they should be retired.
Extract is one of the stupidist language feature conceived precisely because it puts something so ripe for abuse into the hands of idiots. It even has a simple name that practically encourages its abuse.
Or making critical parts of infrastructure work by a cron job that calls wget.
For some reason PHP needs extract, because a dictionary just won't do.
foo["var_x"]
// for some reason needs to be:
$var_xLanguage hacks like the one we're discussing are fine IF:
- You work with people who avoid bad features.
- You can avoid using code that uses bad features (less of a problem, until you have to debug things and then you're in a world of hurt).
- The features are not short-sighted hacks that prevent the language from moving forward (e.g., eliminate the opportunity to make things faster through dynamic compilation or whatever).
Scheme is a great language that is not useful in the real world, while PHP is a terrible language that happens to be in wide use. Neither of these positions are unique, and honestly I'd much rather use a bad language with good tooling than a great language with poor support. But I will continue to point out PHP's flaws, which are many and just howling bad, and work towards improving the alternatives.
$login = '1234';
extract(array('login'=>true, 'messsage'=>'hacker'), 'message');
var_dump($login_, $message);
// => $login='1234', $message='hacker'
(pardon my rusty PHP)You can set the EXTR_SKIP flag to do something like that. There are a bunch of other flags as well to control how it works.
function qux() { return [$foo, $bar, $baz]; }
list($foo, $bar, $baz) = qux();Coffeescript has a really neat destructuring operator for this kind of thing:
qux = () -> {blah: "a", blorp: "b", zoop: "c"}
{blah, blorp, zoop} = quz()It's also error prone if you have several feeder/user functions since you have to change them in multiple places.
Or if you have two code paths that end up with two different sets of variables (probably controlled by a semaphore variable) then this won't work.
Ghetto templating (using a PHP file as a template). Extract an object from the database into local vars to echo in the "template".
Parsing fixed width files, zip the columns with their names, then extract in the processing function.
In both cases you know exactly which vars are being replaced. The real WTF is extract on $_{REQUEST,GET,POST,SERVER,...}.
Encrypted and signed files sent over the wire from a company we do business with. Additionally I was just pairing the values up with local names that I chose (the fixed width file had no column names itself, they sent us a word doc (ugh)). And the function that called extract had exactly one local, the array I built representing a row from the file (unused after the initial call to extract).
Extract also lets you prefix the extracted vars, avoid overwriting name collisions, etc. http://php.net/extract
The code from this article is unsafe because it directly operates on user input, was not explicit about what values were required (you can filter an array by key easily enough...) and doesn't isolate the environment it's extracting in. That's the unsafe behavior.
function foo($option1=1, $option2=2, $option3=3, $option4=4) {
//do stuff with params
}
this is unwieldy and difficult to reason about when you see it used, especially if you are trying to use some of the initial default values. ie. foo(null, null, null, 10)
vs. function foo($options) {
$option1 = 1;
$option2 = 2;
$option3 = 3;
$option4 = 4;
extract($options);
//do stuff with params
}
With this, you can just do foo(array('option4'=>10));
obviously its a preference thing, but I think it is a somewhat harmless use case. If there are any params which you do not want to be overriden you move them beneath the call to extract.I personally like to work with explicit variables within the function rather then array indices. With your system I could easily call extract on your resulting array, but I also like defining the variables I want overridden explicitly in code so I my IDE doesn't think they are undefined (though JetBrains does a pretty good job of determining which variables are being created by extract in most scenarios anyway).
The security problem lies in the fact that the authentication code is conditionally called depending on user input.
if ($login) { … }
is not less safe than if ($_POST['c99shcook']['login']) { … }
would have been.It was not meant to be user input
Imagine this setup:
1. you have Nginx at $DOMAIN, with your well-oiled API service (with no HTML rendering logic to be found within it) mounted to /, and a PHP FCGI instance sitting on /views.
2. Nginx is configured such that /views is only accessible from localhost.
3. Your webapp on / mostly sees requests with "Accept: application/json" or some such. It responds to these directly.
4. If your webapp sees "Accept: text/html", it does the same stuff it would to render a text/json response... but then takes that response body, and sends it in an HTTP subrequest back to Nginx, to, say, /views/accounts.php.
5. PHP does this:
extract(json_decode($_POST['response']));
...and then does whatever it does, simply and elegantly, to render your template.6. Your webapp catches the response from Nginx, and sends it back without modification to the client.