Hello Internals,
I'd like to know what would be people's feelings towards having a numeric
type. I remember reading the nullable casting RFC (
https://wiki.php.net/rfc/nullable-casting) and it's discussion (
https://externals.io/message/105122). Although I would very much prefer to
have nullable casting, it seems to be a bit controversial and perhaps
Numeric Type could be a middle ground solution.
The primary intent would be to safely pass around input that usually comes
from HTTP, CI or Database. Whenever interacting with these data providers,
string is a common format. is_int
will not return true for integer values
that are string, instead we need to rely on is_numeric
. Similarly, I
think we could benefit from having foo(?numeric $value)
as a type-safe
mechanism to transfer data around without having to forcefully check for
numeric value before casting. If we simply (int) $value
, we may end up
casting null
to 0
. Type-hitting ?numeric
could be a compromise.
foo(?int $number);
foo($_GET['param']); // TypeError: foo() expects int or null, string given
foo((int) $_GET['param']); // null becomes 0
foo($_GET['param'] ? (int) $_GET['param'] : null); // expected behavior
bar(?numeric $number);
bar($_GET['param']); // would work with any value that passes `is_numeric()`
I also think this approach have a benefit over PHP 8 Union Type
(int|string) because is_numeric()
does not return true for string values
and for consistency the numeric
type-hint would behave similarly.
Thoughts?
--
Marco Aurélio Deleu
Hi Marco,
The primary intent would be to safely pass around input that usually comes
from HTTP, CI or Database. Whenever interacting with these data providers,
string is a common format.is_int
will not return true for integer values
that are string, instead we need to rely onis_numeric
.
The problem with is_numeric()
is that it uses an extremely broad definition
of "numeric", which is not often what you actually want - for instance,
is_numeric("-2.56E-90") returns true.
The case I frequently see is much stricter: the variable should either be
an integer, or a string representation of an integer. Whenever this comes
up, I struggle for what the correct check actually is:
- is_int($x) won't accept any strings
- is_numeric($x) will accept far too much
- an int type annotation with strict_types=0 is similar to is_numeric; in
strict_types=1 it's the same as is_int - (int)$x will never give an error (meaning it's oddly easy to accidentally
suppress errors by trying to make code run under strict_types=1) - (int)$x != 0 is sometimes good enough, as long as zero is not a valid
value (if you use this when accepting Unix timestamps, for instance, Jan
1st 1970 is going to be rejected!) - ctype_digit( (string)$x ) is confusing to read (the cast is required to
accept actual integers, and the name is odd out of its intended context);
it will also reject any negative numbers, which may or may not be desirable
The only suitable built-in function I'm aware of is filter_var, which has a
pretty horrible API:
if ( filter_var($x, FILTER_VALIDATE_INT) !== false ) ...
In the common case that you want to cast the variable to int on success (to
pass to a strictly typed function), you would need to do something like
this:
$x = filter_var($x, FILTER_VALIDATE_INT);
if ( $x === false ) {
throw new TypeError;
}
I would love to see the language have a built-in short-hand for that, in
the form of some kind of "strict cast", e.g.:
$x = (int!)$x;
or:
$x = (strict int)$x;
Once converted in that way, you could safely pass to a parameter marked int
in strict mode, so wouldn't need a new pseudo-type.
Regards,
Rowan Tommins
[IMSoP]
Hi Rowan,
Although I understand your concerns, I wouldn't consider them a roadblock
for this feature. My rationale is that is_numeric
has been with PHP for
so long that I don't think we would ever be able to have a numeric
type-hint that doesn't align with it, which imho means we either have it or
we don't, a 3rd option where a possible type-hint that is inconsistent with
the function seems worse than not having it. I fully agree with your
concerns that it might not fully represent what the developer wants, but I
would make a judgement call when using it the same way developers have to
make a judgement call when using is_numeric
. It might allow far more than
you want, but there seems to be enough use-case that it fits where the
downside is negligible or even insignificant.
My point is that I don't look at the language validation to protect my code
against unexpected values and I can setup validation to deal with that.
What complicates things is that I want to avoid having a force-cast which
is not safe for my usage because I want to preserve null
while not having
to disable strict_types.
On Tue, Jun 2, 2020 at 5:47 PM Rowan Tommins rowan.collins@gmail.com
wrote:
Hi Marco,
The primary intent would be to safely pass around input that usually
comes
from HTTP, CI or Database. Whenever interacting with these data
providers,
string is a common format.is_int
will not return true for integer
values
that are string, instead we need to rely onis_numeric
.The problem with
is_numeric()
is that it uses an extremely broad definition
of "numeric", which is not often what you actually want - for instance,
is_numeric("-2.56E-90") returns true.The case I frequently see is much stricter: the variable should either be
an integer, or a string representation of an integer. Whenever this comes
up, I struggle for what the correct check actually is:
- is_int($x) won't accept any strings
- is_numeric($x) will accept far too much
- an int type annotation with strict_types=0 is similar to is_numeric; in
strict_types=1 it's the same as is_int- (int)$x will never give an error (meaning it's oddly easy to accidentally
suppress errors by trying to make code run under strict_types=1)- (int)$x != 0 is sometimes good enough, as long as zero is not a valid
value (if you use this when accepting Unix timestamps, for instance, Jan
1st 1970 is going to be rejected!)- ctype_digit( (string)$x ) is confusing to read (the cast is required to
accept actual integers, and the name is odd out of its intended context);
it will also reject any negative numbers, which may or may not be desirableThe only suitable built-in function I'm aware of is filter_var, which has a
pretty horrible API:if ( filter_var($x, FILTER_VALIDATE_INT) !== false ) ...
In the common case that you want to cast the variable to int on success (to
pass to a strictly typed function), you would need to do something like
this:$x = filter_var($x, FILTER_VALIDATE_INT);
if ( $x === false ) {
throw new TypeError;
}I would love to see the language have a built-in short-hand for that, in
the form of some kind of "strict cast", e.g.:$x = (int!)$x;
or:
$x = (strict int)$x;
Once converted in that way, you could safely pass to a parameter marked int
in strict mode, so wouldn't need a new pseudo-type.Regards,
Rowan Tommins
[IMSoP]
--
Marco Aurélio Deleu
is_numeric
has been with PHP for
so long that I don't think we would ever be able to have anumeric
type-hint that doesn't align with it, which imho means we either have it or
we don't, a 3rd option where a possible type-hint that is inconsistent with
the function seems worse than not having it.
I agree, but my conclusion is that the best answer is "don't have it" - I don't think the use cases are common enough to deserve a language feature rather than just using the existing function.
My point is that I don't look at the language validation to protect my code
against unexpected values and I can setup validation to deal with that.
If it's not to protect against unexpected values, then what is it for?
What complicates things is that I want to avoid having a force-cast
which is not safe for my usage because I want to preservenull
Yes, the combination of strict cast and nullable cast would sometimes be useful, although the syntax might get messy - (int?!)$foo looks horrible.
Since we already have ?int though, this doesn't seem a strong reason to add ?numeric meaning almost the same thing.
while not having to disable strict_types
It feels a bit like you want to "have your cake and eat it" here - if you want a loose check and aren't worried about a few unusual values getting through, why are you running in strict_types mode in the first place?
Regards,
--
Rowan Tommins
[IMSoP]
Hello Internals,
I'd like to know what would be people's feelings towards having a
numeric
type. I remember reading the nullable casting RFC (
https://wiki.php.net/rfc/nullable-casting) and it's discussion (
https://externals.io/message/105122). Although I would very much prefer to
have nullable casting, it seems to be a bit controversial and perhaps
Numeric Type could be a middle ground solution.The primary intent would be to safely pass around input that usually comes
from HTTP, CI or Database. Whenever interacting with these data providers,
string is a common format.is_int
will not return true for integer values
that are string, instead we need to rely onis_numeric
. Similarly, I
think we could benefit from havingfoo(?numeric $value)
as a type-safe
mechanism to transfer data around without having to forcefully check for
numeric value before casting. If we simply(int) $value
, we may end up
castingnull
to0
. Type-hitting?numeric
could be a compromise.foo(?int $number); foo($_GET['param']); // TypeError: foo() expects int or null, string given foo((int) $_GET['param']); // null becomes 0 foo($_GET['param'] ? (int) $_GET['param'] : null); // expected behavior bar(?numeric $number); bar($_GET['param']); // would work with any value that passes `is_numeric()`
I also think this approach have a benefit over PHP 8 Union Type
(int|string) becauseis_numeric()
does not return true for string values
and for consistency thenumeric
type-hint would behave similarly.
To clarify, what are the semantics of numeric? If you have a numeric
argument and "42" is passed in, will it be converted to int(42), or will it
remain as "42" (as is_numeric("42") returns true)?
From your description, it seems pretty clear that you really want the
nullable cast instead and "numeric" is just a workaround that makes a
specific case tenable. That does not seem like a good position to argue
from. I think it would make more sense to see the nullable cast RFC through
to the end first. As far as I remember, it never did go to vote.
Regards,
Nikita
To clarify, what are the semantics of numeric? If you have a numeric
argument and "42" is passed in, will it be converted to int(42), or will it
remain as "42" (as is_numeric("42") returns true)?
I imagine it would remain as "42" since implicit casting is the source of
some of the problems with the PHP type system. To be honest, I'd be fine
with either, but I imagine that advocating for yet another implicit casting
on a specific type-hint would just lead to more edge-cases and not be
treated as other types are.
From your description, it seems pretty clear that you really want the
nullable cast instead and "numeric" is just a workaround that makes a
specific case tenable. That does not seem like a good position to argue
from. I think it would make more sense to see the nullable cast RFC through
to the end first. As far as I remember, it never did go to vote.
There's no denying this, however when I looked at the discussion, I got the
feeling that it may not have move forward due to the arguments against it.
The reason I decided to send this email even though it doesn't seem like a
good arguing position is the fact that when I looked back at all the times
I wanted nullable casting they were all int|string
scenarios. If a cheap
feature such as numeric
type-hint can solve most use cases, I'd be more
than willing to deal with the custom code I will continue to have to write
for cases that are not int|string
.
I don't know if there will ever be an interest in deprecating "42" + 10,
which correctly results in 52. If there is, I can understand how the
development of this feature might be counter-productive. Other than that, I
think the benefit of this feature outweight the cost specially when looked
at it from the is_numeric
perspective, which is an existing behavior.
Hello Internals,
I'd like to know what would be people's feelings towards having a
numeric
type. I remember reading the nullable casting RFC (
https://wiki.php.net/rfc/nullable-casting) and it's discussion (
https://externals.io/message/105122). Although I would very much prefer
to
have nullable casting, it seems to be a bit controversial and perhaps
Numeric Type could be a middle ground solution.The primary intent would be to safely pass around input that usually comes
from HTTP, CI or Database. Whenever interacting with these data providers,
string is a common format.is_int
will not return true for integer
values
that are string, instead we need to rely onis_numeric
. Similarly, I
think we could benefit from havingfoo(?numeric $value)
as a type-safe
mechanism to transfer data around without having to forcefully check for
numeric value before casting. If we simply(int) $value
, we may end up
castingnull
to0
. Type-hitting?numeric
could be a compromise.foo(?int $number); foo($_GET['param']); // TypeError: foo() expects int or null, string given foo((int) $_GET['param']); // null becomes 0 foo($_GET['param'] ? (int) $_GET['param'] : null); // expected behavior bar(?numeric $number); bar($_GET['param']); // would work with any value that passes `is_numeric()`
I also think this approach have a benefit over PHP 8 Union Type
(int|string) becauseis_numeric()
does not return true for string values
and for consistency thenumeric
type-hint would behave similarly.To clarify, what are the semantics of numeric? If you have a numeric
argument and "42" is passed in, will it be converted to int(42), or will it
remain as "42" (as is_numeric("42") returns true)?From your description, it seems pretty clear that you really want the
nullable cast instead and "numeric" is just a workaround that makes a
specific case tenable. That does not seem like a good position to argue
from. I think it would make more sense to see the nullable cast RFC through
to the end first. As far as I remember, it never did go to vote.Regards,
Nikita
--
Marco Aurélio Deleu
Hi all,
One thought is that is_numeric(true)
will return false
, but the int
type-hint will accept it as a 1
.
The other thought I had was no one mentioned floats, so maybe a number
type could be argued for but with mixed types that can be solved by
int|float
now.
Thanks,
Peter
Hello Internals,
I'd like to know what would be people's feelings towards having a
numeric
type. I remember reading the nullable casting RFC (
https://wiki.php.net/rfc/nullable-casting) and it's discussion (
https://externals.io/message/105122). Although I would very much prefer to
have nullable casting, it seems to be a bit controversial and perhaps
Numeric Type could be a middle ground solution.The primary intent would be to safely pass around input that usually comes
from HTTP, CI or Database. Whenever interacting with these data providers,
string is a common format.is_int
will not return true for integer values
that are string, instead we need to rely onis_numeric
. Similarly, I
think we could benefit from havingfoo(?numeric $value)
as a type-safe
mechanism to transfer data around without having to forcefully check for
numeric value before casting. If we simply(int) $value
, we may end up
castingnull
to0
. Type-hitting?numeric
could be a compromise.foo(?int $number); foo($_GET['param']); // TypeError: foo() expects int or null, string given foo((int) $_GET['param']); // null becomes 0 foo($_GET['param'] ? (int) $_GET['param'] : null); // expected behavior bar(?numeric $number); bar($_GET['param']); // would work with any value that passes `is_numeric()`
I also think this approach have a benefit over PHP 8 Union Type
(int|string) becauseis_numeric()
does not return true for string values
and for consistency thenumeric
type-hint would behave similarly.Thoughts?
--
Marco Aurélio Deleu