Hey all,
I know given all the discussion about this topic lately, this is a hot
topic. But I whipped together a quick POC patch to implement scalar
type casting for function parameters. Let me describe it:
Patch: https://gist.github.com/1947259
Example:
function foo( (int) $bar ) {
var_dump($bar);
}
foo(1); // int(1)
foo("1"); // int(1)
foo(1.5); // int(1)
foo("foo"); // E_RECOVERABLE_ERROR
- Expected integer
foo(array()); // E_RECOVERABLE_ERROR
Right now, I only implemented the checks for (int), but I add the
parser constructs for (int), (float), (bool), (string) and (object)...
Now, let's talk why I did what I did:
Why did I use cast syntax? Well, there are really three main reasons.
First off, to indicate that a cast may happen. Second, to prevent
needing new tokens (and hence reserved words). And third to provide a
distinction between a string class type hint and a string scalar type
hint.
Why did I only implement (int)? Well, because I just wanted to build
a quick dirty POC that can be executed to see the semantics of
operation. There are issues with it now, so rather than doing all the
work to re-do it later, I just implemented int...
Why implement (object)? Because right now, there's no way to say you
want to accept a generic object without caring about type. So the
(object) cast/hint would then provide that ability to accept a generic
object.
Why not implement (resource)? Because that would require a new parser
token, as it's not available now...
How does the casting work? Right now, it's using a copy of the same
rules that internal functions use with zend_parse_parameters. That
way, it brings the operating semantics of internal functions and
userland functions more inline with each other.
So with that said, there are some (significant) issues with the patch:
- First off, the arg checks happen before separation of the zval on
non-referenced calls. So that means the cast effects the original
zval AND the argument. Which is a no-go for a production patch. So
that means that the cast logic would need to be put after the zval
split. But we'd still want the checks first, so it's not too
difficult to segregate, just requires deeper changes. It's not
difficult (that I can see yet), just more work... Example of the
problem:
sapi/cli/php -r 'function foo((int) $bar) { var_dump($bar); } $a =
"1"; foo($a); var_dump($a);'
int(1)
int(1)
- Right now, the zend_aprse_arg_impl (
http://lxr.php.net/xref/PHP_5_4/Zend/zend_API.c#zend_parse_arg_impl )
that's used by internal functions is defined as static. So we'd be
copying a lot of the code back and forth. In the production patch,
I'd also want to re-factor that out a bit into either functions or
macros to handle the type conversion and casting in both places. That
way, both systems would behave identical (or as close as possible).
So, with that said, what do you think? Is this something worth
pursuing? Are there any fundamental issues that I'm missing? What
else would we need to cover in a production patch and RFC?
Thanks,
Anthony
I can't comment on the internal implementation, but I like the use of
the casting syntax. It's not as pretty, but make the intent clear, and
there's not BC issues with class names.
David
Hey all,
I know given all the discussion about this topic lately, this is a hot
topic. But I whipped together a quick POC patch to implement scalar
type casting for function parameters. Let me describe it:Patch: https://gist.github.com/1947259
Example:
function foo( (int) $bar ) {
var_dump($bar);
}foo(1); // int(1)
foo("1"); // int(1)
foo(1.5); // int(1)
foo("foo"); //E_RECOVERABLE_ERROR
- Expected integer
foo(array()); //E_RECOVERABLE_ERROR
Right now, I only implemented the checks for (int), but I add the
parser constructs for (int), (float), (bool), (string) and (object)...Now, let's talk why I did what I did:
Why did I use cast syntax? Well, there are really three main reasons.
First off, to indicate that a cast may happen. Second, to prevent
needing new tokens (and hence reserved words). And third to provide a
distinction between a string class type hint and a string scalar type
hint.Why did I only implement (int)? Well, because I just wanted to build
a quick dirty POC that can be executed to see the semantics of
operation. There are issues with it now, so rather than doing all the
work to re-do it later, I just implemented int...Why implement (object)? Because right now, there's no way to say you
want to accept a generic object without caring about type. So the
(object) cast/hint would then provide that ability to accept a generic
object.Why not implement (resource)? Because that would require a new parser
token, as it's not available now...How does the casting work? Right now, it's using a copy of the same
rules that internal functions use with zend_parse_parameters. That
way, it brings the operating semantics of internal functions and
userland functions more inline with each other.So with that said, there are some (significant) issues with the patch:
- First off, the arg checks happen before separation of the zval on
non-referenced calls. So that means the cast effects the original
zval AND the argument. Which is a no-go for a production patch. So
that means that the cast logic would need to be put after the zval
split. But we'd still want the checks first, so it's not too
difficult to segregate, just requires deeper changes. It's not
difficult (that I can see yet), just more work... Example of the
problem:sapi/cli/php -r 'function foo((int) $bar) { var_dump($bar); } $a =
"1"; foo($a); var_dump($a);'
int(1)
int(1)
- Right now, the zend_aprse_arg_impl (
http://lxr.php.net/xref/PHP_5_4/Zend/zend_API.c#zend_parse_arg_impl )
that's used by internal functions is defined as static. So we'd be
copying a lot of the code back and forth. In the production patch,
I'd also want to re-factor that out a bit into either functions or
macros to handle the type conversion and casting in both places. That
way, both systems would behave identical (or as close as possible).So, with that said, what do you think? Is this something worth
pursuing? Are there any fundamental issues that I'm missing? What
else would we need to cover in a production patch and RFC?Thanks,
Anthony
Whoops, I linked to the wrong gist... Here's the proper one:
https://gist.github.com/1955338
I can't comment on the internal implementation, but I like the use of
the casting syntax. It's not as pretty, but make the intent clear, and
there's not BC issues with class names.David
Hey all,
I know given all the discussion about this topic lately, this is a hot
topic. But I whipped together a quick POC patch to implement scalar
type casting for function parameters. Let me describe it:Patch: https://gist.github.com/1947259
Example:
function foo( (int) $bar ) {
var_dump($bar);
}foo(1); // int(1)
foo("1"); // int(1)
foo(1.5); // int(1)
foo("foo"); //E_RECOVERABLE_ERROR
- Expected integer
foo(array()); //E_RECOVERABLE_ERROR
Right now, I only implemented the checks for (int), but I add the
parser constructs for (int), (float), (bool), (string) and (object)...Now, let's talk why I did what I did:
Why did I use cast syntax? Well, there are really three main reasons.
First off, to indicate that a cast may happen. Second, to prevent
needing new tokens (and hence reserved words). And third to provide a
distinction between a string class type hint and a string scalar type
hint.Why did I only implement (int)? Well, because I just wanted to build
a quick dirty POC that can be executed to see the semantics of
operation. There are issues with it now, so rather than doing all the
work to re-do it later, I just implemented int...Why implement (object)? Because right now, there's no way to say you
want to accept a generic object without caring about type. So the
(object) cast/hint would then provide that ability to accept a generic
object.Why not implement (resource)? Because that would require a new parser
token, as it's not available now...How does the casting work? Right now, it's using a copy of the same
rules that internal functions use with zend_parse_parameters. That
way, it brings the operating semantics of internal functions and
userland functions more inline with each other.So with that said, there are some (significant) issues with the patch:
- First off, the arg checks happen before separation of the zval on
non-referenced calls. So that means the cast effects the original
zval AND the argument. Which is a no-go for a production patch. So
that means that the cast logic would need to be put after the zval
split. But we'd still want the checks first, so it's not too
difficult to segregate, just requires deeper changes. It's not
difficult (that I can see yet), just more work... Example of the
problem:sapi/cli/php -r 'function foo((int) $bar) { var_dump($bar); } $a =
"1"; foo($a); var_dump($a);'
int(1)
int(1)
- Right now, the zend_aprse_arg_impl (
http://lxr.php.net/xref/PHP_5_4/Zend/zend_API.c#zend_parse_arg_impl )
that's used by internal functions is defined as static. So we'd be
copying a lot of the code back and forth. In the production patch,
I'd also want to re-factor that out a bit into either functions or
macros to handle the type conversion and casting in both places. That
way, both systems would behave identical (or as close as possible).So, with that said, what do you think? Is this something worth
pursuing? Are there any fundamental issues that I'm missing? What
else would we need to cover in a production patch and RFC?Thanks,
Anthony
I do like retaining the same functional behavior afforded to internal functions.
Cast syntax seems awkward to me though. Some things that immediately come to mind:
// ?? lossless, but wrong type. Does this cast or fail?
(function((object)$o){})(array());
// ?? If (object) is allowed, (array) seems like it would be valid too,
// but since it is also different from a straight array type hint, it
// seems like it would do something different. Does this cast or fail?
(function((array)$o){})($object);
Cast syntax differs from documentation meaning the same thing.
Cast syntax may re-open the consistency vs. BC question previously worked out.
John Crenshaw
Priacta, Inc.
Hey all,
I know given all the discussion about this topic lately, this is a hot
topic. But I whipped together a quick POC patch to implement scalar
type casting for function parameters. Let me describe it:Patch: https://gist.github.com/1947259
Example:
function foo( (int) $bar ) {
var_dump($bar);
}foo(1); // int(1)
foo("1"); // int(1)
foo(1.5); // int(1)
foo("foo"); //E_RECOVERABLE_ERROR
- Expected integer foo(array()); //
E_RECOVERABLE_ERROR
Right now, I only implemented the checks for (int), but I add the
parser constructs for (int), (float), (bool), (string) and (object)...Now, let's talk why I did what I did:
Why did I use cast syntax? Well, there are really three main reasons.
First off, to indicate that a cast may happen. Second, to prevent
needing new tokens (and hence reserved words). And third to provide a
distinction between a string class type hint and a string scalar type
hint.Why did I only implement (int)? Well, because I just wanted to build
a quick dirty POC that can be executed to see the semantics of
operation. There are issues with it now, so rather than doing all the
work to re-do it later, I just implemented int...Why implement (object)? Because right now, there's no way to say you
want to accept a generic object without caring about type. So the
(object) cast/hint would then provide that ability to accept a generic
object.Why not implement (resource)? Because that would require a new parser
token, as it's not available now...How does the casting work? Right now, it's using a copy of the same
rules that internal functions use with zend_parse_parameters. That
way, it brings the operating semantics of internal functions and
userland functions more inline with each other.So with that said, there are some (significant) issues with the patch:
- First off, the arg checks happen before separation of the zval on
non-referenced calls. So that means the cast effects the original
zval AND the argument. Which is a no-go for a production patch. So
that means that the cast logic would need to be put after the zval
split. But we'd still want the checks first, so it's not too
difficult to segregate, just requires deeper changes. It's not
difficult (that I can see yet), just more work... Example of the
problem:sapi/cli/php -r 'function foo((int) $bar) { var_dump($bar); } $a =
"1"; foo($a); var_dump($a);'
int(1)
int(1)
- Right now, the zend_aprse_arg_impl (
http://lxr.php.net/xref/PHP_5_4/Zend/zend_API.c#zend_parse_arg_impl )
that's used by internal functions is defined as static. So we'd be
copying a lot of the code back and forth. In the production patch,
I'd also want to re-factor that out a bit into either functions or
macros to handle the type conversion and casting in both places. That
way, both systems would behave identical (or as close as possible).So, with that said, what do you think? Is this something worth
pursuing? Are there any fundamental issues that I'm missing? What
else would we need to cover in a production patch and RFC?Thanks,
Anthony
Anthony Ferrara wrote:
foo(1); // int(1)
foo("1"); // int(1)
foo(1.5); // int(1)
foo("foo"); //E_RECOVERABLE_ERROR
- Expected integer
foo(array()); //E_RECOVERABLE_ERROR
Double-checking, but this is different to normal typecasting, isn't it?
If so, it might be a bit confusing using the typecasting syntax.
--
Ryan McCue
<http://ryanmccue.info/
Ryan McCue wrote:
Double-checking, but this is different to normal typecasting, isn't it?
If so, it might be a bit confusing using the typecasting syntax.
Could have sworn I saw that "123foo" would give E_RECOVERABLE_ERROR, but
I can't find that now, so possibly disregard this.
--
Ryan McCue
<http://ryanmccue.info/
+1 for the syntax. There are two nice side effects I would like to
underline.
Error-raising can be clearly delegated to the type juggling mechanism.
There will be no need to implement anything new here but reuse the existing
type juggling system of PHP. That would be very consistent. At the end of
the day, these two syntaxes are completely equivalent:
function foo((int) $bar) { ... }
function foo($bar) { $bar = (int)$bar; ... }
If we put passing by reference into the picture, it is easy to see why the
following syntax should be a parsing error:
function foo((int) & $bar) { } // parsing error
On the contrary, were there no brackets, the resemblance to the syntax of C
would be confusing.
Lazare INEPOLOGLOU
Ingénieur Logiciel
2012/3/2 Anthony Ferrara ircmaxell@gmail.com
Hey all,
I know given all the discussion about this topic lately, this is a hot
topic. But I whipped together a quick POC patch to implement scalar
type casting for function parameters. Let me describe it:Patch: https://gist.github.com/1947259
Example:
function foo( (int) $bar ) {
var_dump($bar);
}foo(1); // int(1)
foo("1"); // int(1)
foo(1.5); // int(1)
foo("foo"); //E_RECOVERABLE_ERROR
- Expected integer
foo(array()); //E_RECOVERABLE_ERROR
Right now, I only implemented the checks for (int), but I add the
parser constructs for (int), (float), (bool), (string) and (object)...Now, let's talk why I did what I did:
Why did I use cast syntax? Well, there are really three main reasons.
First off, to indicate that a cast may happen. Second, to prevent
needing new tokens (and hence reserved words). And third to provide a
distinction between a string class type hint and a string scalar type
hint.Why did I only implement (int)? Well, because I just wanted to build
a quick dirty POC that can be executed to see the semantics of
operation. There are issues with it now, so rather than doing all the
work to re-do it later, I just implemented int...Why implement (object)? Because right now, there's no way to say you
want to accept a generic object without caring about type. So the
(object) cast/hint would then provide that ability to accept a generic
object.Why not implement (resource)? Because that would require a new parser
token, as it's not available now...How does the casting work? Right now, it's using a copy of the same
rules that internal functions use with zend_parse_parameters. That
way, it brings the operating semantics of internal functions and
userland functions more inline with each other.So with that said, there are some (significant) issues with the patch:
- First off, the arg checks happen before separation of the zval on
non-referenced calls. So that means the cast effects the original
zval AND the argument. Which is a no-go for a production patch. So
that means that the cast logic would need to be put after the zval
split. But we'd still want the checks first, so it's not too
difficult to segregate, just requires deeper changes. It's not
difficult (that I can see yet), just more work... Example of the
problem:sapi/cli/php -r 'function foo((int) $bar) { var_dump($bar); } $a =
"1"; foo($a); var_dump($a);'
int(1)
int(1)
- Right now, the zend_aprse_arg_impl (
http://lxr.php.net/xref/PHP_5_4/Zend/zend_API.c#zend_parse_arg_impl )
that's used by internal functions is defined as static. So we'd be
copying a lot of the code back and forth. In the production patch,
I'd also want to re-factor that out a bit into either functions or
macros to handle the type conversion and casting in both places. That
way, both systems would behave identical (or as close as possible).So, with that said, what do you think? Is this something worth
pursuing? Are there any fundamental issues that I'm missing? What
else would we need to cover in a production patch and RFC?Thanks,
Anthony
Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORS
Personally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...
-
Should (array) be supported? Perhaps. So at that point, foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))... -
Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value (if
possible) as well. -
What about consistency? Well, there currently is no consistency.
Internal function parameters behave one way, and explicit casts behave
another. And even more confusing implicit casts behave yet another
way ($a + $b). So to implement this, we'd need to be consistent with
one of them. Frankly, I would only want to add consistency to
internal function parameters, since the explicit cast is not useful
IMHO (it's identical to $bar = (int) $bar), at which point it's not
worth adding to save only that one line. But if we're consistent with
internal function parameter checking, then it becomes quite useful.
We can throw warnings on unclean conversion and prevent execution of
the function... That way, all function calls behave the same (as much
as I hate the concept of warnings on type hint failure)... So, in
that case, function calls become an implicit cast to the type, which
is then why the stricter error handling (without breaking the spirit
or consistency). -
What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code would
still function fine.
Thoughts? Should I update the patch to be more inline with what I
said above (the implicit hints that are offered by the current
internal function argument mechanism:
sapi/cli/php -r 'function foo((int) $bar) { return $bar; } $a = "1";
var_dump(foo($a));'
int(1)
sapi/cli/php -r 'function foo((int) $bar) { return $bar; } $a =
"foo"; var_dump(foo($a));'
Warning: Argument 1 passed to foo() must be of the type integer,
string given, called in Command line code on line 1 and defined in
Command line code on line 1
However, since it's not raising a terminating error, more changes
would need to be made to the VM to check the return status of the
argument check (which is currently ignored) to determine if to proceed
with the function call, or just return null imediately...
Thoughts?
Anthony
+1 for the syntax. There are two nice side effects I would like to
underline.
Error-raising can be clearly delegated to the type juggling mechanism. There
will be no need to implement anything new here but reuse the existing type
juggling system of PHP. That would be very consistent. At the end of the
day, these two syntaxes are completely equivalent:function foo((int) $bar) { ... }
function foo($bar) { $bar = (int)$bar; ... }
If we put passing by reference into the picture, it is easy to see why the
following syntax should be a parsing error:function foo((int) & $bar) { } // parsing error
On the contrary, were there no brackets, the resemblance to the syntax of C
would be confusing.Lazare INEPOLOGLOU
Ingénieur Logiciel2012/3/2 Anthony Ferrara ircmaxell@gmail.com
Hey all,
I know given all the discussion about this topic lately, this is a hot
topic. But I whipped together a quick POC patch to implement scalar
type casting for function parameters. Let me describe it:Patch: https://gist.github.com/1947259
Example:
function foo( (int) $bar ) {
var_dump($bar);
}foo(1); // int(1)
foo("1"); // int(1)
foo(1.5); // int(1)
foo("foo"); //E_RECOVERABLE_ERROR
- Expected integer
foo(array()); //E_RECOVERABLE_ERROR
Right now, I only implemented the checks for (int), but I add the
parser constructs for (int), (float), (bool), (string) and (object)...Now, let's talk why I did what I did:
Why did I use cast syntax? Well, there are really three main reasons.
First off, to indicate that a cast may happen. Second, to prevent
needing new tokens (and hence reserved words). And third to provide a
distinction between a string class type hint and a string scalar type
hint.Why did I only implement (int)? Well, because I just wanted to build
a quick dirty POC that can be executed to see the semantics of
operation. There are issues with it now, so rather than doing all the
work to re-do it later, I just implemented int...Why implement (object)? Because right now, there's no way to say you
want to accept a generic object without caring about type. So the
(object) cast/hint would then provide that ability to accept a generic
object.Why not implement (resource)? Because that would require a new parser
token, as it's not available now...How does the casting work? Right now, it's using a copy of the same
rules that internal functions use with zend_parse_parameters. That
way, it brings the operating semantics of internal functions and
userland functions more inline with each other.So with that said, there are some (significant) issues with the patch:
- First off, the arg checks happen before separation of the zval on
non-referenced calls. So that means the cast effects the original
zval AND the argument. Which is a no-go for a production patch. So
that means that the cast logic would need to be put after the zval
split. But we'd still want the checks first, so it's not too
difficult to segregate, just requires deeper changes. It's not
difficult (that I can see yet), just more work... Example of the
problem:sapi/cli/php -r 'function foo((int) $bar) { var_dump($bar); } $a =
"1"; foo($a); var_dump($a);'
int(1)
int(1)
- Right now, the zend_aprse_arg_impl (
http://lxr.php.net/xref/PHP_5_4/Zend/zend_API.c#zend_parse_arg_impl )
that's used by internal functions is defined as static. So we'd be
copying a lot of the code back and forth. In the production patch,
I'd also want to re-factor that out a bit into either functions or
macros to handle the type conversion and casting in both places. That
way, both systems would behave identical (or as close as possible).So, with that said, what do you think? Is this something worth
pursuing? Are there any fundamental issues that I'm missing? What
else would we need to cover in a production patch and RFC?Thanks,
Anthony
Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORSPersonally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...
Should (array) be supported? Perhaps. So at that point, foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))...Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value (if
possible) as well.What about consistency? Well, there currently is no consistency.
Internal function parameters behave one way, and explicit casts behave
another. And even more confusing implicit casts behave yet another
way ($a + $b). So to implement this, we'd need to be consistent with
one of them. Frankly, I would only want to add consistency to
internal function parameters, since the explicit cast is not useful
IMHO (it's identical to $bar = (int) $bar), at which point it's not
worth adding to save only that one line. But if we're consistent with
internal function parameter checking, then it becomes quite useful.
We can throw warnings on unclean conversion and prevent execution of
the function... That way, all function calls behave the same (as much
as I hate the concept of warnings on type hint failure)... So, in
that case, function calls become an implicit cast to the type, which
is then why the stricter error handling (without breaking the spirit
or consistency).What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code would
still function fine.Thoughts? Should I update the patch to be more inline with what I
said above (the implicit hints that are offered by the current
internal function argument mechanism:sapi/cli/php -r 'function foo((int) $bar) { return $bar; } $a = "1";
var_dump(foo($a));'
int(1)sapi/cli/php -r 'function foo((int) $bar) { return $bar; } $a =
"foo"; var_dump(foo($a));'
Warning: Argument 1 passed to foo() must be of the type integer,
string given, called in Command line code on line 1 and defined in
Command line code on line 1However, since it's not raising a terminating error, more changes
would need to be made to the VM to check the return status of the
argument check (which is currently ignored) to determine if to proceed
with the function call, or just return null imediately...Thoughts?
Well, this seems like a reasonable approach (at least in terms of general
discussion.)
I would suggest option a), for if it looks like the same type of cast found
within the body of functions. I believe users will expect it to act the
same way, too. Keeping track of two different cast behaviors would add to
the likelihood of misusing one or the other. Additionally, previous
proposals have struggled due to the impedance mismatch perceived between
the proposed hinting solutions and PHP's intrinsic typing qualities.
Keeping things close to the current modus operandus seems like it gives the
proposal more chance of becoming a reality.
I would also suggest that array not be included, if only to limit the scope
of the current proposal and simplify the offering. It could always be added
later. However, if it led to an increased likelihood of being
considered/passed for some reason, then include it :)
Nice work, Anthony.
I am curious what some of the core developers who've been opposed to scalar
type hinting in the past would think of this approach. Zeev, Stas, others,
would this be worth any consideration? It seems like an approach that is
potentially more consistent with PHP's typing mechanisms.
Thoughts?
Adam
Hey all,
Here's a much more robust and updated patch (actually, it seems good
to go to me, but needs significant review)...
https://gist.github.com/1963999
One potential issue is that it requires an API change to
zend_verify_arg_type (which appears to only be called in zend_vm_def.h
- and by generation zend_vm_execute.h)... Otherwise, it's functional
from my perspective...
Here's what's implemented:
The following cast syntaxes to the parameter type hints:
(int)
(float)
(bool)
(string)
(array)
(object)
Basically, they behave exactly as the normal cast works. So it won't
error except in odd edge cases (for example, passing StdClass object
into (string)...
So, based on that:
function ((int) $i) {}
is identical to:
function ($i) {
$i = (int) $i;
}
Additionally, the last 2 are a bit more interesting, as they will cast
it to an array/object if necessary.
To be honest, I'm not as sold on this version (I built it as a POC,
but to see how useful it is). It feels like it's not doing "enough".
All it really does is save 6 characters.
Instead, I think I'd rather see it check for a clean cast, and at
least throw an error on unclean cast (casting (int) "foo" in this
context). However, that's not how the current cast handler works, so
that's not what this does.
Any feedback?
Thanks,
Anthony
Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORSPersonally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...
Should (array) be supported? Perhaps. So at that point, foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))...Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value (if
possible) as well.What about consistency? Well, there currently is no consistency.
Internal function parameters behave one way, and explicit casts behave
another. And even more confusing implicit casts behave yet another
way ($a + $b). So to implement this, we'd need to be consistent with
one of them. Frankly, I would only want to add consistency to
internal function parameters, since the explicit cast is not useful
IMHO (it's identical to $bar = (int) $bar), at which point it's not
worth adding to save only that one line. But if we're consistent with
internal function parameter checking, then it becomes quite useful.
We can throw warnings on unclean conversion and prevent execution of
the function... That way, all function calls behave the same (as much
as I hate the concept of warnings on type hint failure)... So, in
that case, function calls become an implicit cast to the type, which
is then why the stricter error handling (without breaking the spirit
or consistency).What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code would
still function fine.Thoughts? Should I update the patch to be more inline with what I
said above (the implicit hints that are offered by the current
internal function argument mechanism:sapi/cli/php -r 'function foo((int) $bar) { return $bar; } $a = "1";
var_dump(foo($a));'
int(1)sapi/cli/php -r 'function foo((int) $bar) { return $bar; } $a =
"foo"; var_dump(foo($a));'
Warning: Argument 1 passed to foo() must be of the type integer,
string given, called in Command line code on line 1 and defined in
Command line code on line 1However, since it's not raising a terminating error, more changes
would need to be made to the VM to check the return status of the
argument check (which is currently ignored) to determine if to proceed
with the function call, or just return null imediately...Thoughts?
Well, this seems like a reasonable approach (at least in terms of general
discussion.)I would suggest option a), for if it looks like the same type of cast found
within the body of functions. I believe users will expect it to act the same
way, too. Keeping track of two different cast behaviors would add to the
likelihood of misusing one or the other. Additionally, previous proposals
have struggled due to the impedance mismatch perceived between the proposed
hinting solutions and PHP's intrinsic typing qualities. Keeping things close
to the current modus operandus seems like it gives the proposal more chance
of becoming a reality.I would also suggest that array not be included, if only to limit the scope
of the current proposal and simplify the offering. It could always be added
later. However, if it led to an increased likelihood of being
considered/passed for some reason, then include it :)Nice work, Anthony.
I am curious what some of the core developers who've been opposed to scalar
type hinting in the past would think of this approach. Zeev, Stas, others,
would this be worth any consideration? It seems like an approach that is
potentially more consistent with PHP's typing mechanisms.Thoughts?
Adam
Hi, Anthony
At first, thanks for your great work!
As I have to learn C and C++ from scratch it was quite a good help to have
someone like you pushing it forwards.
I like having the casting exactly in the definition of the function, even
if it just saves the 6 characters. You have all information in one place.
As you added array as type-cast ... is it now possible to put an instance
of ArrayIterator in there? Would the following code work out?
function foo((array) $a) { /* do something */ }
$bar = new ArrayIterator( array() );
foo($bar);
I'd pretty much like the idea to break the script if you pass a value that
would produce a E_WARNING
in type-casting.
But here comes another question for type-casting ... is it meant (as php
supports type-juggling) to get something of nearly every value (like to
parse "foo" to an integer) or is it more meant like getting a value in
another type and you should know that it is compatible with this other type
(like only parsing "1" to an integer)?
If it's meant like the last, then I'd may look for another way to implement
this ... Then this code would may make sense (not really to me, but it
would work out) ...
function foo((int) $i) { /* do something */ }
foo( (int)"foo" ); // would work as type-casting tries to get the last bit
of valuable data out of the passed information
foo( "foo" ); // would fail because the passed value is incompatible with
an integer
foo( "10 foo" ); // I personally would like to let it fail ... But maybe we
should let it be parsed to (int)10 silently ...
foo( "1" ); // would work
As far as I know, this is the behavior used for internal function calls -
isn't it?
I hope you'll get the difference ;)
I don't know if this difference actually applies to type-juggling and
type-casting ... As I understand type-juggling is just transforming it into
the other type if it can get something useful out of the content (like
parsing "1" or "10 foo" to an integer but not "foo"). Maybe type-juggling
uses the same functionality as weak-comparison ...
Bye
Simon
2012/3/3 Anthony Ferrara ircmaxell@gmail.com
Hey all,
Here's a much more robust and updated patch (actually, it seems good
to go to me, but needs significant review)...
https://gist.github.com/1963999One potential issue is that it requires an API change to
zend_verify_arg_type (which appears to only be called in zend_vm_def.h
- and by generation zend_vm_execute.h)... Otherwise, it's functional
from my perspective...Here's what's implemented:
The following cast syntaxes to the parameter type hints:
(int)
(float)
(bool)
(string)
(array)
(object)Basically, they behave exactly as the normal cast works. So it won't
error except in odd edge cases (for example, passing StdClass object
into (string)...So, based on that:
function ((int) $i) {}
is identical to:
function ($i) {
$i = (int) $i;
}Additionally, the last 2 are a bit more interesting, as they will cast
it to an array/object if necessary.To be honest, I'm not as sold on this version (I built it as a POC,
but to see how useful it is). It feels like it's not doing "enough".
All it really does is save 6 characters.Instead, I think I'd rather see it check for a clean cast, and at
least throw an error on unclean cast (casting (int) "foo" in this
context). However, that's not how the current cast handler works, so
that's not what this does.Any feedback?
Thanks,
Anthony
On Fri, Mar 2, 2012 at 3:15 PM, Adam Jon Richardson adamjonr@gmail.com
wrote:On Fri, Mar 2, 2012 at 7:51 AM, Anthony Ferrara ircmaxell@gmail.com
wrote:Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORSPersonally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...
Should (array) be supported? Perhaps. So at that point, foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))...Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value (if
possible) as well.What about consistency? Well, there currently is no consistency.
Internal function parameters behave one way, and explicit casts behave
another. And even more confusing implicit casts behave yet another
way ($a + $b). So to implement this, we'd need to be consistent with
one of them. Frankly, I would only want to add consistency to
internal function parameters, since the explicit cast is not useful
IMHO (it's identical to $bar = (int) $bar), at which point it's not
worth adding to save only that one line. But if we're consistent with
internal function parameter checking, then it becomes quite useful.
We can throw warnings on unclean conversion and prevent execution of
the function... That way, all function calls behave the same (as much
as I hate the concept of warnings on type hint failure)... So, in
that case, function calls become an implicit cast to the type, which
is then why the stricter error handling (without breaking the spirit
or consistency).What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code would
still function fine.Thoughts? Should I update the patch to be more inline with what I
said above (the implicit hints that are offered by the current
internal function argument mechanism:sapi/cli/php -r 'function foo((int) $bar) { return $bar; } $a = "1";
var_dump(foo($a));'
int(1)sapi/cli/php -r 'function foo((int) $bar) { return $bar; } $a =
"foo"; var_dump(foo($a));'
Warning: Argument 1 passed to foo() must be of the type integer,
string given, called in Command line code on line 1 and defined in
Command line code on line 1However, since it's not raising a terminating error, more changes
would need to be made to the VM to check the return status of the
argument check (which is currently ignored) to determine if to proceed
with the function call, or just return null imediately...Thoughts?
Well, this seems like a reasonable approach (at least in terms of general
discussion.)I would suggest option a), for if it looks like the same type of cast
found
within the body of functions. I believe users will expect it to act the
same
way, too. Keeping track of two different cast behaviors would add to the
likelihood of misusing one or the other. Additionally, previous proposals
have struggled due to the impedance mismatch perceived between the
proposed
hinting solutions and PHP's intrinsic typing qualities. Keeping things
close
to the current modus operandus seems like it gives the proposal more
chance
of becoming a reality.I would also suggest that array not be included, if only to limit the
scope
of the current proposal and simplify the offering. It could always be
added
later. However, if it led to an increased likelihood of being
considered/passed for some reason, then include it :)Nice work, Anthony.
I am curious what some of the core developers who've been opposed to
scalar
type hinting in the past would think of this approach. Zeev, Stas,
others,
would this be worth any consideration? It seems like an approach that is
potentially more consistent with PHP's typing mechanisms.Thoughts?
Adam
To be honest, I'm not as sold on this version (I built it as a POC, but to
see how useful it is). It feels like it's not doing "enough". All it really
does is save 6 characters.
Personally, I think it saves much more characters (,,,in the
documentation).
And I think, that's enough. Nothing more is needed here. If we want to
expand further on, I believe that the type casting system should be
enhanced with custom casting functions. The other proposal that you've made
fits perfectly here. Would you like to test them combined?
Another point of expansion is to have something similar for object types.
However, this requires first the introduction of custom casting rules for
objects types as well. Maybe in the future.
Great work.
Lazare INEPOLOGLOU
Ingénieur Logiciel
2012/3/3 Anthony Ferrara ircmaxell@gmail.com
Hey all,
Here's a much more robust and updated patch (actually, it seems good
to go to me, but needs significant review)...
https://gist.github.com/1963999One potential issue is that it requires an API change to
zend_verify_arg_type (which appears to only be called in zend_vm_def.h
- and by generation zend_vm_execute.h)... Otherwise, it's functional
from my perspective...Here's what's implemented:
The following cast syntaxes to the parameter type hints:
(int)
(float)
(bool)
(string)
(array)
(object)Basically, they behave exactly as the normal cast works. So it won't
error except in odd edge cases (for example, passing StdClass object
into (string)...So, based on that:
function ((int) $i) {}
is identical to:
function ($i) {
$i = (int) $i;
}Additionally, the last 2 are a bit more interesting, as they will cast
it to an array/object if necessary.To be honest, I'm not as sold on this version (I built it as a POC,
but to see how useful it is). It feels like it's not doing "enough".
All it really does is save 6 characters.Instead, I think I'd rather see it check for a clean cast, and at
least throw an error on unclean cast (casting (int) "foo" in this
context). However, that's not how the current cast handler works, so
that's not what this does.Any feedback?
Thanks,
Anthony
On Fri, Mar 2, 2012 at 3:15 PM, Adam Jon Richardson adamjonr@gmail.com
wrote:On Fri, Mar 2, 2012 at 7:51 AM, Anthony Ferrara ircmaxell@gmail.com
wrote:Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORSPersonally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...
Should (array) be supported? Perhaps. So at that point, foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))...Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value (if
possible) as well.What about consistency? Well, there currently is no consistency.
Internal function parameters behave one way, and explicit casts behave
another. And even more confusing implicit casts behave yet another
way ($a + $b). So to implement this, we'd need to be consistent with
one of them. Frankly, I would only want to add consistency to
internal function parameters, since the explicit cast is not useful
IMHO (it's identical to $bar = (int) $bar), at which point it's not
worth adding to save only that one line. But if we're consistent with
internal function parameter checking, then it becomes quite useful.
We can throw warnings on unclean conversion and prevent execution of
the function... That way, all function calls behave the same (as much
as I hate the concept of warnings on type hint failure)... So, in
that case, function calls become an implicit cast to the type, which
is then why the stricter error handling (without breaking the spirit
or consistency).What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code would
still function fine.Thoughts? Should I update the patch to be more inline with what I
said above (the implicit hints that are offered by the current
internal function argument mechanism:sapi/cli/php -r 'function foo((int) $bar) { return $bar; } $a = "1";
var_dump(foo($a));'
int(1)sapi/cli/php -r 'function foo((int) $bar) { return $bar; } $a =
"foo"; var_dump(foo($a));'
Warning: Argument 1 passed to foo() must be of the type integer,
string given, called in Command line code on line 1 and defined in
Command line code on line 1However, since it's not raising a terminating error, more changes
would need to be made to the VM to check the return status of the
argument check (which is currently ignored) to determine if to proceed
with the function call, or just return null imediately...Thoughts?
Well, this seems like a reasonable approach (at least in terms of general
discussion.)I would suggest option a), for if it looks like the same type of cast
found
within the body of functions. I believe users will expect it to act the
same
way, too. Keeping track of two different cast behaviors would add to the
likelihood of misusing one or the other. Additionally, previous proposals
have struggled due to the impedance mismatch perceived between the
proposed
hinting solutions and PHP's intrinsic typing qualities. Keeping things
close
to the current modus operandus seems like it gives the proposal more
chance
of becoming a reality.I would also suggest that array not be included, if only to limit the
scope
of the current proposal and simplify the offering. It could always be
added
later. However, if it led to an increased likelihood of being
considered/passed for some reason, then include it :)Nice work, Anthony.
I am curious what some of the core developers who've been opposed to
scalar
type hinting in the past would think of this approach. Zeev, Stas,
others,
would this be worth any consideration? It seems like an approach that is
potentially more consistent with PHP's typing mechanisms.Thoughts?
Adam
Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORSPersonally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...
(c) seems the most sane option ot me as well.
- Should (array) be supported? Perhaps. So at that point, foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))...
Turn this around and look at it from the current state of PHP:
function foo($bar)
{
$bar = (array) $bar;
}
If you pass a value of 1 for $bar, $bar is then converted to array(1).
That's what I'd expect the following to do as well:
function foo((array) $bar)
{
}
It's casting, and clearly different than:
function foo(array $bar)
{
}
which is doing a typehint check.
- Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value (if
possible) as well.
I personally would expect casting and references to be mutually
exclusive -- if you're casting, you're changing the value type, and I
wouldn't expect a destructive operation like this from passing a value
to a function/method call.
- What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code would
still function fine.
This is something that should be highlighted. I've seen a lot of folks
claiming type hinting is viral, and the arguments make no sense to me.
What your patch is offering is opt_in type casting of function/method
arguments. You don't have to write your functions or methods using
them, and for those who do, it should have no side effects on code
calling it.
I would LOVE to see this as part of PHP.
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Matthew,
Have you seen the new thread and RFC around this?
https://wiki.php.net/rfc/parameter_type_casting_hints
I went with option A, as I see erroring on cast as a more general
problem. So for consistency, I implemented it exactly like normal
explicit casts...
Anthony
On Mon, Mar 5, 2012 at 10:27 AM, Matthew Weier O'Phinney
weierophinney@php.net wrote:
Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORSPersonally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...(c) seems the most sane option ot me as well.
- Should (array) be supported? Perhaps. So at that point, foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))...Turn this around and look at it from the current state of PHP:
function foo($bar)
{
$bar = (array) $bar;
}If you pass a value of 1 for $bar, $bar is then converted to array(1).
That's what I'd expect the following to do as well:function foo((array) $bar)
{
}It's casting, and clearly different than:
function foo(array $bar)
{
}which is doing a typehint check.
- Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value (if
possible) as well.I personally would expect casting and references to be mutually
<snip>
exclusive -- if you're casting, you're changing the value type, and I
wouldn't expect a destructive operation like this from passing a value
to a function/method call.
- What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code would
still function fine.This is something that should be highlighted. I've seen a lot of folks
claiming type hinting is viral, and the arguments make no sense to me.
What your patch is offering is opt_in type casting of function/method
arguments. You don't have to write your functions or methods using
them, and for those who do, it should have no side effects on code
calling it.I would LOVE to see this as part of PHP.
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Anthony,
I still don't like the null-as-a-default-value solution. I find it
confusing.
I know that something similar appears in class type hinting, but:
- Class type hinting does not do casting (yet).
- Apart from null, no other value could be placed anyway. (Even that is a
little bit wrong as null belongs to a different type than the hinted class).
I have a different proposal. The argument type hinting/casting should not
be bothered with that at all. Instead, we could expand the type juggling
system a little bit, with the introduction of a special type of casting
that leaves null unchanged. Something like this:
(int?) $x
which should be strictly translated to the following, without any way to
change that behavior by any type casting overload system:
is_null($x) ? null : (int)$x
Examples:
(int?) 13 // 13
(int?) '' // 0
(int?) 0 // 0
(int?) null // null
(int?) '342.3Test' // 342
I can think of many real world scenarios that could benefit from this. The
first that comes to my mind is reading from a database, in cases that the
value of null totally different than the value of 0.
$parent_id = (int?) $db['PARENT_ID']; // null and 0 mean different things
here...
A second example is reading from the query string:
$id = (int?) @$_GET['id']; // the error-silencing operator will return
null on error.
Thoughts?
Lazare INEPOLOGLOU
Ingénieur Logiciel
2012/3/5 Anthony Ferrara ircmaxell@gmail.com
Matthew,
Have you seen the new thread and RFC around this?
https://wiki.php.net/rfc/parameter_type_casting_hintsI went with option A, as I see erroring on cast as a more general
problem. So for consistency, I implemented it exactly like normal
explicit casts...Anthony
On Mon, Mar 5, 2012 at 10:27 AM, Matthew Weier O'Phinney
weierophinney@php.net wrote:Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORSPersonally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...(c) seems the most sane option ot me as well.
- Should (array) be supported? Perhaps. So at that point, foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))...Turn this around and look at it from the current state of PHP:
function foo($bar)
{
$bar = (array) $bar;
}If you pass a value of 1 for $bar, $bar is then converted to array(1).
That's what I'd expect the following to do as well:function foo((array) $bar)
{
}It's casting, and clearly different than:
function foo(array $bar)
{
}which is doing a typehint check.
- Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value (if
possible) as well.I personally would expect casting and references to be mutually
<snip>
exclusive -- if you're casting, you're changing the value type, and I
wouldn't expect a destructive operation like this from passing a value
to a function/method call.
- What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code would
still function fine.This is something that should be highlighted. I've seen a lot of folks
claiming type hinting is viral, and the arguments make no sense to me.
What your patch is offering is opt_in type casting of function/method
arguments. You don't have to write your functions or methods using
them, and for those who do, it should have no side effects on code
calling it.I would LOVE to see this as part of PHP.
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Hi, Lazare
In your examples you are accessing an maybe non-existing array-key.
This will raise an E_NOTICE. See the note below this example:
http://php.net/manual/en/language.types.array.php#example-85
Maybe you also want something like that:
isset($x) ? (is_null($x) ? null : (int)$x) : null
But let's discuss that in a different thread.
Bye
Simon
2012/3/5 Lazare Inepologlou linepogl@gmail.com
Anthony,
I still don't like the null-as-a-default-value solution. I find it
confusing.I know that something similar appears in class type hinting, but:
- Class type hinting does not do casting (yet).
- Apart from null, no other value could be placed anyway. (Even that is a
little bit wrong as null belongs to a different type than the hinted
class).
I have a different proposal. The argument type hinting/casting should not
be bothered with that at all. Instead, we could expand the type juggling
system a little bit, with the introduction of a special type of casting
that leaves null unchanged. Something like this:(int?) $x
which should be strictly translated to the following, without any way to
change that behavior by any type casting overload system:is_null($x) ? null : (int)$x
Examples:
(int?) 13 // 13
(int?) '' // 0
(int?) 0 // 0
(int?) null // null
(int?) '342.3Test' // 342I can think of many real world scenarios that could benefit from this. The
first that comes to my mind is reading from a database, in cases that the
value of null totally different than the value of 0.$parent_id = (int?) $db['PARENT_ID']; // null and 0 mean different things
here...A second example is reading from the query string:
$id = (int?) @$_GET['id']; // the error-silencing operator will return
null on error.Thoughts?
Lazare INEPOLOGLOU
Ingénieur Logiciel2012/3/5 Anthony Ferrara ircmaxell@gmail.com
Matthew,
Have you seen the new thread and RFC around this?
https://wiki.php.net/rfc/parameter_type_casting_hintsI went with option A, as I see erroring on cast as a more general
problem. So for consistency, I implemented it exactly like normal
explicit casts...Anthony
On Mon, Mar 5, 2012 at 10:27 AM, Matthew Weier O'Phinney
weierophinney@php.net wrote:Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORSPersonally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...(c) seems the most sane option ot me as well.
- Should (array) be supported? Perhaps. So at that point, foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))...Turn this around and look at it from the current state of PHP:
function foo($bar)
{
$bar = (array) $bar;
}If you pass a value of 1 for $bar, $bar is then converted to array(1).
That's what I'd expect the following to do as well:function foo((array) $bar)
{
}It's casting, and clearly different than:
function foo(array $bar)
{
}which is doing a typehint check.
- Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value (if
possible) as well.I personally would expect casting and references to be mutually
<snip>
exclusive -- if you're casting, you're changing the value type, and I
wouldn't expect a destructive operation like this from passing a value
to a function/method call.
- What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code would
still function fine.This is something that should be highlighted. I've seen a lot of folks
claiming type hinting is viral, and the arguments make no sense to me.
What your patch is offering is opt_in type casting of function/method
arguments. You don't have to write your functions or methods using
them, and for those who do, it should have no side effects on code
calling it.I would LOVE to see this as part of PHP.
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
In your examples you are accessing an maybe non-existing array-key
Yes, this is why I used the error silencing (@) operator. But anyway, it is
irrelevant to the whole proposal.
Lazare INEPOLOGLOU
Ingénieur Logiciel
2012/3/5 Simon Schick simonsimcity@googlemail.com
Hi, Lazare
In your examples you are accessing an maybe non-existing array-key.
This will raise an E_NOTICE. See the note below this example:
http://php.net/manual/en/language.types.array.php#example-85Maybe you also want something like that:
isset($x) ? (is_null($x) ? null : (int)$x) : nullBut let's discuss that in a different thread.
Bye
Simon2012/3/5 Lazare Inepologlou linepogl@gmail.com
Anthony,
I still don't like the null-as-a-default-value solution. I find it
confusing.I know that something similar appears in class type hinting, but:
- Class type hinting does not do casting (yet).
- Apart from null, no other value could be placed anyway. (Even that is a
little bit wrong as null belongs to a different type than the hinted
class).
I have a different proposal. The argument type hinting/casting should not
be bothered with that at all. Instead, we could expand the type juggling
system a little bit, with the introduction of a special type of casting
that leaves null unchanged. Something like this:(int?) $x
which should be strictly translated to the following, without any way to
change that behavior by any type casting overload system:is_null($x) ? null : (int)$x
Examples:
(int?) 13 // 13
(int?) '' // 0
(int?) 0 // 0
(int?) null // null
(int?) '342.3Test' // 342I can think of many real world scenarios that could benefit from this. The
first that comes to my mind is reading from a database, in cases that the
value of null totally different than the value of 0.$parent_id = (int?) $db['PARENT_ID']; // null and 0 mean different things
here...A second example is reading from the query string:
$id = (int?) @$_GET['id']; // the error-silencing operator will return
null on error.Thoughts?
Lazare INEPOLOGLOU
Ingénieur Logiciel2012/3/5 Anthony Ferrara ircmaxell@gmail.com
Matthew,
Have you seen the new thread and RFC around this?
https://wiki.php.net/rfc/parameter_type_casting_hintsI went with option A, as I see erroring on cast as a more general
problem. So for consistency, I implemented it exactly like normal
explicit casts...Anthony
On Mon, Mar 5, 2012 at 10:27 AM, Matthew Weier O'Phinney
weierophinney@php.net wrote:Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORSPersonally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...(c) seems the most sane option ot me as well.
- Should (array) be supported? Perhaps. So at that point,
foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))...Turn this around and look at it from the current state of PHP:
function foo($bar)
{
$bar = (array) $bar;
}If you pass a value of 1 for $bar, $bar is then converted to array(1).
That's what I'd expect the following to do as well:function foo((array) $bar)
{
}It's casting, and clearly different than:
function foo(array $bar)
{
}which is doing a typehint check.
- Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value (if
possible) as well.I personally would expect casting and references to be mutually
<snip>
exclusive -- if you're casting, you're changing the value type, and I
wouldn't expect a destructive operation like this from passing a value
to a function/method call.
- What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code would
still function fine.This is something that should be highlighted. I've seen a lot of folks
claiming type hinting is viral, and the arguments make no sense to me.
What your patch is offering is opt_in type casting of
function/method
arguments. You don't have to write your functions or methods using
them, and for those who do, it should have no side effects on code
calling it.I would LOVE to see this as part of PHP.
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Hi, Lazare
Sorry, I've only looked at your first array-example :)
Bye
Simon
2012/3/5 Lazare Inepologlou linepogl@gmail.com
In your examples you are accessing an maybe non-existing array-key
Yes, this is why I used the error silencing (@) operator. But anyway, it
is irrelevant to the whole proposal.Lazare INEPOLOGLOU
Ingénieur Logiciel2012/3/5 Simon Schick simonsimcity@googlemail.com
Hi, Lazare
In your examples you are accessing an maybe non-existing array-key.
This will raise an E_NOTICE. See the note below this example:
http://php.net/manual/en/language.types.array.php#example-85Maybe you also want something like that:
isset($x) ? (is_null($x) ? null : (int)$x) : nullBut let's discuss that in a different thread.
Bye
Simon2012/3/5 Lazare Inepologlou linepogl@gmail.com
Anthony,
I still don't like the null-as-a-default-value solution. I find it
confusing.I know that something similar appears in class type hinting, but:
- Class type hinting does not do casting (yet).
- Apart from null, no other value could be placed anyway. (Even that is
a
little bit wrong as null belongs to a different type than the hinted
class).
I have a different proposal. The argument type hinting/casting should not
be bothered with that at all. Instead, we could expand the type juggling
system a little bit, with the introduction of a special type of casting
that leaves null unchanged. Something like this:(int?) $x
which should be strictly translated to the following, without any way to
change that behavior by any type casting overload system:is_null($x) ? null : (int)$x
Examples:
(int?) 13 // 13
(int?) '' // 0
(int?) 0 // 0
(int?) null // null
(int?) '342.3Test' // 342I can think of many real world scenarios that could benefit from this.
The
first that comes to my mind is reading from a database, in cases that the
value of null totally different than the value of 0.$parent_id = (int?) $db['PARENT_ID']; // null and 0 mean different
things
here...A second example is reading from the query string:
$id = (int?) @$_GET['id']; // the error-silencing operator will return
null on error.Thoughts?
Lazare INEPOLOGLOU
Ingénieur Logiciel2012/3/5 Anthony Ferrara ircmaxell@gmail.com
Matthew,
Have you seen the new thread and RFC around this?
https://wiki.php.net/rfc/parameter_type_casting_hintsI went with option A, as I see erroring on cast as a more general
problem. So for consistency, I implemented it exactly like normal
explicit casts...Anthony
On Mon, Mar 5, 2012 at 10:27 AM, Matthew Weier O'Phinney
weierophinney@php.net wrote:Well, there are a few questions about the implementation:
- Which type casting rules should it follow?
a. Regular cast rules (like $foo = (int) $foo), where it converts
always without error?
b. Internal function cast rules, where it warnings on error and
prevents execution of the function.
c. Current type hinting rules, where if it can't convert cleanly it
E_RECOVERABLE_ERRORSPersonally, I like C the best. Where if it is passed an invalid
value, it attempts to cleanly convert, but errors out if it can't...
But I can see other arguments being made...(c) seems the most sane option ot me as well.
- Should (array) be supported? Perhaps. So at that point,
foo(array
$bar) would do a "strict" check, and foo((array) $bar) would attempt
to cast. But my question would be: what would attempt to cast mean?
Should it error out if you pass foo(1)? That's what the internal
function cast rules do. And to me that's more obvious than silently
converting it to foo(array(1))...Turn this around and look at it from the current state of PHP:
function foo($bar)
{
$bar = (array) $bar;
}If you pass a value of 1 for $bar, $bar is then converted to
array(1).
That's what I'd expect the following to do as well:function foo((array) $bar)
{
}It's casting, and clearly different than:
function foo(array $bar)
{
}which is doing a typehint check.
- Should references be supported? My feeling is yes, they should.
So if you do foo((array) &$bar), it would cast the original value
(if
possible) as well.I personally would expect casting and references to be mutually
<snip>
exclusive -- if you're casting, you're changing the value type, and I
wouldn't expect a destructive operation like this from passing a
value
to a function/method call.
- What about BC breaks? Well, this entire patch (up to this point)
wouldn't require one. it's only adding the casting functionality
(which is not implemented today), so no problem. Existing code
would
still function fine.This is something that should be highlighted. I've seen a lot of
folks
claiming type hinting is viral, and the arguments make no sense to
me.
What your patch is offering is opt_in type casting of
function/method
arguments. You don't have to write your functions or methods using
them, and for those who do, it should have no side effects on code
calling it.I would LOVE to see this as part of PHP.
--
Matthew Weier O'Phinney
Project Lead | matthew@zend.com
Zend Framework | http://framework.zend.com/
PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc
Hi Lazare,
I'm a bit divided on your proposal.
On one hand I kind of like the simplicity of the syntax and the basic
idea behind it:
(int?) $x
which should be strictly translated to the following, without any way to
change that behavior by any type casting overload system:is_null($x) ? null : (int)$x
But on the other hand, I'm not sure you should mix type casting and
the short if syntax (which is clearly what inspired the question mark
there).
This sort of makes me think you're really turning an (int) type
casting, into more of a (mixed) type casting... I don't really like
that! :)
Therefore I'm divided into thinking this is a cool idea, or if I'd get
so confused about this I would use it as much as the short if syntax:
$var = @$_GET['var'] ?: 'default';
(Which is never, btw!)
How about allowing several entries per type cast (since spaces are
allowed inside casts anyway, but could also be a semicolon or
something):
$int_or_null = (int unset) $x;
This could be usefull for other instances as (string null) or (bool
null) as well... Your thoughts?
Best regards,
~ Daniel Macedo
This could be usefull for other instances as (string null) or (bool
null) as well... Your thoughts?
Typo! The examples should read (string unset) and (bool unset)
BTW: Order would equal what is type casted OR simply accepted!
~ Daniel Macedo
Hi Daniel,
No, it is not inspired from the short ternary operator. It's a rather
common conversion. C# has a similar notion of nullable types (with totally
different mechanics however). By the way, in this particular area PHP's
type system is more sound than that of C#, because null is not just a
special value of any type but it is a value of a separate null type. Kudos
to the original designers for this choice.
Your proposal for (int unset) is not very far away from what I propose. In
PHPDoc, and in many IDEs, there are often mixed types like these:
int|string
int|null
string|bool|null
bool|null
DateTime|null
...
Of all different combinations, the ones between a type and null are very
usual. So, I propose "int?" as a shortcut for "int|null". In that sense, it
is totally equivalent to "int unset" that you say. Personally, I find
"unset" not proper for this case, because it is not going to unset anything
that is not already null.
BTW: Order would equal what is type casted OR simply accepted!
Do you have any examples where this could be useful?
Lazare INEPOLOGLOU
Ingénieur Logiciel
2012/3/5 Daniel Macedo admacedo@gmail.com
This could be usefull for other instances as (string null) or (bool
null) as well... Your thoughts?Typo! The examples should read (string unset) and (bool unset)
BTW: Order would equal what is type casted OR simply accepted!
~ Daniel Macedo
Sorry, I used unset in the same way type casting works, not as in
unset() ... Common gotcha: http://php.net/unset#example-4824
Here's how the manual refers to it:
http://php.net/type-juggling
(int), (integer) - cast to integer
(bool), (boolean) - cast to boolean
(float), (double), (real) - cast to float
(string) - cast to string
(array) - cast to array
(object) - cast to object
(unset) - cast to NULL
(PHP 5)
I understood what you meant but I'd rather have it be more verbose but
more clear as well.
I read your idea, as "Cast to int, or accept NULL" I rather
use/read that as (int unset) rather than (int?)
> BTW: Order would equal what is type casted OR simply accepted!
Do you have any examples where this could be useful?
The same example you gave of data that comes from a database NULL
would be retained, else it's type casted to int.
// Cast to int, accept null
(int unset) 13 // 13
(int unset) '' // 0
(int unset) 0 // 0
(int unset) NULL
// NULL
(int unset) '342.3Test' // 342
My view is, if you use the naming as it's currently used in type
casting, you also get additional functionality:
// Cast to string, accept bool
(string bool) 'test' // 'test'
(string bool) 123 // '123'
(string bool) TRUE
// TRUE
(string bool) FALSE
// FALSE
(string bool) NULL
// ''
// Cast to string, accept array
(string array) 'test' // 'test'
(string array) 123 // '123'
(string array) TRUE
// '1'
(string array) array(1, 2, 3) // array(3) { [0]=> int(1) [1]=>
int(2) [2]=> int(3) }
I find this way more useful/complete/readable than what you proposed.
Best regards,
~ Daniel Macedo