2

I have a function with 2 arguments: a string, and a callable. I want the callable to be optional. See below.

function saySomething($str, $callback){

    echo $str;

    $res = false;
    if(is_callable($callback)){
        $res = $callback();
    }

    if($res){
        echo ', I am cool';
    } else {
        echo ', I am not cool';
    }
}

// This works as I expect
saySomething('piet');// deliberately not supplying a callback
// I want the output to be: piet, I am not cool.
// The output in this case: "piet, I am not cool."

In php 5.4 and php 7 it's possible to declare / type hint a callable in the function argument. The is_callable wont be needed in the function body anymore. Next, if one does so then the callable argument MUST be valid, thus it is not optional anymore.

What do I want?

I want to know if it's possible to use the callable type declaration but keep it as an optional argument.

I tried this:

// This is not possible :-(
// Fatal error: Uncaught ArgumentCountError: Too few arguments to function saySomething()
function saySomething($str, callable $callback = function(){return false;}){

    echo $str;

    $res = $callback();
    if($res){
        echo ', I am cool';
    } else {
        echo ', I am not cool';
    }
}

saySomething('piet'); // deliberately not supplying a callback
// I want the output to be: piet, I am not cool.

I want to the callable to return false when no callable was supplied at the client code.

The possible duplicate PHP 7.1 Nullable Default Function Parameter does not offer a solution for this situation.

Julian
  • 4,396
  • 5
  • 39
  • 51
  • what .... what are you wanting to do? o.O – treyBake Apr 09 '19 at 08:09
  • 4
    `?callable` then? – u_mulder Apr 09 '19 at 08:10
  • @Julian then refer to mulder's comment .. – treyBake Apr 09 '19 at 08:12
  • 2
    Possible duplicate of [PHP 7.1 Nullable Default Function Parameter](https://stackoverflow.com/questions/45320353/php-7-1-nullable-default-function-parameter) – treyBake Apr 09 '19 at 08:13
  • @u_mulder I didn't knew about `?callable`, could you share some more info about the `?callable`? A link to the official php documentation would be helpful. – Julian Apr 09 '19 at 08:19
  • @Julian check my linked duplicate .. – treyBake Apr 09 '19 at 08:25
  • @treyBake I tried the sample they provide in your link. It gave me this error: `Fatal error: Uncaught Error: Function name must be a string.` Therefore, it's not a duplicate question. At this point I think a default callable is not possible. – Julian Apr 09 '19 at 08:39
  • @Julian the principle is the same - you have a syntax error in your function declaration - the answer is probably right when typed correctly – treyBake Apr 09 '19 at 08:42
  • @treyBake I did this: `function saySomething($str, ?callable $callback = null){`. But the fatal error is triggered at this line: `$res = $callback();`. Where is the syntax error? – Julian Apr 09 '19 at 08:47
  • @Julian how did you call the function? – treyBake Apr 09 '19 at 08:49
  • @treyBake like this: `saySomething('piet'); // deliberately not supplying a callback` – Julian Apr 09 '19 at 08:52
  • @Julian well yeah .. that is a fatal error .. you declare the param type to be null or a callback function .. not a string? – treyBake Apr 09 '19 at 08:56
  • @treyBake I guess it's safe to say that **the function argument that has the callable type declaration can't have a default callback**. And thats all I wanted to know for this moment. – Julian Apr 09 '19 at 09:16
  • not specifically just callbacks, but any default argument must be a _constant expression_ which unfortunately I can't find a dedicated phpdoc page for – chiliNUT Apr 09 '19 at 20:45
  • @chiliNUT The only page I found was this: https://www.php.net/manual/en/functions.arguments.php#functions.arguments.default. They explain it like this: `The default value must be a constant expression`. But it is common sense though that a default argument value should be a constant expression. – Julian Apr 10 '19 at 07:18

1 Answers1

6

The only accepted default argument for a callable is NULL. This is partially documented, but it's neither clearly nor fully documented (as far as I can find). Synthesizing from the manual, you can conclude that anonymous functions aren't allowed. Other valid callable types also aren't allowed as defaults for callable-hinted parameters, but this is not in the manual (as far as I can tell).

The function arguments manual page states that only scalars, arrays and NULL can be default arguments. Callables can be objects (Closure or objects with an __invoke method), 2 element arrays (a class or object and a method name) or strings. Objects aren't scalars (as stated on the is_scalar manual page), and thus can't be used as default arguments (which rules out anonymous functions), even for untypedhinted parameters. Going off the manual, this would seem to allow string and array defaults for callable parameters, but if you try to use a string or array, PHP gives the error:

Default value for parameters with callable type can only be NULL

Though arrays and strings are (in general) allowed as default values, they can't always be type checked at compile time for callables; they might refer to a callable that hasn't been defined yet, causing the type check to produce a false negative. I suspect that because of this, they are excluded from being defaults for callables.

If you want to have an optional callable argument, you must use NULL as the default value, then test the variable within the function. The requirements to both have an optional argument and not test the argument are incompatible.

outis
  • 75,655
  • 22
  • 151
  • 221
  • 2
    I agree with you. **Optional callables should have a NULL as the default and WITHOUT a type declaration**. By the way, array's are NOT scalars. `Scalar variables are those containing an integer, float, string or boolean. Types array, object and resource are not scalar. `. See link: https://www.php.net/manual/en/function.is-scalar.php – Julian Apr 10 '19 at 07:09
  • @Julian: thanks for pointing out the error. Originally, the sentence was only about strings, and arrays got tacked on. The answer got rushed to press. – outis Apr 10 '19 at 08:04
  • @Julian, I think it's more accurate and easier to understand if you say this: a scalar is a single value. Arrays and objects do not meet that criteria, and are therefore not scalar. – scott8035 Jul 26 '22 at 22:54