Re: RFC: short and inner classes

From: Date: Tue, 25 Mar 2025 18:51:07 +0000
Subject: Re: RFC: short and inner classes
References: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  Groups: php.internals 
Request: Send a blank email to [email protected] to get a copy of this message
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


Thread (102 messages)