Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:58084 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 40274 invoked from network); 26 Feb 2012 15:39:36 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 26 Feb 2012 15:39:36 -0000 Authentication-Results: pb1.pair.com header.from=laruence@gmail.com; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=laruence@gmail.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.212.42 as permitted sender) X-PHP-List-Original-Sender: laruence@gmail.com X-Host-Fingerprint: 209.85.212.42 mail-vw0-f42.google.com Received: from [209.85.212.42] ([209.85.212.42:44177] helo=mail-vw0-f42.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 46/91-30711-5325A4F4 for ; Sun, 26 Feb 2012 10:39:34 -0500 Received: by vbjk13 with SMTP id k13so290002vbj.29 for ; Sun, 26 Feb 2012 07:39:30 -0800 (PST) Received-SPF: pass (google.com: domain of laruence@gmail.com designates 10.52.26.206 as permitted sender) client-ip=10.52.26.206; Authentication-Results: mr.google.com; spf=pass (google.com: domain of laruence@gmail.com designates 10.52.26.206 as permitted sender) smtp.mail=laruence@gmail.com; dkim=pass header.i=laruence@gmail.com Received: from mr.google.com ([10.52.26.206]) by 10.52.26.206 with SMTP id n14mr5554424vdg.51.1330270770948 (num_hops = 1); Sun, 26 Feb 2012 07:39:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=mime-version:sender:in-reply-to:references:from:date :x-google-sender-auth:message-id:subject:to:cc:content-type :content-transfer-encoding; bh=lyIaXmvr+Nf7tNma07nmXMnspXYV4nhuM4oopeclLuA=; b=kNdSCINji3RAi/uU5hqtgC6a2YgPWmoNqGnFC55u2nVpplnLx9CZifeCuKNoXDb2Y6 kqlaQpmAzdLUNffPHVPIXKCF8lOXw+DYMYLTPVMprDgPr/f/Rh4nlzFAXkWhBIpOQ4Qk mu9Yl1Y9ACX7iO0A7ztLqsjACn2xOZwZVpAvI= Received: by 10.52.26.206 with SMTP id n14mr4528102vdg.51.1330270770234; Sun, 26 Feb 2012 07:39:30 -0800 (PST) MIME-Version: 1.0 Sender: laruence@gmail.com Received: by 10.220.178.130 with HTTP; Sun, 26 Feb 2012 07:39:10 -0800 (PST) In-Reply-To: References: Date: Sun, 26 Feb 2012 23:39:10 +0800 X-Google-Sender-Auth: ffKryEVpE7dH3YqgGVrr8Fmvqso Message-ID: To: Anthony Ferrara Cc: internals@lists.php.net Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] Object Casting - An Alternative to Type Hinting From: laruence@php.net (Laruence) On Sun, Feb 26, 2012 at 10:57 PM, Anthony Ferrara wro= te: > 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. =C2=A0One > technique that has been passed around is to use object wrappers and > pass objects instead of scalars. =C2=A0Such as was suggested in: > http://marc.info/?l=3Dphp-internals&m=3D119543188808737&w=3D2 > > 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. =C2=A0We 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. =C2=A0That way, we could overload the addition operator to > handle the operation. =C2=A0However, this becomes quite problematic, sinc= e > 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. =C2=A0What if we added two ne= w > 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. =C2=A0Let me start with a sample > implementation: > > class Integer { > =C2=A0 =C2=A0protected $value =3D 0; > =C2=A0 =C2=A0public function __construct($value) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0$this->value =3D $value; > =C2=A0 =C2=A0} > =C2=A0 =C2=A0public function __castTo($type) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0switch ($type) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case 'int': > =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case 'numeric': > =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return $this->valu= e; > =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case 'float': > =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return (float) $th= is->value; > =C2=A0 =C2=A0 =C2=A0 =C2=A0} > =C2=A0 =C2=A0 =C2=A0 =C2=A0throw new LogicException('Illegal Cast Operati= on Performed'); > =C2=A0 =C2=A0} > =C2=A0 =C2=A0public static function __castFrom($value) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0if (!is_scalar($value)) { > =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0throw new LogicException('Illega= l Cast Operation Performed'); > =C2=A0 =C2=A0 =C2=A0 =C2=A0} > =C2=A0 =C2=A0 =C2=A0 =C2=A0return new static((int) $value); > =C2=A0 =C2=A0} > } > > Now, that enables us to do something like: > > $int =3D new Integer(2); > echo $int + 2; // 4, since __castTo was called with "numeric" > echo substr("foobar", 0, $int); // "fo" since __castTo was called with "i= nt" > I have to say, it doesn't get work, thinking this: $mixed1 =3D new Interger(2); $mixed2 =3D new Interge(3); $guess_what_type_is =3D $mixed1 + $mixed2; thanks > That demonstrates the __castTo usages. =C2=A0Now for the __castFrom... > > function foo(Integer $int) { > =C2=A0 =C2=A0echo $int + 1; > } > > Now, under current rules, calling foo(1) would result in a fatal > error. =C2=A0However, we could change that to check if the class being > type-hinted for has a __castFrom method on it. =C2=A0If it does, it would > attempt to cast the value into that class. =C2=A0So calling foo(1) would > actually internally call `Integer::__castFrom(1)`. =C2=A0And since that > returns an object of instance Integer, the hint would pass. > > These two additions would solve a few issues with type-hinting. =C2=A0Fir= st > off, it solves the "cast to" vs "error if" debate on passing a string > in the place of an integer. =C2=A0In this case, it would be up to the > __castFrom() method to determine that (thereby enabling both worlds > possible). =C2=A0Second, it solves the problem of having to wrap clumsy > APIs around scalars for hinting purposes ($foo->getInteger() + 1). > Third, it is still completely optional... =C2=A0Fourth, it keeps and trie= s > to embrace the dynamic type-cohersion nature of PHP... > > Now, that's not to say it's not without problems. =C2=A0Here are a few th= at > I can think of: > > 1. How should it deal with references? =C2=A0If I do `function foo(Intege= r > &$int)`, what should happen? > =C2=A0 =C2=A0- I would argue that if you're trying to reference, the cast= ing > functionality should not be expected to work at all. =C2=A0But that > introduces some inconsistency there. =C2=A0Not sure how to solve that... > > 2. Should it support casting from one object to another? =C2=A0Meaning if= I > pass an SPLInt to something expecting Integer (from two different > trees), should __castFrom be called? > =C2=A0 =C2=A0- I would argue that yes, it should. =C2=A0That would open t= he door for > compatibility layers to be built for cross-framework interaction that > happens seamlessly regardless of what was passed in. =C2=A0But it could g= et > 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? =C2=A0We can currently do > (int) $foo. =C2=A0Should we then be able to specify a class in the cast > instead? =C2=A0(Integer) $foo? > =C2=A0 =C2=A0- I like the concept, but that could be a nightmare to imple= ment > as it's hard to tell if it's a class reference or a constant enclosed > in () for the parser. =C2=A0And seeing as you can have a constant with th= e > same name as a class, which should take precedence? > > 4. Should __toString still be called for string contexts? =C2=A0Or would > the presence of __castTo then negate the existance of __toString. =C2=A0S= o > if you don't implement __castTo(), __toString() would still be called > for a string cast. =C2=A0But if you do, __castTo would be called instead.= .. > =C2=A0It 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 > --=20 Laruence =C2=A0Xinchen Hui http://www.laruence.com/