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.
Related
I have this function that sometimes does not assign the variable $infolasterroredu
In these cases I want to tell the function to assign the value "Hello" if the variable is not assigned or is null.
I tried this way but it keeps returning the value "null" when the variable is not assigned.
How would I put a default value?
I've tried all the options and nothing seems to work.
private static function stockstatus(Product $product, $infolasterroredu = 'Hello')
{
//my function code
}
The result of this code is null when debugging, what am I doing wrong?
I call the function like this
self::stockstatus($product, $infolasterroredu);
I know this can be solved this way
self::stockstatus($product, $infolasterroredu ?: "Hello");
But this would take editing all the function calls which are several so I need to do it by direct function.
Note: When I say direct function, it means directly putting the value in the private static function and not editing the calls to the function line by line, which are too many.
By not having a type at all on the second argument, you're saying anything can be passed, but if nothing is passed (not even null), it's Hello. First, I would restrict the type if possible:
function stockstatus(Product $product, string $infolasterroredu = 'Hello') {}
This doesn't solve if '' (empty string) is passed in. You could "solve" this problem with a call-side default that passes in Hello if not present:
stockstatus($product, $infolasterroredu ?: 'Hello')
The problem I see with this is that it gives an internal concern for the function to some other caller, which makes the call more brittle in case the function itself changes.
Instead, I would set it up to allow a null switch but require a string, and default internally:
function stockstatus(Product $product, ?string $infolasterroredu = null) {
$infolasterroredu = $infolasterroredu ?: 'Hello';
...
}
I'm using ?: to catch any empty or nulled value and default it to Hello. You can put 'Hello' in the default in the argument expression (this may help with type assist). However, I prefer to have someone read the function if they don't know how to call it, and I'm not duplicating the actual default value twice (in the argument default and in the internal check).
One downside, if you will, is that this accepts any truthy string, even, say or .. The best practice way of handling this is to use a value object with validation on the setter (or even a default non-thrown exception) as the second argument.
Most probably, you pass two variable names as parameters when you call. You shouldn't put 2nd argument at all.
stockstatus($productInput)
You can check if the second argument is null or not before calling the function. If you call with second parameter stockstatus($productInput, $somethingNull), it would overwrite the default value even if it's null.
There are actually some ways you could do this.
First of all, just check before calling and create your default value before:
if (!$infolasterroredu) {
$infolasterroredu = 'Hello'
}
self::stockstatus($product, $infolasterroredu);
But whenever you call the function in different places this might not be what you want.
Second way is to check for the value inside the function. So kinda the same functionality as before only you move this inside the function 'stockstatus'.
The third way is the way you want to do it. By giving a default value in the parameter. As other already pointed out, you need to call the function with only one parameter, only then PHP will fill in the default value for the missing parameters.
I would suggest the second way though.
Is there any way to implement isset() in a setter method? That is, have the setter method check if the variable exists? From what I can tell no, but hopefully someone can confirm that for me. In a nutshell, I'm looking to avoid having to do
if (isset($arr[0])) $foo->setId($arr[0])
and simply just do
$foo->setId($arr[0])
and somehow implement the isset() logic in the setter method. Thanks!
No, you not can subscript a missing array element and expect it to work. The subscript will be evaluated before being sent to the method. This means the subscript will fail before the method even receives the argument.
Example
$arr = array(0 => 'a', 1 => 'b', 2 => 'c');
If I do $foo->setId($arr[0]) then $foo->setId() will receive a as a string, and it will never know it was subscripted from an array (not does it want to know).
So a $foo->setId($arr[3]) would give an error...
Undefined offset
No.
You're only passing the value of $arr[0] to the setter method, not the $arr[0] variable. $foo::setId has no idea of the existence of $arr or $arr[0], all it gets to see is its value.
And that's a good thing for encapsulation! Imagine if every function would have to check if its parameters actually existed:
function setId($id) {
// $id is defined, it's required (has no default value),
// and yet I can't be sure it actually exists here...?
}
You'll have to check if the variable exists before passing its value to a function.
First, read #alex's answer. (Edit: Read #deceze's answer, too)
In order to mimic what you want you can pass the array and the index separately:
$foo->setId($arr, 0);
And change the setId() method's signature (or add a safeSetId()):
function safeSetId($array, $index) {
if (isset($array[$index])) {
$this->setId($array[$index]);
}
}
This is not very pretty, though :)
Isset is not a function: it's a language construct. Language constructs are allowed to do magic; user-defined functions cant.
There is no way to reimplement the isset logic without isset itself (without modifications to PHP's source code, at least). If you try to implement it inside a function, the argument you receive will be null if the variable doesn't exist and a E_NOTICE error will be raised before your function is even called.
While you can't achieve what you're looking to do for reasons already given, you could still make the code more terse for very little overhead.
If the setId() function can handle null or if it is simply setting a member variable (which will by default be initialized to null), you could write a simple utility function like this:
function getValue($array, $key, $default = null) {
return isset($array[$key]) ? $array[$key] : $default;
}
and then call your setter like so:
$foo->setId(getValue($arr,0));
Again, there are drawbacks due to increased overhead and potential errors if your setter method expects a non-null value but this could decrease the amount of code slightly.
I've seen the following a few times lately:
function foo(array $arg = NULL) { ... }
My question being why make the default of $arg NULL when it will be just cast into an array? Why not do:
function foo(array $arg = array()) { ... }
I know it doesn't really make much difference--it's mostly just reading code--but why encourage PHP to be changing data types all the time.
I've seen this a lot in Kohana.
The real question is why create an array when you don't need one.
If you use $arg = array(), there will be a specific instruction to create an array, even if it's PHP an instruction still consumes CPU cycles. If you just do $arg = NULL, then nothing will be done.
My guess is that they want an array for a parameter, but a NULL value for the parameter is acceptible (and will be used by default if an array -- or an explicit NULL -- isn't given).
Note that the array specification in the function signature is not about casting, but is a type hint. It says that only arrays are accepted for $arg.
=NULL is a way to allow the argument to have a value of NULL:
function foo(array $arg = NULL) { ... }
allows the execution of foo($bar); in the case that $bar === NULL.
function foo(array $arg = array()) { ... }
will trigger an error if $bar === NULL, since NULL is not of hinted type, array.
Take a look at the PHP type hinting page.
This is useful if:
$bar = NULL;
// Do stuff that may involve $bar
// $bar may be an array, or it may still be NULL
foo($bar);
// For the above line to not trigger an error if $bar === NULL, you must use
function foo(array $arg = NULL) { ... }
You get this error unless you do this.
The reason is probably to distinguish between an empty set of objects to process, and slightly different functionality when nothing is put in.
As a hypothetical example, let's say you have a function that creates reports for users. The input array contains the IDs (or even objects) of the users for which the reports are to be generated. The same function might as well be used to process the reports when you need to process all of the users, as opposed to a certain set of them. When you only want to process for specific users, you would throw in the array. But if you wanted to process all of them, then it makes sense that the parameter is a distinct NULL instead of a "no users", which would be an empty array.
For example, let's say that there's a page where an administrator can designate which users to create reports for. But the admin presses the "Create Reports" button without selecting any users, in which case an empty array would get thrown into the function, and the function would accurately process no users, because there are no users in the array.
Then perhaps you might have a different button somewhere else in this hypothetical system, "Create All Reports", in which case you wouldn't throw anything in, and the function would be able to tell the difference between "number of users = 0" and "users not provided", which in this case would mean "all users".
That's one example. In general, I use NULL as a default parameter to be able to distinguish within the function whether something was passed or not, because the behavior of the function might be different when nothing in particular is specified, as per the example given above.
This is a couple of years old I know, but I can't help but say the wrong answer was chosen and nobody clearly answered the full question. So for others that are looking at this and want to know what's really going on here:
function foo(array $arg = NULL) { ... }
When you call foo() the first thing that will happen is PHP will test $arg to see if it's an array. That's right, array $arg is a test. If $arg is not an array you will see this error:
If $arg is a string:
Catchable fatal error: Argument 1 passed to foo() must be an array, string given
Ok, then NULL just means that in case $arg is not passed we will set it to NULL. It's just a way to make $arg optional, or preset its value.
And just to be clear, there's no such thing as type casting function parameters without using the RFC patch, here.
If you use the RFC patch you can do:
function foo((array)$arg){ ... }
maybe if your foo function has to do something when the function is called explicitly with an empty array.. i mean:
if
foo(array());
doesn't do the same thing than
foo();
then you'd have to use the $arg = NULL
I'm using someone elses class and that person has defined a function with five arguments.
in Sentry.php:
function checkLogin($user = '',$pass = '',$group = 10,$goodRedirect = '',$badRedirect = '')
If all five fields are filled in this leads to a login procedure.
Now on the page where he explains how to use this there is a snippet which, according to php.net, doesn't make sense.
in a page that loads the sentry:
require_once('../system/Sentry.php');
$theSentry = new Sentry();
if(!$theSentry->checkLogin(2)){ header("Location: login.php"); die(); }
which by default should behave as such that it checks if the $group argument is <= 10 (default). In this situation it should be two. If the user checked has a group-variable of <=2 this should enable the person to view the page.
However, this doesn't work and for a very obvious reason: the php manual states:
Note that when using default
arguments, any defaults should be on
the right side of any non-default
arguments; otherwise, things will not
work as expected.
So the code, according to phpbuilder.com should have no optional ($variable = default_something) field to fill in with a call to the function and it definitely shouldn't be defined as the third of five arguments.
How can I use the function like this?:
checkLogin(2)
Default arguments are PHP's way of dealing with the lack of overloaded functions. In Java you can write this:
public void login(String username)
public void login(String username, String password)
In PHP you have to solve it like this:
function login($username, $password = '')
This way $username is mandatory and $password is optional. While this may be convenient, it isn't always. In your example there are a log of arguments and they are all optional. A clean solution for this would be to make 1 function that does the work and add 'convenience' methods to make the interface cleaner.
So add:
function checkLoginGroup($group = 10) {
$this->checkLogin('', '', $group);
}
It simply calls the function that already exists, but it allows for a cleaner interface, just call:
$theSentry->checkLoginGroup(2);
It's even neater to make the provided method private (or protected, depending on your needs) and create public 'convience' methods so you can hide the implementation details.
However, if you can not or don't want to change the original class, you might be able to create a subclass.
Changing the order of the arguments is a possibility... But what for the time you'll need another argument to be "more optionnal" or "less optionnal" ? Will you change that order again ?
And, with it, every call that's made to the function ?
To pass only the third argument, considering your function's declaration, the only method I see is to give the three first arguments... Passing the default value (which you'll have to know, of course -- just look at it's declaration, for that) for the first two :
checkLogin('', '', 20);
In my opinion, using this instead of that one :
checkLogin(null, null, 20);
Has the advantage of being explicit : someone looking at the function's declaration will immediatly notice you're using the default values for the first two parameters.
Using NULL, the personn reading your code would have to check inside the function's code to see if NULL is handled in a special way (it could, afterall !) ; it wouldn't be as easy for someone to understand that this is your way of passing default values... As they are not the default values ^^
Other solutions would imply refactoring the function,
either to pass only one array as parameter (with its keys not being required by the code)
ot using Variable-length argument lists
Either way, you'd lose the ability your IDE has to show you the parameters the function is waiting for ; and that's bad :-(
And you'd also lose the phpdoc...
In addition to Mythicas excellent answer, I just want to point out another way of dealing with a lot of optional arguments: Arrays.
function checkLogin($args) {
$defaults = array('user' => null, 'pass' => null, 'group' => null, ...);
$args = array_merge($defaults, $args);
...
}
checkLogin(array('user' => 'x', 'group' => 9));
This essentially bypasses PHPs optional/required argument syntax entirely and instead custom-handles these things internally. It has the advantage of avoiding the problem you describe and making the function call extremely readable. The problem is that it makes the function more complex and you can't take advantage of PHP checking the existence of arguments for you.
The easiest way is just to change the order of arguments so that $group is first.
checkLogin(NULL,NULL,2);
but honestly bad coding style.
Named function parameters can be emulated in PHP if I write functions like this
function pythonic(array $kwargs)
{
extract($kwargs);
// .. rest of the function body
}
// if params are optional or default values are required
function pythonic(array $kwargs = array('name'=>'Jon skeet'))
{
extract($kwargs);
// .. rest of the function body
}
Apart from losing intellisense in IDEs what are the other possible downsides of this approach?
Edit:
Security: Shouldn't security be a non-issue in this case, as the extracted variables are limited to function scope?
I would suggest using the associative array to pass named parameters, but keep them in the array without extracting them.
function myFunc(array $args) {
echo "Hi, " . $args['name'];
// etc
}
There's a couple of reasons for this. Looking at that function, you can quite clearly see that I'm referring to one of the arguments passed into the function. If you extract them, and don't notice the extract() you (or the next guy) will be there scratching your head wondering where this "$name" variable came from. Even if you do know you're extracting the arguments to local variables, it's still a guessing game to a certain degree.
Secondly, it ensures that other code doesn't overwrite the args. You may have written your function expecting only to have arguments named $foo and $bar, so in your other code, you define $baz = 8;, for example. Later on, you might want to expand your function to take a new parameter called "baz" but forget to change your other variables, so no matter what gets passed in the arguments, $baz will always be set to 8.
There are some benefits to using the array too (these apply equally to the methods of extracting or leaving in the array): you can set up a variable at the top of each function called $defaults:
function myFunc (array $args) {
$default = array(
"name" => "John Doe",
"age" => "30"
);
// overwrite all the defaults with the arguments
$args = array_merge($defaults, $args);
// you *could* extract($args) here if you want
echo "Name: " . $args['name'] . ", Age: " . $args['age'];
}
myFunc(array("age" => 25)); // "Name: John Doe, Age: 25"
You could even remove all items from $args which don't have a corresponding $default value. This way you know exactly which variables you have.
Here's another way you could do this.
/**
* Constructor.
*
* #named string 'algorithm'
* #named string 'mode'
* #named string 'key'
*/
public function __construct(array $parameter = array())
{
$algorithm = 'tripledes';
$mode = 'ecb';
$key = null;
extract($parameter, EXTR_IF_EXISTS);
//...
}
With this set up, you get default params, you don't lose intellisense in IDEs and EXTR_IF_EXISTS makes it secure by just extracting array keys that are already existing as variables.
(By the way, creating default values from the example you provided is not good, because if an array of param is provided without a 'name' index, your default value is lost.)
In my experience, this approach is really only beneficial if one of two things is true
For whatever extenuating reasons, your argument signature is big. I kinda go by 6 as a maximum - not for any specific reason though just seems right - but I freely admit that this number is arbitrary.
All or many of your arguments are optional, and sometimes you only need to set a value for the 5th one or some such thing. It's annoying to write someFunc( null, null, null, null, 1 );
If either of these is true for you, faking named params with an associative array might be the right implementation. Aside from knowing when to avoid extract (or avoiding it altogether) I can't immediately think of other downsides.
That being said, oftentimes both of these problems can be solved through refactoring as well.
In my experience, the downside of this method is more code to write.
consider something like this:
function someFunc($requiredArg, $arg1 = "default11", $arg2 = "default2") {
To simulate this behavior when passing everything in an array you'll need to write more code, and the "function signature" will be less "clear and obvious".
function someFunc($requiredArg, $optionalArgs) {
// see other answers for good ways to simulate "named parameters" here
I'm wondering if it would be a good idea for PHP to address that in a future release, maybe have something like Pascal or VB syntax available for function arguments.
Anyway, I only pass parameters in a single array when really needed - like functions that have a parameter set that is likely to change a lot during development. These functions usually also have numerous parameters.
Others have alreay answered your other points, I would just like to comment on security aspect.
Security: Shouldn't security be a non-issue in this case, as the
extracted variables are limited to function scope?
Yes and no. The code you have written could (depends on whether you always initialize your variables after this call) overwrite your vars. Example:
function pythonic(array $kwargs = array('name'=>'Jon skeet'))
{
$is_admin = check_if_is_admin(); // initialize some variable...
extract($kwargs);
// Q: what is the value of $is_admin now?
// A: Depends on how this function was called...
// hint: pythonic([ 'is_admin' => true ])
}
What makes this code "kind-of-secure" is that YOU are the one who is calling it - so user can't supply arbitrary parameters (unless you redirect POST vars there, of course ;).
As a rule of thumb you should avoid such magic. The line with extract() could have unintended side effects, so you should not use it. Actually, I can't think of a legitimate use of extract() function in any application (I don't think I have ever used it myself).