How would I skip optional arguments in a function call? [duplicate]
Asked Answered
C

21

117

OK I totally forgot how to skip arguments in PHP.

Lets say I have:

function getData($name, $limit = '50', $page = '1') {
    ...
}

How would I call this function so that the middle parameter takes the default value (ie. '50')?

getData('some name', '', '23');

Would the above be correct? I can't seem to get this to work.

Cravat answered 30/6, 2009 at 23:31 Comment(5)
@Chuck for what are you looking for exactly? Like a workaround or a class to implement this or ... ?Kipper
@Kipper I'm asking to see if the current version of PHP supports skipping arguments (like other languages do). The current answers may be outdated. From the bounty description: "Current answers are five years old. Does the current version of PHP change things?"Heins
@Chuck Then the answer will probably be: that nothing changed; without a workaround/code you won't get your functionality.Kipper
All the answers here seem to assume you have control over the function being called. If that function is part of a library or framework you have no option but to specify something for argument 2 if you need argument 3. The only option is to look in the source for the function and replicate the default value.Tophus
It's not possible to skip it, but you can pass the default argument using ReflectionFunction. I posted that answer in a similar question.Praxis
B
60

Your post is correct.

Unfortunately, if you need to use an optional parameter at the very end of the parameter list, you have to specify everything up until that last parameter. Generally if you want to mix-and-match, you give them default values of '' or null, and don't use them inside the function if they are that default value.

Baseburner answered 30/6, 2009 at 23:35 Comment(7)
Can you provide an example?Welltodo
@iamme do $limit = is_numeric($limit) ? $limit : '50'; at the beginning of getData functionFormalize
Wrong answer in 2022 with PHP 8.x.Intransitive
The PHP documentation for this is here and here is already an answer on Stackoverflow.Intransitive
@Intransitive what is correct answer in 2022?Combs
@Intransitive AamiR's answer is the only correct answer (today) with respect to php 8Charmian
@OleksandrIY AamiR's answer is correct - with named arguments in PHP 8.x you can skip any defaulted argument - you can check the docsIntransitive
T
45

Nope, it's not possible to skip arguments this way. You can omit passing arguments only if they are at the end of the parameter list.

There was an official proposal for this: https://wiki.php.net/rfc/skipparams, which got declined. The proposal page links to other SO questions on this topic.

Ticker answered 14/5, 2015 at 15:22 Comment(9)
Then how do PHP functions have optional of more/less parameters?Welltodo
PHP has support for default parameter values. But those parameters must to be at the end of the list.Ticker
You can find examples in the PHP documentationTicker
Jerks. Makes me so mad. If you don't like a feature someone else requests, just don't use it. The internet is the worst sometimes.Bushman
@LeeSaxon the issue is readability and portability. 1) If someone decides to skip parameters, and someone debugging code misses this fact, great confusion occurs. 2) new code could not work on older versions without a major over haul and chances of mistakes would be very high.Downwash
Perhaps doing it the way it is solved in Kotlin would be the best. You simply specify the name like this getData(name = "some name", page = "23"). Sadly that's not the caseNuncle
Wrong answer in 2022 with PHP 8.x.Intransitive
@Intransitive care to give more details here? Or, even better, add an answer that covers the features of PHP 8.x that allow skipping arguments with default values? Also note that not everyone can get onboard with the latest PHP, some are stuck in older versions.Ticker
Sure! The PHP documentation for this is here and here is already an answer on Stackoverflow... and also one below.Intransitive
H
42

There's no way to "skip" an argument other than to specify a default like false or null.

Since PHP lacks some syntactic sugar when it comes to this, you will often see something like this:

checkbox_field(array(
    'name' => 'some name',
    ....
));

Which, as eloquently said in the comments, is using arrays to emulate named arguments.

This gives ultimate flexibility but may not be needed in some cases. At the very least you can move whatever you think is not expected most of the time to the end of the argument list.

Helios answered 30/6, 2009 at 23:35 Comment(4)
I would identify the 'something like this' as 'using arrays to emulate named arguments', FWIW. :)Tybie
Despite being more flexible you have to remember the parameters you should/can set (as they are not in the signature). So in the end it might be not as useful as it seems but of course this depends on the context.Supersonic
So what would be the best practices for such a scenario?Arboreous
Wrong answer in 2022 with PHP 8.x.Intransitive
G
14

