PHP "use" over additional parameters for closures? - php

In what situation is using the use keyword with a closure more beneficial then just passing along additional parameters to the closure?
Example #1:
$myClosure = function($var1, $var2) use ($var3){
//Some code
}
Example #2:
$myClosure = function($var1, $var2, $var3){
//Some code
}
Like all things it probably depends, but I don't see any functional difference between the two. Can anyone suggest a situation or example where one example would be preferred over the other?

I don't see any functional difference between the two.
The arguments are provided by the caller of the function. If you are the caller and can provide all the necessary arguments, then there is basically no difference*.
However, you might be passing the function somewhere else, so you are not caller and do not control which arguments are passed. This is the situation that closures solve: You can make values available without calling the function.
See also In PHP 5.3.0, what is the function "use" identifier? .
*: The variables that are bound through use are defined at definition time. So if the value of the variable changes, there will be a difference:
$a = 1;
$b = 2;
$c = 3;
$f1 = function($a, $b) use ($c) {
echo $a, $b, $c;
};
$f2 = function($a, $b, $c) {
echo $a, $b, $c;
};
$c = 42;
$f1($a, $b); // 1, 2, 3
$f2($a, $b, $c); // 1, 2, 42

Say you're using a closure with pre-defined parameters, like preg_replace_callback for example, and you want to use a variable from other places in your code, you'd use the use keyword
$myvar = "hello";
$text = "";
$text = preg_replace_callback('/regex/', function($matches) use ($myvar) {
return $myvar . " " . $matches[1] . "!";
}, $text);
It's a great way to use a variable within a closure with pre-defined parameters, which do not have access to variables outside the closure

Related

php functional-php library less_than function

I saw in source code of php-functional library such function :
function less_than($b)
{
return function ($a ) use ($b) {
return $a < $b;
};
}
I can't get there $a comes from. How is this going to work?
We pass only $b to function less than and $a is undefined.
This is the link to source code where I took it from.
The idea of functional programming is that functions are first-class objects. So you can pass functions as parameters; and you can return functions.
So here you can construct a function:
$func_less = less_than(3);
So we basically construct a function like:
$func_less = function ($a) {
return $a < 3;
};
and now $func_less is a function that takes a parameter $a and checks if that $a is less than three. For example:
php > $func_less_three = less_than(3);
php > var_dump($func_less_three(2));
bool(true)
php > var_dump($func_less_three(14));
bool(false)
So first we construct a function $func_less_three, then we call that function with two numbers 2 and 14. Since 2 is less than 3, it returns bool(true) on the first call, and since 14 is not less than 3, it return bool(false) on the second call.
In most functional programming languages like Haskell, all functions take exactly one parameter. So instead of adding two numbers together, one first constructs a function that adds a (in that function) constant value to a parameter.

php Anonymous function

When I was reading questions for Zend Certified PHP Engineer 5.5 I saw question about anonymous function but I need to explan how it work.
function z($x)
{
return function($y) use ($x)
{
return str_repeat( $y , $x );
};
}
$a = z(2);
$b = z(3);
echo $a(3).$b(2);
The output for this code is:
33222
But in function header there is only $x parameter from where $y got there value!
Function z creates and returns a new function, but an anonymous one. That new function is defined so that it has one argument - $y. However, this anonymous function also uses argument $x from a function z.
To make it simple, function z basically creates a function which can repeat any string, but a fixed number of times. The number of times a string is repeated is determined by the value of argument $x in z.
So, calling z(2) creates a new function which is functionally equivalent to writing
function repeat_two_times($y) {
return str_repeat($y, 2);
}
In you example, hard coded value 2 is determined by the value of $x.
You can read more about this in the documentation. The principle displayed by the example can be quite useful for creating partial functions like add5, inc10, ...
Firstly, you initialize function z:
$a = z(2);
$x in the example is set to 2, so the returned function (anonymous function, also called closure) can now be read as (because $x is used):
$a = function($y) {
return str_repeat($y, 2);
}
When invoking this function:
echo $a(3);
You are supplying this return function with the parameter 3 ($y).
The output is: 33
Anonymous functions are also known as Closures.
You ask where $y gets its value. The code example is difficult to decipher because you use 2s and 3s everywhere. Things would be clearer if your last lines were
$a = z(2);
$b = z(3);
echo $a('A').$b('B');
That would result in:
AABBB
But let's follow your code. Notice that there are two related function calls
$a = z(2);
and
echo $a(3);
calling function z() with argument 2 returns a function (that is assigned name $a) where line
return str_repeat($y, $x);
is in reality :
return str_repeat($y, 2);
now, you call that function $a() with argument 3. That 3 (value of $y) is repeated two times
The same analysis applies to the other related function calls:
$b = z(3);
...
echo ... $b(2);
But in this case 2 is repeated 3 times
function z($x)
{
return function($y) use ($x)
{
return str_repeat( $y , $x );
};
}
$a = z(2);// here you are setting value of x by 2
$b = z(3);// here you are setting value of x by 3
echo $a(3).$b(2);// here $a(3) 3 is value of y so it becomes str_repeat( 3 , 2 ); which is 33

