I have a function with multiple parameters.
Sometimes the user may need to specify, say, the first, second, and fourth, but not the third.
In JavaScript we can do that easily using an anonymous object as a single parameter for the whole function :
function foo(args) {
if(args.arg0 != null) alert(args.arg0);
if(args.arg1 != null) alert(args.arg2);
if(args.arg2 != null) alert(args.arg1);
}
foo({
arg0: 'foo',
arg2: 10
});
If i want to do that in PHP, i can use an associative array to play the same role as this anonymous objects "args" in the function above :
foo(array(
'arg0' => 'foo',
'arg2' => 10
));
Which would not be possible using multiple parameters as one may not write :
foo('foo', , 10);
For some reason i find using arrays dirty for that, and wonder if there isn't a "cleaner" way.
Thanks for your help :)
I agree with other commenters that say an array is a good solution. However, if you still want a different way to handle this check func_get_args()
Related
Bear with me, I'm learning.
I often see snippets like the one below:
<?p
$imageArray = get_field('image_field');
$imageAlt = $imageArray['alt'];
$imageURL = $imageArray['url'];
?>
It is pedagogical and clear and organized. But is it necessary to get the entire array before querying the array for values? Can I not define the variable in just a single line? Something like the below (which doesn't work, neither the other variants I have tried):
$imageAlt = get_field('image_field', ['alt']);
$imageURL = get_field('image_field', ['url']);
Yes, you can.
As of PHP 5.4 it is possible to array dereference the result of a function or method call directly. Before it was only possible using a temporary variable. - Source
$imageAlt = get_field('image_field')['alt'];
https://eval.in/548036
The question you are asking can be answered by asking 2 questions:
Is it doable ?
Is it a good idea to do it that way ?
Is it doable ?
Yes! You do not have to store the array in a variable and re-use it later.
For instance, you could do:
$imageAlt = get_field('image_field')['alt'];
Note: This will work in PHP 5.4+ and is called: Array dereferencing.
But that is not the only consideration...
Is it a good idea to do it that way ?
No. It's not a good idea in many cases. The get_field() function, depending on your context, is probably doing a lot of work and, each time you call it, the same work is don multiple times.
Let's say you use the count() function. It will count the number of items in an array. To do that, it must iterate through all items to get the value.
If you use the count() function each time you need to validate number of items in an array, you are doing the task of counting each and every time. If you have 10 items in your array, you probably won't notice. But if you have thousands of items in your array, this may cause a delay problem to compute your code (a.k.a. it will be slow).
That is why you would want to do something like: $count = count($myArray); and use a variable instead of calling the function.
The same applies to your question.
While PHP 5.4+ allows you to directly dereference a function return value like this:
get_field('image_field')['alt']
...in this particular case I would not suggest you do so, since you're using two values from the resulting array. A function call has a certain overhead just in itself, and additionally you don't know what the function does behind the scenes before it returns a result. If you call the function twice, you may incur a ton of unnecessary work, where a single function call would have done just as well.
This is not to mention keeping your code DRY; if you need to change the particulars of the function call, you now need to change it twice...
PHP allows you to play around quite a bit:
function foo(){
return array('foo' => 1, 'bar' => 2);
}
Option 1
echo foo()['foo']; // 1
# Better do this if you plan to reuse the array value.
echo ($tmp = foo())['foo']; // 1
echo $tmp['bar']; // 2
It is not recommended to call a function that returns an array, to specifically fetch 1 key and on the next line doing the same thing.
So it is better to store the result of the function in a variable so you can use it afterwards.
Option 2
list($foo, $bar) = array_values(foo());
#foo is the first element of the array, and sets in $foo.
#bar is the second element, and will be set in $bar.
#This behavior is in PHP 7, previously it was ordered from right to left.
echo $foo, $bar; // 12
Option 3
extract(foo()); // Creates variable from the array keys.
echo $foo, $bar;
extract(get_field('image_field'));
echo $alt, $url;
Find more information on the list constructor and extract function.
I think this is quite interesting!!! :).
What I've got?
In the application that I'm using on some level in some objects (doesn't really matter) I get an array, for example:
$array = array(
'argument_label' => 'value_label',
'argument_name' => 'value_name',
'argument_id' => 'value_id'
)
I don't have any impact on how and when this array is created. Next, I've got a method:
public function arrayArgument($array) {
$label = isset($array['argument_label']) ? $array['argument_label'] : 'default_label';
$name = isset($array['argument_name']) ? $array['argument_name'] : 'default_name';
$id = isset($array['argument_id']) ? $array['argument_id'] : 'default_id';
// Do something
return 'something';
}
I really hate it. There is no way of proper documentation for the method arguments (as PHPDocumentator work not so well with arrays), and those issets are driving me crazy. Additionally it is a nightmare for someone who will work with this code in the future, when I will already be a "retired nerd".
What I want?
I want to have a function like that:
public function notArrayArgument(
$id='default_id',
$label='default_label',
$name='default_name'
) {
// Do something
return 'something';
}
What I can do?
When I get array, I can change some code, and make my own method run. So I need some kind of solution to get from here:
$array = array(
'argument_label' => 'value_label',
'argument_name' => 'value_name',
'argument_id' => 'value_id'
)
To here:
notArrayArgument('value_id', 'value_label', 'value_name');
Or here:
notArrayArgument($array['argument_id'], $array['argument_label'], $array['argument_name']);
What are the problems?
This is not template like. The number of variables is always different, the names are always different, and sometimes some of them are passed, sometimes not.
It should work really fast...
Calling the method arguments in the right order. Array can be sorted, not sorted or random sorted, while the arguments inside method are always in the same order. The array should be reordered to match the method arguments order, and after that the method should be called.
What I came with?
I've got an idea using reflectionClass. I can check the names of method arguments, get them in order, reorder the array and try to call this method. But this is quite resource eating solution, as reflectionClass is not so fast I think.
Solution using extract? This would be great. But after extract, I need to use exact variables names in code. And I don't know them as every time they are different, and I need an universal approach.
NEW (thx to comment): call_user_func_array(). It is great, but it only works with indexed arrays, so this will be the last but not least step of the possible solution. As the order of arguments is still unknown...
Does this problem have a nice semantic, pragmatic solution?
I read my question once more, and I hope it is clear to understand. If not, please post a comment and I will do my best to describe the problem better.
Kudos for thinking about the maintainer, but I'd argue simplicity is just as important as nice semantics and pragmatism. Consider this: if you had to ask how to write such a pattern, what are the chances that it will be obvious to the reader? I'd much rather come across code where I can just think "Yep, that's clear" than "Oh cool that's a really intricate and clever way of setting array defaults".
With this in mind, it seems to me that an array is being used in a situation more suited to a class. You have an id, a label and a name, which surely represents an entity - exactly what classes are for! Classes make it easy to set defaults and provide PHPDoc on each of their properties. You could have a constructor that simply takes one of your existing arrays and array_merge()s it with an array of defaults. For the reverse conversion, conveniently, casting an object to an array in PHP results in an associative array of its properties.
Try to use classes as George Brighton mentioned.
If you can't for some legacy or library constraint, you will have to use reflection. Don't worry too much about the performance of reflection classes, a lot of frameworks use them to do the request routing.
You can use a function like:
function arrayArgument($object, $method, $array)
{
$arguments = [];
$reflectionMethod = new ReflectionMethod(get_class($object), $method);
foreach ($reflectionMethod->getParameters() as $parameter)
{
$arguments[] = isset($array[$parameter->name]) ? $array[$parameter->name] : $parameter->getDefaultValue();
}
call_user_func_array(array($object, $method), $arguments);
}
So you can do
$instance = new MyClass();
arrayArgument($instance, 'notArrayArgument', ['name' => 'myname']);
I need to define a function in PHP that contains multiple arguments (as shown below):
function the_function($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, ...)
but I believe there is a more efficient and clean method of doing this. Can anyone help me? I thought of using an array, something like that. obs: sorry for the noob question! :b
Edit: i'll use something like
function foo(
'number of topics', 'topics per page', 'topics per line'
, 'type of topic', 'number of excerpt words'
);
I think you can go three ways with this:
If all arguments are semantically the same, use func_get_args() within your function to iterate over the given arguments. A single array argument is a respected alternative.
If most arguments are semantically the same, group them together as a single array argument. Treat the other arguments separately.
In all other cases, group related arguments together into a separate object.
They're not entirely mutually exclusive btw.
An array is the standard approach to avoiding a ton of arguments (if a function does truly need that many). Just consider whether or not the type of things in the array are numerical in nature or if naming them makes sense. You may want to pass in an associate array rather than numerical. Of course, be absolutely sure that the function really needs those arguments and can't be broken down further.
Here's just a random example where an associative array might make sense.
$phone = [
'home' => '555-5551',
'cell' => '555-5552',
'work' => '555-5553'
];
function updateUserInfo($userId, $age, array $phone) {
}
rather than:
function updateUserInfo($userId, $age, $homePhone, $cellPhone, $workPhone) {
}
I recommend sticking to declaring your arguments explicitly. It makes for easier testing and less ambiguity. Also, you can enforce some type checking on objects and arrays.
function foo( User $user, array $user_friend_ids);
I am trying to create a little MVC framework for myself and am using a url structure similar to: http://www.example.com/className/methodName/var1/var2/var3. I want to make it able to accomodate for any number of variables.
I have all of the information how I need it, except for the /var1/var2/var3 part. Currently I have it in an array exploded() upon the "/", so: var[0] = "var1",var[1] = "var2",var[2] = "var3"
Since the method which will be called will be unknown and each method can require a different amount parameters I want to figure out a way to be able to pass the parameters as:
$controller->$function($var1, $var2, $var3);
rather than
$controller->$function($var);
I realize I can string together a comma delimited variable such as $var1, $var2, $var3 and use eval() to do what I want, but I am wondering if there is a better way to go about it. I would rather not use eval() for this as it will be using user submitted data.
Worst case scenario, I figure I would just try to cleanse the data before the eval, but I'd still like to avoid it.
One other potential Idea I had was to use a foreach() and loop through each element inside of the the method call. But even that seems a little messy to me.
On a side note, is the standard way to pass a variable amount of parameters to group them together in an array and pass in a single parameter?
If anyone knows of a more elegant way to do this, I would really appreciate it.
Use call_user_func_array
call_user_func_array(array($controller, $function), array($var1, $var2...));
#Dogbert's answer would complement what I'm about to suggest.
You could create a function that accepts no parameters and you're free to pass as many parameters (or no parameters at all), you'll only have to use PHP functions like: func_get_args(), func_num_args() and func_get_arg(int arg_index) to do your manipulation.
An Example follows below:
function syncOrderRelation(){
$num_arguments = func_num_args();
$arguments = func_get_args();
#gets all the arguments in the request
list($userID,$spID,$productID) = $arguments;
#if you know what you're expecting or
for($i = 0; $i < $num_arguments; $i++){
$argument_at_index = func_get_arg($i);
}
}
You could call this function in the following ways:
syncOrderRelation();
syncOrderRelation($var1,$var2,var3);
syncOrderRelation($var1);
#or you could call it as #Dogbert suggested since the function will accept whatever
#arguments you supply
The rest is up to you...Happy coding!
Is there a way to make the arguments of a function act as an array? I'm finding this difficult to explain.
Here's kind of an example.. When you declare an array, you can define the keys => values like so:
$array = array(
"key" => "value",
"other_key" => "other_value"
);
And if I make a function that for an example outputs these onto the document, I could have:
function write($ar)
{
foreach($ar as $key => $value)
echo "$key: $value<br />";
}
write($array); // parse previously mentioned array
What I want to be able to do is omit the need to parse an array like above or below examples..
write(array(
"key" => "value",
"other_key" => "other_value"
));
I know I can use func_get_args() to list any amount of arguments, but is there a similar function that lets you parse key => value pairs rather than just a list of values?
Hope I described this in a way that makes sense, what I essentially want to end up with is something like:
write(
"key" => "value",
"other_key" => "other_value"
);
A function cannot take the argument as an array structure, so your best bet would be to use the method you specified in your second to last example:
write(array(
"key" => "value",
"other_key" => "other_value"
));
You could then have a default array within your function (if desired) so you could merge the two together so you always have a decent set of data.
EDIT
Unless you want to go crazy and pass it through as a string:
write('
"key" => "value",
"other_key" => "other_value"
');
And then parse that out on the other side... but IMO I wouldn't bother, potentially opening yourself up to issues here.
I know I can use func_get_args() to list any amount of arguments, but is there a similar function that lets you parse key => value pairs rather than just a list of values?
No, that's not possible in PHP. However you can fork PHP and implement such a syntax or make a code preprocessor ;)
well, I assume that you can send an object instead of an array and declare like that.
//the class
class CustomType{
public $key1;
public $key2;
}
//the function
function write(CustomType $customType){
}
//call the funcion write
$a = new CustomType();
$a->key1 = 1;
$a->key2 = 2;
write($a);
the above will force the user to send a custom object.
it is better than arrays, but then again, the user can ignore and not set $key2.
BTW, if you choose this method, you might as well do it in JAVA style with getters and setters. like here: http://www.abbeyworkshop.com/howto/java/salesTaxBean/index.html
that is as good as it gets since php is typeless, but i think it is much better then arrays
Edit
One of the bad usage in PHP is using arrays as params.
Almost every framework does that and you can find your self diving into sources in order to understand the array structure.
When you declare a class, you get it as autocomplete in your editor, and if not you just need to open the class to understand its structure.
I believe this is better programming style.