Hi, My biggest concern about supporting friend classes is the ability to access non-intentional to be accessed code outside of the original class's knowledge. This by itself is very dangerous.
I do see however package-private classes as a possibility (I actually have a partially running patch for that) and allowing it to access protected (not private) members is a second stage of my goal, so very similar to what your suggested, with the exception that the original class defined what can be accessed. Regards, On Mon, Dec 7, 2015 at 10:55 PM, Dustin Wheeler <mdwhe...@ncsu.edu> wrote: > 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: mdwhe...@ncsu.edu | w: 5-9786 > "If you don't know where you're going, it's easy to iteratively not get > there." > -- Guilherme Blanco MSN: guilhermebla...@hotmail.com GTalk: guilhermeblanco Toronto - ON/Canada