45

I just felt on pieces of php (symfony/laravel) code using question mark in method type hints :

public function functionName(?int $arg = 0)

In other occasions the ?type was not the last one, but I did not find any of these with no default yet.

Problem is, I cannot find any information about this, and I checked :

And same with 7.2, but since the code only requires 7.1, it seems rather normal.

I also googled, and searched here, but either this is not documented or the question marks topic is defeating search engines.

So I feel a little dumb now, and I would really appreciate if someone could enlighten me on the significance of this question mark in method signatures arguments.

Thanks

Basheer Kharoti
  • 4,202
  • 5
  • 24
  • 50
fab2s
  • 838
  • 2
  • 8
  • 14
  • 4
    https://wiki.php.net/rfc/nullable_types – brombeer Feb 20 '18 at 09:17
  • You're right lol. The worst thing is I knew about the meaning as return value. Thanks for the enlightenment – fab2s Feb 20 '18 at 21:07
  • Does this answer your question? [What is the purpose of the question marks before type declaration in PHP7 (?string or ?int)?](https://stackoverflow.com/questions/48450739/what-is-the-purpose-of-the-question-marks-before-type-declaration-in-php7-stri) – Syscall Jan 30 '22 at 08:59

3 Answers3

47

It's a new feature in php7.1

http://php.net/manual/en/migration71.new-features.php

A question mark means that the type hinted parameter (or return value) is also allowed to be null.

So in your example $arg can be null or any integer.

Pim_nr_47
  • 983
  • 8
  • 10
16

Just a note to add to the previous answers - it must be either null or have a value in the specified type i.e. - you cannot just omit it - have a look at an example:

class TestClass {

    public function fetch(?array $extensions)
    {
        //...
    }        
}

Now if you call

(new TestClass)->fetch();

this will throw

ArgumentCountError : Too few arguments to function fetch() ...

To make it work without passing array of $extensions you'd have to call it with null as argument

(new TestClass)->fetch(null);

It works best in the situations, where you are passing argument initially set to null to another method for processing i.e.

class TestClass {

    public function fetch(array $extensions = null)
    {
        //...

        $this->filter($extensions);
    }

    private function filter(?array $extensions)
    {
        //...
    }
}

Now you can call the fetch method without the argument

(new TestClass)->fetch();
Sebastian Sulinski
  • 5,815
  • 7
  • 39
  • 61
  • 1
    Exactly, and it makes perfect sens, without a default value, you only have the right to explicitly set the argument to null with the question mark, and with one, you can completely omit it. – fab2s Jul 22 '19 at 13:04
  • What's the difference between having `private function filter(?array $extensions)` and `private function filter(array $extensions = null)` – Quiquetas Feb 18 '21 at 02:28
  • 1
    @Quiquetas `(?array $p)` $p must be an array or null, anyway it must be provided on call. `(?array $p=null)` just the same, $p must be an array or null, but with default value to null, this parameter could be omitted on call this time. And about the `public function fetch(array $extensions = null)` snippet, well, ti's just a potential error because $extensions cannot be null. But would trigger only if fetch is called with no arguments, like this `$obj->fetch();`. The why is written above. – Niki Romagnoli Oct 01 '21 at 09:40
8

Consider the following functions:

<?php

function testFnc1(string $param)
{
    var_dump($param);
}

function testFnc2(string $param = 'some string')
{
    var_dump($param);
}
    
function testFnc3(string $param = null)
{
    var_dump($param);
}

function testFnc4(?string $param)
{
    var_dump($param);
}

function testFnc5(?string $param = 'some string')
{
    var_dump($param);
}


function testFnc6(?string $param = null)
{
    var_dump($param);
}

Now we test the functions with different values.

fnc no argument null '' 'other string'
1 FATAL ERROR Uncaught ArgumentCountError FATAL ERROR Uncaught TypeError: Argument 1 passed to testFnc1() must be of the type string, null given string(0) "" string(12) "other string"
2 string(11) "some string" FATAL ERROR Uncaught TypeError: Argument 1 passed to testFnc2() must be of the type string, null given string(0) "" string(12) "other string"
3 NULL NULL string(0) "" string(12) "other string"
4 FATAL ERROR Uncaught ArgumentCountError NULL string(0) "" string(12) "other string"
5 string(11) "some string" NULL string(0) "" string(12) "other string"
6 NULL NULL string(0) "" string(12) "other string"

Notice the difference between testFnc3(string $param = null) and testFnc4(?string $param) when:

  1. no argument is passed
  2. the argument is null

You can call testFnc3() but not testFnc4() as it does not declare a default value.

You must call testFnc4(null).

So testFnc4(?string $param) allows $param to be null, but such parameter MUST be provided.

Quiquetas
  • 427
  • 7
  • 10