Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:89732 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 18087 invoked from network); 8 Dec 2015 03:55:38 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 8 Dec 2015 03:55:38 -0000 Authentication-Results: pb1.pair.com header.from=mdwheele@ncsu.edu; sender-id=unknown Authentication-Results: pb1.pair.com smtp.mail=mdwheele@ncsu.edu; spf=permerror; sender-id=unknown Received-SPF: error (pb1.pair.com: domain ncsu.edu from 209.85.215.51 cause and error) X-PHP-List-Original-Sender: mdwheele@ncsu.edu X-Host-Fingerprint: 209.85.215.51 mail-lf0-f51.google.com Received: from [209.85.215.51] ([209.85.215.51:34279] helo=mail-lf0-f51.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id B5/12-31749-8B456665 for ; Mon, 07 Dec 2015 22:55:37 -0500 Received: by lffu14 with SMTP id u14so4599146lff.1 for ; Mon, 07 Dec 2015 19:55:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ncsu-edu.20150623.gappssmtp.com; s=20150623; h=mime-version:date:message-id:subject:from:to:content-type; bh=7Qh7sncan00F/Z493rtR9C1FGt1IabGvKeB156Z/MGE=; b=VxInf9YGOBGz54QMpZOWiPRLXdvniEJ3yk6hyQ5L8XI+gtNpAaNklad44VlPWpYmdE MCNrH1/IngVP2py58Rb124b0ksvbiBQXgFuIT78a47s6Qsi8pucrASO02PPC67ptYRJ4 c0XZP4CbQmYpsBiOkErUIRN79fVzv2j8Y+adCErESnADWLgstlo83PfdQAzDRH1XDyNx PbA5JRCtzK9Muk+XlNgmZia00dueHB6f7Umc2OIdb9kPaT2gAijbRFJ9NL1db9wJ6oPG ePYBbdy7gufvn1xPZS3mWPGXxmrPSuPz3QaL8wpLUcBcWa1XQTDiKJ5XmZORDUrzDoos Dq3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:date:message-id:subject:from:to :content-type; bh=7Qh7sncan00F/Z493rtR9C1FGt1IabGvKeB156Z/MGE=; b=ZyeulIFDMbJAMNyxe7nUe91Wzjjh+hxfoDH9jHiSlj9rcYVc69hHEU6ePpIuCPeQ2m LB1MH18vRZclZw64KkpOe1GX3jBaPadbF3WWu/RDOoLrGyDxoNLkr6AsuFAnDE3GnqX2 e+LXG5b0bPzREeN5Kge6K1o5hhB6wWUIvUuThxMFZ0cHLBmmSVUCNHrRrhtP+fS8B0KO ZQkGfUc+XXQWKjM4UFZu5ACQu0Dd1LgOULwyO+Jyvtl2B12dksaARPOQuuhP0Bgg5sAF WxpHkAyoEjxFUW3yO5LzNkdISmFA5OYX9IU9guJNd5zl9AUIfEI/gyd2Yafa1r1JruD5 Ev+Q== X-Gm-Message-State: ALoCoQkXbBucrpGj8VIVP5P4I2j4nNyMI+8Vn5ZkT6PHoer30bKbckZmf3BSwXdYfkXkp4OFv+fvtvtjOnAAuQqUyf6dg1afnv7lVgm76p01Hcap0Hgello= MIME-Version: 1.0 X-Received: by 10.25.153.146 with SMTP id b140mr490422lfe.33.1449546932964; Mon, 07 Dec 2015 19:55:32 -0800 (PST) Received: by 10.112.108.233 with HTTP; Mon, 7 Dec 2015 19:55:32 -0800 (PST) Date: Mon, 7 Dec 2015 22:55:32 -0500 Message-ID: To: internals@lists.php.net Content-Type: multipart/alternative; boundary=001a11402182066bf905265af242 Subject: Class Friendship in PHP From: mdwheele@ncsu.edu (Dustin Wheeler) --001a11402182066bf905265af242 Content-Type: text/plain; charset=UTF-8 Hi! The topic of class / function friendship has been recently discussed and previously covered in the past through this list as well as through feature requests against bugs.php.net. I've recently developed an interest in the feature after reaching for a tool that just didn't exist in a language I really love. Given the opportunity to learn something new, I decided I would learn what it took to add a new feature to PHP7. That said, I would like to describe the case for class friendship in the context of a landscape of discussion around private collaborators, in general. I would then like to submit an RFC for discussion that proposes class friendship (without friend functions; reasons described below). In addition, I have a functional implementation with tests that is ready for review. I have begun a draft of the RFC but am unable to post it do to lack of wiki karma. If I need to contribute in other ways to gain karma, please let me know! As an aside (before I continue), I would like to contribute implementation notes I took along this path to the project wiki and would like review (fact-checking) from those more knowledgeable to that regard as there is little recent online documentation on implementing a feature that cuts across language internals like this does. In order to implement this feature, I had to source information from a 2012 article from nikic, the RFC for AST and random tweets to SaraMG. Not exactly the clearest path for a newbie! I would like to get this information somewhere it might be useful so that others may benefit. That said, I do not want the fact that I have implemented this feature before engaging this mailing list to sway consideration for RFC discussion. I did this as a learning opportunity, first; to scratch an itch, second. To continue; class friendship allows an object to be better encapsulated by granting per-class access to private and protected properties that would otherwise have to be marked public or exposed through public getters. This feature affords developers an opportunity to better model objects as behavioral units while making explicit presentation concerns through friendship. Admittedly, class friendship has a narrow use-case, but is nonetheless a valuable expression for object modeling when used properly. The purpose of the feature should not be conflated / confused with the goals of something like package protected classes or package visibility, in general. I feel those features apply more closely to the types of behaviors user-land sees in Symfony (and other framework) packages that mark properties as `@internal` but are forced to make them public to share access between internal data structures. I don't necessarily feel that class friendship is the "Right Answer TM" in this case, but I think that the dance package developers currently have to do to express "don't use this property, we use this internally to help you" is worth improving. In my opinion, class friendship has at least two known (to me) applications: 1. White-box (characterization) testing as a tactical refactoring tool when approaching legacy codebases 2. Modeling a tightly-coupled relationship between behavioral and presentation concerns in a domain model Class friendship allows a developer to make a specific test-case a friend of a unit under test. This grants the test-case access to internal implementation details of what may or may not be a properly factored behavioral unit. While it may be "best practice" to model behavior rather than expose state, the latter is a grossly common case in many PHP code-bases, thus lending value to the strategy of white-box testing / characterization testing in the short-term to enable a safe refactoring process. In a language that does not implement class friendship (or private collaborators, in general) a developer is left with a trade-off to sacrifice modeling concerns for the benefit of a passing test-suite. If the situation dictates characterization tests as "what's best", then the developer either marks internal state as public or has to implement public getters to proxy calls to internal state. In either case, for the duration of refactor, the unit is vulnerable to misuse by clients of the package the unit is a member of. Thus, a reason for the birth of the `@internal` doc-block. In addition, explicitly marking a class with a collaborator given special access is an opportunity for static analysis that highlights isolated coupling between collaborators. In the case of testing, you could use this as a means of determining next-work towards moving beyond the characterization / white-box test-suite towards a more behavioral / spec-driven test-suite. This, rather than simply deleting the characterization tests after they are no longer needed; saving some level of effort. Class friendship allows an explicit and concise expression of tight coupling between collaborators to split responsibilities. A common use-case here is splitting presentation concerns from what would otherwise be a "tell don't ask" behavioral unit. This has immediate practical applications in a variety of scenarios. Without this feature, developers are left with a trade-off of marking internal properties public to make them available and sacrificing design opportunities in the future under a major revision. Another common occurrence is the addition of getters that simply proxy internal state and grow the public API of a unit by necessity. To be sure, this feature is not about pulling off "access to private class properties", but about modeling a specific relationship between two collaborators. In fact, access to private class properties is already possible through closure scope "juggling" or nasty use of `debug_backtrace`. Both of these are included in references below. While it is technically possible to execute friend-like features in user-land, I would rather be able to concisely represent the relationship with a known and well-documented concept supported by the language. The syntax I have implemented is very similar to `trait`. It introduces a new keyword `friend` followed by a name_list. Here is a simple example of syntax: https://gist.github.com/mdwheele/6d9b178dc25ebb829e4c#file-1sample-php-L3-L14. It feels natural and is forwards-compatible with friend functions, should it be discussed and implemented. That brings me to one of my leading statements: my current un-submitted RFC is only for friend classes, but includes future scope towards implementation of friend functions as class methods and global functions. I want to limit the scope of the RFC for two reasons: 1. An RFC that implements friend classes affords the benefits I've described above immediately. The RFC vote will also either prove desire for the feature or will put-to-rest further discussion of related features. Supporting friend functions as class methods offers tighter control over property access by limiting access to a single method on the friend. This IS valuable, but pales in comparison to what friend classes offer, by default. The testing use-case would not really leverage this quality as each test method would presumably require access to the system under test. Expressing that tight of control isn't really something I would personally advise as far as modeling goes. 2. It seems with recent feature additions (return types, primarily) the syntax around class methods needs some time to settle and prove itself before disrupting it through the addition of additional keywords. If friend classes were accepted or folks are interested in having productive discussion, I am definitely willing to discuss syntax options. It's just the value-add at the trade-off of syntax disruption isn't very favorable, to me. I have more examples of class friendship available in the RFC but as per documented processes want to "test the waters" so-to-say before officially submitting an RFC draft for review. I would like to hold those for now as I have a hard time organizing code samples via email, but am willing to answer questions on other properties of class friendship (non-symmetric, non-transitive, not inherited, access inherited). Finally, it is my opinion that implementing class friendship -- as well as considering other features in the domain of private collaborators -- opens up new opportunities in object modeling for the PHP community and while it not be the "next big thing", would be a welcome addition to the lovely language that is PHP. Thanks for your consideration and I look forward to further discussion! P.S.: Congratulations on PHP7! Previous discussion / related materials: - https://en.wikipedia.org/wiki/Friend_class - https://bugs.php.net/bug.php?id=34044 - https://marc.info/?l=php-internals&m=144639394529142 - http://ocramius.github.io/blog/accessing-private-php-class-members-without-reflection/ -- Dustin Wheeler | Software Developer NC State University m: mdwheele@ncsu.edu | w: 5-9786 "If you don't know where you're going, it's easy to iteratively not get there." --001a11402182066bf905265af242--