Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:56383 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 14124 invoked from network); 18 Nov 2011 05:18:43 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 18 Nov 2011 05:18:43 -0000 Authentication-Results: pb1.pair.com header.from=ralph@ralphschindler.com; sender-id=unknown Authentication-Results: pb1.pair.com smtp.mail=ralph@ralphschindler.com; spf=permerror; sender-id=unknown Received-SPF: error (pb1.pair.com: domain ralphschindler.com from 209.85.161.170 cause and error) X-PHP-List-Original-Sender: ralph@ralphschindler.com X-Host-Fingerprint: 209.85.161.170 mail-gx0-f170.google.com Received: from [209.85.161.170] ([209.85.161.170:36780] helo=mail-gx0-f170.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 75/01-06569-2BAE5CE4 for ; Fri, 18 Nov 2011 00:18:43 -0500 Received: by ggnk1 with SMTP id k1so1079145ggn.29 for ; Thu, 17 Nov 2011 21:18:39 -0800 (PST) Received: by 10.236.184.225 with SMTP id s61mr2278914yhm.80.1321593519471; Thu, 17 Nov 2011 21:18:39 -0800 (PST) Received: from ralph-macbook.local (ip174-73-14-247.no.no.cox.net. [174.73.14.247]) by mx.google.com with ESMTPS id l18sm9841107anb.22.2011.11.17.21.18.38 (version=SSLv3 cipher=OTHER); Thu, 17 Nov 2011 21:18:38 -0800 (PST) Message-ID: <4EC5EAAD.5060708@ralphschindler.com> Date: Thu, 17 Nov 2011 23:18:37 -0600 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.9) Gecko/20061207 Thunderbird/1.5.0.9 Mnenhy/0.7.4.666 MIME-Version: 1.0 To: Anthony Ferrara CC: internals References: <4EC578C9.4000408@ralphschindler.com> <4EC5924C.8000605@ralphschindler.com> In-Reply-To: Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Subject: Re: [PHP-DEV] Changes to constructor signature checks From: ralph@ralphschindler.com (Ralph Schindler) One point that was missed, that I'd like to reiterate is that: Again, CTOR is not "special" b/c it's a PHP "magic method" its "special" because it is shares qualities of being a static method as well as an instance method. That said, the constructor is not just another instance method. Put another way, constructors are static methods that have instance level visibility access to the newly created subtype via $this. How are they static? They are not called by $obj->__construct() as normal instance methods are, they are called by $obj = new ConcreteType() (and the engine looks for a __construct() implementation upwards from concrete to abstract, if it exists). >> So, since PHP lets you do bad things in the first place (like have >> constructors and static methods in interfaces, and abstract ctors in >> abstract classes), we follow that up with another "bad" of breaking general >> LSP expectations of how things work? >> >> Isn't this trying to make two wrongs make a right? > > That's why I tossed out the concept of raising a notice or E_STRICT > error if a constructor is used in an interface/declared abstract. That too would be a massive BC break. >> Lets look at that, the proxy pattern that is. This is no longer valid usage >> in 5.4: > > That's not a proxy. That's a Bridge or Adapter pattern. Proxy > usually delays instantiation until first call (See > http://sourcemaking.com/design_patterns/proxy ). So in your example, > the proxy class would be: > > class ProxyNamedAgedPerson extends NamedAgedPerson { > public $proxy = null; ... > The usage you gave would make no sense for this discussion (the > difference in constructor implementations). However, in the proxy > example above, it makes a significant difference. If you didn't put > it in the abstract class/interface, you could get a fatal on > instantiation. Not saying it's hugely significant, but it's a valid > use-case. For the intents and purposes of this argument, we are using a proxy here - and, I'd argue that this particular pattern is central to the problem at hand. (Doctrine2 also uses this pattern, to open up access to a entity's members as well as to provide lazy loading.) The only real requirement is that a proxy object implements all of the same interfaces/parent-types as the subject object (compile time checking). This ensures that for any "instanceof Subject" that you find, objects of type SubjectProxy and type Subject pass this is_a test, and can exhibit the expected behavior. A way of obtaining this cleanly is through composition. While you could have a method outside of the constructor handle the subject reference, the cleanest approach is: class SubjectProxy extends Subject { protected $subject = null; public function __construct(Subject $subject) { $this->subject = $subject; } } I shouldn't have to care if Subject has a __construct somewhere else because only SubjectProxy knows how to instantiate subtypes of SubjectProxy in a stateful and correct way. >> First, if I consume a system that has practiced this abstract ctor in >> abstract class thing, I cannot create valid subtypes on my own in PHP 5.4. >> The above will throw an E_FATAL. > > As it should, since you have a contract expecting what the subtype > doesn't implement. So, what you're promoting is that a parent type can enforce the way subtypes are created. This is contrary to all understanding of class based implementations (to my knowledge): java or c# as far as I've checked. Put yet another way: *Concrete types define ctors in accordance with their own needs, not the needs of their parents or grandparents.* >> Of course, this argument is mostly pedantic to be honest, as I'd be >> instructing people to never attempt to put abstract ctors in abstract >> classes. I won't be doing that either, but, the argument is largely that >> the engine shouldn't be doing these kind of things to begin off with. > > I don't know. In PHP, a method is a method. Sure, some are called > specially by the engine, but why should the constructor be any > difference (conceptually). Sure, you usually type-hint against See the above comments as per why the ctors are considered special. > interfaces after construction, but you clearly don't have to. You > could argue if it belongs or not, and I think that's worth talking > about. But if you want consistency, it should either apply to all We couldn't consider this until php 6 though as it represents a massive BC break. > engine-called magic methods or none of them. The two cases above > (__toString and __invoke) could be solved with a pair of interfaces > (stringable and invokable possibly) to use instead of the methods. > Then simply remove all magic methods from interfaces... Honestly, I > don't feel strongly either way (keep all or remove all), but I think > it should be one or the other, not both... If the honest-to-goodness goal is to be able to create dynamic types and be able to bypass their constructor, use the facilities that are (now) there for that: http://us2.php.net/manual/en/reflectionclass.newinstancewithoutconstructor.php or, we can drop that functionality down into core by reusing an existing keyword: $x = new Foo(empty); // bypass any __construct Either way, my argument stands that we shouldn't be bucking the trend when we've been so good at adhereing to a sane is_a model with respect to our class/object model. -ralph