What is the proper way to set default method parameter in PHP? - php

Let's say you have a method that expects a numerical value as an argument. What's the proper way to set a default value in the event the argument doesn't exist?
public function saveme($myvar = false) {}
public function saveme($myvar = '') {}
public function saveme($myvar = NULL) {}
public function saveme($myvar = 0) {}

All of those are valid, and neither one is "more correct" for all cases. It depends entirely on what the valid range of values are for the function and what the function is supposed to do. Typically, the default type will match the expected input type, unless you want a place-holder for "no value" in which case null is typically used.
If your function can perform something useful with default of 0, use 0. If your function needs to tell the difference between having been explicitly given a single argument of 0 and having been given no arguments, or if 0 is not a sane default, use null.

From the PHP manual:
function myFunc ($myType = 0) {}

Related

Arguments and type declarations using "?" for nullable values

PHP Version 7.3.8
References:
https://www.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration
I've been reading this sentence on the above page:
As of PHP 7.1.0, return values can be marked as nullable by prefixing the type name with a question mark (?). This signifies that the function returns either the specified type or NULL.
The following values were passed and the results are documented here:
$marketID = 'abcdef'; // Result is: Throws a type error.
$marketID = '12345'; // Result is: It will be cast as an int.
$marketID = 12345; // Result is: It will successfully execute.
$marketID = null; // Result is: It will successfully execute.
// App controller
protected function setMarketID(?int $marketID)
{
$this->marketID = $marketID;
return $this;
}
protected function getMarketID()
{
// Will return an int or null.
return $this->marketID;
}
Is it considered acceptable to code like this. IE: Use (?) in this way to accept type and null as the manual states return values can be marked... NOT incoming values but it works? (Please see Edit)
Edit for anyone in the future reading this post:
// Will return an int or null.
If you pass an int argument to the setter the getter will automatically return an int and if you pass a null argument to the setter the getter will automatically return null.
#yivi
Yes very helpful thanks. I have added strict types and it works perfectly and I have also added your advice of declaring a return value. IE:
protected function getMarketId():?int
And again works perfectly.
You haven't declared a single return type in your example.
In setMarketId() you declared a parameter type (?int). So this method will accept either an integer or null.
If you declare you are using strict_types, then the method will not even accept '12345' or 123.5, and only accept a proper int or a null value.
Declaring a return value like the one that you expect (and consistent with what getMarketId() would return), would be accomplished like this:
protected function getMarketId():?int
{
return $this-marketId;
}
Of course it is "acceptable" to declare a type is nullable. When it makes sense or not it will depend entirely on your application, but it's a very common usage.

What is the difference between ?int $number and int $number = null?

Given this explanation
Nullable types: Type declarations for parameters and return values can now be marked as nullable by prefixing the type name with a question mark. This signifies that as well as the specified type, NULL can be passed as an argument, or returned as a value, respectively.
https://www.php.net/manual/en/migration71.new-features.php
The following code :
public function test(?int $var) {
}
Means that test() can be called with $var either as int or as null.
And the following code :
public function test(int $var = null) {
}
Means that test() can be called with $var either as int or as null as well.
What are the differences between those two methods ? Is any of these more performant than the other?
It is important to distinguish between the two language features being discussed here, that is, type declarations and default argument values.
The first function is only using type declarations, this means that the input argument has to be of the type int or NULL.
The second function is using both type declarations and default argument values, this means that the argument has to be of the type int or NULL but if omitted it will default to NULL.
Take your first function, if you simply called test() without passing anything to it, you'd get:
PHP Fatal error: Uncaught ArgumentCountError: Too few arguments to function test() [...]
which is correct as the function expects either int or NULL but got neither whereas for the second, as you have defined the argument with a default value, it would run without errors.
Code
function test(?int $var) {
var_dump($var);
}
function test2(int $var = null) {
var_dump($var);
}
test(1); // fine
test(); // error
test2(1); // fine
test2(); // fine
As far as performance goes, the difference is probably negligible, nothing significant enough that would be a cause for concern.
Live Example
Repl
If the language were designed today, int $var = null would probably be an error, and should really be written ?int $var = null. The two parts mean different things:
The ? indicates that null is a valid value for that parameter.
The = null indicates that null is the default if the parameter is not passed.
However, before the ?type syntax was introduced, there was a special case in the language: if null is given as the default for a parameter, then it is legal to pass null to that parameter, even if the type declaration would otherwise prevent it.
The difference is how you can call the function:
// public function test(?int $var)
$foo->test("x"); // does not work (Argument 1 passed to Foo::test() must be of the type int or null, string given)
$foo->test(123); // works
$foo->test(null); // works
$foo->test(); // does not work (Too few arguments to function Foo::test(), 0 passed)
// public function test(int $var = null)
$foo->test("x"); // does not work (Argument 1 passed to Foo::test() must be of the type int or null, string given)
$foo->test(123); // works
$foo->test(null); // works
$foo->test(); // works
The difference is that you cannot call the function as ->test() using the first syntax.

