2020-09-13 17:58 GMT, Benjamin Eberlei <kont...@beberlei.de>: > On Sat, Sep 12, 2020 at 10:23 PM Olle Härstedt <olleharst...@gmail.com> > wrote: > >> Hi internals! >> >> Separation of data and behaviour is both a fun and hard discussion, >> especially considering: >> >> * "It should be possible to add new features without touching old code"; >> and >> * "Principle of Least Privilege" (never expose more than you have to) >> (https://en.wikipedia.org/wiki/Principle_of_least_privilege). >> >> There should (could) be a way to add new behaviour to old data without >> touching the old data (class). Traits won't work in this use-case, >> since they assume the same internal structure for all trait-using >> classes. Imagine the `stringable` interface and a `toString` trait. A >> __toString() method needs knowledge about the internal structure of a >> class Foo. Yet if we want to keep adding behaviour to Foo, we'll end >> up with either exposing too much of Foo, or expanding the class file >> indefinitely. Please note that composition is not a proper solution, >> since it requires exposure of Foo; composition leads to lack of proper >> encapsulation, or representation exposure. >> >> In Haskell it's possible to split instance implementation of >> type-classes into separate files. In Rust you can have a struct with >> private fields and put impl of behaviour in different files (but same >> crate). >> >> A similar feature in PHP could look like (using new keyword `expand` >> but could be anything, or even `extend` in new context): >> >> ``` >> // File FooStringable.php >> expand Foo implements stringable { >> public function __toString() { >> // Full access to Foo's all private fields here. >> // Assumes you can autoload Foo. >> // Assumes usage of $foo->__toString(); will be configured with >> autoload to dynamically find the correct behaviour of Foo. >> } >> } >> ``` >> >> If you'd use composition instead, you'd maybe have a formatter class >> with a method `$formatter->toString(stringable $foo)`. This has the >> problem I mentioned with exposing too much of $foo; it breaks >> encapsulation. It has the benefit of being able to provide multiple >> toString methods with different formats, but would have to assume >> similar structure of the objects passed to it (defined with an >> interface), which is not always possible or desirable. >> >> The other way is inheritance, which doesn't scale over multiple >> behaviours. `FooWithStringable extends Foo`? No. >> >> Was I clear here? Do you understand the issues that this design >> pattern is trying to solve? Its purpose is to solve "keep adding new >> feature to old data" in a clean and proper way, while keeping >> information encapsulation. >> > > Do I understand you correctly, it would be somewhat like "opening" up a > class and making changes to it in another file? > > Certainly a powerful concept, but I would be very interested in the details > how that would interact with autoloading. If I have a class Foo loaded, and > its "extension" FooString with toString method not, then it would lead to > the "toString" code missing.
Yes, a little like opening up, *but* with clear restrictions. It was explained to me that this won't work without either: 1) A module system to define which files are part of a class 2) Manually write in the "main" class file which other extensions to this class should be loaded. The reason is again encapsulation - it should not be possible for any file to just get access to private fields by adding a new interface implementation. Option (2) can be achieved if we allow "include <file>;" in PHP *inside* a class definition. Again note that this is different from a trait, since it gives access to private properties that are *different* between the classes using it, like toString() or toQuery(). Option (2) does not need configured autoloading. Option (1) is more elaborate, maybe composer.json would need to configure something, *or* it is assumed that a class is defined in a single folder instead of a single file. Which already is kind of like a module system. In Rust, it's possible to spread out behaviour (impl) to a certain data (struct) in the same _crate_ while keeping access to private fields: https://users.rust-lang.org/t/implement-private-struct-in-different-files/29407 C# has something called "partial class": https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods Olle -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php