Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:93079 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 92661 invoked from network); 5 May 2016 06:35:24 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 5 May 2016 06:35:24 -0000 Authentication-Results: pb1.pair.com header.from=dmitry@zend.com; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=dmitry@zend.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain zend.com designates 157.56.110.135 as permitted sender) X-PHP-List-Original-Sender: dmitry@zend.com X-Host-Fingerprint: 157.56.110.135 mail-bn1on0135.outbound.protection.outlook.com Received: from [157.56.110.135] ([157.56.110.135:34976] helo=na01-bn1-obe.outbound.protection.outlook.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 31/B7-16256-AA9EA275 for ; Thu, 05 May 2016 02:35:24 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=RWSoftware.onmicrosoft.com; s=selector1-zend-com; h=From:To:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=LHg0vbgxQoL5JVOaK10oRFKvueRmeSO66xc3Uf0zFsg=; b=z6EBfDxrzCXiysc/TGVpRRGBu4EJsvXUDqhsDSd5glkX1cDklyIhtfeA0eOjaOJzVL3UoT3o6YAI2FwP9yBGHPGFbiB+s6qBdQ3tsznL5zmMuDieJr34+9fVXT0CEBgQVxS+q9sAlgYhPv9SalEHZIy/b8Ct4xv2qZLSNWxv4Qc= Authentication-Results: lists.php.net; dkim=none (message not signed) header.d=none;lists.php.net; dmarc=none action=none header.from=zend.com; Received: from tpl2.home (92.62.57.172) by BLUPR0201MB1777.namprd02.prod.outlook.com (10.162.239.11) with Microsoft SMTP Server (TLS) id 15.1.485.9; Thu, 5 May 2016 06:35:15 +0000 To: Larry Garfield , 'PHP internals' References: <5723F2AE.2020806@garfieldtech.com> Message-ID: Date: Thu, 5 May 2016 09:35:01 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.0 MIME-Version: 1.0 In-Reply-To: <5723F2AE.2020806@garfieldtech.com> Content-Type: text/plain; charset="utf-8"; format=flowed Content-Transfer-Encoding: 7bit X-Originating-IP: [92.62.57.172] X-ClientProxiedBy: VI1PR02CA0015.eurprd02.prod.outlook.com (10.162.7.153) To BLUPR0201MB1777.namprd02.prod.outlook.com (10.162.239.11) X-MS-Office365-Filtering-Correlation-Id: 4bb09dc1-348c-4aee-3a79-08d374af6b7c X-Microsoft-Exchange-Diagnostics: 1;BLUPR0201MB1777;2:MrQ/2YTLoO4nsqWyf8qLMDHlYNe9F8GCRd2g/ENjV6E/zDHGk59yTmS0ZY+EPC/lZqFmZNCDBA5ncVAfse326ycUCknwpSJOP5D+lW9oB+XgEVr/pjR+/ln7Qv0LRS+hfMiW4PhtSHzHGINGtVqM19Tzp+GQFyyi/CZuP8ZVQoA1XcJSeMF9S8KBDRfmjpSQ;3:7JN4DpbcDPUxvrN0w430TmGBGz1tr+uDledv3M89z1+12FV0pXgfwoo9dnvVnEWCPIUT2vQIyKRH9GF7fDUEUbQfxxdTuVY4eZUtw8WHci4SXeoyceyc4A9ZtU/qT5L5;25:2tWJ5Snk3Y+HI3zTBV4uAWW1j0w8Yx0TXuuhLdhwpK4Nx+OVJOH6RGgmUnjx6+S8Fm+Ia62gfP7xkioeTC6AYlmOCwsFWhGMrESjpveH0q4jSd9/w/6tAlad0ZERJgOD2il7FOYshY3gM/YQPq2VIQVf7VnNH0pObHXc22/xh1lg9FB7Flrc5W1F1RmGQxWMsHFVDxindW6nOVqTYoeoESpiXMH8DJiOpEJWgkbcvyq2tXSR+wwJLddxmuvs9XUpCVWpbfGSb0s/bZV+pthBp9c11mQKtsz2MRm4IwDXIhPXCpnbLgkYhJEljXE37teivL1VOaJSW+0xsr85hGdOBXQgPlLHtJXFmyMX9U03xGlMiv4OZUeqlssAywEJVtjREeZJW0c5WB/t/03mdG8ZXfwXKy8lx2dTqY0857Y0qPpqxQGqZovim2PvWQa8N+PA X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BLUPR0201MB1777; X-Microsoft-Exchange-Diagnostics: 1;BLUPR0201MB1777;20:Mo9Q3DJiSfeCAVRTSTWKMy6rcGx861xx79f2Ll4YoVK9v6+FBRKWFp+YGH7jZyTlbBTIYdtqBxFb/YilJqyYp9srpbkOXQuUzyGZF54ByMWEBo3m8rIODFaFSOY8XO5s9gwH+HYpzuo4Lvs3CYCjzRt5gSJNC5qCYS6WMs94R+BsiPiItSM5m38zMof5eiDDEuxiJzVXfZrrNeVS62QBL2yGGb+T/tQu5jT4fquYr04cGS/MObU6gByWQNSwlS4bXZl7HOMHIz/74X9zRrvepOZ+snXcx0C8wxFK/AJT34c3JXw7xx6ISThNzxu0QJCLj8TpZauH+vmF/aRUY/3IZPnxZSg9RbzxHN+wYLi/30OIjpBp7cdYdH8LnbMyqK/tN+iscPHA5BCSW9Ok7k4hbP5aU0x0WLQEfokcz9R+X/JDUgAJwrogEkikh0S5iL1vg1dB0Zp4Rp1EkasVzkNB+HT/lm7y4cj5vaaT5CUegAT53dB1pet9qnKm6wGTZ7Ld;4:QgvIxIEklzx3halWQjuzW+DQZHXfO3jX2QBiH5u1OkitVwsMPpc24TQOyNOqUzIfiSY3LNtqRRDcMsd+W24pKitouj7eTZ/Vg2rlvHNC/A6SBowR7m77jQCiE3NdU9dwpm33nWE68+qxmGyzct/zjH+OHR/nYSSTBB2iYCUM2njjCG/CchEU7ejvoA4jcDX3jgA5MVis0XWn+a6dID3y+t8ptrSzkwL97Ajp2rMHpV0ft4xdMToNFTid5TMAPp+oe2wsUaAk2ZyY0czoh66jFHdJaAshI4CBQfQ3llChOh/Ivep9mkJu7PcTuOdvUCBneBRjyHp8FiCqGSoRDWTeEW7M9FqtXyEueM2C79MWbpHF8YFVmi96tcG7udN47YwCe6ALuAiJNE77A55MCyVNfQ== X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(9101521098)(601004)(2401047)(8121501046)(5005006)(10201501046)(3002001);SRVR:BLUPR0201MB1777;BCL:0;PCL:0;RULEID:;SRVR:BLUPR0201MB1777; X-Forefront-PRVS: 0933E9FD8D X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10019020)(4630300001)(979002)(6009001)(24454002)(377454003)(42186005)(64126003)(31686004)(5001770100001)(586003)(50466002)(189998001)(2950100001)(47776003)(230700001)(2906002)(107886002)(66066001)(65956001)(31696002)(5008740100001)(4001350100001)(65806001)(33646002)(54356999)(77096005)(76176999)(50986999)(86362001)(6116002)(36756003)(23676002)(83506001)(5004730100002)(92566002)(81166005)(3846002)(65826006)(969003)(989001)(999001)(1009001)(1019001);DIR:OUT;SFP:1102;SCL:1;SRVR:BLUPR0201MB1777;H:tpl2.home;FPR:;SPF:None;MLV:ovrnspm;PTR:InfoNoRecords;LANG:en; X-Microsoft-Exchange-Diagnostics: =?utf-8?B?MTtCTFVQUjAyMDFNQjE3Nzc7MjM6Vi9Lc2h6dENOT0xMRU5mQnJsRGQwNUdh?= =?utf-8?B?bFM3eTFiRkhxRmxnS1Bwd3Fwa09nQy9ZZFlhS1RQYTcwS2lnR0hBNnEzMG9C?= =?utf-8?B?cjVmcVppTW4xU2hWN1lFUmFvWEx2RlJneFpqRmQrK2hGcFFmVGpmclFOc2xI?= =?utf-8?B?MDFyT2lpaHRnc3ZYNE5vVXJJUXp1OFlDMXhDOW1nZER3WDEwT0s3MGZHM2pr?= =?utf-8?B?SzVtUFRwSFZMZkJ0b2QzYWN1QXArMVN6UFBnditzblNWaXY1RHBocjJqSTA2?= =?utf-8?B?SU9lNXoxVkI5NFZzWll2QTBXYWNqRE1uU1Q0VDlaTkNjdnNONHc3Y3VGUE9E?= =?utf-8?B?Q0Y1SzhWeG1PVUxxTWhCRVEvamtCMUdSOVE1WFZJaldwM09CakN0QlQrSGhX?= =?utf-8?B?bHd1bENmcmEyam1STEhKZVJURFFyUFBCaXJyY0dLd3JTTEttb0VMM295ZCtr?= =?utf-8?B?V1RVeUMrb1MwNTViQTR5M0ZNRHdxV2xHNmFwcDA0MjFpeFIwd1hEQ1NTbjV0?= =?utf-8?B?R2pkMUZHOXlrNUlueEFvMHJVbVNtdnZZRnRoUytqWTYvdlkvMEd3Vkl2bG5V?= =?utf-8?B?cGtuekJzNXI1Ukl1VFlSZ08wZTA1aTBGNXlFZEx5QUt6ZUZpdjhmYnhCNis5?= =?utf-8?B?dGJ1Y3NQYWJGSFlxdzFWV3JDaE9mRWVTSmZ4aVdYeXAzUnNGMW5Fai9NcURo?= =?utf-8?B?TGpnUDNuYjVsMTVIdndXOGQ3bGVDRmNKNkZuWC8zMEEzOGdtczI2MC9TWU84?= =?utf-8?B?Y2hCQXlGNXEvdWYwWEVkb0gvRStGNVVHcTNWcWp4UkliRzUyR2NxYk9rSjFZ?= =?utf-8?B?bVFsZW4wMHB5NSt4eEc1Mkp5L3dTMkxkS3YrNkxEKzR1ZkV6ZUc0MHVuZDA4?= =?utf-8?B?YTZxM1h6YWpQd20xRFgzbUx4MS8rVGFnUlNjaXgyY1lRVy9UQ3ZmNzY2ZmZQ?= =?utf-8?B?Z1RyZTRLZS9HQm5DME0wd0lQYXZ3aVFCczRDdkFuamZtcHZPWmRIcCtZT3p5?= =?utf-8?B?b0c1M0MxWEJLZ3podnRtU3E3dEQzN05SQ0NuYUY4aFN1K1dwbHBNVElvQ243?= =?utf-8?B?TTJOVVk4Snd6WnE1dTVRMmduUEIzUHhpeGlJNkRwM3FpVGUzdzFiVTVYcHdW?= =?utf-8?B?d0x5a3BqSloydWo2R2lYN29qY0tsNmdPSjdrT09FekdjNy83NE92RDJROGhh?= =?utf-8?B?NVFNQXdNVE9kRzBLcTJYQjd0N082djl1VXdIdTRMZWE5a3pSR3FHZWoxL0tW?= =?utf-8?B?UUpBQ0tEZHluc2Z1bUphcWJJeHlyUGZxeDFhMnUvSXJjVU55cWpMVStPdGx0?= =?utf-8?B?d1lkVkpTaXkrMy92ZXVTR3BYUUg3azZ2cEpnUVNxdDhjekFXbFlNTFRzU25C?= =?utf-8?B?Qkp6alFRS3hRVS9uSWVCbGhlWkZva2RoR0JpTVFXQ3RoUDlNSnVtOVorWTRu?= =?utf-8?B?NTJ6bkxmd0tobjY1OUFZRXVJekNLUUZhR2FndTRxL040T0gzQzdqN0tQWWND?= =?utf-8?B?TGpoYUt3PT0=?= X-Microsoft-Exchange-Diagnostics: 1;BLUPR0201MB1777;5:Q8MfFDgKaDiemr97yc8AelXzEXrLTt4cQQp8VHPKY0nJmJsYpVptbExrL02PsTvv10lkW8MgAKa1p4Eh66cQEv13ht1aLfR1DgM1YjvZetYLCFmifiny4Xhe+GpPmx5XEuhMZGTVVBSUAq3rMgeU4w==;24:vPvkrF9XhN2CCyiUvih15oqDgFkfpKsMS/Yzt2egme91c+V3sAuoSEHpMfEXrXH4dkhtsLcGeXm96HhfoCgPpJ27LNfcSmu1O0o4AHwrU8w=;7:5BeQPOxporEa/Wld/T2bwhyR3C58fzJJxqdBInFjkyI1odFq5gnoV0ybUEaGIwxeVyOhOalqkfYo390z39NpBSHVuA5TdTbbNF2zDcgAWKxJK3eHqIOpZpXCYI8TwJwR9tE75ZeHvQX19RFXs8QX1hcmuq/htkaDCD8OnGE4zfBVxWAwA6SrZfTGNHwwaTYM SpamDiagnosticOutput: 1:23 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: zend.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 05 May 2016 06:35:15.3742 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: BLUPR0201MB1777 Subject: Re: [PHP-DEV] Attributes/Annotations Case Study: Drupal From: dmitry@zend.com (Dmitry Stogov) On 04/30/2016 02:47 AM, Larry Garfield wrote: > Most of the examples that have been given so far are either trivial > boolean flags or data validation rules to be evaled. In practice, > very little of Drupal's use of annotations in Drupal 8 fit either > category. Rather, they're used primarily as, in essence, a serialized > metadata object describing a class, which is used for registering that > class and potentially others. I figured I'd give the proposed syntax > a try with some Drupal examples and see how well it fit. > > Disclaimer: I'm sure someone will pipe up with "your use case is > invalid because you shouldn't be using annotations that way." I will > be the first to agree that Drupal loves to take everything it does to > an extreme, and some things may be better done other ways. However, > these are still real-world use cases (currently built with Doctrine > Annotations) that people are going to want to try and reimplement > eventually using a core language feature. This much data is put in > one place primarily for DX reasons, to give developers a one-stop-shop > for defining a given extension. Saying "well just abandon your > approach entirely" is not a satisfying answer. > > Summary: It doesn't fit well at all, and there's features missing that > would prevent Drupal from being able to use the proposed attributes > RFC as-is, even without talking about classes-as-annotations. A > series of improvement request/suggestions are listed at the end of > this email. > > Simple example: > > Drupal plugins (usually) use annotations. Here's a simple example: > > /** > * Provides a block to display 'Site branding' elements. > * > * @Block( > * id = "system_branding_block", > * admin_label = @Translation("Site branding") > * ) > */ > class SystemBrandingBlock { > > } > > This defines a "block" (type of plugin). It's unique machine name > identifier is "system_branding_block", and its human-facing label is > "Site branding", which is marked as a translatable string. That all > seems reasonable to include here. > > Here's what I came up with for a possible attributes version: > > <> > <> > <> > class SystemBrandingBlock { > > } > > Not too bad at first blush, but there's 2 problems. It's also possible to write: <> Then you'll need you own layer that translates "Drupal" attributes from AST to everything you like. > > 1) There's no indication that the label is a translatable string. One > could hard-code that logic into whatever processing happens for > PluginAdminLabel, but then there's no indication for our gettext > scanner that "Site branding" is translatable and should be extracted > for translation. > > 2) If we want to say that the value "Block" corresponds to a class > (something that would be up to the parser to do), there's no > indication of the namespace against which to resolve "Block". The > alternative would be to require including the full class name string, > like so: > > <> > > But that DX is quite terrible. We introduced ::class in 5.5 for a > reason. Better would be: > > <> > > But that works only if the attribute parser resolves Block::class > against the currently "use"d namespaces so that it's a full class name > string when reflection picks it up. If not, then that means the > user-space parser needs to catch that, then go back to the file and > figure out the available use statements and resolve against those. > It's doable, but ugly and certainly more work than I'd want to put in > as someone writing such a parser. > > I don't know if that's a feature of the patch at the moment, but it > would need to be. > > So even in a simple case we have insufficient functionality. > > Complex example: > > OK, let's go to the other end and look at an example that is way more > complicated. (Yes, maybe too complicated.) Doctrine annotations are > also used to define Entity Types, which correspond to a class. Here's > the annotation for a Node, in all its glory: > > /** > * Defines the node entity class. > * > * @ContentEntityType( > * id = "node", > * label = @Translation("Content"), > * bundle_label = @Translation("Content type"), > * handlers = { > * "storage" = "Drupal\node\NodeStorage", > * "storage_schema" = "Drupal\node\NodeStorageSchema", > * "view_builder" = "Drupal\node\NodeViewBuilder", > * "access" = "Drupal\node\NodeAccessControlHandler", > * "views_data" = "Drupal\node\NodeViewsData", > * "form" = { > * "default" = "Drupal\node\NodeForm", > * "delete" = "Drupal\node\Form\NodeDeleteForm", > * "edit" = "Drupal\node\NodeForm" > * }, > * "route_provider" = { > * "html" = "Drupal\node\Entity\NodeRouteProvider", > * }, > * "list_builder" = "Drupal\node\NodeListBuilder", > * "translation" = "Drupal\node\NodeTranslationHandler" > * }, > * base_table = "node", > * data_table = "node_field_data", > * revision_table = "node_revision", > * revision_data_table = "node_field_revision", > * translatable = TRUE, > * list_cache_contexts = { "user.node_grants:view" }, > * entity_keys = { > * "id" = "nid", > * "revision" = "vid", > * "bundle" = "type", > * "label" = "title", > * "langcode" = "langcode", > * "uuid" = "uuid", > * "status" = "status", > * "uid" = "uid", > * }, > * bundle_entity_type = "node_type", > * field_ui_base_route = "entity.node_type.edit_form", > * common_reference_target = TRUE, > * permission_granularity = "bundle", > * links = { > * "canonical" = "/node/{node}", > * "delete-form" = "/node/{node}/delete", > * "edit-form" = "/node/{node}/edit", > * "version-history" = "/node/{node}/revisions", > * "revision" = "/node/{node}/revisions/{node_revision}/view", > * } > * ) > */ > class Node { > > } > > Yoinks. Now let's try and turn that into attributes. Here's my first > attempt: > > <> > <> > <> > <> > <> > <> > <> > <> > <> > <> > <> > <> > <> > // ... > class Node { > > } > you don't need to split your annotation into many attributes. You should just adopt its syntax to become a valid PHP expression. This expression is not going to be evaluated. It's going to be just parsed into AST. and then you may traverse this AST and transform it into other data structures. The key idea of RFC was not to invite another language for meta-data, but use PHP language itself. > I gave up at that point, as I'd already identified several problems. > > 1) The attribute names themselves are horribly long, because they're > not namespaced at all. That means I have to namespace them myself in > the annotation, which leads to grotesquely long tokens. > > 2) Again, I need to list the name of a class in the annotation. That > means either a stupid-long-string (which therefore means I can't catch > stupid typos with static analysis) or resolving ::class constants. I > went with the latter here because the alternative is basically unusable. > > 3) Nested data is unsupported, except through manual namespacing. > > 4) Hashes are totally unsupported. > <> is a terrible > idea, as even with my own parser I now cannot tell which part of that > string is supposed to be a class name to map the data to and which isn't. > > 5) Because the attribute names are just strings, I am repeating very > long values with no static analysis support at all. One typo in that > string, even though it's not quoted like a string, means stuff will > just not work and I don't know how to catch it other than visual > inspection (aka, "hey developer, guess!"). > > OK, let's try using the AST format. That gets a little bit better: > > <> > <> > <> > <> > <> > <> > <> > <> > <> > < NodeForm::class, > 'delete' => NodeDeleteForm::class > 'edit' => NodeForm::class]>> > < NodeRouteProvider::class]>> > class Node { > > } > > At least now I can define the hash in a parsable fashion. However, > it's not clear to me if I can catch errors there with static > analysis. Note, for instance, that I omitted the comma on the > 'delete' line. That would be a parse error in normal code. Would it > be a parse error on compile? I would hope so, since it would be a > parse error when I tried to reflect the attributes. It doesn't > address the other issues, though. yes. it's going to be a parse error at compile time. The patch was provided and it's better to try it. > > OK, let's take that to its logical conclusion and directly map in the > full annotation: > > < 'id' => 'node', > 'label' => 'Content', > 'bundle_label' => 'Content type', > 'handlers' => [ > 'storage' => NodeStorage::class, > 'storage_schema' => NodeStorageSchema::class, > 'view_builder' => NodeViewBuilder::class, > 'access' => NodeAccessControllerHandler::class, > 'views_data' => NodeViewsData::class, > 'form' => [ > 'default' => NodeForm::class, > 'delete' => NodeDeleteForm::class, > 'edit' => NodeForm::class, > ], > 'route_provder' => [ > 'html' => NodeRouteProvider::class, > ], > ], > // ... > ])>> > <> > < "canonical" => "/node/{node}", > "delete-form" => "/node/{node}/delete", > "edit-form" => "/node/{node}/edit", > "version-history" => "/node/{node}/revisions", > "revision" => "/node/{node}/revisions/{node_revision}/view", > ])>> > class Node { > > } > > I broke it up a little bit, but that may or may not make sense in > practice. > > What do we get with this approach? Well, we can model the full data > structure. That's good. If ContentEntity is supposed to map to a > class in my system it would be slightly less fugly to use a full class > name there now, as there's only maybe 3 attributes instead of 30. > Still, we're really only getting any benefit out of it if: > > 1) The ::class constant acutally works as I've shown here. > > 2) The parser understands that I'm trying to define an array structure > and can catch syntax typos for me. > > Otherwise, there's really no benefit over sticking the string in the > docblock as we do now. > > Additionally, even then it's still "just an array", by which I mean an > anonymous struct, by which I mean "you get no help at all on typos." > You may have noticed that I typoed "route_provder" instead of > "route_provider" above. An an array key, that is a silent runtime > error that's crazy hard to debug. Forcing any meaningful data > structure into anonymous structs is doing everyone a disservice. (As > a Drupal developer, I can attest to that fact with personal scars.) > > Recommended changes: > > 1) ::class constants should resolve at compile time, relative to use > statements, anywhere in the annotation. we don't have fully constructed classes at compile time. Classes may be used during transformation from plain arrays and AST into application specific data structures. > > 2) Namespacing for attribute names. This is necessary for both > collision-avoidance and to minimize typing of obscenely long attribute > names. The easiest way to do that would be: > > 3) Some way to provide a data definition for the annotation that can > be checked at compile time. This could be classes, a la Doctrine. It > could be a new Annotation type as others have suggested. It could be > something else, akin to a struct like Liz Smith keeps toying with. > I'm flexible here. But some way to provide a data definition for the > annotation that is more robust than "you get an AST for an associative > array that you can eval to an array yourself, good luck" is, I > believe, mandatory for these to be successful for more than the most > trivial use cases. If that data definition is a class or class-like > type, that also resolves the namespace question very easily. > > Note: We do NOT need objects created at compile time or include time, > or even instantiation of the annotated class time. Only at > reflection-lookup time. All that's needed before that is syntax > validation (both compiler and IDE, once IDEs catch on). If that can > go as far as key validation at compile time, great. If that can only > happen at reflection-lookup time, that's acceptable as long as it > happens. This is the way patch works. It performs only syntax validation at compile time (no key validation). And at reflection time you may validate and translate base structures into whatever you like. > > 4) I don't know if there's a reasonable way to "nest" annotations. I > do not have a solution for the translatable string marker issue, but > it is a feature of Doctrine that Drupal leverages heavily and we would > be loathe to lose. (It may even be a deal breaker.) @ is used as silence operator, but it's still a valid PHP expression syntax <> > > I hope this case study has been useful. To reiterate, I am in favor > of PHP adding annotation/attribute/whatever-you-call-it support; I > just want to make sure it is robust enough to support the use cases > that I know exist in the wild. yeah. thanks for spending your time. I hope my answers would help you adopting attributes idea for your needs. Thanks. Dmitry. > > --Larry Garfield >