php Anonymous function - php

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

Related

Is there a clean way to use undefined variables as optional parameters in PHP?

Is there any nice way to use (potentially) undefined Variables (like from external input) as optional Function parameters?
<?php
$a = 1;
function foo($a, $b=2){
//do stuff
echo $a, $b;
}
foo($a, $b); //notice $b is undefined, optional value does not get used.
//output: 1
//this is even worse as other erros are also suppressed
#foo($a, $b); //output: 1
//this also does not work since $b is now explicitly declared as "null" and therefore the default value does not get used
$b ??= null;
foo($a,$b); //output: 1
//very,very ugly hack, but working:
$r = new ReflectionFunction('foo');
$b = $r->getParameters()[1]->getDefaultValue(); //still would have to check if $b is already set
foo($a,$b); //output: 12
the only semi-useful method I can think of so far is to not defining the default value as parameter but inside the actual function and using "null" as intermediary like this:
<?php
function bar ($c, $d=null){
$d ??= 4;
echo $c,$d;
}
$c = 3
$d ??= null;
bar($c,$d); //output: 34
But using this I still have to check the parameter twice: Once if it is set before calling the function and once if it is null inside the function.
Is there any other nice solution?
Ideally you wouldn't pass $b in this scenario. I don't remember ever running into a situation where I didn't know if a variable existed and passed it to a function anyway:
foo($a);
But to do it you would need to determine how to call the function:
isset($b) ? foo($a, $b) : foo($a);
This is kind of hackish, but if you needed a reference anyway it will be created:
function foo($a, &$b){
$b = $b ?? 4;
var_dump($b);
}
$a = 1;
foo($a, $b);
I would do something like this if this was actually a requirement.
Just testing with sum of the supplied values just for showing an example.
<?php
$x = 1;
//Would generate notices but no error about $y and t
//Therefore I'm using # to suppress these
#$sum = foo($x,$y,4,3,t);
echo 'Sum = ' . $sum;
function foo(... $arr) {
return array_sum($arr);
}
Would output...
Sum = 8
...based on the array given (unknown nr of arguments with ... $arr)
array (size=5)
0 => int 1
1 => null
2 => int 4
3 => int 3
4 => string 't' (length=1)
array_sum() only sums up 1,4 and 3 here = 8.
Even if above actually works I would not recommend it, because then whatever data can be sent to your function foo() without you having any control over it. When it comes to user input of any kind you should always validate as much as you can in your code before using the actual data from the user.

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 "use" over additional parameters for closures?

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

String concatenation while incrementing