This feature is implemented in PHP 8.0

PHP 8 introduced named arguments

which:

allows skipping default values arbitrarily

The documentation for reference

No changes necessary to use this feature:

lets use OPs function function getData($name, $limit = '50', $page = '1')

Usage

getData(name: 'some name', page: '23');

Native functions will also use this feature

htmlspecialchars($string, double_encode: false);
// Same as
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

Netbeans IDE 12.3 Feature Supported

This feature is supported, with the exception of code completion for named arguments, looks better ;)

enter image description here

Gynandromorph answered 31/10, 2021 at 13:44 Comment(0)
T
7

Nothing has changed regarding being able to skip optional arguments, however for correct syntax and to be able to specify NULL for arguments that I want to skip, here's how I'd do it:

define('DEFAULT_DATA_LIMIT', '50');
define('DEFAULT_DATA_PAGE', '1');

/**
 * getData
 * get a page of data 
 *
 * Parameters:
 *     name - (required) the name of data to obtain
 *     limit - (optional) send NULL to get the default limit: 50
 *     page - (optional) send NULL to get the default page: 1
 * Returns:
 *     a page of data as an array
 */

function getData($name, $limit = NULL, $page = NULL) {
    $limit = ($limit===NULL) ? DEFAULT_DATA_LIMIT : $limit;
    $page = ($page===NULL) ? DEFAULT_DATA_PAGE : $page;
    ...
}

This can the be called thusly: getData('some name',NULL,'23'); and anyone calling the function in future need not remember the defaults every time or the constant declared for them.

Tito answered 18/5, 2015 at 12:25 Comment(1)
You'd probably want strict comparison ($limit===null)Pigweed
S
5

The simple answer is No. But why skip when re-arranging the arguments achieves this?

Yours is an "Incorrect usage of default function arguments" and will not work as you expect it to.

A side note from the PHP documentation:

When using default arguments, any defaults should be on the right side of any non-default arguments; otherwise, things will not work as expected.

Consider the following:

function getData($name, $limit = '50', $page = '1') {
    return "Select * FROM books WHERE name = $name AND page = $page limit $limit";
}

echo getData('some name', '', '23');   // won't work as expected

The output will be:

"Select * FROM books WHERE name = some name AND page = 23 limit"

The Correct usage of default function arguments should be like this:

function getData($name, $page = '1', $limit = '50') {
    return "Select * FROM books WHERE name = $name AND page = $page limit $limit";
}

echo getData('some name', '23');  // works as expected

The output will be:

"Select * FROM books WHERE name = some name AND page = 23 limit 50"

Putting the default on your right after the non-defaults makes sure that it will always retun the default value for that variable if its not defined/given Here is a link for reference and where those examples came from.

Edit: Setting it to null as others are suggesting might work and is another alternative, but may not suite what you want. It will always set the default to null if it isn't defined.

Stich answered 20/5, 2015 at 11:26 Comment(1)
This is the best answer since it provides clearly illustrated syntactical explanations! Thank you!Ecthyma
B
3

For any parameter skipped (you have to) go with the default parameter, to be on the safe side.

(Settling for null where the default parameter is '' or similar or vice versa will get you into troublew...)

Bucktooth answered 29/1, 2013 at 10:7 Comment(0)
S
3

You can't skip arguments but you can use array parameters and you need to define only 1 parameter, which is an array of parameters.

function myfunction($array_param)
{
    echo $array_param['name'];
    echo $array_param['age'];
    .............
}

And you can add as many parameters you need, you don't need to define them. When you call the function, you put your parameters like this:

myfunction(array("name" => "Bob","age" => "18", .........));
Symptom answered 15/5, 2015 at 7:42 Comment(0)
N
2

As mentioned above, you will not be able to skip parameters. I've written this answer to provide some addendum, which was too large to place in a comment.

@Frank Nocke proposes to call the function with its default parameters, so for example having

