I am trying to rewrite some code to eliminate the call_user_func_array calls, and have the following code so far :
if($stmt = $this->_db->prepare($sql)) {
$bind_arguments = $this->params;
// remove first element (eg. 'ssiss')
$bind_arguments = array_shift($bind_arguments);
// execute query passing values
$res = $stmt->execute($bind_arguments);
}
Here is a var_export of $this->params :
array ( 0 => 's', 1 => 'admin', )
$res always evaluates to null when trying the above method. According to this comment, it appears that everything is correct, except my array has integer keys, and there is an additional , after the last element.
What do I need to do to format this array (removing the first element), to pass to the $stmt->execute method as the variable containing the array elements (this is not a static array. elements in it are modified by other methods in the class to add or remove elements from it).
First of all, to make things straight: you cannot pass an array to mysqli_execute() as this function accepts no parameters at all. What you really need is to pass an array to mysqli_bind_param()
However, the only way around call_user_func_array call is a splat operator available since PHP 5.6
function query($query, $params = NULL, $types = NULL)
{
$statement = $this->mysqli->prepare($select);
$types = $types ?: str_repeat('s', count($params));
$statement->bind_param($types, ...$params);
$statement->execute();
return $statement;
}
Yet the preferred and bullet-proof solution would be apparently switching to PDO, which indeed has the capability of sending an array with parameters right into execute().
Array should be associative array and key name should be column name of table and data should be value of key. Like the following:
$arr = [ 'column1' => 'value1', 'column2' => 'value2' ];
than use $res = $stmt->execute($arr);
Related
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);
This is a simplified version of a very large user-defined function I have...
function validate_promotional_code($code, $params = ["reference" => "code", "input-type" => "single", "delimiter" => ","]) {
global $node_connection;
$operator_array = [
"Free With Purchase",
"Buy One Get One Free"
];
return $params;
}
I have used this method of passing an associative array with default values into a user-defined function many times. It lets me set default values for my function without having to worry about which variable is where, etc.
Unfortunately, this function returns nothing, where it should return the $params array containing my default values.
Have I made a syntax error? Why is my function not receiving my default values?
The default $params array will be used by your function – but only if the function call does not contain a $params argument at all.
If there is any value passed for $params, that one will be used instead of your default. PHP does not care that it’s an array and does not merge it. You’ll have to do that on your own:
function validate_promotional_code($code, $params = []) {
$defaults = ["reference" => "code", "input-type" => "single", "delimiter" => ","];
$params = array_merge($defaults, $params);
// ... work with $params ...
}
The array_merge() function will merge the two arrays, while values in the latter one will overwrite values in the first. That’s why we put $defaults as the first argument.
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
In Python it is possible to have a function with several variables all having a default value. And then just passing the value of one of the values. So if I have
function foo(a=10,b=50, c=70)
pass
pass
return
Then I can call
foo(b=29)
and it would call
foo(10,29,70)
(using the default for all the values, and the exact value for that one variable).
Is something similar possible in PHP?
No there is no equivalent to that in PHP. You can have default values for function arguments, but they are evaluated from left to right and are not named:
function test($var1 = 'default1', $var2 = 'default2')
{
}
In that example the two variables are optional, but you must specify the first argument if you want to specify the second.
test(); // works
test('arg1'); // works
test('arg1', 'arg2'); // works
test('arg2'); // this will set the first argument, not the second.
A common workaround if you need flexibility on your optional arguments is to pass an array as the argument:
function test($options)
{
}
This can have a variable number of arguments in the form of a single associative array:
$options = array('var1' => 'arg1', 'var2' => 'arg2');
test($options);
Use array as an argument. For example:
function a(array $params) {
$defaults = array(
'a' => 10,
'b' => 50,
'c' => 70,
);
$params += $defaults;
// use $params
}
a(array('b' => 29));
I have an array $params with the following content:
Array(
0 => "array"
1 => "arrayName" //variable
2 => "key1" //optional&variable ie. 0 or 'foo'
3 => "key2" //optional&variable ie. 0 or 'foo'
//etc.
)
Also, I have an stdObject that contains all my variables for the page that is being requested (MVC style).
So, I could have an array in there like this:
$std = new stdObject();
$std->arrayName->array('foo', 'bar');
Now I want the first value of the $std array. So I am using the "key1" parameter in $params and set this to '0' so it will pick the $std->arrayName[0] value ('foo'). Note that I am not using the "key2" parameter since I don't want to select $std->arrayName[key1][key2].
But what if I need the value of a nested array?
$std = new stdObject();
$std->arrayName->array('foo', array('bar', 'fish'));
I will request the $std->arrayName[1][1] value ('fish') by setting the "key1" parameter to 1 and the "key2" parameter to 1 as well. So it will select $std->arrayName[key1][key2].
But what if I have 10 arrays nested in each other? highly unlikely, but it's possible. I want to select $std->arrayName[key1][key2][key3][..][key10] to select a certain value that's located there.
For example: $std->arrayName[0][3][4][6][2][9]['foo']['bar][9][2];
So my question is: how do I select the value of a certain array (arrayName) by the values defined in another array ($params) that act as the keys for the first array (arrayName). But, the amount of keys (values in $params) is optional.
Maybe the question isn't completely understandable, so feel free to ask more information.
You need to loop the key array and assign each level to a variable, until you reach the end when you should have the value you want.
Something like this (untested, no error checking):
function array_value_at ($array, $keys) {
foreach ($keys as $key) {
$array = $array[$key];
}
return $array;
}