What is the difference between ?int $number and int $number = null? - php

Given this explanation
Nullable types: Type declarations for parameters and return values can now be marked as nullable by prefixing the type name with a question mark. This signifies that as well as the specified type, NULL can be passed as an argument, or returned as a value, respectively.
https://www.php.net/manual/en/migration71.new-features.php
The following code :
public function test(?int $var) {
}
Means that test() can be called with $var either as int or as null.
And the following code :
public function test(int $var = null) {
}
Means that test() can be called with $var either as int or as null as well.
What are the differences between those two methods ? Is any of these more performant than the other?

It is important to distinguish between the two language features being discussed here, that is, type declarations and default argument values.
The first function is only using type declarations, this means that the input argument has to be of the type int or NULL.
The second function is using both type declarations and default argument values, this means that the argument has to be of the type int or NULL but if omitted it will default to NULL.
Take your first function, if you simply called test() without passing anything to it, you'd get:
PHP Fatal error: Uncaught ArgumentCountError: Too few arguments to function test() [...]
which is correct as the function expects either int or NULL but got neither whereas for the second, as you have defined the argument with a default value, it would run without errors.
Code
function test(?int $var) {
var_dump($var);
}
function test2(int $var = null) {
var_dump($var);
}
test(1); // fine
test(); // error
test2(1); // fine
test2(); // fine
As far as performance goes, the difference is probably negligible, nothing significant enough that would be a cause for concern.
Live Example
Repl

If the language were designed today, int $var = null would probably be an error, and should really be written ?int $var = null. The two parts mean different things:
The ? indicates that null is a valid value for that parameter.
The = null indicates that null is the default if the parameter is not passed.
However, before the ?type syntax was introduced, there was a special case in the language: if null is given as the default for a parameter, then it is legal to pass null to that parameter, even if the type declaration would otherwise prevent it.

The difference is how you can call the function:
// public function test(?int $var)
$foo->test("x"); // does not work (Argument 1 passed to Foo::test() must be of the type int or null, string given)
$foo->test(123); // works
$foo->test(null); // works
$foo->test(); // does not work (Too few arguments to function Foo::test(), 0 passed)
// public function test(int $var = null)
$foo->test("x"); // does not work (Argument 1 passed to Foo::test() must be of the type int or null, string given)
$foo->test(123); // works
$foo->test(null); // works
$foo->test(); // works
The difference is that you cannot call the function as ->test() using the first syntax.

Related

Arguments and type declarations using "?" for nullable values

PHP Version 7.3.8
References:
https://www.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration
I've been reading this sentence on the above page:
As of PHP 7.1.0, return values can be marked as nullable by prefixing the type name with a question mark (?). This signifies that the function returns either the specified type or NULL.
The following values were passed and the results are documented here:
$marketID = 'abcdef'; // Result is: Throws a type error.
$marketID = '12345'; // Result is: It will be cast as an int.
$marketID = 12345; // Result is: It will successfully execute.
$marketID = null; // Result is: It will successfully execute.
// App controller
protected function setMarketID(?int $marketID)
{
$this->marketID = $marketID;
return $this;
}
protected function getMarketID()
{
// Will return an int or null.
return $this->marketID;
}
Is it considered acceptable to code like this. IE: Use (?) in this way to accept type and null as the manual states return values can be marked... NOT incoming values but it works? (Please see Edit)
Edit for anyone in the future reading this post:
// Will return an int or null.
If you pass an int argument to the setter the getter will automatically return an int and if you pass a null argument to the setter the getter will automatically return null.
#yivi
Yes very helpful thanks. I have added strict types and it works perfectly and I have also added your advice of declaring a return value. IE:
protected function getMarketId():?int
And again works perfectly.
You haven't declared a single return type in your example.
In setMarketId() you declared a parameter type (?int). So this method will accept either an integer or null.
If you declare you are using strict_types, then the method will not even accept '12345' or 123.5, and only accept a proper int or a null value.
Declaring a return value like the one that you expect (and consistent with what getMarketId() would return), would be accomplished like this:
protected function getMarketId():?int
{
return $this-marketId;
}
Of course it is "acceptable" to declare a type is nullable. When it makes sense or not it will depend entirely on your application, but it's a very common usage.

