what is the difference between these two statements in php? - php

I was reading about arrow functions in php documentions and I came across this code
fn&($x = 42) => $x;
fn(&$x) => $x;
what is the difference between these two statements and what they do ?

in the second you use &$x and that's passing a variable by reference. it calls reference by value
so the $x out of the function will change if you do something in the function.
reference
here the final value of $a is 5:
$a = 5;
$y = fn ($x) => $x++;
$y($a);
echo $a;//5
but in reference by value, the final value of $a changed and it's 6:
$a = 5;
$y = fn (&$x) => $x++;
$y($a);
echo $a;//6
So you can see in reference by value, the original value of the variable can be changed

& sets a reference to the variable
fn($x = 42) : is a function with one or 0 parameter, if 0, default value 42
fn(&$x) : is a function with one parameter passed as reference, meaning if changed in the function, the new value of $x will be pass in the main.

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.

Rewriting an anonymous function in php 7.4

There is the following anonymous recursive function:
$f = function($n) use (&$f) {
return ($n == 1) ? 1 : $n * $f($n - 1);
};
echo $f(5); // 120
I try to rewrite to version 7.4, but there is an error, please tell me what I'm missing?
$f = fn($n) => ($n == 1) ? 1 : $n * $f($n - 1);
echo $f(5);
Notice: Undefined variable: f
Fatal error: Uncaught Error: Function name must be a string
Just like Barmar said, you can't use $f from the outside scope, because when the implicit binding takes place $f is still undefined.
There is nothing stopping you from passing it later as a parameter.
$f = fn($f, $n) => $n == 1 ? 1 : $n * $f($f, $n - 1);
echo $f($f, 5); // 120
The way arrow functions work, is that during definition time they will use by-value binding of the outer scope's variables.
As already mentioned, arrow functions use by-value variable binding. This is roughly equivalent to performing a use($x) for every variable $x used inside the arrow function. - https://wiki.php.net/rfc/arrow_functions_v2
The assignment of the closure to the variable $f happens after closure's definition and the variable $f is undefined prior to it.
As far as I am aware, there isn't any mechanism to bind by reference while defining arrow functions.
I don't think you can rewrite the function as an arrow function.
An arrow function will capture the value of any external variables by value at the time the function is created. But $f won't have a value until after the function is created and the variable is assigned.
The original anonymous function solves this problem by capturing a reference to the variable with use (&$f), rather than use ($f). This way, it will be able to use the updated value of the variable that results from the assignment.
I think I just found one of the legitimate (no?) uses of $GLOBALS
$f = fn ($n) =>($n == 1) ? 1 : $n * $GLOBALS['f']($n - 1);
echo $f(5); // 120
Sidenote: what if $n < 1 ?
As a slight variation of what #scorpion35 was saying, you could also apply currying:
$f = fn($f) => fn($x) => $x <= 1
? $x
: $f($f)($x-1) * $x;
$factorial = $f($f);
$factorial(7); //5040

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

Optional parameters in php

I am new to php and have coding experience in java and C. So optional parameter is a bit confusing for me. I would like to know that if a function has two optional parameters and if I pass a single argument, which parameter will be mapped against the argument?
The parameter order never changes (from left to right), so if you supply only one argument, the first parameter will receive the value, the second will have the default value.
For instance:
function foo( $a = 1, $b = 2) {}
foo(); // $a = 1, $b = 2
foo( 3); // $a = 3, $b = 2
foo( 4, 5); // $a = 4, $b = 5
Note that there is no way to set a value for $b while still leaving the default value for $a, due to the order of the parameters. You would have to call foo() like this to accomplish that:
foo( 1, 6);
The first argument is always passed to the first variable in the parameters, the second to the second and so on, regardless of whether they are optional or not.
The only difference between an optional and a non-optional parameter is that a warning is thrown when there are not enough arguments in the call to fulfill all non-optional parameters.
function f($a = 1, $b, $c, $d = 2)
{
var_dump($a, $b, $c, $d);
}
f('x');
Inside the function $a will be "x", $b and $c will be null and $d will be 2 but a warning will be thrown.
Therefore it makes no sense to have optional parameters left of non-optional ones - although that would be syntactically correct - because you could never leave them out in the call without generation a warning.
For example
function test($test = null, $test_2 = null, $test_3 = null) {
}
test(1) => $test=1;
test(1,1) => $test=1;$test_2=1;
test(1,1,1) => $test=1;$test_2=1;$test_3=1;