PHP Default parameter values: How does the compiler know which passed parameter-value is for which parameter?

In the following snippet, how does printPhrase know if the passed arguments are $a and $b (so it uses default value of $c, or $a and $c (so it uses default value of $b)?
private function printPhrase ($a, $b='black', $c='candle!' ) {
echo $a . $b . $c; //Prints A black cat! or A black candle!
}
private function callprintPhrase () {
printPhrase('A ', ' cat!');
}
In php arguments always passes from left to right with out skip. So printPhrase('A ', ' cat!'); always fills with values first and second argument of function.
http://php.net/manual/en/functions.arguments.php#functions.arguments.default
There is exists proposal to skip params.
If you want to use default params you need to rewrite your code like in this answer: https://stackoverflow.com/a/9541822/1503018
private function callprintPhrase () {
printPhrase('A ', ' cat!');
}
since you have passed 2 arguments they will be considered as arguments for $a and $b. So it will possible print something like A cat candle! You need to pass null value in the second argument if it is to take the value of $b i.e.
private function callprintPhrase () {
printPhrase('A ','', ' cat!');
}
This will give you an output A black cat!

Returning two values as function to two different variables in php

function foo($a)
{
$b = ...;
$c = ...;
return (both b and c);
}
and so I could get $b value to $first and $c value to $second
I know you can return more than 1 variable by return array($b,$c) but then it should be $var[0] and $var[1] and then I need to type $first = $var[0] and $second = $var[1] and so I'm creating more useless variables
So is it possible to do so without array?
Fundamentally, functions only have one return value. You could return a class with member variables first and second, or an associative array with keys "first" and "second", but you'll still only be returning a single object.*
Alternatively, you could references to $first and $second into your function:
function foo($a, &$b, &$c)
{
$b = ...;
$c = ...;
}
foo(42, $first, $second);
I'm not a big fan of this approach, though, because it's not immediately clear from the call-site that $first and $second are going to be modified.
* Note that if you return an array, you can always use the short-hand list($first,$second) = foo(42);.
No, it can't.
But you can still return an array from function, but use "list" to accept the result for convenient:
list ($first, $second) = foo ($a);
No, you cannot to that. Function returns only one result.
What you can do, if possible in you case, is pass a variable by reference.
function foo($a, &$b, &$c){
$b = ...;
$c = ...;
}
The following will make changes to $b and $c visible outside of the function scope.
The only alternative to avoid returning an array is to return an object, or serialized data, but you don't "win" something from that.

How to imitate list() with own function / method in PHP?

We all know calls like this:
list($a, $b) = explode(':', 'A:B');
But how to make a custom function that can be use the same way?
$obj->mylist($a, $b) = explode(':', 'A:B');
mylist($a, $b) = explode(':', 'A:B');
If i use the above lines of code i always get: "Can't use method return value in write context in [...]"
How to define the function / class method to accept that type of assignment? How to the the array returned by explode()? Via func_get_args()?
Sure, there are other ways to assign the result of explode(), but that is not the subject of this question. E.g.
$obj->mylist($a, $b, explode(':', 'A:B'));
mylist($a, $b, explode(':', 'A:B'));
It is impossible as you cannot write a method that can be used as an lvalue.
list() is not a function but a language construct, that's why it can be used in the way it's used.
You can't do that. list() is a language construct, not a function, and there is no way in PHP to make a function behave like that. It will cause a syntax error.
What meaning would you want from mylist($a, $b) = explode(':', 'A:B'); anyways? If it were list instead of mylist it would just be equivalent to:
$arr = explode(':', 'A:B');
$a = $arr[0];
$b = $arr[1];
unset($arr);
How could you redefine the meaning of that for a function on the left? With your object example you can do something like:
list($obj->a, $obj->b) = explode(':', 'A:B');
What behaviour exactly are you trying to get? Perhaps there is another way.

Categories