Re: Object Casting - An Alternative to Type Hinting

From: Date: Sun, 26 Feb 2012 15:39:10 +0000
Subject: Re: Object Casting - An Alternative to Type Hinting
References: 1  Groups: php.internals 
Request: Send a blank email to internals+get-58084@lists.php.net to get a copy of this message
On Sun, Feb 26, 2012 at 10:57 PM, Anthony Ferrara <ircmaxell@gmail.com> wrote: > 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" > I have to say, it doesn't get work, thinking this: $mixed1 = new Interger(2); $mixed2 = new Interge(3); $guess_what_type_is = $mixed1 + $mixed2; thanks > 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 > -- Laruence  Xinchen Hui http://www.laruence.com/

« previous php.internals (#58084) next »