Laravel passing array by reference to an event subscriber - php

Is it possible to pass an array by reference into an event subscriber?
I have an event "user.create.show" and I want to pass an array to the event so I could modify it if needed.
The call looks like this.
Event::fire('user.create.show', array($data));
The event subscriber looks like this.
public function createShow(&$data)
{
$data['foo'] = 'bar';
return $data;
}
I get a "Parameter 1 to UserSubscriber::createShow() expected to be a reference, value given" PHP error.
The line causing the error is the following:
return call_user_func_array($callable, $data);
I know I could return it the value, but Laravel returns an array with the variables and an multidimensional array if multiple variables were passed into the event. I could parse the return value but it would make my code a lot cleaner and easier if I could just pass by reference.

Well, using Event::fire('user.create.show', array($data)); you are clearly passing a value since you use array constructor in the call. Change it to the following:
$data = array($data);
Event::fire('user.create.show', $data);
Also pay attention to the notes here and to the solution of passing array by reference here.

This is how i pass by reference:
\Event::listen('foo', function(&$ref){
$ref = 'bar';
});
$foo = 'foo';
$args[] = &$foo;
\Event::fire('foo', $args);
echo $foo; // bar

Related

PHP - passing an array to a method does not reflect change, but object does

When passing an array to a method, we have to return it in order to reflect the changes inside the passed array, as values are only copied to methods, and not passed-by-reference. We can only achieve this by adding & to the method signature, but I feel its bad practice to do so (and the code gets smelly IMO).
However, for Objects its a bit different. Any object passed to a method will be set even if the return type of the method is void.
Lets say we have this method:
public function test1()
{
$array = ['test' => 1, 'foo' => 'bar'];
$this->test2($array);
var_dump($array);
}
public function test2($array)
{
foreach(range(1,10) as $step) {
$array['steps'][$step] = true;
}
}
The result of this will be:
array(2) {
["test"]=>
int(1)
["foo"]=>
string(3) "bar"
}
How can I pass an array as reference without using & and without having to write something like this: $data = $this->test2($data);, or is it simply impossible due to PHPs pointer table?
You've sort of answered your own question. Simple answer is this is how PHP works, test2() is working with a copy of the array, unless you pass the array as a reference.
https://www.php.net/manual/en/language.references.pass.php
Alternatively you can return your array from test2(), and assign the returned value to your original array.
Edit: The reason this works with objects is that the object variable itself is just an identifier for the object, so technically the variable is also a copy when passed to another method, but the copy contains the same object identifier as your original variable. More on that here: https://www.php.net/manual/en/language.oop5.references.php

Class member reference to another object in PHP

First code and then the question:
class MyArray
{
private $arrayRef;
function __construct(&$array){
$this->arrayRef = $array;
}
function addElement($newElement){
$this->arrayRef[] = $newElement;
}
function print(){
print_r($this->arrayRef);
}
}
$array = ['first', 'second'];
$arrayObject = new MyArray($array);
$arrayObject->addElement('third');
print_r($array); // prints array containing 2 elements
echo '<br/>';
$arrayObject->print(); // prints array containing 3 elements
Class member $arrayRef, in this example doesn't work as a reference to another array provided in constructor. Argument in constructor is passed by reference, but I guess that doesn't make member $arrayRef also a reference to that array as well.
Why doesn't it work like that and how to make it work?
If you still don't get what I mean: first print_r prints array containing 2 elements, even thought it may be expected to contain 3.
When I pass third element to $arrayObject via addElement() I also want it to be added in the $array that I passed to constructor of class.
The answer is actually quite simple. Yes, you pass the array by reference via &$array but this reference gets lost when you assign/copy it to the member variable. To keep the reference, you can use the =& operator like so
$this->arrayRef =& $array;
See it work in this fiddle. You can read more about it in this question/answer (just look for reference).
Beware not to use &= (which does a bitwise operation) instead of =& (which assigns by reference).

PHP: How to get single value from array that was returned from a class?