function a($b=0, $c=NULL, $d=''){ //...

you should use

$var = a(0, NULL, 'ddd'); 

which will functionally be the same as omitting the first two ($b and $c) parameters.

It is not clear which ones are defaults (is 0 typed to provide default value, or is it important?).

There is also a danger that default values problem is connected to external (or built-in) function, when the default values could be changed by function (or method) author. So if you wouldn't change your call in the program, you could unintentionally change its behaviour.

Some workaround could be to define some global constants, like DEFAULT_A_B which would be "default value of B parameter of function A" and "omit" parameters this way:

$var = a(DEFAULT_A_B, DEFAULT_A_C, 'ddd');

For classes it is easier and more elegant if you define class constants, because they are part of global scope, eg.

class MyObjectClass {
  const DEFAULT_A_B = 0;

  function a($b = self::DEFAULT_A_B){
    // method body
  }
} 
$obj = new MyObjectClass();
$var = $obj->a(MyObjectClass::DEFAULT_A_B); //etc.

Note that this default constant is defined exactly once throughout the code (there is no value even in method declaration), so in case of some unexpected changes, you will always supply the function/method with correct default value.

The clarity of this solution is of course better than supplying raw default values (like NULL, 0 etc.) which say nothing to a reader.

(I agree that calling like $var = a(,,'ddd'); would be the best option)

Navarra answered 15/5, 2015 at 10:56 Comment(0)
H
1

This is kind of an old question with a number of technically competent answers, but it cries out for one of the modern design patterns in PHP: Object-Oriented Programming. Instead of injecting a collection of primitive scalar data types, consider using an "injected-object" that contains all of the data needed by the function. http://php.net/manual/en/language.types.intro.php

The injected-object may have property validation routines, etc. If the instantiation and injection of data into the injected-object is unable to pass all of the validation, the code can throw an exception immediately and the application can avoid the awkward process of dealing with potentially incomplete data.

We can type-hint the injected-object to catch mistakes before deployment. Some of the ideas are summarized in this article from a few years ago.

https://www.experts-exchange.com/articles/18409/Using-Named-Parameters-in-PHP-Function-Calls.html

Hipster answered 12/1, 2019 at 0:1 Comment(0)
G
1

I had to make a Factory with optional parameters, my workaround was to use the null coalescing operator:

public static function make(
    string $first_name = null,
    string $last_name = null,
    string $email = null,
    string $subject = null,
    string $message = null
) {
    $first_name = $first_name ?? 'First';
    $last_name  = $last_name ?? 'Last';
    $email      = $email ?? '[email protected]';
    $subject    = $subject ?? 'Some subject';
    $message    = $message ?? 'Some message';
}

Usage:

$factory1 = Factory::make('First Name Override');
$factory2 = Factory::make(null, 'Last Name Override');
$factory3 = Factory::make(null, null, null, null 'Message Override');

Not the prettiest thing, but might be a good pattern to use in Factories for tests.

Goon answered 6/7, 2020 at 22:11 Comment(0)
K
0

Well as everyone else already said, that what you want won't be possible in PHP without adding any code lines in the function.

But you can place this piece of code at the top of a function to get your functionality:

foreach((new ReflectionFunction(debug_backtrace()[0]["function"]))->getParameters() as $param) {
    if(empty(${$param->getName()}) && $param->isOptional())
        ${$param->getName()} = $param->getDefaultValue();
}

So basically with debug_backtrace() I get the function name in which this code is placed, to then create a new ReflectionFunction object and loop though all function arguments.

In the loop I simply check if the function argument is empty() AND the argument is "optional" (means it has a default value). If yes I simply assign the default value to the argument.

Demo

Kipper answered 15/5, 2015 at 20:4 Comment(0)
R
0

Set the limit to null

function getData($name, $limit = null, $page = '1') {
    ...
}

and call to that function

getData('some name', null, '23');

if you want to set the limit you can pass as an argument

getData('some name', 50, '23');
Rorry answered 16/5, 2015 at 7:3 Comment(0)
L
0

As advised earlier, nothing changed. Beware, though, too many parameters (especially optional ones) is a strong indicator of code smell.

Perhaps your function is doing too much:

// first build context
$dataFetcher->setPage(1);
// $dataFetcher->setPageSize(50); // not used here
// then do the job
$dataFetcher->getData('some name');

Some parameters could be grouped logically:

$pagination = new Pagination(1 /*, 50*/);
getData('some name', $pagination);
// Java coders will probably be familiar with this form:
getData('some name', new Pagination(1));

In last resort, you can always introduce an ad-hoc parameter object:

$param = new GetDataParameter();
$param->setPage(1);
// $param->setPageSize(50); // not used here
getData($param);

(which is just a glorified version of the less formal parameter array technique)

Sometimes, the very reason for making a parameter optional is wrong. In this example, is $page really meant to be optional? Does saving a couple of characters really make a difference?

// dubious
// it is not obvious at first sight that a parameterless call to "getData()"
// returns only one page of data
function getData($page = 1);

// this makes more sense
function log($message, $timestamp = null /* current time by default */);
Laborsaving answered 19/5, 2015 at 21:31 Comment(0)
D
0

This snippet:


    function getData($name, $options) {
       $default = array(
            'limit' => 50,
            'page' => 2,
        );
        $args = array_merge($default, $options);
        print_r($args);
    }

    getData('foo', array());
    getData('foo', array('limit'=>2));
    getData('foo', array('limit'=>10, 'page'=>10));

Answer is :


     Array
    (
        [limit] => 50
        [page] => 2
    )
    Array
    (
        [limit] => 2
        [page] => 2
    )
    Array
    (
        [limit] => 10
        [page] => 10
    )

Doings answered 20/5, 2015 at 13:7 Comment(2)
Nice. But a little explanation would be helpful.Mechanist
Good answer, but if you want to ommit optional parameter do it this way: function getData($name, $args = array()) { $defaults = array('limit':=>50,'page'=>2); $args=array_merge($defaults,$args); } Now it is possible to call this function with only first parameter like this: getData('foo');Unquestionable
P
0

This is what I would do:

<?php

    function getData($name, $limit = '', $page = '1') {
            $limit = (EMPTY($limit)) ? 50 : $limit;
            $output = "name=$name&limit=$limit&page=$page";
            return $output;
    }

     echo getData('table');

    /* output name=table&limit=50&page=1 */

     echo getData('table',20);

    /* name=table&limit=20&page=1 */

    echo getData('table','',5);

    /* output name=table&limit=50&page=5 */

    function getData2($name, $limit = NULL, $page = '1') {
            $limit = (ISSET($limit)) ? $limit : 50;
            $output = "name=$name&limit=$limit&page=$page";
            return $output;
    }

    echo getData2('table');

    // /* output name=table&limit=50&page=1 */

    echo getData2('table',20);

    /* output name=table&limit=20&page=1 */

    echo getData2('table',NULL,3);

    /* output name=table&limit=50&page=3 */

?>

Hope this will help someone

Pyrexia answered 18/12, 2016 at 9:13 Comment(0)
A
0

As of PHP 8.0.0, declaring mandatory arguments after optional arguments is deprecated.

You can now omit optional parameters.

Example:

<?php

function foo ( $a = '1', $b = '2', $c = '3'  ){
   return "A is " . $a . ", B is " . $b . ", C is " . $b  
}

echo foo(c: '5');  
// Output A is 1, B is 2, C is 5
Agrapha answered 30/8, 2022 at 21:17 Comment(0)
T
-1

You can not skip middle parameter in your function call. But, you can work around with this:

function_call('1', '2', '3'); // Pass with parameter.
function_call('1', null, '3'); // Pass without parameter.

Function:

function function_call($a, $b='50', $c){
    if(isset($b)){
        echo $b;
    }
    else{
        echo '50';
    }
}
Teetotal answered 19/5, 2015 at 19:6 Comment(1)
I'm not sure why you have down votes, other than the $c also needing a default value (you cannot have any required arguments after an optional argument).Esau
S
-2

Try This.

function getData($name, $limit = NULL, $page = '1') {
               if (!$limit){
                 $limit = 50;
               }
}

getData('some name', '', '23');
Seward answered 19/5, 2015 at 8:10 Comment(1)
if limit is 0, it will be replaced with 50Mcandrew
A
-2

As @IbrahimLawal pointed out. It's best practice to just set them to null values. Just check if the value passed is null in which you use your defined defaults.

<?php
define('DEFAULT_LIMIT', 50);
define('DEFAULT_PAGE', 1);

function getData($name, $limit = null, $page = null) {
    $limit = is_null($limit) ? DEFAULT_LIMIT : $limit;
    $page = is_null($page) ? DEFAULT_PAGE : $page;
    ...
}
?>

Hope this helps.

Ashraf answered 20/5, 2015 at 2:35 Comment(1)
You simply duplicated the response. Rather you could have upvoted or commented on it.Tito
K
-6
getData('some name');

just do not pass them and the default value will be accepted

Kirstinkirstyn answered 19/5, 2015 at 7:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.