In PHP you have the create_function() function which creates a unique named lambda function like this:
$myFunction = create_function('$foo', 'return $foo;');
$myFunction('bar'); //Returns bar
Is this actually any better (apart from being more easy) then just doing:
do{
$myFunction = 'createdFunction_'.rand();
}
while(function_exists($myFunction));
eval("function $myFunction(\$foo) { return \$foo; }");
$myFunction('bar'); //Returns bar
Is create_function really better? (apart from the fact that it is more easy)
Using eval() will clutter the global function list, create_function() will not, apart from that there's no big difference. However, both methods require writing the function body inside a PHP string which is error-prone and if you were working on my project I would order you to just declare a helper function using the normal syntax.
Anonymous functions in PHP are so poorly implemented that your code is actually better off not using them. (Thankfully this will be fixed in PHP 5.3).
On my understanding of the relevant docs,[1] they both do the same thing, create_function() just comes up with a unique function name for you.
To address some other comments on this question:
create_function can be assigned to a variable making the function accessible to other parts of your code, whereas eval is only useful for the given scope.
It may well be that eval() runs in the current scope, but function definitions get dumped into the global namespace anyway.[2] So whenever you define a function, it will be accessible everywhere else in your program.
Using eval() will clutter the global function list, create_function() will not
create_function() only returns a string with the name of the new function,[3] not some special callback type. So, both techniques will pollute your global namespace.
So no, apart from create_function() being easier, it does not appear to be any better than eval().
Footnotes:
[1] http://au2.php.net/manual/en/functions.user-defined.php ; http://au.php.net/create_function ; http://au.php.net/eval
[2] http://au2.php.net/manual/en/functions.user-defined.php
[3] http://au.php.net/create_function
Personally, I've found that create_function() is extremely handy when sorting arrays.
In fact, I just searched the web, and it seems that the PHP documentation has a good example of this.
http://us.php.net/create_function
Scroll down to Example #3 Using anonymous functions as callback functions.
create_function can be assigned to a variable making the function accessible to other parts of your code, whereas eval is only useful for the given scope.
(apart from the fact that it is more easy)
I don't understand how you can dismiss this so readily. Given your two examples, which is easier to understand at a glance? Create_function tells you what you intend to accomplish. Eval doesn't.
Related
In PHP, one can write
$a="$b$c";
This concatenates the assumed strings in $b and $c into $a elegantly (that is, using minimal yet clear syntax).
Now I want to move this statement into a function. The problem is that $b and $c are naturally interpreted as being local to this function, not its caller (which can be the global code or a function). Even if the caller defines $b and $c, this new function cannot see those definitions.
I want to do something clever that requires the statement to work the same (except for the location of $a) even though it has been moved into a function.
Furthermore, I want this to be efficient. No use of extract() or debug_backtrace(). Just want to use the caller's local scope. I don't mind if I use Zend to get the caller's symbol table, or any other hack, so long as it's efficient.
Don't worry, I'm not going to use this as general programming practice (ugh), just for one specific and wonderful purpose.
I know you don't want extract, but could this help?
<?php
function foo()
{
$b = 'Butter';
$c = 'Cup';
$v = get_defined_vars();
echo bar($v);
}
function bar($v)
{
extract($v);
return $b . $c;
}
foo();
Output:
ButterCup
There is no native PHP functionality for violating function scope in this way, other than backtraces included in debugging functions and exceptions.
I'd suggest looking at the C code for debug_backtrace() and seeing if you can write an extension that implements the bit of it you need (walking up the stack one step to find the parent symbol table) but which performs better than the existing function (I've no idea if that's possible, I don't know where the overhead comes from).
However, I would also recommend thinking really really hard about what problem you're actually trying to solve, and why it's led you to want to break function isolation in such an unusual way. Maybe there's a completely different solution.
For instance, if what you want is a kind of macro, rather than a function, you could write a pre-processor (or find one that's already been written) so that the code is literally pasted into place, and you don't need to hack the symbol table.
An important note: $GLOBALS are dirty and evil. Don't use them. Ever. Never ever ever.
Please focus on the fact that it doesn't work and not why you would be doing this in the first place, it is purely a theoretical question about a technical exercise.
This is a rather weird one. I'm attempting to construct a variable variable using a string named $GLOBALS.
From the global scope
Let's see what we get when var_dump()ing this in the global scope.
$g = sprintf('%s%s%s%s%s%s%s', chr(71), chr(76), chr(79), chr(66), chr(65), chr(76), chr(83));
var_dump($$g);
The result is an array of global variables, which you can see here. Great! So, let's try this in a function.
From a function scope
First, let's just make sure that we can actually run an $GLOBALS check within a function.
function globalAllTheThings()
{
var_dump($GLOBALS);
}
globalAllTheThings();
The result is: it works!! You can see this here.
Now, let's try the first test that we used in the global scope, within the function, and see what happens.
function globalAllTheThings()
{
$g = sprintf('%s%s%s%s%s%s%s', chr(71), chr(76), chr(79), chr(66), chr(65), chr(76), chr(83));
var_dump($$g);
}
globalAllTheThings();
For simplicity's sake
You can also try this without the weird obfuscation (don't ask).
function globalAllTheThings()
{
$g = 'GLOBALS';
var_dump($$g);
}
globalAllTheThings();
It returns NULL. What's that about?? Why does it return NULL, and what can I do to get this working. Why, you ask? For educational purposes of course, and for science!
Because the manual says so:
Warning
Please note that variable variables cannot be used with PHP's Superglobal arrays within functions or class methods. The variable $this is also a special variable that cannot be referenced dynamically.
http://php.net/manual/en/language.variables.variable.php
It's simply "special". PHP is "special". Superglobals don't play by the same rules as regular variables to begin with. Someone forgot to or decided against making them compatible with variable variables in functions. Period.
1I know it may sound silly from the get go...
and let me tell you right off the batt, this ain't the same question as The advantage / disadvantage between global variables and function parameters in PHP. asked right here on stackoverflow. There, asker wonders local vars vs global vars. Here, globals vs globals. My question is all about the PHP's internal way of handling the global variable access and speed.
Here is the question, in the below examples, is the function_1 supposed to run faster than the function_2?
function function_1 ( &$global_variable_x) {
//do something with $global_variable_x
}
function function_2 () {
global $global_variable_x;
//do something with $global_variable_x
}
Let me highlight what's the difference...
In case 1, you pass the global in the function arguments and not only that, you pass it as by ref so the memory location is handed to PHP directly. Because of this trick, there is no need for the use of the global keyword within the function, and because of this very fact, there is no time spent by PHP looking up the global in the global name space. Then the question is why not do it? It's got to be faster, ain't it?
Of course, it is easy to misinterpret this question and get into the usual chores of talking about
Globals are bad
Globals do not need to be passed thru function args because globals well... are globals, so they can be accessed anywhere anyway.
and finally, it does not make sense to pass a global thru a function argument from a semantical point of view, it confuses the hell out of people.
none of which addresses the question being asked.
It's all about speed.
if its global it makes no sense to use it as an argument to a function which can see that global. It either 1) won't be faster or 2) it will run barely slower 3) it will run faster by very little and the reason for this will defy formal logic.
Why don't the function handling functions like call_user_func() support passing parameters by reference?
The docs say terse things like "Note that the parameters for call_user_func() are not passed by reference." I assume the PHP devs had some kind of reason for disabling that capability in this case.
Were they facing a technical limitation? Was it a language design choice? How did this come about?
EDIT:
In order to clarify this, here is an example.
<?php
function more(&$var){ $var++; }
$count = 0;
print "The count is $count.\n";
more($count);
print "The count is $count.\n";
call_user_func('more', $count);
print "The count is $count.\n";
// Output:
// The count is 0.
// The count is 1.
// The count is 1.
This is functioning normally; call_user_func does not pass $count by reference, even though more() declared it as a referenced variable. The call_user_func documentation clearly says that this is the way it's supposed to work.
I am well aware that I can get the effect I need by using call_user_func_array('more', array(&$count)).
The question is: why was call_user_func designed to work this way? The passing by reference documentation says that "Function definitions alone are enough to correctly pass the argument by reference." The behavior of call_user_func is an exception to that. Why?
The answer is embedded deep down in the way references work in PHP's model - not necessarily the implementation, because that can vary a lot, particularly in the 5.x versions. I'm sure you've heard the lines, they're not like C pointers, or C++ references, etc etc... Basically when a variable is assigned or bound, it can happen in two ways - either by value (in which case the new variable is bound to a new 'box' containing a copy of the old value), or by reference (in which case the new variable is bound to the same value box as the old value). This is true whether we're talking about variables, or function arguments, or cells in arrays.
Things start to get a bit hairy when you start passing references into functions - obviously the intent is to be able to modify the original variables. Quite some time ago, call-time pass-by-reference (the ability to pass a reference into a function that wasn't expecting one) got deprecated, because a function that wasn't aware it was dealing with a reference might 'accidentally' modify the input. Taking it to another level, if that function calls a second function, that itself wasn't expecting a reference... then everything ends up getting disconnected. It might work, but it's not guaranteed, and may break in some PHP version.
This is where call_user_func() comes in. Suppose you pass a reference into it (and get the associated the call-time pass-by-reference warning). Then your reference gets bound to a new variable - the parameters of call_user_func() itself. Then when your target function is called, its parameters are not bound where you expect. They're not bound to the original parameters at all. They're bound to the local variables that are in the call_user_func() declaration. call_user_func_array() requires caution too. Putting a reference in an array cell could be trouble - since PHP passes that array with "copy-on-write" semantics, you can't be sure if the array won't get modified underneath you, and the copy won't get detached from the original reference.
The most insightful explanation I've seen (which helped me get my head around references) was in a comment on the PHP 'passing by reference' manual:
http://ca.php.net/manual/en/language.references.pass.php#99549
Basically the logic goes like this. How would you write your own version of call_user_func() ? - and then explain how that breaks with references, and how it fails when you avoid call-time pass-by-reference. In other words, the right way to call functions (specify the value, and let PHP decide from the function declaration whether to pass value or reference) isn't going to work when you use call_user_func() - you're calling two functions deep, the first by value, and the second by reference to the values in the first.
Get your head around this, and you'll have a much deeper understanding of PHP references (and a much greater motivation to steer clear if you can).
See this:
http://hakre.wordpress.com/2011/03/09/call_user_func_array-php-5-3-and-passing-by-reference/
Is it possible to pass parameters by reference using call_user_func_array()?
http://bugs.php.net/bug.php?id=17309&edit=1
Passing references in an array works correctly.
Updated Answer:
You can use:
call_user_func('more', &$count)
to achieve the same effect as:
call_user_func_array('more', array(&$count))
For this reason I believe (unfoundedly) that call_user_func is just a compiler time short cut. (i.e. it gets replaced with the later at compile time)
To give my view on you actual question "Why was call_user_func designed to work this way?":
It probably falls under the same lines as "Why is some methods strstr and other str_replace?, why is array functions haystack, needle and string functions needle, haystack?
Its because PHP was designed, by many different people, over a long period of time, and with no strict standards in place at the time.
Original Answer:
You must make sure you set the variable inside the array to a reference as well.
Try this and take note of the array(&$t) part:
function test(&$t) {
$t++;
echo '$t is '.$t.' inside function'.PHP_EOL;
}
$t = 0;
echo '$t is '.$t.' in global scope'.PHP_EOL;
test($t);
$t++;
echo '$t is '.$t.' in global scope'.PHP_EOL;
call_user_func_array('test', array(&$t));
$t++;
echo '$t is '.$t.' in global scope'.PHP_EOL;
Should output:
$t is 0 in global scope
$t is 1 inside function
$t is 2 in global scope
$t is 3 inside function
$t is 4 in global scope
Another possible way - the by-reference syntax stays the 'right' way:
$data = 'some data';
$func = 'more';
$func($more);
function more(&$data) {
// Do something with $data here...
}
I am trying to have a function that among other things declares global variables based on a variable that i give it.
the part that fails is making the variables global
function setGlobalVariable($name) {
global $name, $arrayname_{$name};
}
any idea?
thanks :)
Really, stop messing with global variables that way.
Anywaym here's your solution if you really want to do that:
function setGlobalVariable($name) {
$GLOBALS['arrayname_' . $name] = 'yourvalue';
}
You should not do that. Global variables are in general a sign of poor design. What is it that you are trying to achieve? I am sure that there is a better solution. Besides that, global does not work like that. global makes other variables outside your function locally available. Use $_GLOBAL to create globals.
Take a look at the Registry Pattern (http://martinfowler.com/eaaCatalog/registry.html).
A well-known object that other objects
can use to find common objects and
services.
There are various PHP implementations, for example Zend_Registry: http://framework.zend.com/manual/en/zend.registry.html
You're almost right, but not quite; a variable variable takes the form of ${"name"}, so what you're looking for is something like global ${"arrayname_$name"};.
http://www.reddit.com/r/programming/comments/dst56/today_i_learned_about_php_variable_variables/c12np38 is fascinating reading on the topic, if you feel so inclined.
It's likely a terrible idea, though, and if you're resorting to that sort of thing, it's a good indication that your code may be poorly designed. Consider refactoring it (for example, to keep a single known array that your other arrays are kept in, and may be referenced by key.)