This works without warning:
function test($a)
{
echo 1;
}
test(2, 1);
This will cause warning:
function test($a)
{
echo 1;
}
test();
If it's standard, any reason for this?
Because in the first example test() may use func_get_args() to access its arguments, so it shouldn't throw an error.
In the second example, the $a is not optional. If you want it to be optional, tack a default in the arguments signature like so
function test($a = 1)
{
echo 1;
}
test();
So yes, it is default behaviour, and it makes sense once you know the above.
Concerning Edit
In the edited first example, you will be able to access 2 as $a, however the 1 will only be accessible via func_get_args() (or one of its similar functions like func_get_arg()).
That is the correct behavior. Your function declaration states that it is expecting one argument. If you want to make a function argument optional, you should apply a default value. Otherwise you will raise an exception. Read the PHP documentation on Function Arguments for full details on how to declare your functions and the ways you can pass in values.
[Edit] This should work fine for you:
function test($a = null)
{
echo 1;
}
test();
I'm just speculating, but a function not receiving a parameter it's expecting is potentially more dangerous than a function receiving an extra parameter it can safely ignore.
Also, I wonder if it might have something to do with the fact that PHP will let you declare defaults for the parameter values, so the warning may be to prevent a situation where you've just forgotten to give $a a default
Related
I pass parameters to a function in PHP. I dont know if the parameter is defined or not.
Right now I check if the parameter is defined and cast to null if not before calling the function.
Is there a more elegant way to do it by casting or something similar? I want the function default value to be used if the parameter is not defined.
Example:
//Params['x'] may or may not be defined
// I want to use this:
a(params['x']);
//Currently I use this (which I want to avoid) :
a( isset (params['x'])?params['x']:null);
function a (data=null){
// Here I want data to be null if params['x'] is not defined.
}
It is possible if you declare this argument as reference:
function fun(&$param = null) {
}
Keep in mind, that it will also create this entry if it was not present in passed array, so if you have
$x = array('a' => 1);
And you will call fun($x['b']);, your $x will look like
$x = array('a' => 1, 'b' => null);
For everything which you're not sure exists, there has to be one point where you test that and/or ensure that it exists. You can do that with isset/empty or with an operation that ensures the value exists, like:
$params += array('x' => null);
You should not design your function to work around parameters the caller may not have. If the function requires the parameter then let it require the parameter, period. The sooner you get out of the may-or-may-not-be-there mode in your code the sooner you can require and return defined values, which cuts down on the amount of code you need to write and debug. By that I mean that the only uncertainty should be user input; punch that uncertain user input into a defined form as early as possible, after that your application internally should be strict about what it requires and what it returns and not be uncertain at every single point.
What you have already done does all you need to do.
With this function prototype you are saying, accept whatever parameter may be passed by the caller, or use NULL if nothing is passed.
function a ($data=null){
if ( $data == null ) {
// no parameter passed by caller
}
}
Just forget about all the fussing with the parameters on the call of the function. Its all done for you.
While working with Laravel framework, more specific - Form macros, I stumbled upon a weird error.
At first, I thought it's something wrong with Laravel, but then I took everything out of context:
<?php
// placeholder function that takes variable as reference
$function = function(&$reference)
{
// append to variable
$reference = $reference . ':' . __METHOD__;
};
// test with straight call
$variable = 'something';
$function($variable);
echo $variable;
// test with call_user_func(), that gets called in Laravels case
$variable = 'something'; // reset
call_user_func($function, $variable);
echo $variable;
While the first call to $function executes properly, the second try with call_user_func(), produces (excerpt from Codepad):
Warning: Parameter 1 to {closure}() expected to be a reference, value given
PHP Warning: Parameter 1 to {closure}() expected to be a reference, value given
Fiddle: Codepad # Viper-7
While writing this, I thought about call_user_func_array(): fiddle here, but the same error is produced.
Have I got something wrong about references or is this a bug with PHP?
I would call this a bug with PHP, although it's technically a bug with call_user_func. The documentation does mention this, but perhaps not in a very enlightening way:
Note that the parameters for call_user_func() are not passed by
reference.
It would be perhaps clearer to say that the arguments to call_user_func() are not passed by reference (but note that technically it's not necessary to say anything at all; this information is also embedded in the function signature).
In any case, this means is that when call_user_func finally gets to invoking its target callable, the ZVAL (PHP engine internal data structure for all types of values) for the argument being passed is not marked as "being-a-reference"; the closure checks this at runtime and complains because its signature says that the argument must be a reference.
In PHP < 5.4.0 it is possible to work around this by using call-time pass by reference:
call_user_func($function, &$variable);
but this produces an E_DEPRECATED warning because call-time pass by reference is a deprecated feature, and will flat out cause a fatal error in PHP 5.4 because the feature has been removed completely.
Conclusion: there is no good way to use call_user_func in this manner.
This works:
call_user_func_array($function, array(&$variable));
I used this code
<?php
$myfunction = function &($arg=3)
{
$arg = $arg * 2;
return $arg;
};
echo $myfunction();
?>
Worked like a charm. :)
What happens if you do this?
call_user_func($function, &$variable);
What is the difference between these two code excerpts, respectively, in PHP?:
function eat_apple($apple)
{
return true;
}
And:
function eat_apple()
{
$apple = func_get_arg(0);
return true;
}
Or is there a difference? If there is not, what is the point of func_get_arg()? Surely it serves some purpose I'm not aware of?
func_get_arg (and func_get_args) make it possible to have functions with a variable number of parameters, so called variadic functions.
Take array_merge as an example, which takes a variable number of arrays to be merged.
Hello. What is the difference between these two code excerpts, respectively, in PHP?:
-function eat_apple($apple)
+function eat_apple()
{
+ $apple = func_get_arg(0);
return true;
}
what is the point of func_get_arg()? Surely it serves some purpose I'm not aware of?
The officially stated purpose is to:
Return an item from the argument list
-- http://php.net/func-get-arg
In truth, there isn't much difference, and a standard passed argument is usually preferable over func_get_arg().
There are two occasions when you might want to use func_get_arg() rather than using defined arguments:
If you want to have an unlimited number of arguments. Typically, this would be better done with a single array argument, but there are times when you may want to allow unlimited arguments. A real example of a function that works this way is PHP's built-in printf function.
Where you have a complex function where the number of arguments given may alter what the arguments are used for and/or their data type. For example, passing a single argument may cause the function to expect a string, whereas passing two arguments, it may expect an integer first, and then the string. I can't think of any examples of this off the top of my head, but I'm fairly sure there are some. Ordinarily, I would say that this sort of behaviour is bad practice, but I can see how it may be useful for adding features while maintaining backward compatibility.
There are two things that you will definitely lose if you use fung_get_arg() instead of standard function arguments:
The ability to pass by reference.
The ability for your IDE to do any type hinting or auto-completion.
The other possible reason I could think of is avoiding a fatal error.
You have a function:
<?php
function display ($a, $b, $c) {
echo $a . " is " . $b . " " . $c;
}
?>
and you could call
<?php
display ("Rock", "good", "boy");
- this will not throw any error.
display ("Rock", "good");
- this will throw fatal error.
?>
So, now think of the second call, you could avoid the fatal error, see how many arguments are passed with func_get_args and process your code with func_get_arg(1) etc.
Several reasons:
You can use that magic to emulate overloading
You can write function (like printf) which can take undetermined number of arguments.
Example for first:
class a{
function X(MyOBJ $obj){...}
}
class b extends a{
function X(string $s,int $i){...}
}
class c extends b{
function X(){...}
}
This will throw a warning. If you use func_get_args() inside the function to get relevant parameters, no warning will be thrown.
Example for the second:
//not good, you can only send 3 strings
function merge_strings($s1, $s2, $s3){
return $s1 . $s2 . $s3;
}
//good one
function merge_strings(){
return join('',func_get_args());
}
I have been blissfully unaware of the php function func_get_args(), and now that I have discovered it I want to use it everywhere. Are there any limitations of using func_get_args as compared to explicit argument declaration in the function definition?
You shouldn't use func_get_args unless you actually need it.
If you define a function to take a specific number of arguments, PHP will raise an error if you don't supply enough arguments at call time.
If you take any number of arguments via func_get_args, it's up to you to specifically check that all the arguments you're expecting have been passed to your function.
Similarly, you lose the ability to use type hinting, you can't supply default values, and it becomes much harder to tell what arguments your function expects at a glance.
In short, you prevent PHP from helping you catch (potentially difficult to debug) logic errors.
function do_stuff(MyClass tmpValue, array $values, $optional = null) {
// This is vastly better...
}
function do_stuff() {
// ... than this
}
Even if you want to allow a variable number of arguments, you should explicitly specify as many arguments as you can:
/**
* Add some numbers
* Takes two or more numbers to add together
*/
function add_numbers($num_1, $num_2 /* ..., $num_N */) {
$total = 0;
for ($i = 0; $i < func_num_args(); ++$i)
$total += func_get_arg($i);
return $total;
}
add_numbers(1,2); // OK!
add_numbers(1,2,3); // OK!
add_numbers(1) // Error!
For starters I think it has a performance impact.
It makes your code much harder to read and understand.
No automatic error alert will make debugging a pain.
I think auto-completion is harder if not impossible to do for the IDE (it might use the phpdoc #param declaration though)
EDIT: you may use it when your have only one argument : a one-dimensional array, which keys do not matter. It then becomes very handy.
The only limitation I'm aware of is that stuff is harder to check at parse time. Note that this includes parsers for, say, automated documentation tools, since the functions have args that aren't right there in the function declaration
I'm pretty sure the answer to this question is no, but in case there's some PHP guru
is it possible to write a function in a way where invalid arguments or non existent variables can be passed in and php will not error without the use of '#'
Much like empty and isset do. You can pass in a variable you just made up and it won't error.
ex:
empty($someBogusVar); // no error
myHappyFunction($someBogusVar); // Php warning / notice
You don't get any error when a variable is passed by reference (PHP will create a new variable silently):
function myHappyFunction(&$var)
{
}
But I recommend against abusing this for hiding programming errors.
Summing up, the proper answer is no, you shouldn't (see caveat below).
There are workarounds already mentioned by many people in this thread, like using reference variables or isset() or empty() in conditions and suppressing notices in PHP configuration. That in addition to the obvious workaround, using #, which you don't want.
Summarizing an interesting comment discussion with Gerry: Passing the variable by reference is indeed valid if you check for the value of the variable inside the function and handle undefined or null cases properly. Just don't use reference passing as a way of shutting PHP up (this is where my original shouldn't points to).
You can do this using func_get_args like so:
error_reporting(E_ALL);
ini_set('display_errors', 1);
function defaultValue() {
$args = func_get_args();
foreach($args as $arg) {
if (!is_array($arg)) {
$arg = array($arg);
}
foreach($arg as $a) {
if(!empty($a)) {
return $a;
}
}
}
return false;
}
$var = 'bob';
echo defaultValue(compact('var'), 'alpha') . "\n"; //returns 'bob'
echo defaultValue(compact('var2'), 'alpha') . "\n"; //returns 'alpha'
echo defaultValue('alpha') . "\n"; //return
echo defaultValue() . "\n";
This func goes one step further and would give you the first non empty value of any number of args (you could always force it to only take up to two args but this look more useful to me like this).
EDIT: original version didn't use compact to try and make an array of args and STILL gave an error. Error reporting bumped up a notch and this new version with compact is a little less tidy, but still does the same thing and allows you to provide a default value for non existent vars.
There are valid cases where checking becomes cumbersome and unnessesary.
Therfore i've written this little magic function:
/**
* Shortcut for getting a value from a possibly unset variable.
* Normal:
* if (isset($_GET['foo']) && $_GET['foo'] == 'bar') {
* Short:
* if (value($_GET['foo']) == 'bar') {
*
* #param mixed $variable
* #return mixed Returns null if not set
*/
function value(&$variable) {
if (isset($variable)) {
return $variable;
}
}
It doesn't require any changes to myHappyFunction().
You'll have to change
myHappyFunction($someBogusVar);
to
myHappyFunction(value($someBogusVar));
Stating your intent explicitly. which makes it good practice in my book.
No, because this isn't really anything to do with the function; the error is coming from attempting to de-reference a non-existent array key. You can change the warning level of your PHP setup to surpress these errors, but you're better off just not doing this.
Having said that, you could do something like
function safeLookup($array, $key)
{
if (isset($array, $key))
return $array[$key];
return 0;
}
And use it in place of array key lookup
defaultValue(safeLookup($foo, "bar"), "baz);
Now I need to take a shower :)
is it possible to write a function in a way where invalid arguments or non existent variables can be passed in and php will not error without the use of '#'
Yes you can!
porneL is correct [edit:I don't have enough points to link to his answer or vote it up, but it's on this page]
He is also correct when he cautions "But I recommend against abusing this for hiding programming errors." however error suppression via the Error Control Operator (#) should also be avoided for this same reason.
I'm new to Stack Overflow, but I hope it's not common for an incorrect answer to be ranked the highest on a page while the correct answer receives no votes. :(
#Brian: I use a trinary operation to do the check for me:
return $value ? $value : $default;
this returns either $value OR $default. Depending upon the value of $value. If it is 0, false, empty or anything similar the value in $default will be returned.
I'm more going for the challenge to emulate functions like empty() and isset()
#Sean That was already answered by Brian
return isset($input) ? $input : $default;
Sean, you could do:
$result = ($func_result = doLargeIntenseFunction()) ? $func_result : 'no result';
EDIT:
I'm sure there could be a great
discussion on ternary operators vrs
function calls. But the point of this
question was to see if we can create a
function that won't throw an error if
a non existent value is passed in
without using the '#'
And I told you, check it with isset(). A ternary conditional's first part doesn't check null or not null, it checks true or false. If you try to check true or false on a null value in PHP, you get these warnings. isset() checks whether a variable or expression returns a null value or not, and it returns a boolean, which can be evaluated by the first part of your ternary without any errors.
I'm sure there could be a great discussion on ternary operators vrs function calls. But the point of this question was to see if we can create a function that won't throw an error if a non existent value is passed in without using the '#'
While the answer to the original question is "no", there is an options no one has mentioned.
When you use the # sign, all PHP is doing is overriding the error_reporting level and temporarily setting it to zero. You can use "ini_restore('error_reporting');" to set it back to whatever it was before the # was used.
This was useful to me in the situation where I wanted to write a convenience function to check and see if a variable was set, and had some other properties as well, otherwise, return a default value. But, sending an unset variable through caused a PHP notice, so I used the # to suppress that, but then set error_reporting back to the original value inside the function.
Something like:
$var = #foo($bar);
function foo($test_var)
{
ini_restore('error_reporting');
if(is_set($test_var) && strlen($test_var))
{
return $test_var;
}
else
{
return -1;
}
}
So, in the case above, if $bar is not set, I won't get an error when I call foo() with a non-existent variable. However, I will get an error from within the function where I mistakenly typed is_set instead of isset.
This could be a useful option covering what the original question was asking in spirit, if not in actual fact.
If you simply add a default value to the parameter, you can skip it when calling the function. For example:
function empty($paramName = ""){
if(isset($paramName){
//Code here
}
else if(empty($paramName)){
//Code here
}
}
With a single line, you can acomplish it: myHappyFunction($someBogusVar="");
I hope this is what you are looking for. If you read the php documentation, under default argument values, you can see that assigning a default value to an function's argument helps you prevent an error message when using functions.
In this example you can see the difference of using a default argument and it's advantages:
PHP code:
<?php
function test1($argument)
{
echo $argument;
echo "\n";
}
function test2($argument="")
{
echo $argument;
echo "\n";
}
test1();
test1("Hello");
test1($argument);
$argument = "Hello world";
test1($argument);
test2();
test2("Hello");
test2($argument);
$argument = "Hello world";
test2($argument);
?>
Output for test1() lines:
Warning: Missing argument 1 for test1() .
Hello.
.
Hello world.
Output for test2() lines:
.
Hello.
Hello world.
This can also be used in combination to isset() and other functions to accomplish what you want.
And going further up the abstraction tree, what are you using this for?
You could either initialize those values in each class as appropriate or create a specific class containing all the default values and attributes, like:
class Configuration {
private var $configValues = array( 'cool' => 'Defaultcoolval' ,
'uncool' => 'Defuncoolval' );
public setCool($val) {
$this->configValues['cool'] = $val;
}
public getCool() {
return $this->configValues['cool'];
}
}
The idea being that, when using defaultValue function everywhere up and down in your code, it will become a maintenance nightmare whenever you have to change a value, looking for all the places where you've put a defaultValue call. And it'll also probably lead you to repeat yourself, violating DRY.
Whereas this is a single place to store all those default values. You might be tempted to avoid creating those setters and getters, but they also help in maintenance, in case it becomse pertinent to do some modification of outputs or validation of inputs.