On Tue, Mar 25, 2025, at 6:59 AM, Rob Landers wrote:
>> When I say module scope, I'm referring to something along the lines that Arnaud and I
>> were exploring a while back. tldr, "cluster of files with a common namespace root, which can
>> get loaded together." It was mostly about performance, but did offer module-private as well,
>> with some nice potential. At the moment it's stalled out on "there's nasty hard edge
>> cases and we're not sure if it's worth it" concerns.
>>
>> Concept brain dump here: https://github.com/Crell/php-rfcs/blob/master/modules/spec-brainstorm.md
>> Code exploration from Arnaud here: https://github.com/arnaud-lb/php-src/pull/10
>>
>> Still well short of RFC state, of course, but provided for context.
>
> My email has been broken for a few days, so sorry for the late response...
No worries, I'm about to leave town myself. :-)
>> In this case, I am not seeing what the nesting gets you. Making Destination a normal class
>> doesn't hurt anything here, does it?
>
> I wasn't intending to write a definitive example, just to illustrate
> it. A better example might be a lazy cache, using hooks to notify the
> outer class it has possibly been updated:
>
> (note this is using the new syntax, but this syntax is NOT currently
> reflected in the RFC text, yet)
>
> namespace Caching;
>
> class Cache {
> private array $items = [];
> private array $dirty = [];
>
> public function getItem(string $key): CacheItem {
> return $this->items[$key] ?? ($this->items[$key] = new
> CacheItem($this, $key, null));
> }
>
> public function saveChanges(): void {
> foreach ($this->dirty as $key => $value) {
> echo "Saving $key to persistent storage\n";
> }
> $this->dirty = [];
> }
>
> private function markDirty(string $key): void {
> $this->dirty[$key] = $this->items[$key];
> }
>
> public class CacheItem {
> public string|null $value {
> get {
> return $this->_value;
> }
> set {
> $this->_value = $value;
> $this->cache->markDirty($this->key);
> }
> }
>
> public function __construct(
> private readonly Cache $cache,
> public private(set) string $key,
> private string|null $_value,
> ) {}
> }
> }
>
> $cache = new Cache();
> $item1 = $cache->getItem('foo');
> $item1->value = 'bar';
>
> $cache->saveChanges();
>
> This outputs:
>
> Saving foo to persistent storage
>
> This provides for a simple API for outside the Cache class: getItem()
> and saveChanges(); that's it. For CacheItem's, you only have the props
> for the key or value, and updating the value marks the item as "dirty".
>
> Currently, if you want this same API, the only way to do this is by:
>
> 1. using reflection,
> 2. using a closure and binding it to the other class's scope,
> 3. providing a listener + observer (more formal version of [2]), or
> 3. iterating over items while saving to find dirty items.
>
> Those choices are not ideal (IMHO).
>
> Reflection feels "hacky" as does using a bound closure. The
> listener/observer pattern is probably overkill here, unless you already
> have one lying around (i.e., using symfony/laravel or a related
> framework), but still might require making some of the "internal" api
> public. Finally, iterating over all the items to find dirty ones is
> naive and inefficient.
>
> Hopefully this is a better illustration of what nesting provides?
>
> -- Rob
I have a similar if less involved use case in my ordering library, though it doesn't need the
look-back functionality.
So the use case that nested classes would enable that isn't currently covered by "just use
separate files and @internal, deal" is around the lesser class having private access to the
greater class. Which... I believe fileprivate and modules would also address, albeit in a different
way, yes? (Presuming fileprivate is available on properties, not just the class itself.)
--Larry Garfield