class Test {
public function results() {
$return['first'] = 'one';
$return['second'] = 'two';
return $return;
}
}
$test = new Test;
print_r($test->results()); // Returns entire array
I just want to return a single specified element from the array, such as the value of key "second". How do I do this without sifting through the entire array after it's returned?
I just want to return a single specified element from the array, such as the value of key "second"
Pass in an argument to identify which element to return, and return that (or false if it doesn't exist - for example);
public function results($key = null)
{
$return['first'] = 'one';
$return['second'] = 'two';
// check the key exists
if (!array_key_exists($key, $return)) {
return false;
}
return $return[$key];
}
Then:
print_r($test->results('second')); // two
How do I do this without sifting through the entire array after it's returned?
It's important to note that you do not need to "sift through the entire array" to retrieve a value by its key. You know the key, so you can access it directly.
class Test {
private $arr; //private property of object
__construct(){
//create arr in constructor
$this->arr=[];//create new array
$this->arr['first'] = 'one';
$this->arr['second'] = 'two';
}
/**
/* get array
**/
public function getResults(){
return $this->arr;
}
/**
/* get single array element
**/
public function getResult($key) {
return isset($this->arr[$key])?$this->arr[$key]:null;//return element on $key or null if no result
}
}
$test = new Test();
print_r($test->getResult("second")); // Returns array element
//or second possibility but the same result
print_r($test->getResults()["second"]); // Returns array element
Few advices:
Create data structure in constructor ($arr in this particular case) because creating it on very results method call is not any kind of using objects or objective programming. Imagine that if array is created in results method then on every call new array is located in memory, this is not efficent, not optimal and gives no possibility to modify this array inside class Test.
Next in method results add parameter to get only this key what is needed and hide all array in private class property $arr to encapsulate it in object.
And last my private opinion for naming style:
Use camelCase when naming method names.
In PHP an array value can be dereferenced from the array by its key.
$arr = ["foo" => "bar", "baz" => "quix"];
echo $arr["foo"]; // gives us "bar"
echo $arr["baz"]; // gives us "quix"
If the method/function returns an array the same can be done with the return value, whether by assigning the return value to a variable and using the variable to dereference the value by key, or by using function array dereferencing.
class Test {
public function results() {
return ["foo" => "bar", "baz" => "quix"];
}
}
$test = new Test;
$arr = $test->results();
echo $arr["foo"]; // gives us "bar"
echo $arr["baz"]; // gives us "quix"
// Using FAD (Function Array Dereferencing)
echo $test->results()["foo"]; // gives us "bar"
echo $test->results()["baz"]; // gives us "quix"
Of course there are two important caveats to using function array dereferencing.
The function is executed each time you do it (i.e no memoziation)
If the key does not exist or the function returns something other than array, you get an error
Which means it's usually safer to rely on assigning the return value to a variable first and then doing your validation there for safety... Unless you are sure the function will always return an array with that key and you know you won't need to reuse the array.
In PHP 5.4 and above:
print_r($test->results()['second']);
In older versions which you shouldn't be running as they are out of security maintenance:
$results = $test->results();
print_r($results['second']);
Edit: The first example originally said 5.6 and above but array dereferencing was introduced in 5.4! For the avoidance of doubt, 5.6 is the lowest php version within security maintenance.

Accessing an Object Property from a string

I would like to be able to access the value of a property from a single string...
$obj->Test->FGH = "Well Done!";
I have tried
var_dump($obj->{'Test->FGH'});
And
var_dump( eval( '$obj->Test->FGH' ) );
I know, the following will work, but it has to be defined from a string
var_dump ($obj->Test->FGH);
I also know the following will work, but it doesnt access the FGH property;
var_dump ($obj->{'Test'});
So how is it possible to return the value of $obj->Test->FGH, from a string?
You need to iterate through the object structure recursively until you find the property.
Here is a recursive function that does the job.
It only works if the searched value is not an object. You will have to modify it if the property you are looking for is an object, relying on wether the $props array is empty or not.
The $props argument needs to be ordered in the same way the object properties are nested.
You could also modify it to have a string as second argument, for example Test/FGH
function search_property($obj, $props) {
$prop = array_shift($props);
// If this is an object, go one level down
if (is_object($obj->$prop)) {
return search_prop($obj->$prop, $props);
}
if (!isset($obj->$prop)) {
return false;
}
return $obj->$prop;
}
$val = search_property($obj, array('Test', 'FGH'));

php, I cant pass an array as reference

this is the function:
public function func(&$parameters = array())
{
}
now I need to do this:
$x->func (get_defined_vars());
but that fails. Another way:
$x->func (&get_defined_vars());
it drops an error: Can't use function return value in write context in ...
Then how to do it?
get_defined_vars() returns an array, not a variable. As you can only pass variables by reference you need to write:
$definedVars = get_defined_vars();
func($definedVars);
Though I don't really see a reason to pass the array by reference here. (If you are doing this for performance, don't do it, as it won't help.)
public function func(&$parameters = array())
{
}
Not defined correctly.
Try this way:-
call_user_func_array( 'func', $parameters );
See the notes on the call_user_func_array() function documentation for more information.

Categories