How to assign a closure's return value to a variable by-reference, even if the closure returns by-value?

I have a method which gets a callable as an argument. The callable is called with some arguments which can be gotten by-value or by-reference, and it may return a value, either by-value or by-reference, or not. In the case of returning a value, it must be returned from the closure by the method as exactly as is. If the value is by-reference, the method must return it by-reference, and vice versa.
The problem is that there is no way to determine whether the value is returned by-reference or not. Yes, it can be detected whether the closure returns any values or not; but when I try to get the reference of a closure's return value which is returned by-value, I will get the following notice:
PHP Notice: Only variables should be assigned by reference in ... on line xxx
Again, the method returns the closure's return value as is (i.e. dynamically). This is what I try to achieve:
class Example
{
protected $data = 15;
// Pay attention to this, returning by-reference
protected function &doOnData(callable $function)
{
return $function($this->data);
}
public function increment(): void
{
// No notices, because the closure is defined to return by-reference
$data = &$this->doOnData(function &(&$data) {
return $data;
});
$data++;
}
public function get()
{
// Notice, why you defined the closure to return by-value?
// Defining the closure to return by-reference fixes the problem
// temporarily (i.e. defining as: function &($data))
return $this->doOnData(function ($data) {
return $data;
});
}
public function printData()
{
echo $this->data;
}
}
$example = new Example();
$example->increment();
$example->printData(); // Prints: 16
$data = $example->get(); // Notice!
echo $data; // Prints: 16
As you see in the example, the notice will only be produced when you pass a returning-by-value closure to Example::doOnData(), and you cannot get its return value reference. In this case, the above notice will be generated (should it be generated? I don't know!).
A solution might be using the # (at sign) operator, which is stupidly bad (see: 1 and 2). Saving the reference of the closure to a variable, and then returning the variable doesn't change anything (AFAIK). Also, adding an ampersand (&) before the closure inside Example::get(), as described in code comments, is not a solution for that, and have some side effects (e.g. every closure must be defined to return by-reference to prevent the notice, too bad, the user doesn't know that). So, please don't suggest these things!
Is there any way to prevent the notice? Am I doing something wrong? Generally speaking, can I determine whether the value is returned by-value or by-reference (i.e. something like is_reference())? Is there any other solutions to get it done?

adding default parameters of the function as result of other function

Hi I have question it is possible to adding default parameters of the function as result of other function? For example:
static function addParameter(){
return rand(10,100)
}
function doSomething($year=$this->addParameter()) or
function doSomething($year=class::addParameter())
I need to pass actual year and month to my function. When i pass
function($month=3, $year=2016)
it work then but i not want to write this from hand but want to use function or something to always return actual month and year.
Short answer: no, you can't.
http://php.net/manual/en/functions.arguments.php states that:
The default value must be a constant expression, not (for example) a variable, a class member or a function call.
You can use default null value (if normally function does not take one) and check if parameter is null, then get value from function:
function testfun($testparam = null) {
if ($testparam == null) $testparam = myclass::funToReturnParam();
// Rest of function body
}
Yo can implement it like this
<?php
class Core {
static function addParameter(){
return rand(10,100);
}
}
$core = new Core();
function doSomething($rand){
echo 'this is rand '.$rand;
}
doSomething(Core::addParameter());
You can change the function defination according to your requirement.
hope it helps :)
From the PHP Manual (emphasis mine):
The default value must be a constant expression, not (for example) a variable, a class member or a function call.
So only scalar types(boolean, int, string etc), arrays and null are allowed.
A workaround could be to check if the parameter is null and set it within the function:
function doSomething($year = null) {
if (!$year) {
$year = addParameter();
}
//actually do something
}

