[PSR-5] Nullable type shorthand in PHPDoc (e.g. `@return ?string`)

1,452 views
Skip to first unread message

Tyson Andre

unread,
Oct 15, 2018, 7:17:32 PM10/15/18
to PHP Framework Interoperability Group
Earlier discussion about including this in PSR-5 is found in https://github.com/phpDocumentor/fig-standards/issues/153 , I'm re-opening the discussion here.


Reasons:

- Nullable types are part of the language syntax for real signatures, so adding this in PHPDoc makes some sense
- @return ?string is shorter than @return string|null
- Many static analyzers support this syntax in PHPDoc already (Phan/Psalm/PHPStan (and phpdocumentor3 will?)).

Questions that were already brought up:

- A canonical way to represent the union type after parsing it in union types - ?int|string, ?(int|string), ?int|?string, int|string|null are equivalent, but which should be preferred for the output of tools
  (not sure if that would need to be part of the standard)
- Operator precedence (e.g. ?string[]) - I suggested treating that as ?(string[]), others suggested forbidding ambiguous cases and forcing phpdoc authors to add brackets - either explicitly write (?string)[] or ?(string[]).

Jan Schneider

unread,
Oct 16, 2018, 3:26:51 AM10/16/18
to php...@googlegroups.com

Zitat von Tyson Andre <tysona...@gmail.com>:

For the sake of unambiguousity I'd prefer the latter, i.e. forcing to use brackets. This would also solve the first question.


Jan Schneider
The Horde Project
https://www.horde.org/

Chuck Burgess

unread,
Oct 16, 2018, 8:54:17 AM10/16/18
to PHP Framework Interoperability Group
Normally I lean toward more explicit too.  In the case of nullable, though, I can concede that the more concise "just `?` at the beginning" could be good enough.  Presumably, if the first datatype in a union is allowed to be nullable, then *all* datatypes in the union list *have* to be considered nullable... right? 

@param string|array|\DateTime|null $arg

It wouldn't be reasonable to say "string can be null, but array can't be", because if you pass a null, then what datatype are you saying you are nulling? :-D

Based on this thinking, I can see just having a single `?` prefix without brackentheses (TM), at least not needing the wrapper to denote scope of the `?`.

Scope on a trailing `[]` in a union list... that's another story :-D
CRB

Chuck Burgess

unread,
Nov 8, 2018, 2:16:09 PM11/8/18
to PHP Framework Interoperability Group
Revisiting this now, I'm having trouble thinking of a "[]" example that would break the assumption that "?" at the beginning isn't good enough to mean that "if any listed type is nullable, then of course every listed type is nullable".

Anyone?  If not, then I'm leaning towards just saying that a simple "?" at the beginning is fully enough for the Nullable Type use case.
CRB

Adrien Crivelli

unread,
Nov 8, 2018, 10:59:13 PM11/8/18
to PHP Framework Interoperability Group
I don't think "?" at the beginning is fully enough for the Nullable Type use case. 

You can easily express a "null or an array of elements that must be string" with "?string[]". But how can you express "an array of elements that must be string or null" ?

In code that would be:

/**
 * @param ?string[] $strings
 */

function nullOrArrayOfString(?array $strings) {
}

/**
 * @param WHAT SHOULD I WRITE HERE ? $stringsOrNulls
 */

function arrayOfStringOrNull(array $stringsOrNulls) {
}

nullOrArrayOfString
(null);
nullOrArrayOfString
(['foo', 'bar']);
nullOrArrayOfString
(['foo', null]); // Invalid call !

arrayOfStringOrNull
(null); // Invalid call !
arrayOfStringOrNull
(['foo', 'bar']);
arrayOfStringOrNull
(['foo', null]);


I think something like "(?string)[]" would be necessary to cover all null cases.

Chuck Burgess

unread,
Nov 9, 2018, 8:05:03 AM11/9/18
to php...@googlegroups.com
I think I would argue that this example is not quite the same thing as a Nullable Type.  I see the Nullable Type as a special case involving nulls, but not as *every* case involving nulls.  I see it as a method signature type hint equivalent of `AnyClass|null`, so that the *signature* can still provide runtime type enforcement of AnyClass on a passed argument, *but* will still accept *no argument* (literal null of course being treated equivalent to no argument).

Your example does *not* allow for no argument at all... you are absolutely requiring an array.  So this is not the use case of a Nullable Type.  Your example should lean on the Union operator "|" to denote that the values in the required array should be strings or literal nulls:

@param (string|null)[] $stringsOrNulls you MUST send me an array, but inside it, your values can be strings and/or literal nulls

--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to php-fig+u...@googlegroups.com.
To post to this group, send email to php...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/php-fig/c0ba7fdf-5c45-493c-8f66-ff9bccbd7a14%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Adrien Crivelli

unread,
Nov 21, 2018, 3:18:44 AM11/21/18
to PHP Framework Interoperability Group
I see your point and I agree. My case would properly be solved with union syntax as discussed in https://groups.google.com/forum/#!topic/php-fig/W1VyAtoqGQ8. That means I can write it like you suggested:

@param (string|null)[] $stringsOrNulls

Or I can also write the shorthand version too:

@param (?string)[] $stringsOrNulls
Reply all
Reply to author
Forward
0 new messages