I have an Eventbus that takes a filter name as its first parameter and a Closure as second parameter. Like this:
$this->EventBus->subscribe('FilterTestEvent', function(){/*Do Something*/});
It's called like this:
$filteredValue = $this->EventBus->filter('FilterTestEvent', $anyValue);
What I want now is to pass an array as reference to the Closure that then is changed in any way (here: add elements) and then return something as the filtered value:
$item_to_change = array('e1' => 'v1', 'e2' => 'v2');
$this->EventBus->subscribe('FilterTestEvent', function(&$item){
$item['new'] = 'LoremIpsum';
return true;
});
$filtered = $this->EventBus->filter('FilterTestEvent', $item_to_change);
Now I would a print_r($item_to_change) expect to look like the following:
Array
(
[e1] => v1
[e2] => v2
[new] => LoremIpsum
)
But instead it looks like the original array:
Array
(
[e1] => v1
[e2] => v2
)
The eventbus internally stores all closures and calls them if needed through call_user_func_array() with the closure as first argument and the value as the only argument array element.
How can I achieve what it's meant to do?
Source Code to the Eventbus: http://goo.gl/LAAO7B
Probably this line:
$filtered = $this->EventBus->filter('FilterTestEvent', $item_to_change);
is supposed to return a new filtered array, not modify the original one.
So check it:
print_r($filtered);
Passing by reference is possible by modifying a function (adding &):
function filter(&$array){ //Note & mark
$array['new_index'] = "Something new" ;
}
$array = array("a"=> "a");
filter($array); //The function now receives the array by reference, not by value.
var_dump($array); //The array should be modified.
Edit:
Make your callback return the filtered array:
$this->EventBus->subscribe('FilterTestEvent', function(&$item){
$item['new'] = 'LoremIpsum';
return $item ;
});
Passing by reference should not work here, because in the source code that $value variable is swapped with another value and returned after.
Ok. I found the answer. The filter function needs to be changed so that it accepts arrays as value, in which I can save the reference. For details see difference Revision 1 and Revision 2 of the Eventbus source code, here: goo.gl/GBocgl
Related
I have arrays like this
$InternetGatewayDevice['DeviceInfo'][0]['SoftwareVersion'][1]['_value']
and also like this
$InternetGatewayDevice['DeviceInfo'][1]['SoftwareVersion'][2]['_value']
actually, both of them return same value, which is the software version for the router, but because routers belong to different vendors, I have that problem, so
actually, I want to know the path that I have to go in, in order to get my value
so I want to have somethings like this
InternetGatewayDevice.DeviceInfo.0.SoftwareVersion.1._value
as a string
I mean I want a function where I can provide to it the array and the key ,so the function will return to me the path of the array that I have to follow in order to get the value like this
getpath($array,"SoftwareVersion")
whhich will return value like this
InternetGatewayDevice.DeviceInfo.0.SoftwareVersion
are there any way to do this in php ?or laravel package
or is there any way in PHP to get the value whatever the number key is?
I mean like this
$InternetGatewayDevice['DeviceInfo'][*]['SoftwareVersion'][*]
so what ever the key it will return the value
You could try to use he data_get helper function provided by Laravel.
public function getSoftwareVersion(array $data, int $deviceInfoIndex, int $softwareVersionIndex)
{
$index = "DeviceInfo.{$deviceInfoIndex}.SoftwareVersion.{$softwareVersionIndex}";
return data_get($data, $index);
}
Then it can be used like
$softwareVersion = getSoftwareVersion($internetGatewayDevice, 1, 0);
Laravel Docs - Helpers - Method data_get
you can use the get function from lodash php
https://github.com/lodash-php/lodash-php
Example:
<?php
use function _\get;
$sampleArray = ["key1" => ["key2" => ["key3" => "val1", "key4" => ""]]];
get($sampleArray, 'key1.key2.key3');
// => "val1"
get($sampleArray, 'key1.key2.key5', "default");
// => "default"
get($sampleArray, 'key1.key2.key4', "default");
// => ""
function values($id,$col)
{
$vals = [1=>['name'=>'Lifting Heavy Boxes']];
return $vals[$id][$col];
}
$complete = [1=>["id"=>"2","sid"=>"35","material_completed"=>"1","date"=>"2017-12-18"]];
$form = 'my_form';
array_walk($complete, function(&$d,$k) use($form) {
$k = values($k, 'name').' ['.date('m/d/y',strtotime($d['date'])).'] ('.$form.')';
echo 'in walk '.$k."\n";
});
print_r($complete);
the echo outputs:
in walk Lifting Heavy Boxes [12/18/17] (my_form)
the print_r outputs:
Array
(
[1] => Array
(
[id] => 2
[sid] => 35
[material_completed] => 1
[date] => 2017-12-18
)
)
I have another array walk that is very similar that is doing just fine. The only difference I can perceive between them is in the one that's working, the value $d is already a string before it goes through the walk, whereas in the one that's not working, $d is an array that is converted to a string inside the walk (successfully, but ultimately unsuccessfully).
Something I'm missing?
And here's the fixed version:
array_walk($complete, function(&$d,$k) use($form) {
$d = values($k, 'name').' ['.date('m/d/y',strtotime($d['date'])).'] ('.$form.')';
});
That's what I was trying to do anyway. I wasn't trying to change the key. I was under the mistaken impression that to change the value you had to set the key to the new value.
You cannot change the key of the array inside the callback of array_walk():
Only the values of the array may potentially be changed; its structure cannot be altered, i.e., the programmer cannot add, unset or reorder elements. If the callback does not respect this requirement, the behavior of this function is undefined, and unpredictable.
This is also mentioned in the first comment:
It's worth nothing that array_walk can not be used to change keys in the array.
The function may be defined as (&$value, $key) but not (&$value, &$key).
Even though PHP does not complain/warn, it does not modify the key.
I have an HTML form that uses POST to send data to a PHP script. Until now I have not used an array in an HTML form, but this form calls for some fields to be grouped (e.g., price and qty) while other fields remain single input fields (name, email, etc).
I would like to sanitize the input when it is received by the PHP script. With single input fields, I used to loop through the fields like this:
if( !empty($_POST) ) {
foreach( $_POST as $x => $y ) {
$_POST[$x] = htmlspecialchars($y);
$_POST[$x] = trim($y);
}
}
How can I sanitize the input when some of the items are in an array?
Modifying all of the leaf nodes in your multidimensional array is easily done with the native function array_walk_recursive() because it visits all of the "leaf nodes" by design.
Code: (Demo) (or as an anonymous one-liner)
$sweet = ['a' => 'apple ', 'b' => ' "banana" '];
$array = ['sweet' => $sweet, 'test' => " <a href='test'>Test</a>"];
function mySanitizer(&$value) {
$value = htmlspecialchars(trim($value));
}
array_walk_recursive($array, 'mySanitizer');
var_export($array);
Output:
array (
'sweet' =>
array (
'a' => 'apple',
'b' => '"banana"',
),
'test' => '<a href=\'test\'>Test</a>',
)
Notice the use of & on the value parameter. This tells the script to modify the data by reference -- otherwise no changes would persist outside of the scope of array_walk_recursive
How to apply this technique...
To apply this technique to all elements in the $_POST superglobal array, call:
array_walk_recursive($_POST, 'mySanitizer');
Of course, this requires you to write the custom function declaration (function mySanitizer() {...}).
Alternatively, if you don't wish to declare the custom function mySanitizer, then this is all you need to write:
array_walk_recursive($_POST, function(&$value){
$value = htmlspecialchars(trim($value));
});
It is more commonplace to have a return value from most functions, however array_walk_recursive() does not offer return data. For this function to be effective for your requirements, the input array must be directly affected by the custom function that it contains. "Modifying a variable by reference" means that you don't need to overwrite the $_POST variable by assignment (like $_POST = ...). Simply by feeding the input array into the native function, writing & before the $value parameter, then overwriting each encountered $value while iterating, your $_POST variable will be sanitized.
As for how array_walk_recursive() "iterates/loops"... there is a special behavior to enjoy. The function will traverse every level of your array. If it finds an "iterable" element, it will loop through the elements that it contains. If it encounters a non-iterable element (scalar elements might be a string, integer, float, boolean, null) it will execute a function / callback (whatever you command it to) on it.
Another example of a php function that modifies by reference is sort(). You don't make an assignment with this function, you just pass your data through it and when you next access the variable's data, it is already modified.
You need to create a recursive function for this.
function htmlentitiesRecursive($data)
{
if (is_array($data)) {
// If the data is an array, iterate through it and convert each item
foreach ($data as $key => $value) {
$data[$key] = htmlentitiesRecursive($value);
}
return $data;
}
// If the data is a string, convert it into html entities
return is_string($data)
? htmlentities(trim($data))
: $data;
}
Usage:
$_POST = htmlentitiesRecursive($_POST);
I have an intersect in my update method:
$inputs = $request->intersect('message','name','email','is_read');
If i send an update request where is_read=0 the intersect returns an empty array. Works fine with anything else (false, 1 etc)
Any tips?
Thanks!
ALERT
Try to move to another implementation and stop using intersect() method, it will be removed from the future versions of Laravel: Link
IF you mean that the is_read key is missing from the final array (and not the the whole array is empty, see my comment), this is because of the implementation of the intersect() method.
The intersect method simply wraps the only() method of the Illuminate\Http\Request class and do an array_filter over the result.
This is the implementation:
/**
* Intersect an array of items with the input data.
*
* #param array|mixed $keys
* #return array
*/
public function intersect($keys)
{
return array_filter($this->only(is_array($keys) ? $keys : func_get_args()));
}
In your case, we can decompose the code as this:
step1
$results = $request->only('message','name','email','is_read');
At this point, $results is
Array
(
[message] => message
[name] => name
[email] => email
[is_read] => 0
)
However, at step2
step2
$filteredResults = array_filter($results);
The result becomes
Array
(
[message] => message
[name] => name
[email] => email
)
And this is because of how array_filter works. It expect an array as the first parameter, then an optional callback (used to filter the array) and a flag.
From php reference
What happens when no callback is provided (like in this case?)
If no callback is supplied, all entries of array equal to FALSE (see converting to boolean) will be removed.
If you look at the link converting to boolean you'll see that 0 (zero) is assumed to be FALSE and, for that reason, removed from the array.
Could someone explain me, why this code works properly without crashing initial array structure?
function setArrayValueByPath($path, $value, &$array)
{
foreach ($path as $p) {
$array = &$array[$p];
}
$array = $value;
return true;
}
$array = [
'a' => 'v1',
'b' => 'v2',
];
setArrayValueByPath(['hello', 'world'], '!!!', $array);
echo '<pre>';
print_r($array);
echo '</pre>';
When I run the code, I see:
Array
(
[a] => v1
[b] => v2
[hello] => Array
(
[world] => !!!
)
)
Due to the line in function:
$array = $value;
it should replace $array value, but it does not happen.
My function is based on code snippets are given here: Using a string path to set nested array data
Thank you.
Let's examine this one step at a time.
The parameter $array is a local variable within the function which contains a reference to some external array being passed in.
foreach ($path as $p) {
This iterates over ['hello', 'world']
$array = &$array[$p];
Take the original array, and "index" it with $p (i.e. [hello]). This does not currently exist so it is added to the original array. Then take a reference to that new member and save it in the local variable $array. I.e. you just created a new member of the original array, and the local variable $array no longer points to the original external array.
On the second iteration, take the variable currently pointed to by $array (see just above) and index it with $p (world). This does not exist, so create it.
}
At this point $array points to the member {original array}[hello][world]. I use the syntax {original array} here because you no longer have a reference to it, only a reference to an array two levels nested within it.
$array = $value;
This sets the value of that member to !!!, giving exactly the data structure you see.