How to make function which can handle with diffetent number of parameters with php?

I want to edit user's information. User have a attributes:
User:
name
password
enabled
group
It's fine if i edit all attributes at once - i just get 4 parameters from POST and use function for save changes:
function editUser($oldname,$newname,$password,$enabled,$group){
//**some code**//
$mgroup->getMember()->setName($newname);
$mgroup->getMember()->setPassword($password);
$mgroup->getMember()->setEnabled($enabled);
$mgroup->getMember()->setGroup($group);
//**some code**//
}
But if i am editing just a one or two parameters i cant use my function.
So how can i change function depending on the number of parameters?
For example i edit just pass and enabled attributes, for this my function gonna do:
function editUser($oldname,$password,$enabled){
//**some code**//
$mgroup->getMember()->setPassword($password);
$mgroup->getMember()->setEnabled($enabled);
//**some code**//
}
It's possible to do?
In such cases you usually just pass NULL as argument for the fields where you don't change anything and verify in your setter function:
if ($val !== NULL) {
$this->property = $val; // set it!
}
If you have really a lot of arguments; I'd pass an array to improve readability:
editUser(array('oldname' => …, 'newname' => …, …));
And change the function to:
function editUser ($array) {
if (isset($array['newname']))
$mgroup->getMember()->setName($array['newname']);
// …
}
Try with
function editUser($oldname,$newname,$password,$enabled = true,$group = DEFAULT_GRP_ID)
You should be abled to call editUser with 3, 4 or 5 parameters. PHP isn't like java where you need to redeclare methods.
D.
You can do it like so:
function editUser($oldname,$newname,$password,$enabled,$group){
if($newname != null){
$mgroup->getMember()->setName($newname);
}
//rinse and repeat for all others
}
Then when you call the function pass null for values you don't want to change.
editUser("oldName", "newName", null, null, "group");
why don't you use associated array like array('oldname'=>$oldname,.....) then use value which is not null
Can you define default values for each variable in your function?
function editUser($oldname='',$newname='',$password='',$enabled='',$group='')
{
if !empty($oldname){do something}
....
}
A simple solution to this is to give your arguments default values, check if they're null inside the function and only deal with them if they're not.
function editUser($oldname, $password, $enabled = null, $newname = null, etc.){
//**some code**//
$mgroup->getMember()->setName($newname);
$mgroup->getMember()->setPassword($password);
if ($newname != null)
$mgroup->getMember()->setEnabled($enabled);
if ($group != null)
$mgroup->getMember()->setGroup($group);
//**some code**//
}
One drawback with this method is that you cannot leave $enabled = null implicitly and still set $newname = something explicitly (i.e. you need to call editUser($oldname, $password, null, $newname, etc.), which is both hard to read and maintain when the number of arguments grow. A solution to this is to use named variables. Since PHP does not directly supported named variables, the manual suggests passing an associative array as argument:
function editUser($args) {
$member = $mgroup->getMember();
$member->setName($args["newname"]);
$member->setPassword($args["password"]);
if (isset($args["enabled"]))
$member->setEnabled($args["enabled"]);
...
}

Categories