Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:49826 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 16597 invoked from network); 30 Sep 2010 19:49:23 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 30 Sep 2010 19:49:23 -0000 X-Host-Fingerprint: 65.19.76.48 unknown Date: Thu, 30 Sep 2010 15:49:22 -0400 Received: from [65.19.76.48] ([65.19.76.48:20116] helo=localhost.localdomain) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id F3/69-04170-1C9E4AC4 for ; Thu, 30 Sep 2010 15:49:22 -0400 Message-ID: To: internals@lists.php.net References: <4CA3A7EF.6000201@sugarcrm.com> <4CA4D840.6050907@sugarcrm.com> User-Agent: slrn/pre1.0.0-16 (Linux) X-Posted-By: 65.19.76.48 Subject: Re: [PHP-DEV] Bug in 5.3 __invoke() magic method? From: weierophinney@php.net (Matthew Weier O'Phinney) On 2010-09-30, Stas Malyshev wrote: > > feels like this architectural decision -- separating properties and > > methods into their own hash tables -- should be revisited. Most other > > Having properties and methods in the same hash would make zero sense, > unless we convert all methods into closures - which I currently don't > feel we need to (though some languages do exactly that). > That, however, does not prevent us from prohibiting methods and vars > with the same name - what prevents us is described below. > > > needs to change to be more easily predictable. Assigning a closure or > > other invokable to a class should "just work" -- developers shouldn't > > need to know how the Zend Engine works in order to understand the > > behavior and limitations. > > That is a question of expectations - if you expect properties and > methods be the same, it "just works", if you don't - it does not. > Currently there are two barriers to making it work: > > 1. Legitimate use-cases are easily done in user-land by using __call, > making all the effort kind of unnecessary. Using __call() for this, quite honestly, sucks. You have to define it in each and every class you write -- leading to code duplication -- or have all your classes inherit from a common object -- leading to an inheritance nightmare. Additionally, there are huge performance implications. The most flexible variants utilize call_user_func_array() inside __call(), which, from profiling and benchmarking I've done, can be around 6x slower than simply calling a function. Combine the two situations, and I might as well simply avoid using the features at all. > 2. It would create conflicting semantics in __get/__call which would > hugely complicate matters (e.g.: who gets called first and why? should > is_callable call __get? or maybe __isset? What happens if $this-> a is a > string - should that work too? It works for $a... What if you have a > property named __get? etc, etc) I think these can all be answered. * Only dereference objects with __invoke() or closures; don't worry about other callback types. * If __get resolves a property that's invokable, dereference it and invoke it. Anything else, simply treat as was done before -- and fallback to __call(). (That said, I realize where you're heading with this -- you now have overhead when overloading, as you have to try first with __get then __call, making resolution harder.) * is_callable() should call __get and determine if the return value is callable. * Don't allow defining magic methods as properties. > Without overcoming those barriers with some kind of solution I do not > see how we can make it work. Yes, I know it would be nice if we could > "just make it work", but unfortunately it's more complicated that that. > I feel "methods are different from properties" is much easier to > understand (even if one would wish it weren't so, it's still not hard to > comprehend) than "they are the same, except for a dozen of exceptions > and special cases when __get/__call is involved and they also different > from regular variables in these special cases, etc." Closures and invokable objects blur the lines between properties and methods, making the "methods are different from properties" mantra more difficult to understand. My property is invokable -- why can't I invoke it without first casting it to a temporary variable? > > * BC break for existing codebases that have properties and methods of > > the same name. (That's a code smell, anyways, and tools can help > > developers refactor to fix such cases.) > > I'm afraid it's not a good approach. If it breaks substantial amount of > code, nobody is going to use it. And then it doesn't matter what you > think about that code because all the work would be for nothing. It's a pipe dream, I know. :) > > * What if method "a" exists, and a developer tries to re-assign it as a > > closure or property? JavaScript and Ruby allow this; PHP wouldn't > > necessarily need to -- e.g. could raise an exception. Whatever the > > choice, just need to cleanly document the behavior. Again, potential > > BC break. > > I don't like it, for two reasons: > 1. Current patterns of PHP coding don't allow for raising exceptions > left and right - because people don't expect exceptions to come out of > ordinary things. That means the code would be very fragile - any > $foo->bar=1 turns into a bomb that could explode at any moment. It's > very hard to write robust code if you can't trust even a simple assignment. > 2. What happens if you first assign closure to $this->a and then > integer? If you assigned closure to it it should work like a method, > right? Then it should prohibit assigning integer to it like methods do? > So you have assign-once variable? Quite a weird concept, don't you think? I'd argue that you can assign at any time. It's up to the manual and instructors to ensure developers follow some best practices ("if you need to rely on being able to call the closure, define it as a method," etc.) > Summarily, as I said, unfortunately generating consistent model that > would allow this use case to work without rewriting half of PHP is not > as easy as it seems. You (or anybody else) is welcome to try and prove > me wrong with a good RFC, though :) But take care to actually go a > number of steps beyond simplest use cases - that's where the trouble starts. Exactly -- I know a number of the edge cases already. I'll see if I can find some time after ZendCon to write something up... and somebody willing to work with me to see if it's possible. (I can write tests...) -- Matthew Weier O'Phinney Project Lead | matthew@zend.com Zend Framework | http://framework.zend.com/ PGP key: http://framework.zend.com/zf-matthew-pgp-key.asc