Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:67271 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 24018 invoked from network); 2 May 2013 14:33:27 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 2 May 2013 14:33:27 -0000 Authentication-Results: pb1.pair.com header.from=rasmus@mindplay.dk; sender-id=unknown Authentication-Results: pb1.pair.com smtp.mail=rasmus@mindplay.dk; spf=permerror; sender-id=unknown Received-SPF: error (pb1.pair.com: domain mindplay.dk from 209.85.128.169 cause and error) X-PHP-List-Original-Sender: rasmus@mindplay.dk X-Host-Fingerprint: 209.85.128.169 mail-ve0-f169.google.com Received: from [209.85.128.169] ([209.85.128.169:42573] helo=mail-ve0-f169.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 03/96-06131-53972815 for ; Thu, 02 May 2013 10:33:26 -0400 Received: by mail-ve0-f169.google.com with SMTP id jz10so543429veb.14 for ; Thu, 02 May 2013 07:33:23 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:x-received:in-reply-to:references:date:message-id :subject:from:to:cc:content-type:x-gm-message-state; bh=0tIlquWSg2NVL8yLc5sGM873HM+yYBMWdj3+lbMfGWI=; b=BLUWtPNTp86zKge1aIeRPTRqVyJtQrNjFyEpvrA8SQt07bCiUd9uufYTxNiFjSSjxB bWZCSETXUVT3uKZE6zstoAZyhvAg7U2w+O1rvlyE23pm3gDVPTT5gY1EF1T3bJD2GlLJ 5nZ/uxndbTvWd8BYbxIMb9BfSTOVyUevVV79JHNtILL9/vt0ts326BpDUH7kCGOJS+K7 VevYcCbQwbwKzO5eqxegyM/twVzfjmgeohFrLeMl6pVgHKu3Iuq1z9AgP3TihJzVu1Dh 3O0ydc7g1L8sCCPPXlo4nGGtLgiVRBanmix3dNn6xJCtBveRHQTJNHiq3x8QfExP+nsB SpcA== MIME-Version: 1.0 X-Received: by 10.221.3.2 with SMTP id nw2mr2245018vcb.55.1367505203114; Thu, 02 May 2013 07:33:23 -0700 (PDT) Received: by 10.58.28.134 with HTTP; Thu, 2 May 2013 07:33:22 -0700 (PDT) In-Reply-To: References: <6245ED6B-2BF7-47B7-80C0-D3B3D8E0B312@strojny.net> <51803086.6020002@sugarcrm.com> <518030FD.5030504@lerdorf.com> Date: Thu, 2 May 2013 10:33:22 -0400 Message-ID: To: Bernhard Schussek Cc: Adam Harvey , PHP internals Content-Type: multipart/alternative; boundary=089e013a16ecdc4e7504dbbd1db9 X-Gm-Message-State: ALoCoQmdhpTu5uXLSYMQgqxyw4Yl/XtKMO2TGvZIeQvvuTqX0cPVbL1Q+GJ6U/FKzD8+QALmjHLu Subject: Re: [PHP-DEV] property de-referencing From: rasmus@mindplay.dk (Rasmus Schultz) --089e013a16ecdc4e7504dbbd1db9 Content-Type: text/plain; charset=ISO-8859-1 > > "authors[0].personalDetails[firstName]" > > >> which translates to > > >> ->getAuthors()[0]->getPersonalDetails()['firstName'] > > It's indirection via strings. This particular example isn't great for what I'm proposing, because you're not accessing a property - using an array-index, you get a value. There is no property - hence nothing to reflect on. Assuming you have accessors for those get() and set() methods, and assuming $firstName is an object-property and not just a hashed string, why not simply: $p = ^$authors[0]->personalDetails->firstName; Assuming you added static information about the type of $authors, what was previously a string (for which you need a run-time parser and a bunch of code) is now IDE-friendly, with auto-completion as you type, inspections, accurate refactoring, etc. (without writing framework-specific plug-ins for every major IDE.) If $authors is an array, this might translate roughly into: $p = new PropertyReference($authors[0]->personalDetails, 'firstName'); Or perhaps with more lazy evaluation: $p = new PropertyReference( function () use (&$authors) { return $authors[0]->personalDetails; } , 'firstName' ); It's still dynamic, but the source-code now contains more static information - you're using PHP syntax instead of inventing custom syntax and parsing it out of strings, for things the language can already do, but doesn't do elegantly. You now have the ability to reflect on the type of the $personalDetails object and the $firstName property and establish defaults based on annotation parsing (as as popular in Symfony) or conventions for labels ('firstName' => 'First Name') etc. You can assert early on if this code is going to work. You can know if you making a break change somewhere along that object graph. You don't need a library to parse custom syntax for value paths at run-time anymore, you're not inventing new syntax for something the language can already do, and developers no longer need to learn that custom syntax. You can use automated refactoring. Your IDE can provide auto-complete and inline documentation as you type. I don't like strings for references to source-code elements. It's ugly, it's unsafe, it can't be analyzed or refactored, and it's bloody backwards - to use a different example, look at this oddly popular approach to configuration: $config = array( 'foo' => 'bar', 'services' => array( 'db' => array( 'username' => '...', 'password' => '...', ), 'email' => array( 'host' => '....', ), ), ); Those are not objects and properties, they are arrays and strings, and they are dead matter. But they map 1:1 to objects and properties. So for all intents and purposes, they are a dead form of objects and properties that can only be revived at run-time - nothing can be known about the existence of any of those objects or properties prior to actually running the code. That means you can expect some ugly surprises down the line, when one of these unchecked references turn out to be wrong, or some change/refactoring breaks code that worked before. Or you can spend a lot of time writing unit-tests for things that shouldn't be able to fail in the first place. There are ways around that, and it involves relying on the static qualities of the language: $config[] = function (PDO $db) { $db->username = 'foo'; $db->password = 'bar'; }; Thanks to the type-hint - a popular static language feature - this code can inspected and checked. If you wanted the same for your nested arrays and strings, you would have to write plug-ins for every major IDE - more code. I like simple things. That's one of the key reasons I like PHP. Dynamic is not automatically simpler. Especially not when you have to learn and write and maintain oodles of static source-code annotations to bring the code-quality up to a level that is accepted by the PHP community at large. If you think keeping the language simple makes life simpler, you're fooling yourself. It's good that people like Bernhard have the time and energy to come up with solutions to problems like these - it's bad when we have to resort to unchecked code and custom syntax and parsers and IDE plug-ins to solve these problems and work comfortably with those solutions. Exclusively static languages aren't the answer, but neither are dominantly dynamic languages - the majority of PHP code is heavily embedded with static information for a reason. I would never want to see the dynamic features of the language crippled or encumbered in favor of static features - but to ignore the need for more static features in the language is to hold back a natural development that is already happening whether you approve of it or not. Maybe this particular feature is wrong for other reasons, I don't know, that's why we're discussing it. But don't reject the idea because it's static and doesn't fit with your ideas of a dynamic language. On Thu, May 2, 2013 at 9:06 AM, Bernhard Schussek wrote: > 2013/5/1 Rasmus Schultz > >> > One could >> > write a PropertyReference class right now with literally the only >> > difference being the lack of a builtin operator (ie new >> > PropertyReference($obj, 'prop') versus ^$obj->prop): the fact that >> > nobody seems to have done this in a major framework I know of suggests >> > that there isn't a strong need for encapsulating the indirection >> > beyond the $obj->$prop syntax that's worked forever. >> > >> >> Look at the Symfony form-builder example - encapsulating the indirection >> is >> *precisely* what they're doing: the object reference is stored in the >> form-builder, and property-names are added subsequently. >> > > As the developer of the Symfony Form component, I would like to clarify > that this is not true. At the time the property reference is stored, the > object it refers to is not (necessarily) known. You could build an abstract > form definition for "Author" instances, but only later instantiate a form > for that definition with a concrete object. So to be really useful in a > form context, property references need to be separated from object > instances (something like "^Author::$firstName" could work). > > Second, it is important that references can span multiple nodes in an > object/array graph. In Symfony, we built the PropertyAccess component for > that [1] which allows references like > > "authors[0].personalDetails[firstName]" > > which translates to > > ->getAuthors()[0]->getPersonalDetails()['firstName'] > > or > > ->authors[0]->getPersonalDetails()['firstName'] > > etc., depending on whether accessors exist or the properties themselves > are public (also works for writing values). This is a concept borrowed from > Java and known as "property paths". [2] Without the possibility of > chaining, property references are useless IMO. > > Cheers, > Bernhard > > [1] > http://symfony.com/doc/current/components/property_access/introduction.html > [2] > http://static.springsource.org/spring-data/data-commons/docs/1.3.0.M1/api/org/springframework/data/mapping/PropertyPath.html > --089e013a16ecdc4e7504dbbd1db9--