This is my code:
$a = 5;
$b = &$a;
echo ++$a.$b++;
Shouldn't it print 66?
Why does it print 76?
Alright. This is actually pretty straight forward behavior, and it has to do with how references work in PHP. It is not a bug, but unexpected behavior.
PHP internally uses copy-on-write. Which means that the internal variables are copied when you write to them (so $a = $b; doesn't copy memory until you actually change one of them). With references, it never actually copies. That's important for later.
Let's look at those opcodes:
line # * op fetch ext return operands
---------------------------------------------------------------------------------
2 0 > ASSIGN !0, 5
3 1 ASSIGN_REF !1, !0
4 2 PRE_INC $2 !0
3 POST_INC ~3 !1
4 CONCAT ~4 $2, ~3
5 ECHO ~4
6 > RETURN 1
The first two should be pretty easy to understand.
ASSIGN - Basically, we're assinging the value of 5 into the compiled variable named !0.
ASSIGN_REF - We're creating a reference from !0 to !1 (the direction doesn't matter)
So far, that's straight forward. Now comes the interesting bit:
PRE_INC - This is the opcode that actually increments the variable. Of note is that it returns its result into a temporary variable named $2.
So let's look at the source code behind PRE_INC when called with a variable:
static int ZEND_FASTCALL ZEND_PRE_INC_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op1;
zval **var_ptr;
SAVE_OPLINE();
var_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
if (IS_VAR == IS_VAR && UNEXPECTED(var_ptr == NULL)) {
zend_error_noreturn(E_ERROR, "Cannot increment/decrement overloaded objects nor string offsets");
}
if (IS_VAR == IS_VAR && UNEXPECTED(*var_ptr == &EG(error_zval))) {
if (RETURN_VALUE_USED(opline)) {
PZVAL_LOCK(&EG(uninitialized_zval));
AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval));
}
if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
SEPARATE_ZVAL_IF_NOT_REF(var_ptr);
if (UNEXPECTED(Z_TYPE_PP(var_ptr) == IS_OBJECT)
&& Z_OBJ_HANDLER_PP(var_ptr, get)
&& Z_OBJ_HANDLER_PP(var_ptr, set)) {
/* proxy object */
zval *val = Z_OBJ_HANDLER_PP(var_ptr, get)(*var_ptr TSRMLS_CC);
Z_ADDREF_P(val);
fast_increment_function(val);
Z_OBJ_HANDLER_PP(var_ptr, set)(var_ptr, val TSRMLS_CC);
zval_ptr_dtor(&val);
} else {
fast_increment_function(*var_ptr);
}
if (RETURN_VALUE_USED(opline)) {
PZVAL_LOCK(*var_ptr);
AI_SET_PTR(&EX_T(opline->result.var), *var_ptr);
}
if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
Now I don't expect you to understand what that's doing right away (this is deep engine voodoo), but let's walk through it.
The first two if statements check to see if the variable is "safe" to increment (the first checks to see if it's an overloaded object, the second checks to see if the variable is the special error variable $php_error).
Next is the really interesting bit for us. Since we're modifying the value, it needs to preform copy-on-write. So it calls:
SEPARATE_ZVAL_IF_NOT_REF(var_ptr);
Now, remember, we already set the variable to be a reference above. So the variable is not separated... Which means everything we do to it here will happen to $b as well...
Next, the variable is incremented (fast_increment_function()).
Finally, it sets the result as itself. This is copy-on-write again. It's not returning the value of the operation, but the actual variable. So what PRE_INC returns is still a reference to $a and $b.
POST_INC - This behaves similarly to PRE_INC, except for one VERY important fact.
Let's check out the source code again:
static int ZEND_FASTCALL ZEND_POST_INC_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
retval = &EX_T(opline->result.var).tmp_var;
ZVAL_COPY_VALUE(retval, *var_ptr);
zendi_zval_copy_ctor(*retval);
SEPARATE_ZVAL_IF_NOT_REF(var_ptr);
fast_increment_function(*var_ptr);
}
This time I cut away all of the non-interesting stuff. So let's look at what it's doing.
First, it gets the return temporary variable (~3 in our code above).
Then it copies the value from its argument (!1 or $b) into the result (and hence the reference is broken).
Then it increments the argument.
Now remember, the argument !1 is the variable $b, which has a reference to !0 ($a) and $2, which if you remember was the result from PRE_INC.
So there you have it. It returns 76 because the reference is maintained in PRE_INC's result.
We can prove this by forcing a copy, by assigning the pre-inc to a temporary variable first (through normal assignment, which will break the reference):
$a = 5;
$b = &$a;
$c = ++$a;
$d = $b++;
echo $c.$d;
Which works as you expected. Proof
And we can reproduce the other behavior (your bug) by introducing a function to maintain the reference:
function &pre_inc(&$a) {
return ++$a;
}
$a = 5;
$b = &$a;
$c = &pre_inc($a);
$d = $b++;
echo $c.$d;
Which works as you're seeing it (76): Proof
Note: the only reason for the separate function here is that PHP's parser doesn't like $c = &++$a;. So we need to add a level of indirection through the function call to do it...
The reason I don't consider this a bug is that it's how references are supposed to work. Pre-incrementing a referenced variable will return that variable. Even a non-referenced variable should return that variable. It may not be what you expect here, but it works quite well in almost every other case...
The Underlying Point
If you're using references, you're doing it wrong about 99% of the time. So don't use references unless you absolutely need them. PHP is a lot smarter than you may think at memory optimizations. And your use of references really hinders how it can work. So while you think you may be writing smart code, you're really going to be writing less efficient and less friendly code the vast majority of the time...
And if you want to know more about References and how variables work in PHP, checkout One Of My YouTube Videos on the subject...
I think the full concatenate line is first executed and than send with the echo function.
By example
$a = 5;
$b = &$a;
echo ++$a.$b++;
// output 76
$a = 5;
$b = &$a;
echo ++$a;
echo $b++;
// output 66
EDIT: Also very important, $b is equal to 7, but echoed before adding:
$a = 5;
$b = &$a;
echo ++$a.$b++; //76
echo $b;
// output 767
EDIT: adding Corbin example: https://eval.in/34067
There's obviously a bug in PHP. If you execute this code:
<?php
{
$a = 5;
echo ++$a.$a++;
}
echo "\n";
{
$a = 5;
$b = &$a;
echo ++$a.$b++;
}
echo "\n";
{
$a = 5;
echo ++$a.$a++;
}
You get:
66 76 76
Which means the same block (1st and 3rd one are identical) of code doesn't always return the same result. Apparently the reference and the increment are putting PHP in a bogus state.
https://eval.in/34023

How to treat string as a string and not as an int in PHP

I was reading PHP manual and I came across type juggling
I was confused, because I've never came across such thing.
$foo = 5 + "10 Little Piggies"; // $foo is integer (15)
When I used this code it returns me 15, it adds up 10 + 5 and when I use is_int() it returns me true ie. 1 where I was expecting an error, it later referenced me to String conversion to numbers where I read If the string starts with valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero)
$foo = 1 + "bob3"; /* $foo is int though this doesn't add up 3+1
but as stated this adds 1+0 */
now what should I do if I want to treat 10 Little Piggies OR bob3 as a string and not as an int. Using settype() doesn't work either. I want an error that I cannot add 5 to a string.
If you want an error, you need to trigger an error:
$string = "bob3";
if (is_string($string))
{
trigger_error('Does not work on a string.');
}
$foo = 1 + $string;
Or if you like to have some interface:
class IntegerAddition
{
private $a, $b;
public function __construct($a, $b) {
if (!is_int($a)) throw new InvalidArgumentException('$a needs to be integer');
if (!is_int($b)) throw new InvalidArgumentException('$b needs to be integer');
$this->a = $a; $this->b = $b;
}
public function calculate() {
return $this->a + $this->b;
}
}
$add = new IntegerAddition(1, 'bob3');
echo $add->calculate();
This is by design as a result of PHP's dynamically typed nature and of course lack of an explicit type declaration requirement. Variable types are determined based on context.
Based on your example, when you do:
$a = 10;
$b = "10 Pigs";
$c = $a + $b // $c == (int) 20;
Calling is_int($c) will of course always evaluate to a boolean true because PHP has decided to convert the result of the statement to an integer.
If you're looking for an error by the interpreter, you won't get it since this is, like I mentioned, something built into the language. You might have to write a lot of ugly conditional code to test your data types.
Or, if you want to do that for testing arguments passed to your functions - that's the only scenario which I can think of where you might want to do this - you can trust the client invoking your function to know what they are doing. Otherwise, the return value can simply be documented to be undefined.
I know coming from other platforms and languages, that might be hard to accept, but believe it or not a lot of great libraries written in PHP follow that same approach.

Categories