Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:56380 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 4859 invoked from network); 18 Nov 2011 04:08:38 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 18 Nov 2011 04:08:38 -0000 Authentication-Results: pb1.pair.com smtp.mail=ircmaxell@gmail.com; spf=pass; sender-id=pass Authentication-Results: pb1.pair.com header.from=ircmaxell@gmail.com; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.160.170 as permitted sender) X-PHP-List-Original-Sender: ircmaxell@gmail.com X-Host-Fingerprint: 209.85.160.170 mail-gy0-f170.google.com Received: from [209.85.160.170] ([209.85.160.170:39354] helo=mail-gy0-f170.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 41/AC-30628-44AD5CE4 for ; Thu, 17 Nov 2011 23:08:37 -0500 Received: by ghrr20 with SMTP id r20so105190ghr.29 for ; Thu, 17 Nov 2011 20:08:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type:content-transfer-encoding; bh=WTGw/wPVDD58I9TUYGyqnVnSfIiRK/zTvEuRYiQBmdA=; b=sK76YLhHcJdQPPImoMnGtleflgQmdx2pcesC7qSEL88/tDLm9sAS9p+GoxVei1OwKB Pu54ocJF9O0Oa5iUWvJExlpM1lhKSkzV8cFCqWTDgFEKqST6ujI0yAbTMWbPyT3mKkmT AfSd1gqCHgaESTVFcaIAfVOcJ2acTJbApiNIw= MIME-Version: 1.0 Received: by 10.224.18.69 with SMTP id v5mr510389qaa.31.1321589313374; Thu, 17 Nov 2011 20:08:33 -0800 (PST) Received: by 10.229.5.84 with HTTP; Thu, 17 Nov 2011 20:08:33 -0800 (PST) In-Reply-To: <4EC5924C.8000605@ralphschindler.com> References: <4EC578C9.4000408@ralphschindler.com> <4EC5924C.8000605@ralphschindler.com> Date: Thu, 17 Nov 2011 23:08:33 -0500 Message-ID: To: Ralph Schindler Cc: internals Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] Changes to constructor signature checks From: ircmaxell@gmail.com (Anthony Ferrara) Some responses inline... > 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 gener= al > 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. > The rest of the __magic() methods by and large are purely instance method= s > (part of the instance API). =A0Having them inside an interface is complet= ely > logical. =A0(BTW, these methods can be considered engine-level/promoted d= uck > typing since you don't have to implement an interface for them to do what > they do, but that's not important here) Well, that's true. However, with the exception of invoke and toString, the same functionality can be provided by the class. __call can be avoided by hard-coding the method (well, within reason). __get, __set and __isset can be avoided by making the members public. __sleep can be avoided by using the Serializable interface. That's why I'm pushing them in to that discussion. It's an implementation detail that's not really associated with the contract provided. That was my point (which is really not 100% related to the constructor, but is related in passing)... > Lets look at that, the proxy pattern that is. =A0This 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 =3D null; protected $proxyClass =3D ''; public function __construct($proxyClass) { $this->proxyClass =3D $proxyClass; } public function setAge($age) { if (empty($this->proxy)) { $class =3D $this->proxyClass; $this->proxy =3D new $class; } $this->proxy->age =3D $age; } } 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. > 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= . > =A0The above will throw an E_FATAL. As it should, since you have a contract expecting what the subtype doesn't implement. > Secondly, (AND WORSE) is that by simply removing the abstract keyword fro= m > the abstract classes __construct(), I get completely different behavior, > everything works without E_FATAL. You should get different behavior. Without Abstract, you no longer have a contract, so there's nothing to be enforced. Saying that's a problem is like saying "But my code works if I remove the private keyword from the method declaration". It changes the meaning of the construct significantly... (whether abstract/inferface constructors should be allowed is another story, but if it is, you should get the fatal)... > Thirdly, (EVEN WORSE!), is that if I comment out the $name, $age part of = the > NamedAgedPerson (see: COMMENT OUT HERE above), no E_FATAL is thrown on > *grandchildren* who violate this abstract ctor. Now that's a huge problem. All descendants should be forced to the same contract... I'd classify that as a bug... > So, if we choose to do this wrong+wrong=3D~right thing, we should at leas= t do > it correctly/consistently all the way through the inheritance type > hierarchy. =A0Don't you think? Agree > 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. =A0I won't be doing that either, but, the argument is largely th= at > 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 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 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... > Also, I'd has to see this kind of decision impacting future decisions (li= ke > this) b/c it was "ratified" in 5.4. Again, no argument at all. Anthony