PHP7: Methods with a scalar type declaration refuse to type juggle NULL values, even in weak/coercive mode

As of PHP 7.0, the scalar type hints int, float, string, and bool can be included in method signatures. By default, these type declarations operate in weak/coercive mode (or "type juggling" mode). According to the PHP manual:
PHP will coerce values of the wrong type into the expected scalar type if possible. For example, a function that is given an integer for a parameter that expects a string will get a variable of type string.
But even though it is possible to coerce NULL into the integer 0, a method with an int typehint will refuse to coerce an inbound value of NULL to the integer 0.
<?php
class MyClass
{
public function test(int $arg)
{
echo $arg;
}
}
$obj = new MyClass();
$obj->test('123'); // 123
$obj->test(false); // 0
$obj->test(null); // TypeError: Argument 1 passed to MyClass::test()
// must be of the type integer, null given
And similarly, even though it is possible to coerce NULL into the boolean false, a method with a bool typehint will refuse to coerce an inbound value of NULL to the boolean false. The same goes for the float and string type hints as well.
This behavior seems to contradict the documentation on php.net. What's going on here?
There is currently no way to allow a method with a scalar type hint to automatically type juggle inbound NULL values to the declared type.
Per the RFC responsible for introducing this feature into PHP 7:
The weak type checking rules for the new scalar type declarations are mostly (emphasis added) the same as those of extension and built-in PHP functions. The only exception to this is the handling of NULL: in order to be consistent with our existing type declarations for classes, callables and arrays, NULL is not accepted by default, unless it is a parameter and is explicitly given a default value of NULL.
However, NULL values can be accepted as NULLs in the following scenarios:
<?php
class MyClass
{
// PHP 7.0+
public function testA(int $arg = null)
{
if (null === $arg) {
echo 'The argument is NULL!';
}
}
// PHP 7.1+
// https://wiki.php.net/rfc/nullable_types
public function testB(?int $arg)
{
if (null === $arg) {
echo 'The argument is NULL!';
}
}
}
$obj = new MyClass();
$obj->testA(null); // The argument is NULL!
$obj->testB(null); // The argument is NULL!

Default value for parameters can only be null [duplicate]

