Is there a way in PHP to use a function which has optional parameters in its declaration where I do not have to pass an optional arguments which already have values declared and just pass the next argument(s) which have different values that are further down the parameter list.
Assuming I have a function that has 4 arguments, 2 mandatory, 2 optional. I don't want to use null values for the optional arguments. In usage, there are cases where I want to use the function and the value of the 3rd argument is the same as the default value but the value of the 4th argument is different.
I am looking for a not so verbose solution that allows me to just pass the argument that differs from the default value without considering the order in the function declaration.
createUrl($host, $path, $protocol='http', $port = 80) {
//doSomething
return $protocol.'://'.$host.':'.$port.'/'.$path;
}
I find myself repeating declaring variables so that I could use a function i.e to use $port, I redeclare $protocol with the default value outside the function scope i.e
$protocol = "http";
$port = 8080;
Is there any way to pass the 2nd optional parameter($port) without passing $protocol and it would "automatically" fill in the default value of $protocol i.e
getHttpUrl($server, $path, $port);
This is possible in some languages like Dart in the form of Named Optional parameters.See usage in this SO thread. Is their a similar solution in PHP
You could potentially use a variadic function for this.
Example:
<?php
function myFunc(...$args){
$sum = 0;
foreach ($args as $arg) {
$sum += $arg;
}
return $sum;
}
Documentation:
http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
PHP doesn't allow at this state to call functions parameters in the order we want.Maybe in the future it will.However you can easily achieve your purpose by using an associative array as the only argument, and then define, the default parameter in the function.For the call you will need to pass an array with only the values which interest you.This array will be merged with the default array.You can even implement required parameters and call them in any order you want.
example:
function mysweetcode($argument){
$required=['first'];//specify required parameters here
$default=['first'=>0,'second'=>1,'third'=>2];//define all parameters with their default values here
$missing=[];
if(!is_array($argument)) return false;
$argument=array_intersect_key($argument,$default);
foreach($required as $k=>$v){//check for missing required parameters
if(!isset($argument[$v]))
$missing[]=$v;
}
if(!empty($missing)){// if required are missing trigger or throw error according to the PHP version
$cm=count($missing);
if (version_compare(PHP_VERSION, '7.0.0') < 0) {
trigger_error(call_user_func_array('sprintf',
array_merge(array('Required '.(($cm>1)?'parameters:':'parameter:').
str_repeat('%s,',$cm).(($cm>1)?' are':' is').' missing'),$missing)),
E_USER_ERROR);
}else{
throw new Error(call_user_func_array('sprintf',array_merge(
array('Required '.(($cm>1)?'parameters:':'parameter:').
str_repeat('%s',$cm).(($cm>1)?' are':' is').' missing'),$missing)));
}
}
$default=array_merge($default,$argument);//assign given values to parameters
extract($default);/*extract the parameters to allow further checking
and other operations in the function or method*/
unset($required,$missing,$argument,$default,$k,$v);//gain some space
//then you can use $first,$second,$third in your code
return $first+$second+$third;
}
var_dump(mysweetcode(['first'=>9,'third'=>8]));//the output is 18
var_dump(mysweetcode(['third'=>8]));//this throws Error on PHP7 and trigger fatal error on PHP5
You can check a live working code here
Well, this should work:
function myFunc($arg1, $arg2, $arg3=null, $arg4= null){
if ( is_null( $arg3 ) && is_null( $arg4 ) {
$arg3 = 3;
$arg4 = 4;
} else if ( is_null( $arg4 ) ) {
$arg4 = $arg3;
$arg3 = 3;
}
echo $arg1 + $arg2 + $arg3 + $arg4;
}
However I suggest you to rethink your problem (as a whole) because this is not a very good idea.
You could refactor this to use a parameter object; this way, you could include the default parameters in this object and set them in any order (with a trade-off of more verbose code). As an example using your above code,
<?php
class AdditionParameters
{
private $arg1 = 0;
private $arg2 = 0;
private $arg3 = 3;
private $arg4 = 4;
public function getArg1() { return $this->arg1; }
public function getArg2() { return $this->arg2; }
public function getArg3() { return $this->arg3; }
public function getArg4() { return $this->arg4; }
public function setArg1($value) { $this->arg1 = $value; return $this; }
public function setArg2($value) { $this->arg2 = $value; return $this; }
public function setArg3($value) { $this->arg3 = $value; return $this; }
public function setArg4($value) { $this->arg4 = $value; return $this; }
}
From there, you could simply call the function while passing in this new object.
function myFunc(AdditionParameters $request) {
return $request->getArg1()
+ $request->getArg2()
+ $request->getArg3()
+ $request->getArg4();
}
echo myFunc((new AdditionParameters)->setArg1(1)->setArg2(2)->setArg4(6));
// or echo myFunc((new AdditionParameters)->setArg1(1)->setArg4(6)->setArg2(2));
Otherwise, PHP doesn't allow you to have named optional parameters. (e.g. myFunc(1, 2, DEFAULT, 4);)
You have the response in your question, you can declare your function like
function myFunc($arg1, $arg2, $arg3 = null, $arg4 = null){
//here you check if the $arg3 and $arg4 are null
}
then you call your function using
myFunc($arg1, $arg2);
There is no such way in PHP(like in python for example).
You have to use some tricks in order to do that but will not always work.
For example:
// creates instances of a class with $properties.
// if $times is bigger than 1 an array of instances will be returned instead.(this is just an example function)
function getInstance($class, $properties = [], $times = 1){
//my code
}
$user = getInstance("User", ["name" => "John"]); // get one instance
$users = getInstance("User", ["name" => "John"],2); // get two instances.
If you want to use the function without passing the $parameters argument, like this:
$users = getInstance("User",2);
you can change the function to:
// creates instances of a class with $properties.
// if times is bigger than 1 an array of instances will be returned instead.
function getInstance($class, $properties = [], $times = 1){
if(is_numberic($properties)){
$times = $properties;
$properties = [];
}
//my code
}
Of course, this strategy will work only if you parameters have different types.
PS. This method is use in the Laravel Framework a lot. From there I got the inspiration.
This is modified from one of the answers and allows arguments to be added in any order using associative arrays for the optional arguments
function createUrl($host, $path, $argument = []){
$optionalArgs = [
'protocol'=>'http',
'port'=>80];
if( !is_array ($argument) ) return false;
$argument = array_intersect_key($argument,$optionalArgs);
$optionalArgs = array_merge($optionalArgs,$argument);
extract($optionalArgs);
return $protocol.'://'.$host.':'.$port.'/'.$path;
}
//No arguments with function call
echo createUrl ("www.example.com",'no-arguments');
// returns http://www.example.com:80/no-arguments
$argList=['port'=>9000];
//using port argument only
echo createUrl ("www.example.com",'one-args', $argList);
//returns http://www.example.com:9000/one-args
//Use of both parameters as arguments. Order does not matter
$argList2 = ['port'=>8080,'protocol'=>'ftp'];
echo createUrl ("www.example.com",'two-args-no-order', $argList2);
//returns ftp://www.example.com:8080/two-args-no-order
As of version 8.0, PHP now has named arguments. If you name the arguments when calling the function, you can pass them in any order and you can skip earlier default values without having to explicitly pass a value for them.
For example:
function createUrl($host, $path, $protocol = 'http', $port = 80)
{
return "$protocol://$host:$port/$path";
}
createUrl(host: 'example.com', path: 'foo/bar', port: 8080);
// returns: "http://example.com:8080/foo/bar"
Related
How may I use this function and inform only the first and the last arguments?
Function
function foo($first = false, $second = false, $third = false, $last = false)
{
if($first && $last)
{
echo 'ok';
}
}
I've tried the code below, but it didn't work...
foo($first = true, $last = true);
PHP doesn't do named arguments as python does. See this question for more info.
However, life can be made easier by using other techniques like...
Modify the signature of the function to accept arguments as an associative array
Function
function foo($parameters)
{
// Provide default values if parameters are not specified
$first = isset($parameters['first']) ? $parameters['first'] : false;
$second = isset($parameters['second']) ? $parameters['second'] : false;
$third = isset($parameters['third']) ? $parameters['third'] : false;
$last = isset($parameters['last']) ? $parameters['last'] : false;
if($first && $last)
{
echo 'ok';
}
}
Call
foo(['first' => true, 'last' => true]);
This way is suitable when you have a number of parameters big and variative enough and you have a complex logic inside the function so that writing all this code pays off.
It is not very convenient, however, because the default values are specified not in an obvious way, there's extra code and it's hard to track parameter usages.
Modify the signature of the function to accept a parameter object which holds all the necessary info
This is the way to go for complex signatures and especially if you have a cascade of methods which use the same arguments. I love it because it solved a big problem with passing up to 10 query parameters through processing pipeline. Now it's just one object with possibility to find every parameter usage and friendly autosuggestion of available parameters when typing ->.
Parameter object class
class ParameterObject
{
public $first = false;
public $second = false;
public $third = false;
public $last = false;
}
Function
function foo(ParameterObject $paramObj)
{
if($paramObj->first && $paramObj->last)
{
echo 'ok';
}
}
Call
$paramObj = new ParameterObject();
$paramObj->first = true;
$paramObj->last = true;
foo($paramObj);
Note! You can modify the object to use method for setting parameters which will provide you with possibility of chaining if you return $this in every set method. So the function call would like like this:
$paramObj = new ParameterObject();
foo($paramObj->setFirst(true)->setSecond(true));
maybe
foo(true, false, false, true);
or change the position of arguments like
function foo($first, $last, $second=false, $third=false)
foo(true, true);
?
if you want to use last argument of function in php you must enter all argument before it and you can't use name of arguments when call functions. in some language like swift can call function with name of argument but not in php
I'm trying to write a simple function which takes two arguments, adds them together and returns the result of the calculation.
Before performing the calculation the function checks whether either of the two arguments are undefined and if so, sets the argument to 0.
Here's my function:
Function - PHP
function returnZeroAdd ($arg, $arg2)
{
if(!isset($arg))
{
$arg = 0;
}
if(!isset($arg2))
{
$arg2 = 0;
}
echo $arg + $arg2;
}
I've tried to execute it like so :
returnZeroAdd($bawtryReturnCount, $bawtryFReturnCount);
But this throws up an undefined variable $bawtryFReturnCount error.
I do not know why the function isn't setting $bawtryFReturnCount) to 0 before performing the calculation thereby negating the 'undefined variable' error.
Can anybody provide a solution?
You cannot do this the way you want. As soon as you use an undefined variable, you will get this error. So the error doesn't occur inside your function, but already occurs in the call to your function.
1. Optional parameters
You might make a parameter optional, like so:
function returnZeroAdd ($arg = 0, $arg2 = 0)
{
return $arg + $arg2;
}
This way, the parameter is optional, and you can call the function like this:
echo returnZeroAdd(); // 0
echo returnZeroAdd(1); // 1
echo returnZeroAdd(1, 1); // 2
2. By reference
But I'm not sure if that is what you want. This call will still fail:
echo returnZeroAdd($undefinedVariable);
That can be solved by passing the variables by reference. You can then check if the values are set and if so, use them in the addition.
<?php
function returnZeroAdd (&$arg, &$arg2)
{
$result = 0;
if(isset($arg))
{
$result += $arg;
}
if(isset($arg2))
{
$result += $arg2;
}
return $result;
}
echo returnZeroAdd($x, $y);
Note that you will actually change the original value of a by reference parameter, if you change it in the function. That's why I changed the code in such a way that the parameters themselves are not modified. Look at this simplified example to see what I mean:
<?php
function example(&$arg)
{
if(!isset($arg))
{
$arg = 0;
}
return $arg;
}
echo example($x); // 0
echo $x // also 0
Of course that might be your intention. If so, you can safely set $arg and $arg2 to 0 inside the function.
The error is not thrown by the function itself, as the function is not aware of the global scope. The error is thrown before even the function is executed, while the PHP interperter is trying to pass $bawtryFReturnCount to the function, one does not find it, and throws error, however, it's not a fatal one and the execution is not stopped. THerefore, the function is executed with a non-set variable with default value of null, where I guess, isset will not work, as the arguments are mandatory, but not optional. A better check here will be empty($arg), however the error will still be present.
Because the functions are not and SHOULD NOT be aware of the global state of your application, you should do these checks from outside the functions and then call it.
if (!isset($bawtryReturnCount)) {
$bawtryReturnCount = 0
}
returnZeroAdd($bawtryReturnCount);
Or assign default values to the arguments in the function, making them optional instead of mandatory.
Your function could be rewritten as:
function returnZeroAdd ($arg = 0, $arg2 = 0)
{
echo $arg + $arg2;
}
You missunderstand how variables work. Since $bawtryFReturnCount isn't defined when you call the function; you get a warning. Your isset-checks performs the checks too late. Example:
$bawtryReturnCount = 4;
$bawtryFReturnCount = 0;
returnZeroAdd($bawtryReturnCount, $bawtryFReturnCount);
Will not result in an error.
If you really want to make the check inside the function you could pass the arguments by reference:
function returnZeroAdd (&$arg, &$arg2)
{
if(!isset($arg))
{
$arg = 0;
}
if(!isset($arg2))
{
$arg2 = 0;
}
echo $arg + $arg2;
}
However this will potentially modify your arguments outside the function, if it is not what you intend to do then you need this:
function returnZeroAdd (&$arg, &$arg2)
{
if(!isset($arg))
{
$localArg = 0;
}
else
{
$localArg = $arg;
}
if(!isset($arg2))
{
$localArg2 = 0;
}
else
{
$localArg2 = $arg2;
}
echo $localArg + $localArg2;
}
You can now pass undefined variables, it won't throw any error.
Alternatively you might want to give a default value to your arguments (in your case 0 seems appropriate):
function returnZeroAdd ($arg = 0, $arg2 = 0)
{
echo $arg + $arg2;
}
You have to define the variable before pass it to an function. for example
$bawtryReturnCount=10;
$bawtryFReturnCount=5;
define the two variable with some value and pass it to that function.
function returnZeroAdd ($arg=0, $arg2=0)
{
echo $arg + $arg2;
}
if you define a function like this means the function takes default value as 0 if the argument is not passed.
for example you can call the functio like this
returnZeroadd();// print 0
returnZeroadd(4);// print 4
returnZeroadd(4,5);// print 9
or you can define two variables and pass it as an argument and call like this.
$bawtryReturnCount=10;
$bawtryFReturnCount=5;
returnZeroadd($bawtryReturnCount, $bawtryFReturnCount);
I'm aware that you can have PHP functions with optional arguments like so:
function do_something($argument = null)
{
// argument not provided
if ($argument === null)
........
}
Consider the case that null/false and other values are all valid arguments to my function. How can I determine whether an argument was provided or not?
do_something(null); // argument provided
do_something(false); // argument provided
do_something(0); // argument provided
do_something("test"); // argument provided
do_something(new stdClass()); // argument provided
do_something(); // nothing provided
How can I detect the last case? I have thought about using func_num_args which would work in most cases but it doesn't work if I have several optional arguments.
Is there anything that solves this problem?
func_num_args() should work exactly as you want it to, because you might be assuming something that's actually not the case: You can't have optional arguments left out if they are in the middle of your arguments list.
So let's look at this function:
function test1 ($param1 = null, $param2 = null) {
return func_num_args();
}
If I call that with different parameter combinations I get the following results:
test1() => 0
test1(true) => 1
test1(true, true) => 2
There is just no way to call the function in a way where $param2 would be set while $param1 isn't. So you can map every possible output of func_num_args() to exactly one parameter configuration.
In the example above you can rely on the fact that
if the return value is 1, $param2 definitely hasn't been set, while $param1 has been.
For 0 it's 100% sure that neither one has been given.
And, of course, if it's 2 both are there.
What you actually would need are named parameters, as many other languages have them. PHP doesn't at the moment. NikiC actually wrote an RFC that suggests the addition of named parameters to PHP, but I think that's still way off in the future. You can check that out here: https://wiki.php.net/rfc/named_params
As these are not yet available, here are a few workarounds you can try:
Workaround 1
If you really need to be able to have all the parameters optional, try a parameter array:
function test1 (array $opts) {
if (!isset($opts['opt1'])) { $opts['opt1'] = 'default1'; }
if (!isset($opts['opt2'])) { $opts['opt2'] = 'default2'; }
}
Then you can call it like this:
test1(array('opt2' => true))
It would set the first parameter to "default1" while keeping the second. And there are definitely better and more elegant ways to do this (e.g. using an object instead), but the general idea is the same.
Workaround 2
You could also go with alias functions:
function test ($param1, $patam2) { ... ]
function testNoParam1 ($param2) {
test("default1", $param2);
}
That at least makes it very easy to read, but of course you need to pick the right function depending on the parameters you have.
Workaround 3
By adding a lot of additional code you could get really fancy and use a FactoryObject:
class FunctionExecutor {
private $param1 = "default1";
private $param2 = "default2";
public function param1($val) {
$this->param1 = $val;
return $this;
}
public function param2($val) {
$this->param2 = $val;
return $this;
}
public function execute() {
return yourFunction($this->param1, $this->param2);
}
}
This could be used like this:
$doSomething = new FunctionExecutor();
$returnValue = $doSomething->param2(42)->execute();
In this approach it would probably be a better idea to actually put your function into the object instead of defining it globally. Anyway...this is definitely a possibility, but not the most practical one. Just wanted to add it, because it has some benefits.
perhaps this will help: http://www.php.net//manual/en/function.func-get-args.php
$args = func_get_args();
if(!isset($arg[0])) {
echo 'no argument';
}
or
isset(func_get_arg(0));
Passing "null", "0", or "false" means that you allocate memory to store a variable, regardless it's scope, type, or size. Then it is used as a parameter to a function.
In PHP you cannot override functions by arguments, but you can access them by calling the "func_get_args()", and this is the only way to handle different numbers / types of arguments passed to a function:
function do_something() {
$args = func_get_args();
//do_something(stdClass, 1)
if($args[0] instanceof stdClass && is_numeric($args[1])) {
//handle
//return
//do_something(1, "string")
} else if(is_numeric($args[0]) && is_string($args[1])) {
//handle
//return
}
throw new Exception('invalid arguments');
}
do_something(new StdClass(), 100); //ok
do_something(100, "hell world") // ok
do_someting(); //throws Exception('invalid arguments');
In PHP 7, you can do:
function action(...$args) {
if (count($args) === 0) {
return action_default();
}
$var1 = array_shift($args);
$var2 = array_shift($args);
$var3 = array_shift($args);
// etc.
}
So I'd like to use the call_user_func to pass data to an optional parameter of a function.
Here's the example of a code, the optional parameter $data represents a functional called data that was declared in another file. I just want it to be called by using call_user_func that will set the parameter with the function's name and call it within the createtable function, but doesn't seem to work.
I got the example from the PHP Manual, but createTable contains many parameters. How can I make call_user_func only assign the string data to the optional parameter $data set to NULL as default?
function createTable($database, $table, $patch,$data = NULL)
{
echo "INFO: Adding '$table'in database '$database'.\n";
$sql = "SHOW TABLES FROM $database WHERE Tables_in_$database='$table';";
$result = mysql_query($sql);
$result_count = mysql_num_rows($result);
if ( $result_count != 1 ) {
echo "ERROR: Can not find table '$table' in database '$database'.\n";
$result = mysql_query($patch);
if ( false === $result ) {
echo "ERROR: Adding Table '$table' in database '$database' ... Failed\n";
return false;
}
else {
echo "INFO: Adding Table '$table'in database '$database' ... Success\n";
// using the optional parameter here
$data();
return true;
}
} else {
if ( $result_count == 1 ) {
echo "ERROR: Table '$table'already in database '$database'.\n";
return false;
}
}
}
// Now I'm passing value to the optional parameter $ data that is NULL as default.
call_user_func('createTable', "data");
Even with call_user_func you have to pass all the parameters.
Anyway, call_user_func is intended for use when the name of the function isn't necessarily known up front. For instance, you might have several functions and a variable, and the variable contains the name of the function to call.
Personally I think it's on par with eval and variable variables: A horrible idea. After all, if you have $foo = "function_name"; then you can call $foo() and it will call function_name.
Anyway, back to the point, just call it as a normal function and give it the parameters it needs. Pass null if you have to.
you must pass value like this
call_user_func('createTable', $database, $table, $patch,$data);
or this for call from class
call_user_func(array(&$objectName->{$anotherObject},$functionName), $arg1, $arg2, $arg2);
or you can use this can get arg as array
call_user_func_array("createTable", array("one", "two"));
or this for call from class can get arg as array
call_user_func_array(array($foo, "bar"), array("three", "four"));
or This can help you too it not need to pass all args
function __named($method, array $args = array())
{
$reflection = new ReflectionFunction( $method);
$pass = array();
foreach($reflection->getParameters() as $param)
{
/* #var $param ReflectionParameter */
if(isset($args[$param->getName()]))
{
$pass[] = $args[$param->getName()];
}
else
{
try{
$pass[] = $param->getDefaultValue();
}catch(Exception $e){
$pass[] = NULL;
}
}
}
return $reflection->invokeArgs( $pass);
}
I hope It Work
sample:
__named('createTable', array('data' => 'value'));
and it is for use in class
public function __named($method, array $args = array())
{
$reflection = new ReflectionMethod($this, $method);
$pass = array();
foreach($reflection->getParameters() as $param)
{
/* #var $param ReflectionParameter */
if(isset($args[$param->getName()]))
{
$pass[] = $args[$param->getName()];
}
else
{
try{
$pass[] = $param->getDefaultValue();
}catch(Exception $e){
$pass[] = NULL;
}
}
}
return $reflection->invokeArgs($this,$pass);
}
if you Don't set any value __named Put Null instead of Unset Value
It seems you just want to pass the last param, and not worry about the 1st three. I don't think call_user_func is the right tool here at all.
Why not just make a function that calls your function?
function call_createTable($data){
$database = '...';
$table = '...';
$patch = '...';
return createTable($database, $table, $patch, $data);
}
Then just simply call it like this: call_createTable("data");.
I have a function which takes in about 10 arguments, in which most of them are optional. I was wondering if I could implement it in such a way that the user of the function does not need to bother with the order of the parameters.
For example:
public function foo($arg1, $arg2, $arg3='',$arg4='', $arg5='', $arg6='', $arg7=''){}
Now, when I use this function I can simply
$this->foo($arg1val, $arg2val, $arg6val);
Is there a way in php to do so?
Here is how I implemented this:
I've listed the parameters accepted by the function in the API, so the user can pass the parameters in any order in an array with key=>value pairs.
For example:
public function argumentsFilter($origParams, $newParams){
$tmpArr = array();
foreach ($origParams as $origKey){
foreach($newParams as $newKey => $newVal){
if($newKey == $origKey){
$tmpArr[$origKey] = $newVal;
}
}
if(empty($tmpArr[$origKey])){
$tmpArr[$origKey] = '';
}
}
return $tmpArr;
}
public function foo($arg1, $arg2, $arg=array()){
$validArgList = array('arg3', 'arg4', 'arg5', 'arg6', 'arg7');
$correctedArgList = $this->argumentsFilter($validArgList, $arg);
}
Is there a more elegant way to do this?
10 parameters for a function is clearly too much. Pass arrays instead:
function foo(array $params) {
$defaults = array('foo' => true, 'bar' => false, ...);
$params = array_intersect_key($params, $defaults) + $defaults;
// work with $params['foo']
// maybe extract($params)
}
This example shows a function that accepts an arbitrary number of "named parameter" in any order, filters invalid values and establishes defaults values.
It is not possible, since php doesn't support named arguments.
You have 2 choices: to use array or to redesign your function so it has fewer parameters (the latter is preferred).
There are many ways to do that, but I recommend this method:
function doSomething($required, /*optional*/ $arguments = array()) {
$arguments = array_merge(array(
// set defaults
"argument" => "default value",
), $arguments);
var_dump($arguments);
}
It is very clean and easy to understand.