Using Default Arguments in a Function

I am confused about default values for PHP functions. Say I have a function like this:
function foo($blah, $x = "some value", $y = "some other value") {
// code here!
}
What if I want to use the default argument for $x and set a different argument for $y?
I have been experimenting with different ways and I am just getting more confused. For example, I tried these two:
foo("blah", null, "test");
foo("blah", "", "test");
But both of those do not result in a proper default argument for $x. I have also tried to set it by variable name.
foo("blah", $x, $y = "test");
I fully expected something like this to work. But it doesn't work as I expected at all. It seems like no matter what I do, I am going to have to end up typing in the default arguments anyway, every time I invoke the function. And I must be missing something obvious.
I would propose changing the function declaration as follows so you can do what you want:
function foo($blah, $x = null, $y = null) {
if (null === $x) {
$x = "some value";
}
if (null === $y) {
$y = "some other value";
}
code here!
}
This way, you can make a call like foo('blah', null, 'non-default y value'); and have it work as you want, where the second parameter $x still gets its default value.
With this method, passing a null value means you want the default value for one parameter when you want to override the default value for a parameter that comes after it.
As stated in other answers,
default parameters only work as the last arguments to the function.
If you want to declare the default values in the function definition,
there is no way to omit one parameter and override one following it.
If I have a method that can accept varying numbers of parameters, and parameters of varying types, I often declare the function similar to the answer shown by Ryan P.
Here is another example (this doesn't answer your question, but is hopefully informative:
public function __construct($params = null)
{
if ($params instanceof SOMETHING) {
// single parameter, of object type SOMETHING
} elseif (is_string($params)) {
// single argument given as string
} elseif (is_array($params)) {
// params could be an array of properties like array('x' => 'x1', 'y' => 'y1')
} elseif (func_num_args() == 3) {
$args = func_get_args();
// 3 parameters passed
} elseif (func_num_args() == 5) {
$args = func_get_args();
// 5 parameters passed
} else {
throw new \InvalidArgumentException("Could not figure out parameters!");
}
}
Optional arguments only work at the end of a function call. There is no way to specify a value for $y in your function without also specifying $x. Some languages support this via named parameters (VB/C# for example), but not PHP.
You can emulate this if you use an associative array for parameters instead of arguments -- i.e.
function foo(array $args = array()) {
$x = !isset($args['x']) ? 'default x value' : $args['x'];
$y = !isset($args['y']) ? 'default y value' : $args['y'];
...
}
Then call the function like so:
foo(array('y' => 'my value'));
It is actually possible:
foo( 'blah', (new ReflectionFunction('foo'))->getParameters()[1]->getDefaultValue(), 'test');
Whether you would want to do so is another story :)
UPDATE:
The reasons to avoid this solution are:
it is (arguably) ugly
it has an obvious overhead.
as the other answers proof, there are alternatives
But it can actually be useful in situations where:
you don't want/can't change the original function.
you could change the function but:
using null (or equivalent) is not an option (see DiegoDD's comment)
you don't want to go either with an associative or with func_num_args()
your life depends on saving a couple of LOCs
About the performance, a very simple test shows that using the Reflection API to get the default parameters makes the function call 25 times slower, while it still takes less than one microsecond. You should know if you can to live with that.
Of course, if you mean to use it in a loop, you should get the default value beforehand.
function image(array $img)
{
$defaults = array(
'src' => 'cow.png',
'alt' => 'milk factory',
'height' => 100,
'width' => 50
);
$img = array_merge($defaults, $img);
/* ... */
}
PHP 8 using Named Arguments
What if I want to use the default value for argument $x and set a value for $y?
foo($blah, $x = "some value", $y = "some other value")
Usage-1:
foo(blah: "blah", y: "test");
Usage-2:
// use names to address last arguments
foo("blah", y: "test");
Native functions will also use this feature
htmlspecialchars($string, double_encode: false);
// Same as
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);
About the Feature and Documentation
Named arguments allow passing arguments to a function based on the parameter name, rather than the parameter position. This makes the meaning of the argument self-documenting, makes the arguments order-independent, and allows skipping default values arbitrarily.
IDE Support
Netbeans 12.3 fully supports PHP 8.0 syntax including this feature, with the exception of code completion for named arguments.
The only way I know of doing it is by omitting the parameter. The only way to omit the parameter is to rearrange the parameter list so that the one you want to omit is after the parameters that you HAVE to set. For example:
function foo($blah, $y = "some other value", $x = "some value")
Then you can call foo like:
foo("blah", "test");
This will result in:
$blah = "blah";
$y = "test";
$x = "some value";
I recently had this problem and found this question and answers. While the above questions work, the problem is that they don't show the default values to IDEs that support it (like PHPStorm).
if you use null you won't know what the value would be if you leave it blank.
The solution I prefer is to put the default value in the function definition also:
protected function baseItemQuery(BoolQuery $boolQuery, $limit=1000, $sort = [], $offset = 0, $remove_dead=true)
{
if ($limit===null) $limit =1000;
if ($sort===null) $sort = [];
if ($offset===null) $offset = 0;
...
The only difference is that I need to make sure they are the same - but I think that's a small price to pay for the additional clarity.
In PHP 8 we can use named arguments for this problem.
So we could solve the problem described by the original poster of this question:
What if I want to use the default argument for $x and set a different argument for $y?
With:
foo(blah: "blah", y: "test");
Reference: https://wiki.php.net/rfc/named_params (in particular the "Skipping defaults" section)
You can't do this directly, but a little code fiddling makes it possible to emulate.
function foo($blah, $x = false, $y = false) {
if (!$x) $x = "some value";
if (!$y) $y = "some other value";
// code
}
You can also check if you have an empty string as argument so you can call like:
foo('blah', "", 'non-default y value', null);
Below the function:
function foo($blah, $x = null, $y = null, $z = null) {
if (null === $x || "" === $x) {
$x = "some value";
}
if (null === $y || "" === $y) {
$y = "some other value";
}
if (null === $z || "" === $z) {
$z = "some other value";
}
code here!
}
It doesn't matter if you fill null or "", you will still get the same result.
My 2 cents with null coalescing operator ?? (since PHP 7)
function foo($blah, $x = null, $y = null) {
$varX = $x ?? 'Default value X';
$varY = $y ?? 'Default value Y';
// ...
}
You can check more examples on my repl.it
This is case, when object are better - because you can set up your object to hold x and y , set up defaults etc.
Approach with array is near to create object ( In fact, object is bunch of parameters and functions which will work over object, and function taking array will work over some bunch ov parameters )
Cerainly you can always do some tricks to set null or something like this as default
Pass an array to the function, instead of individual parameters and use null coalescing operator (PHP 7+).
Below, I'm passing an array with 2 items. Inside the function, I'm checking if value for item1 is set, if not assigned default vault.
$args = ['item2' => 'item2',
'item3' => 'value3'];
function function_name ($args) {
isset($args['item1']) ? $args['item1'] : 'default value';
}
PHP 8 way of doing this:
function foo($blah, ?$x, ?$y) {
$x = $x ?? "some value";
$y = $y ?? "some other value";
}
Just another way to write:
function sum($args){
$a = $args['a'] ?? 1;
$b = $args['b'] ?? 1;
return $a + $b;
}
echo sum(['a' => 2, 'b' => 3]); // 5
echo sum(['a' => 2]); // 3 (2+1)
echo sum(['b' => 3]); // 4 (1+3)
echo sum([]); // 2 (1+1)
<?php
function info($name="George",$age=18) {
echo "$name is $age years old.<br>";
}
info(); // prints default values(number of values = 2)
info("Nick"); // changes first default argument from George to Nick
info("Mark",17); // changes both default arguments' values
?>

Categories