PHP 5 Type Hinting
PHP 5 introduces Type Hinting. Functions are now able to force parameters to be objects (by specifying the name of the class in the function prototype) or arrays (since PHP 5.1). However, if NULL is used as the default parameter value, it will be allowed as an argument for any later call.
The following excerpt from the above:
if NULL is used as the default parameter value, it will be allowed as an argument for any later call.
Does the above mean:
if default parameters are to used use with type hinting, it can have only have NULL as the default value.
i.e. the code in code1 is wrong and results in:
Fatal error: Default value for parameters with a class type hint can only be NULL
code1:
function setName ( string $name = "happ") {
...
}
Where as code in code2 is right:
code2:
function setName ( string $name = NULL) {
...
}
Why is this constraint assigned in php?
You can't typehint strings, you can only typehint objects and arrays, so this is incorrect:
function setName ( string $name = "happ") {
(The reason you don't get a compile-time error here is because PHP is interpreting "string" as the name of a class.)
The wording in the docs means that if you do this:
function foo(Foo $arg) {
Then the argument passed to foo() must be an instance of object Foo. But if you do this:
function foo(Foo $arg = null) {
Then the argument passed to foo() can either be an instance of object Foo, or null. Note also that if you do this:
function foo(array $foo = array(1, 2, 3))
Then you can't call foo(null). If you want this functionality, you can do something like this:
function foo(array $foo = null) {
if ($foo === null) {
$foo = array(1, 2, 3);
}
[Edit 1] As of PHP 5.4, you can typehint callable:
function foo(callable $callback) {
call_user_func($callback);
}
[Edit 2] As of PHP 7.0, you can typehint bool, float, int, and string. This makes the code in the question valid syntax. As of PHP 7.1, you can typehint iterable.
Type declarations (also known as type hints in PHP 5) of a string type are supported in PHP 7.
The valid types are:
Class/interface name (>=PHP 5.0.0);
self (>=PHP 5.0.0);
array (>=PHP 5.1.0);
callable (>=PHP 5.4.0);
bool, float, int, string (>=PHP 7.0.0);
iterable - either an array or an instanceof Traversable (>=PHP 7.1.0).
This is a matter of compilation time versus run time values. At compilation only literal values (strings, numbers, booleans and NULL) are allowed. The PHP processor can't know about all the possible classes at this time and so you can't create an instance of an object in the function arguments.
What I'm expecting from the excerpt is that, while normally passing NULL into a type hinted function will throw an Exception/Error. If you set a default as NULL then it won't throw this exception if NULL is passed. I haven't tested it, just what I would expect.

What is the proper way to set default method parameter in PHP?

Let's say you have a method that expects a numerical value as an argument. What's the proper way to set a default value in the event the argument doesn't exist?
public function saveme($myvar = false) {}
public function saveme($myvar = '') {}
public function saveme($myvar = NULL) {}
public function saveme($myvar = 0) {}
All of those are valid, and neither one is "more correct" for all cases. It depends entirely on what the valid range of values are for the function and what the function is supposed to do. Typically, the default type will match the expected input type, unless you want a place-holder for "no value" in which case null is typically used.
If your function can perform something useful with default of 0, use 0. If your function needs to tell the difference between having been explicitly given a single argument of 0 and having been given no arguments, or if 0 is not a sane default, use null.
From the PHP manual:
function myFunc ($myType = 0) {}

Type Hinting: Default Parameters

PHP 5 Type Hinting
PHP 5 introduces Type Hinting. Functions are now able to force parameters to be objects (by specifying the name of the class in the function prototype) or arrays (since PHP 5.1). However, if NULL is used as the default parameter value, it will be allowed as an argument for any later call.
The following excerpt from the above:
if NULL is used as the default parameter value, it will be allowed as an argument for any later call.
Does the above mean:
if default parameters are to used use with type hinting, it can have only have NULL as the default value.
i.e. the code in code1 is wrong and results in:
Fatal error: Default value for parameters with a class type hint can only be NULL
code1:
function setName ( string $name = "happ") {
...
}
Where as code in code2 is right:
code2:
function setName ( string $name = NULL) {
...
}
Why is this constraint assigned in php?
You can't typehint strings, you can only typehint objects and arrays, so this is incorrect:
function setName ( string $name = "happ") {
(The reason you don't get a compile-time error here is because PHP is interpreting "string" as the name of a class.)
The wording in the docs means that if you do this:
function foo(Foo $arg) {
Then the argument passed to foo() must be an instance of object Foo. But if you do this:
function foo(Foo $arg = null) {
Then the argument passed to foo() can either be an instance of object Foo, or null. Note also that if you do this:
function foo(array $foo = array(1, 2, 3))
Then you can't call foo(null). If you want this functionality, you can do something like this:
function foo(array $foo = null) {
if ($foo === null) {
$foo = array(1, 2, 3);
}
[Edit 1] As of PHP 5.4, you can typehint callable:
function foo(callable $callback) {
call_user_func($callback);
}
[Edit 2] As of PHP 7.0, you can typehint bool, float, int, and string. This makes the code in the question valid syntax. As of PHP 7.1, you can typehint iterable.
Type declarations (also known as type hints in PHP 5) of a string type are supported in PHP 7.
The valid types are:
Class/interface name (>=PHP 5.0.0);
self (>=PHP 5.0.0);
array (>=PHP 5.1.0);
callable (>=PHP 5.4.0);
bool, float, int, string (>=PHP 7.0.0);
iterable - either an array or an instanceof Traversable (>=PHP 7.1.0).
This is a matter of compilation time versus run time values. At compilation only literal values (strings, numbers, booleans and NULL) are allowed. The PHP processor can't know about all the possible classes at this time and so you can't create an instance of an object in the function arguments.
What I'm expecting from the excerpt is that, while normally passing NULL into a type hinted function will throw an Exception/Error. If you set a default as NULL then it won't throw this exception if NULL is passed. I haven't tested it, just what I would expect.

Categories