Hello All,
In PHP we currently have the ability to type hint classes in method
signatures, however, sometimes it would be useful to convert the items to a
different instance / type.
An example is collections and arrays. If implemented properly, an array and
a collection could be used interchangeably, although a collection may be
preferred. For consistency, having the ability to dynamically cast from an
array to a collection would be very useful.
An idea of how this may look would be:
<?php
class Collection extends ...
{
protected $items;
public function __construct(array $items)
{
$this->items = $items;
}
...
public function __cast(array $items) : Collection
{
return new static($items)
}
}
--
The __cast method MUST return an instance of itself, so that when called
like so:
function convert_me(Collection $collection)
{
var_dump($collection);
}
convert_me([1,2,3]);
the result there would be a Collection instance, instead of an array.
If the type passed is not accepted by __cast() or __cast() throws an
exception, then the current errors of invalid type are thrown, or perhaps
the custom exception message.
I'm not sure if this has been proposed before, and if it has, please could
someone assist in me finding it, I couldn't find it by a quick search on
the wiki.php.net/rfc page.
Thanks.
Alex.
Sorry, forgot a little bit of information.
If the type passed is already satisfactory (an instance), then __cast is
NOT called.
And because the email formatting appears bad (at least in externals.io)
Here it is in a gist:
https://gist.github.com/alexbowers/9520c8df746249ecae2d9c7aad2e54ae
var_dump() should uses __toString() here, not?
2016-12-02 11:44 GMT-02:00 Alex Bowers bowersbros@gmail.com:
And because the email formatting appears bad (at least in externals.io)
Here it is in a gist:
https://gist.github.com/alexbowers/9520c8df746249ecae2d9c7aad2e54ae
--
David Rodrigues
Hi,
And because the email formatting appears bad (at least in externals.io)
Here it is in a gist:
https://gist.github.com/alexbowers/9520c8df746249ecae2d9c7aad2e54ae
There's been a few times I've wanted something like that, but I don't
really like the idea of yet another magic method.
Especially given the obvious limitation of just one parameter being
accepted, the whole thing may work with just an interface:
interface Castable {
public function __construct($whatever);
}
Cheers,
Andrey.
Sorry for dupe, hit reply not reply-all.
I don't see how the interface is equivalent.
The benefit of this, is that you can convert types passed into a method to
the type you expect automagically, Castable wouldn't allow that, only a new
magic method (or reflection and user land code possibly?) would allow this.
As for the limitation of only one parameter being accepted, what would the
other possible parameters be?
Since this is based entirely on the casted variable, there can only
possibly be one variable there, the one passed into that position in the
functions arguments.
Arguably, this feature would resolve some bugs in peoples code, because it
can allow better enforcing of types.
Currently, a place that accepts a Collection would not be able to type hint
it if it wants to also accept an array, instead it would have to check and
change inside the method, but that shouldn't be the role of the method.
The method just wants to assume, and work on the assumption that it is the
correct type, but without losing flexibility.
The ability to cast to your own type automatically would help with this
assumption, as now everywhere would be able to assume Collection, and use
it as so, but allow the current flexibility of arrays being passed in also.
Hello :-),
Casting is already a very large surface of unsafety and bugs. Adding
“magic” before this is like multiplying the surface by infinite.
The only goal I see here is to save a new Collection(…)
, and this is
not a sufficient reason from my point of view. If array
was a type,
and IntoCollection
a trait, implemented for the array
type, then we
could do things like [1, 2, 3].intoCollection()
for instance. However
PHP does not bring this kind of features. So let's reverse this:
Collection::fromArray([1, 2, 3])
. But wait, this is exactly what new Collection([1, 2, 3])
does for this specific case.
Do you have better use cases? A casting comes with a non-negligeable
cost. You have to define your data clearly, and avoid casting as much as
possible.
Cheers.
Hello All,
In PHP we currently have the ability to type hint classes in method
signatures, however, sometimes it would be useful to convert the items to a
different instance / type.An example is collections and arrays. If implemented properly, an array and
a collection could be used interchangeably, although a collection may be
preferred. For consistency, having the ability to dynamically cast from an
array to a collection would be very useful.An idea of how this may look would be:
<?php
class Collection extends ...
{
protected $items;public function __construct(array $items) { $this->items = $items; } ... public function __cast(array $items) : Collection { return new static($items) }
}
--
The __cast method MUST return an instance of itself, so that when called
like so:function convert_me(Collection $collection)
{
var_dump($collection);
}convert_me([1,2,3]);
the result there would be a Collection instance, instead of an array.
If the type passed is not accepted by __cast() or __cast() throws an
exception, then the current errors of invalid type are thrown, or perhaps
the custom exception message.I'm not sure if this has been proposed before, and if it has, please could
someone assist in me finding it, I couldn't find it by a quick search on
the wiki.php.net/rfc page.Thanks.
Alex.
Hello All,
In PHP we currently have the ability to type hint classes in method
signatures, however, sometimes it would be useful to convert the items
to a
different instance / type.An example is collections and arrays. If implemented properly, an array
and
a collection could be used interchangeably, although a collection may
be
preferred. For consistency, having the ability to dynamically cast from
an
array to a collection would be very useful.
I think there are really two different features wrapped up here.
First, allowing user defined classes to define casting behaviour to or from other types. This has certainly been discussed in different forms over the years, although mostly in terms of casting to another type (e.g. a generalisation of __toString). One of the big challenges is that without method overloading, how do you nicely declare two different casts, to or from different types? The generalised case also suffers from the problem of multiple despatch - do you call Foo::fromBar() or Bar::toFoo()?
The second part is making that casting automatic when a type hint is encountered. I can sort of understand this as a generalisation of coercive type hints on scalars, but it feels like it could get rather hard to debug.
It seems easier to read in your example to just type hint the collection, and make the user manually create one:
function whatever(Collection $c) { ... }
$a = array( ... );
whatever( Collection::fromArray($a) );
Explicit casts would take you from there to:
function whatever(Collection $c) { ... }
$a = array( ... );
whatever( (Collection)$a );
Either of these is more typing than the cast being implicitly added by the engine, but it's clearer that there's some conversion happening. Imagine if the cast is an expensive operation - calling it explicitly over and over again would be an obvious clue to refactor, but having it silently called repeatedly might go unnoticed.
Regards,
--
Rowan Collins
[IMSoP]