I definitely like the idea of being able to cast objects, I've frequently 
wished to be able to cast an object to an array.  I would argue against the 
single __castTo() and __castFrom() magic methods as large switches get to be 
difficult to find/read and doesn't support separation.

I would prefer something along the lines of C++ such as:

Cast From:
public __construct(Integer $i);

Cast To: 
public Integer();

This would require function overloading (__construct overloading by type) but 
with Type Hinting already available for function parameters, this could be done 
relatively easily I think.

Doing this would keep code isolated with smaller functions.

-Clint

-----Original Message-----
From: Anthony Ferrara [mailto:ircmax...@gmail.com] 
Sent: Sunday, February 26, 2012 8:57 AM
To: internals@lists.php.net
Subject: [PHP-DEV] Object Casting - An Alternative to Type Hinting

I've gone back and re-read a bunch of the old posts on Type Hinting, and have 
come to the conclusion that it won't be done any time soon.
Not because it doesn't have merit, but because there are at least a few 
fundamental difficulties that are non-trivial to figure out while still keeping 
the usefulness.

So, I started thinking of a way that we can work around it.  One technique that 
has been passed around is to use object wrappers and pass objects instead of 
scalars.  Such as was suggested in:
http://marc.info/?l=php-internals&m=119543188808737&w=2

One of the problems associated with this, is that before you work with these 
types, you need to manually cast them back to the native type they represent.  
We can try to deal with this problem using __toString, but I don't think that's 
granular enough to really be of much use in solving this problem...

Another method we could use, is if we supported operator overloading in 
objects.  That way, we could overload the addition operator to handle the 
operation.  However, this becomes quite problematic, since ordering of 
operations and interaction with disparate class trees is going to get rather 
messy (and extremely fragile) quite quick.

Let me throw out another possible solution.  What if we added two new magic 
methods to objects:

public function __castTo($type);
public static function __castFrom($value);

With these two methods, we could enable type-hinting by using something very 
similar to auto-boxing.  Let me start with a sample
implementation:

class Integer {
    protected $value = 0;
    public function __construct($value) {
        $this->value = $value;
    }
    public function __castTo($type) {
        switch ($type) {
            case 'int':
            case 'numeric':
                return $this->value;
            case 'float':
                return (float) $this->value;
        }
        throw new LogicException('Illegal Cast Operation Performed');
    }
    public static function __castFrom($value) {
        if (!is_scalar($value)) {
            throw new LogicException('Illegal Cast Operation Performed');
        }
        return new static((int) $value);
    }
}

Now, that enables us to do something like:

$int = new Integer(2);
echo $int + 2; // 4, since __castTo was called with "numeric"
echo substr("foobar", 0, $int); // "fo" since __castTo was called with "int"

That demonstrates the __castTo usages.  Now for the __castFrom...

function foo(Integer $int) {
    echo $int + 1;
}

Now, under current rules, calling foo(1) would result in a fatal error.  
However, we could change that to check if the class being type-hinted for has a 
__castFrom method on it.  If it does, it would attempt to cast the value into 
that class.  So calling foo(1) would actually internally call 
`Integer::__castFrom(1)`.  And since that returns an object of instance 
Integer, the hint would pass.

These two additions would solve a few issues with type-hinting.  First off, it 
solves the "cast to" vs "error if" debate on passing a string in the place of 
an integer.  In this case, it would be up to the
__castFrom() method to determine that (thereby enabling both worlds possible).  
Second, it solves the problem of having to wrap clumsy APIs around scalars for 
hinting purposes ($foo->getInteger() + 1).
Third, it is still completely optional...  Fourth, it keeps and tries to 
embrace the dynamic type-cohersion nature of PHP...

Now, that's not to say it's not without problems.  Here are a few that I can 
think of:

1. How should it deal with references?  If I do `function foo(Integer &$int)`, 
what should happen?
    - I would argue that if you're trying to reference, the casting 
functionality should not be expected to work at all.  But that introduces some 
inconsistency there.  Not sure how to solve that...

2. Should it support casting from one object to another?  Meaning if I pass an 
SPLInt to something expecting Integer (from two different trees), should 
__castFrom be called?
    - I would argue that yes, it should.  That would open the door for 
compatibility layers to be built for cross-framework interaction that happens 
seamlessly regardless of what was passed in.  But it could get a bit 
interesting, since that also could wind up having really non-obvious 
side-effects, mainly because of object references...

3. Should "class casting" then be supported?  We can currently do
(int) $foo.  Should we then be able to specify a class in the cast instead?  
(Integer) $foo?
    - I like the concept, but that could be a nightmare to implement as it's 
hard to tell if it's a class reference or a constant enclosed in () for the 
parser.  And seeing as you can have a constant with the same name as a class, 
which should take precedence?

4. Should __toString still be called for string contexts?  Or would the 
presence of __castTo then negate the existance of __toString.  So if you don't 
implement __castTo(), __toString() would still be called for a string cast.  
But if you do, __castTo would be called instead...
 It would then work for backwards compatibility, while enabling __toString to 
be eventually deprecated in favor of __castTo (not for a long time mind you, 
but eventually, possibly 6 or 7)...

What do you think?

Anthony

--
PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: 
http://www.php.net/unsub.php


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to