According to PHP documentation, a variable passed in ::send() method of generator class is assigned to yield keyword.
it makes sense in following statement :
$v = yield;
But what about the statements like :
$v = yield $a;
yield keyword is already assigned and it shall return the variable passed into. Then what does $a do?
I've tried to figure this out and searched many posts and questions but none of them explain this.
Answering by myself...
$v = yield $a;
meaning, first yield $a just as generally it does, then assign a variable passed by send() method to $v.
Still can't get clear explanation. But I think this is somewhat an answer of this matter.
Using send() will define $v, so if you use $v = yield $a; and before the iteration, you sent a value with send() it will yield the value which you sent it.
It can be hard to get your head around so here is an example, which creates two parts, the foreach iterator which loops over the generator and then on iteration 10 it sends a stop to the while loop. Then using getReturn gets the last value of the yield, like your $v in $v = yield $a;
<?php
$engine = function($callback) {
$i = 0;
while (true) {
$state = (yield $callback($i++));
if ($state == 'stop') {
return $i;
}
}
};
$generator = $engine(function($i) {
return $i;
});
foreach ($generator as $value) {
echo "{$generator->key()} = {$value}\n";
if ($generator->key() == 10) {
$generator->send('stop');
}
}
echo 'Stopped on: '.$generator->getReturn();
https://3v4l.org/dY8rV
Result:
0 = 0
1 = 1
2 = 2
3 = 3
4 = 4
5 = 5
6 = 6
7 = 7
8 = 8
9 = 9
10 = 10
Stopped on: 11
The yield keyword can be used for three things:
On its own, to return control temporarily to calling code. This is the basis of all the other uses.
With a value after (yield $foo), to pass a variable to calling code.
With an assignment before ($bar = yield), to receive a variable from calling code.
These are sort of similar to how you can use a function call:
On its own, like doSomething(), to pass control temporarily to the definition of doSomething.
With a parameter, like doSomething($foo), to pass a variable to the function.
With an assignment before, like $var = doSomething(), to receive a variable from the function.
You are hopefully quite familiar with writing $bar = doSomething($foo); to pass $foo to a function, and get $bar out afterwards. $bar = yield $foo is similar, but the value is passed "out" to the calling code, and the new value received back from that calling code afterwards.
$foo and $bar are not connected in any way, they are just the input and output of that particular yield. In the calling code, the $foo part of yield $foo or $bar = yield $foo can be accessed by calling ->next() or ->current() or getting the value in a foreach. The calling code then calls ->send() with some value which becomes the $bar in $bar = yield or $bar = yield $foo.
Related
I'm trying to write a function that will take any number of arguments, and will return the first argument, in order, that is valid (in my case !empty).
I've been able to pretty much get it working how I want, except I'm getting some notices because of undefined variables. See below for examples:
function cascade()
{
if (func_num_args() < 1) return false;
foreach (func_get_args() as &$arg) {
if (!empty($arg)) return $arg;
}
return false;
}
You can see that I've tried to declare that each $arg of the foreach is passed by reference, but that doesn't seem to have done the trick.
To elaborate on how I plan to use this function, see below:
$a = 'a';
$c = 'c';
echo cascade($z, $b, $a, $c);
Since $z and $b are undefined, the first non-empty variable in the list is $a so the output is a as expected. However, you then get the undefined variable notices, which I wish to get rid of.
I realise I can just say echo #cascade($z, $b, $a, $c); which would suppress the errors, but I want to know if there is a way around this issue, so that the reference can be passed somehow. Any thoughts?
Edit:
To further highlight what I'm trying to acheive, see the following function that DOES work without throwing errors, even when passed an undefined variable:
// returns default value if input variable is not set
function ifset(&$var, $default = false) {
return isset($var) ? $var : $default;
}
With this function, if param 1 is not set, then the default value in param 2 is returned. Either way, no error is thrown.
What I am trying to achieve is the same result, but with ANY number of arguments, as this function is limited to 1, unless I nest them (gets messy).
A real life example:
This is WHY I want this function and how I would use it in a real life scenario:
<input type="text" name="customerName" value="<?= cascade($order->fullName, $currentUser->fullName, 'Anonymous') ?>">
So if we have an order in the making, and there is a name available from that, we use that, if that info hasn't been saved yet, we use the logged in user's name as the default value, if no one is logged in, we use 'Anonymous'.
Not exactly what I would do in real life, but perhaps it highlights example usage?
For those who are suggesting defining the variables to mitigate the errors, the who point of this function is to work though a chain of values, giving priority to the ones that come first, then moving to the next if that is 'empty' and so on, until eventually a FALSE default value is returned if all are empty.
The Notice you're seeing is triggered at the function call, not inside the function itself. Therefore, there is nothing you can do inside the function to solve the problem. However, there are a number of ways of solving this problem depending on how your variables are prepared before the function call.
Solution #1: Define the variables before the function call.
for instance:
$z = ''; OR $z = null;
or any falsy value like : null, "", 0, "0", 0.0, [], ..., your function will still work as expected and you won't see the notice.
Solution #2: Test for validity before the function call.
if( !isset($z) ){ $z = ''; }
echo cascade($z);
Solution #3: Test for validity as part of the function call.
This is the same thing as solution #2 but a bit more elegant. Use the Ternary function to pass the variable value or an empty string depending on whether or not the variable is set.
echo cascade(
isset($z)?$z:'',
isset($b)?$b:'',
isset($a)?$a:'',
isset($c)?$c:''
);
Solution #4: If using PHP 7 or above, you can use the new Null Coalescing Operator. This is the same thing as solution #3, but more elegant still.
echo cascade($z ?? '', $b ?? '', $a ?? '', $c ?? '');
If you want a clean alternative to your function, you can try this:
function cascade()
{
$args = func_get_args();
while (!($arg = array_shift($args)));
return $arg ? $arg : false;
}
BIG THANKYOU to JBH for Editing significantly this answer.
The undefined variable notice is not from the code in function but it is from the function call cascade($z, $b, $a, $c)
You are passing undefined variables ($z and $b) as arguments to the function. Since they are not defined any where in the code, you are getting notice.
To get rid of the the notices, define the variables before passing them as arguments.
$a = 'a';
$c = 'c';
$z = '';
$b = '';
echo cascade($z, $b, $a, $c);
OR
echo cascade('', '', $a, $c);
Don't know if this is "good enough".
Create an array of the variables and use array filter to remove the null values.
Use array values to reset the keys.
Now $arr[0] is the first non empty item.
https://3v4l.org/PlmrS
$a = 'a';
$c = 'c';
$arr =array_values(array_filter([$z, $b, $a, $c]));
Var_dump($arr);
Thank you for all your kind answers, but unfortunately none of them solved my problem. Perhaps I didn't explain it well enough, or I over-complicated it with too much information confusing the issue, but anyway I've been able to find the answer.
The following function does exactly what I wanted. It uses the variadic syntax (php 5.6 and over) which allows a variable number of arguments, all of which are passed by reference.
function cascade(&...$args)
{
if (count($args) < 1) return false;
foreach ($args as &$arg) {
if (!empty($arg)) return $arg;
}
return false;
}
I have seen in my journey to creaitng and building some of my php applications, the & symbol within front of vars, = and class names.
I understand that these are PHP References, but the docs i have seen and looked at seem to just not explain it in a way that i understand or confusing. How can you explain the following examples that i have seen to make them more understandable.
public static function &function_name(){...}
$varname =& functioncall();
function ($var, &$var2, $var3){...}
Much appreciated
Let's say you have two functions
$a = 5;
function withReference(&$a) {
$a++;
}
function withoutReference($a) {
$a++;
}
withoutReference($a);
// $a is still 5, since your function had a local copy of $a
var_dump($a);
withReference($a);
// $a is now 6, you changed $a outside of function scope
var_dump($a);
So, passing argument by reference allows function to modify it outside of the function scope.
Now second example.
You have a function which returns a reference
class References {
public $a = 5;
public function &getA() {
return $this->a;
}
}
$references = new References;
// let's do regular assignment
$a = $references->getA();
$a++;
// you get 5, $a++ had no effect on $a from the class
var_dump($references->getA());
// now let's do reference assignment
$a = &$references->getA();
$a++;
// $a is the same as $reference->a, so now you will get 6
var_dump($references->getA());
// a little bit different
$references->a++;
// since $a is the same as $reference->a, you will get 7
var_dump($a);
Reference functions
$name = 'alfa';
$address = 'street';
//declaring the function with the $ tells PHP that the function will
//return the reference to the value, and not the value itself
function &function_name($what){
//we need to access some previous declared variables
GLOBAL $name,$address;//or at function declaration (use) keyword
if ($what == 'name')
return $name;
else
return $address;
}
//now we "link" the $search variable and the $name one with the same value
$search =& function_name('name');
//we can use the result as value, not as reference too
$other_search = function_name('name');
//any change on this reference will affect the "$name" too
$search = 'new_name';
var_dump($search,$name,$other_search);
//will output string 'new_name' (length=8)string 'new_name' (length=8)string 'alfa' (length=4)
Usually you use the method with Objects that implemented the same interface, and you want to choose the object you want to work with next.
Passing by reference:
function ($var, &$var2, $var3){...}
I'm sure you saw the examples, so I'll just explain how and when to use it.
The basic scenario is when do you have a big logic that you want to apply to a current object/data, and you do not wish to make more copies of it (in memory).
Hope this helps.
I have a simple question here. Is there a difference between passing a variable by reference in a function parameter like:
function do_stuff(&$a)
{
// do stuff here...
}
and do it inside the function like:
function do_stuff($a)
{
$var = &$a;
// do stuff here...
}
What are the differences (if any) between using these two?. Also, can anybody give me a good tutorial that explains passing by reference? I can't seem to grasp this concept 100%.
Thank you
Here's a set of examples so you can see what happens with each of your questions.
I also added a third function which combines both of your questions because it will also produce a different result.
function do_stuff(&$a)
{
$a = 5;
}
function do_stuff2($a)
{
$var = &$a;
$var = 3;
}
function do_stuff3(&$a)
{
$var = &$a;
$var = 3;
}
$a = 2;
do_stuff($a);
echo $a;
echo '<br />';
$a = 2;
do_stuff2($a);
echo $a;
echo '<br />';
$a = 2;
do_stuff3($a);
echo $a;
echo '<br />';
They're not at all equivalent. In the second version, you're creating a reference to an undefined variable $a, causing $var to point to that same null value. Anything you do to $var and $a inside the second version will not affect anything outside of the function.
In the first version, if you change $a inside the function, the new value will be present outside after the function returns.
In your first example, if you modify $a inside the function in any way, the original value outside the function will be modified as well.
In your second example, whatever you do to $a or its reference $var will not modify the original value outside the function.
In the second function, the $a passed into the function is a copy of the argument passed in, (unless $a is an object), so you are making a $var a reference to the $a inside the function but it will still be separate from the variable passed to the function.
Assuming you are using a recent version of PHP, objects are automatically passed by reference too, so that could make a difference.
Please see this code:
function addCounter(&$userInfoArray) {
$userInfoArray['counter']++;
return $userInfoArray['counter'];
}
$userInfoArray = array('id' => 'foo', 'name' => 'fooName', 'counter' => 10);
$nowCounter = addCounter($userInfoArray);
echo($userInfoArray['counter']);
This will show 11.
But! If you remove "&"operator in the function parameter, the result will be 10.
What's going on?
The & operator tells PHP not to copy the array when passing it to the function. Instead, a reference to the array is passed into the function, thus the function modifies the original array instead of a copy.
Just look at this minimal example:
<?php
function foo($a) { $a++; }
function bar(&$a) { $a++; }
$x = 1;
foo($x);
echo "$x\n";
bar($x);
echo "$x\n";
?>
Here, the output is:
1
2
– the call to foo didn’t modify $x. The call to bar, on the other hand, did.
Here the & character means that the variable is passed by reference, instead of by value. The difference between the two is that if you pass by reference, any changes made to the variable are made to the original also.
function do_a_thing_v ($a) {
$a = $a + 1;
}
$x = 5;
do_a_thing_v($x);
echo $x; // echoes 5
function do_a_thing_r (&$a) {
$a = $a + 1;
}
$x = 5;
do_a_thing_v($x);
echo $x; // echoes 6
When using the ampersand prior to a variable in a function call, it associates with the original variable itself. With that, the code you posted is saying that it will add 1 to the counter of the original array. Without the ampersand, it takes a copy of the data and adds to it, then returns the new counter of 11. The old array still remains intact at 10 and the new counter variable returned turns into 11.
http://www.phpreferencebook.com/samples/php-pass-by-reference/
is a good example.
Maybe I can add to the other answers that, if it is an object, then it is not "the object passed as value", but it is "the object's reference is passed as a value" (although I am asking what the difference is between "the object is passed by reference" vs "the object's reference is passed by value" in the comments). An array is passed by value by default.
Information: Objects and references
Example:
class Foo {
public $a = 10;
}
function add($obj) {
$obj->a++;
}
$foo = new Foo();
echo $foo->a, "\n";
add($foo);
echo $foo->a, "\n";
Result:
$ php try.php
10
11
Returning by reference is useful when
you want to use a function to find to
which variable a reference should be
bound. Do not use return-by-reference
to increase performance. The engine
will automatically optimize this on
its own. Only return references when
you have a valid technical reason to
do so.
whats does the bolded mean?
does it refer to something like
public function &getHellos() {
$sql = 'SELECT id, greeting FROM #__hello';
$data = $this->_getList($sql);
return $data;
}
where i am not binding to any variable?
We return by reference when we want the function GetRef() to decide which variable, $foo or $bar, the reference $foo_or_bar should be bound:
$foo = "foo";
$bar = "bar";
function &GetRef(){
global $foo, $bar;
if(rand(0, 1) === 1){
return $foo;
}else{
return $bar;
}
}
$foo_or_bar =& GetRef();
$foo_or_bar = 'some other value';
var_dump($foo); // either one of this will be 'some other value'
var_dump($bar); // either one of this will be 'some other value'
Derick Ethans also elaborated on this in "References in PHP: An In-Depth Look":
This [returning by reference] is useful, for example, if you want to
select a variable for modification with a function, such as selecting
an array element or a node in a tree structure.
Example code demonstrating selecting array element via return-by-reference:
function &SelectArrayElement(&$array, $key){
return $array[$key];
}
$array = array(0, 1, 2);
$element =& SelectArrayElement($array, 0);
$element = 10;
var_dump($array); // array(10, 1, 2)
Na. You can't pass a reference to a function name.
When passing a variable by reference, if you change it's value in your function, it's value will also be changed outside of the function.
For example :
function test(&$var) {
$var = strtolower($var);
}
function second_test($var) {
$var = strtolower($var);
}
$var = 'PHP';
second_test($var);
echo $var;
echo "\r\n";
test($var);
echo $var;
This will display :
PHP
php
As the second_test method doesn't have the variable passed by reference, it's updated value is only updated inside the function.
But the test method as the variable passed by reference. So it's value will be updated inside and outside of this function.
I believe it's referring to byref arguments not functions. For example this:
function doStuff(&$value1, &$value2) {
...
}
is an acceptable use of byref because the doStuff() function has to return 2 values. If it only doStuff() only needed to affect one value, it would be more elegant to have the function return it, by value, of course.
The bolded part means it's useful if you want to keep a reference to a variable, instead of the value of this variable.
The example about returning references, on php.net, explains it pretty well, IMO.