Nullable return types in PHP7
Asked Answered
B

3

205

PHP 7 introduces return type declarations. Which means I can now indicate the return value is a certain class, interface, array, callable or one of the newly hintable scalar types, as is possible for function parameters.

function returnHello(): string {
    return 'hello';
}

Often it happens that a value is not always present, and that you might return either something of some type, or null. While you can make parameters nullable by setting their default to null (DateTime $time = null), there does not appear to be a way to do this for return types. Is that indeed the case, or am I somehow not finding how to do it? These do not work:

function returnHello(): string? {
    return 'hello';
}

function returnHello(): string|null {
    return 'hello';
}
Bony answered 9/11, 2015 at 12:10 Comment(4)
PHP7 won't allow nullable return types yet, but there's an RFC that aims to address this in PHP 7.1 here. The propesed notation would then be function returnString(?string $stringNull) : ?string { return $stringNull;}Stacistacia
I've ended up emulating nullability by abusing exceptions in my application for now. If you're fine with being silly as well, this might be of use: github.com/JeroenDeDauw/OhMyPhp/blob/master/src/…Bony
Perhaps it might make more sense to use the PHP7 Trowable interface (specifically, extending the TypeError)Stacistacia
See also nullable types on PHP7.1Eulogist
S
329

PHP 7.1 Now supports nullable return types. The first RFC I linked to is the one they went for:

function nullOrString(int $foo) : ?string
{
    return $foo%2 ? "odd" : null;
}

old answer:

Since my comment was actually an answer to the question:

PHP 7 won't support nullable return-types just yet, but there's an RFC out to address just that, it aims to land in PHP 7.1. If it passes, the syntax would then affect all type-hints (both return types and type-hints):

public function returnStringOrNull(?array $optionalArray) : ?string
{
    if ($optionalArray) {
        return implode(', ', $optionalArray);//string returned here
    }
    return null;
}

There's also a competing RFC to add union types, which would be able to do the same thing, but would look different:

public function returnStringOrNull(array|null $optionalArray) : string|null
{
    if ($optionalArray) {
        return implode(', ', $optionalArray);//string returned here
    }
    return null;
}

For now, though, you'll have to write:

public function returnStringOrNull( array $optionalArray = null)
{
    if ($optionalArray) {
        return implode(', ', $optionalArray);
    }
}

Or just return an empty string to be consistent with the return type, and check falsy value:

public function returnStringOrNull( array $optionalArray = null) : string
{
    if ($optionalArray) {
        return implode(', ', $optionalArray);
    }
    return '';
}
//call
$string = $x->returnStringOrNull();
if (!$string) {
    $string = $x->returnStringOrNull(range(1, 10));
}
Stacistacia answered 9/11, 2015 at 12:34 Comment(6)
PHP 7 won't support nullable return-types just yet, but there's an RFC out to address just that - yeah, RFC, "just yet". Do not get me wrong - I'm really heavy PHP user since really crappy PHP3 times till now, no gaps, but when I saw all these RFCs they rejected for 7, my impression was just "WTF?!". Users see the mess and are willing to clean it up in the backward compatible way and they just get "no". Clean methods naming mess? Fixed null not being too special citizen? No, not needed. Add option to make all stuff case sensitive? Nah.. And then, surprise that people switch.Bentwood
@MarcinOrlowski: A nullable return type-hint would make sense. I've followed a couple of the RFC's for 7, and I agreed for the most part with them rejecting a lot of them. The changes they were focussing on weren't so much on the language as they were on the runtime and compiler. For some of the RFC's that were declined, it's worth reading through the discussion threads to understand why they chose not to implement those changes (eg deprecating var). What is a pitty is that instead, they did accept one too many nice-to-have's (spaceship operator for example)Stacistacia
@EliasVanOotegem Nullable types are now properly supported, as 7.1 was released on 1 December.Bos
@lonesomeday: Indeed it does, added the link + basic example to the bottom of my answerStacistacia
mmm ish a good type to update this answer! i.e. the union type does not appear to be supported in PHP 7.1Dejecta
the line if ($optionalArray) ... is not necessary. implode returns an empty string if the array is empty. You could just say return implode(', ', array($optionalArray)) to be sure the second parameter is an arrayHeim
L
75

Nullable Types are available in PHP 7.1.

This is a syntax example:

public function getName(): ?string
{
    return $this->name; // name can be null
}

PHP 7.1 is now GA and you can upgrade from PHP 7.0 (there are only few backward incompatible changes that you have to check)

Lowpressure answered 30/6, 2016 at 17:7 Comment(9)
IMO it is a joke to deliver return type declarations without implementing "nullable". Return types are unusable until "nullable" feature is implemented.Hornsby
@Hornsby IMO strictly typed return values should always be of that type, a null return doesn't hold to that contract and should rather throw an exception giving more meaning to the reason of a null value.Sickly
@SteveBuzonas if you consider a method getAgeInYears() on an object representing a Person, how would you model a person that haven't told us his/her age? Return null? Return 0? Returning null semantically means "we don't know", while 0 semantically means "the person is 0 years old". Therefore I'd argue getAgeInYears(): ?int to be the best design. Throwing exceptions should be reserved for well... exceptional cases. Not knowing a person's age in most systems should not be considered an exceptional case.Hornsby
@Hornsby very true, and that is a common practice. However, your implementation now needs to be aware that the field is nullable and explicitly handle accordingly. Which very well may be do x except when null which could just as easily be implemented with a try/catch. Further, if one were to actually require a value in that nullable field to continue execution, an exception is likely a better option.Sickly
I've noticed that this syntax causes PHPMD to throw a lot of errors. Hopefully they fix it soon.Malacology
@SteveBuzonas what you say makes sense, from a point of view; and you are right that the implementation needs to be aware; in my point of view there is nothing exceptional in knowing which are my application requirements (x() except when null can also be wank() if age not null else noAdultContentForYou(). I think that business rules should define whether a missing age is an exception or a valid case; I don't think is responsibility of the language constructs to decide if my data is valid or not in my domain. [...]Kleinstein
[...] What about a case where maybe I need to respect privacy and ignore age by default, except in the legit case of providing adult content? Without nullable return values the developer is not free to decide on that (why couldn't I expect a method to correctly return null?) and needs to take care of a FooDomainNullException, making his code unnecessarily more complex/prolix, for something that in his domain is perfectly legit (and that can actually be expressed with a single/simple ? character in PHP7.1). [or am I saying bullshits? 😮]Kleinstein
@Kleinstein I agree the business logic should dictate how to handle it. However, I strongly believe that business logic should be isolated with strict adherence to the single responsibility principal. If the responsibility of the method is to implement business rules, then by all means deal with it there. In the circumstance of a DAO, I would suggest you treat null as unknown as SQL behaves. In circumstances where the value is to be expected, exceptions have significantly more value. You can express not only that the value is missing, but possibly why.Sickly
[...] Neither approach is incorrect. I prefer to use exceptions often, you get the same result in a try/catch as you do an if/else. My design goals are generally fail fast and fail hard. If you bail at the earliest sign of a potential problem, you don't have to concern yourself with sanity checks. The exception approach gives you the assurance that you have exactly what you are expecting, and if you don't, you can just let it bubble out to the point that has the responsibility of deciding what to do next.Sickly
R
1

It works with any type.
Example:

public function getOpportunity(): ?Opportunity
{
    return $this->opportunity;
}
Radiant answered 4/12, 2019 at 17:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.