Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127510 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id 436671A00BC for ; Fri, 30 May 2025 07:57:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1748591752; bh=jiyLJ56Bkre6y3JjSh2FKRPi37MaEQnQM1+HyORvhhY=; h=Date:From:To:Cc:In-Reply-To:References:Subject:From; b=fYCX2+KRFR8v+nkvljDX2vh5cbsCfbcNvTq4Hs6hJtnVZtJXgwOG0q8ob48DyU69o hCQ7CBmDnD4x+mXCDHOV1OgsOqILANzRR21UOGD1Kq4KLDDWvuiU9SJcCd0tDvup+Q Goy+4759voZan68ZuF1dsDm3vw0UOb+l8OChaUJhkbfI+zSIqS+4BRs+YiUT5dGryd pgeVL777HT6TBf6V7JOwSRUVTZiaoYBuCbor3owC4nnB2QQojT0qLBFvJTDyxhv+M/ mSnoyUf7Dcr8u1HbF/RD6uTlRy4IhSrbDmEa0Y0t+WtGps51NFLp17LD7wNj+oeAoj 6z6SjCs+5AIig== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 9FA1F1801E5 for ; Fri, 30 May 2025 07:55:50 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-2.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from fout-a7-smtp.messagingengine.com (fout-a7-smtp.messagingengine.com [103.168.172.150]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Fri, 30 May 2025 07:55:50 +0000 (UTC) Received: from phl-compute-05.internal (phl-compute-05.phl.internal [10.202.2.45]) by mailfout.phl.internal (Postfix) with ESMTP id C5B551380350; Fri, 30 May 2025 03:57:55 -0400 (EDT) Received: from phl-imap-05 ([10.202.2.95]) by phl-compute-05.internal (MEProxy); Fri, 30 May 2025 03:57:55 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:cc:content-type:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm2; t=1748591875; x= 1748678275; bh=ZOKgzYTV9GdGsh+YcHxKsxf0xmvxbAhCPFr309zIObo=; b=M ZlJ9Yb1btlxjsf8jH1FEcz5tOpOYWG98OXdfyquF5dGk3B0F3UVefH+O5bfcs9CS hA/8yeBZgcpanRy8TCGBuLepa64hmZnNlu3xUlL+tKPTRWqs/BxVoKBKuBfB4jEN WXfq9ZWB9RgV9BQ9FZMc98R3FlCVymr/PHefWAvkelriBmzeVS+Vs0np39DHy0Th LlgNV0d4HFsIdS2UCWt9R6sz7M9lTQ3QAwCgHW2btXi4tRIGOEAf7uL+hu+H0DmW BQcMKW4uqYdYadAuhIcEv+dxECQNa6DNYoD2yKPx4ZjwFN4DX+YDRJuszDnNGFIs N+pHNpTR69879ROA6riGQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; t= 1748591875; x=1748678275; bh=ZOKgzYTV9GdGsh+YcHxKsxf0xmvxbAhCPFr 309zIObo=; b=nrfcDIhwJg81PzrBtGXqG08FJNqruri8TrotcGj3piNodmYDNSa vvEdmcVxtVQjBtLEkpPMT80l85zOb6nIWKPe4el6XgFLCxDdb9MWkWr7Lc7HeDib 5A0M74fi0CWp68dkUiXbEBAmmjqJ5vr8GHPs5jbXfQuhiEmkpIPqkZWrkLGLlcuP WKJgFOhjnvnJmC4QLRCd+PqbC1OCrE3M6kQ8/ataFVLL1+KHrJ38PP/0uBBVLBuL jxaifSciXmWrf3qAav39WaEObUbDnEUjdR3NUblfMB2CVdSDetl3REeVcJWpwvFR no1MAbf7Apm/86mdIl78njEiGEeqQgMbncw== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtddtgddvkeegjeculddtuddrgeefvddrtd dtmdcutefuodetggdotefrodftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpggft fghnshhusghstghrihgsvgdpuffrtefokffrpgfnqfghnecuuegrihhlohhuthemuceftd dtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjughrpefoggffhffvvefk jghfufgtsegrtderreertdejnecuhfhrohhmpedftfhosgcunfgrnhguvghrshdfuceorh hosgessghothhtlhgvugdrtghouggvsheqnecuggftrfgrthhtvghrnhepieeuteehvddv feejhffgieehleehhedthfefkeejffelgfevvdekudetjeejtddtnecuvehluhhsthgvrh fuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheprhhosgessghothhtlhgvugdr tghouggvshdpnhgspghrtghpthhtohepfedpmhhouggvpehsmhhtphhouhhtpdhrtghpth htoheprghlfihinhesghgrrhhsihdruggvpdhrtghpthhtohepihhnthgvrhhnrghlshes lhhishhtshdrphhhphdrnhgvthdprhgtphhtthhopehimhhsohhprdhphhhpsehrfigvtg drtghordhukh X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 4E8551820064; Fri, 30 May 2025 03:57:55 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: T018bd9ffc60205f7 Date: Fri, 30 May 2025 09:57:34 +0200 To: "Alwin Garside" , "Rowan Tommins [IMSoP]" Cc: "PHP Internals" Message-ID: In-Reply-To: References: <9A26F72B-D0EF-414F-B193-BED3CAB26A0B@rwec.co.uk> <9f6a0d6e-27c3-4f77-aed6-e55147442b6f@app.fastmail.com> <673fd2db-b07f-439b-a4f2-e9519108d159@app.fastmail.com> <78641D8B-AF1D-4912-920A-D75A37C32F05@rwec.co.uk> <354cb888-97c4-4f8c-a0da-359d1e63c0f9@rwec.co.uk> <10D95B6E-094B-4EAE-A18A-AF6B795CB352@rwec.co.uk> <2adbff61-5e11-4d39-ab5c-d7950a4550a6@app.fastmail.com> <79E7FA26-2F5A-470C-B1DF-12CC46A08FE5@rwec.co.uk> <1c6dcd84-9016-48e1-971f-de7749cbdce8@rwec.co.uk> Subject: Re: [PHP-DEV] Module or Class Visibility, Season 2 Content-Type: multipart/alternative; boundary=7974be9a3f774dc39f6117cf1e6ddb10 From: rob@bottled.codes ("Rob Landers") --7974be9a3f774dc39f6117cf1e6ddb10 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Mon, May 26, 2025, at 21:39, Alwin Garside wrote: > Hey all, >=20 > It took me a while, but I'm finally caught up with this thread, and wo= uld like to give my 2 cents. >=20 > On 25 May 2025, at 23:17, Rowan Tommins [IMSoP] = wrote: > >=20 > > On 25/05/2025 21:28, Larry Garfield wrote: > >> Even if we develop some way such that in Foo.php, loading the class= \Beep\Boop\Narf pulls from /beep/boop/v1/Narf.php and loading it from B= ar.php pulls the same class from /beep/boop/v2/Narf.php, and does someth= ing or other to keep the symbols separate... Narf itself is going to loa= d \Beep\Boop\Poink at some point. So which one does it get? Or rather,= there's now two Narfs. How do they know that the v1 version of Narf sh= ould get the v1 version of Poink and the v2 version should get the v2 ve= rsion. > >=20 > >=20 > > The prefixing, in my mind, has nothing to do with versions. There is= no "v1" and "v2" directory, there are just two completely separate "ven= dor" directories, with the same layout we have right now. > >=20 > > So it goes like this: > >=20 > > 1. Some code in wp-plugins/AlicesCalendar/vendor/Beep/Boop/Narf.php = mentions a class called \Beep\Boop\Poink > > 2. The Container mechanism has rewritten this to \__Container\Alices= Calendar\Beep\Boop\Poink, but that isn't defined yet > > 3. The isolated autoloader stack (loaded from wp-plugins/AlicesCalen= dar/vendor/autoload.php) is asked for the original name, \Beep\Boop\Poink > > 4. It includes the file wp-plugins/AlicesCalendar/vendor/Beep/Boop/P= oink.php which contains the defintion of \Beep\Boop\Poink > > 5. The Container mechanism rewrites the class to \__Container\Alices= Calendar\Beep\Boop\Poink and carries on > >=20 > > When code in wp-plugins/BobsDocs/vendor/Beep/Boop/Narf.php mentions = \Beep\Boop\Poink, the same thing happens, but with a completely separate= sandbox: the rewritten class name is \__Container\BobsDocs\Beep\Boop\Po= ink, and the autoloader was loaded from wp-plugins/BobsDocs/vendor/autol= oad.php >=20 > In this thread I see a lot of talking about Composer and autoloaders. = But in essence those are just tools we use to include files into our cur= rent PHP process, so for the sake of simplicity (and compatibility), let= 's disregard all of that for a moment. Instead, please bear with me whil= e we do a little gedankenexperiment... >=20 > First, imagine one gigantic PHP file, `huge.php`, that contains all th= e PHP code that is included from all the libraries you need during a sin= gle PHP process lifecycle. That is in the crudest essence how PHP's incl= ude system currently works: files get included, those files declare symb= ols within the scope of the current process. If you were to copy-paste a= ll the code you need (disregarding the `declare()` statements) in one hu= ge PHP file, you essentially get the same result. >=20 > So in our thought experiment we'll be doing just that. The only rule i= s that we copy all the code verbatim (again, disregarding the `declare()= ` statements), because that's how PHP includes also work. >=20 > ```php > =20 > // ... >=20 > namespace Acme; > class Foo {} >=20 > namespace Acme; > class Bar > { > public readonly Foo $foo; > public function __construct() > { > // For some reason, there is a string reference here. Don't as= k. > $fooClass =3D '\Acme\Foo'; > $this->foo =3D new $fooClass(); > } > } >=20 > namespace Spam; > use Acme\Bar; > class Ham extends Bar {} >=20 > namespace Spam; > use Acme\Bar; > class Bacon extends Bar {} >=20 > // ... > ``` >=20 > Now, the problem here is that if we copy-paste two different versions = of the same class with the same FQN into our `huge.php` file they will t= ry to declare the same symbols which will cause a conflict. Let's say ou= r `Ham` depends on one version of `Acme\Bar` and our `Bacon` depends on = another version of `Acme\Bar`: >=20 > ```php > =20 > // ... >=20 > namespace Acme; > class Foo {} >=20 > namespace Acme; > class Bar { > public readonly Foo $foo; > public function __construct() > { > // For some reason, there is a string reference here. Don't as= k. > $fooClass =3D '\Acme\Foo'; > $this->foo =3D new $fooClass(); > } > } >=20 > namespace Spam; > use Acme\Bar; > class Ham extends Bar {} >=20 > namespace Acme; > class Foo {} // Fatal error: Cannot declare class Foo, because the na= me is already in use >=20 > namespace Acme; > class Bar { // Fatal error: Cannot declare class Bar, because the na= me is already in use > public readonly Foo $foo; > public function __construct() > { > // For some reason, there is a string reference here. Don't as= k. > $fooClass =3D '\Acme\Foo'; > $this->foo =3D new $fooClass(); > } > } >=20 > namespace Spam; > use Acme\Bar; > class Bacon extends Bar {} >=20 > // ... > ``` >=20 > So how do we solve this in a way that we can copy-paste the code from = both versions of `Acme\Foo`, verbatim into `huge.php`? >=20 > Well, one way is to break the single rule we have created: modify the = code. What if we just let the engine quietly rewrite the code? Well, the= n we quickly run into an issue. Any non-symbol references to classes are= hard to detect and rewrite, so this would break: >=20 > ```php > =20 > // ... >=20 > namespace Acme; > class Foo {} >=20 > namespace Acme; > class Bar { > public readonly Foo $foo; > public function __construct() > { > // For some reason, there is a string reference here. Don't as= k. > $fooClass =3D '\Acme\Foo'; > $this->foo =3D new $fooClass(); > } > } >=20 > namespace Spam; > use Acme\Bar; > class Ham extends Bar {} >=20 > namespace Spam\Bacon\Acme; // Quietly rewritten > class Foo {} >=20 > namespace Spam\Bacon\Acme; // Quietly rewritten > class Bar { > public readonly Foo $foo; > public function __construct() > { > // For some reason, there is a string reference here. Don't as= k. > $fooClass =3D '\Acme\Foo'; // <=3D=3D Whoops, missed this one= !!! > $this->foo =3D new $fooClass(); > } > } >=20 > namespace Spam; > use Spam\Bacon\Acme\Bar; // Quietly rewritten > class Bacon extends Bar {} >=20 > // ... > ``` >=20 > So let's just follow our rule for now. Now how do we include Foo and B= ar twice? Well, let's try Rowan's approach of "containerizing." Let's ta= ke a very naive approach to what that syntax might look like. We simply = copy-paste the code for our second version of `Acme\Bar` into the scope = of a container. For the moment let's assume that the container works lik= e a "UnionFS" of sorts, where symbols declared inside the container over= ride any symbols that may already exist outside the container:=20 >=20 > ```php > =20 > // ... >=20 > namespace Acme; > class Foo {} >=20 > namespace Acme; > class Bar { > public readonly Foo $foo; > public function __construct() > { > // For some reason, there is a string reference here. Don't as= k. > $fooClass =3D '\Acme\Foo'; > $this->foo =3D new $fooClass(); > } > } >=20 > namespace Spam; > use Acme\Bar; > class Ham extends Bar {} >=20 > container Bacon_Acme { > namespace Acme; > class Foo {} > =20 > namespace Acme; > class Bar { > public readonly Foo $foo; > public function __construct() > { > // For some reason, there is a string reference here. Don'= t ask. > $fooClass =3D '\Acme\Foo'; > $this->foo =3D new $fooClass(); > } > } > } >=20 > namespace Spam; > use Bacon_Acme\\Acme\Bar; > class Bacon extends Bar {} >=20 > // ... > ``` >=20 > That seems like it could work. As you can see, I've decided to use dou= ble backspace (`\\`) to separate container and namespace in this example= . You may wonder how this would look in the real world, where not all co= de is copy-pasted into a single `huge.php`. Of course, part of this depe= nds on the autoloader implementation, but let's start with a first step = of abstraction by replacing the copy-pasted code with includes: >=20 > ```php >=20 > // ... >=20 > require_once '../vendor/acme/acme/Foo.php'; > require_once '../vendor/acme/acme/Bar.php'; >=20 > namespace Spam; > use Acme\Bar; > class Ham extends Bar {} >=20 > container Bacon_Acme { > require_once '../lib/acme/acme/Foo.php'; > require_once '../lib/acme/acme/Bar.php'; > } >=20 > namespace Spam; > use Bacon_Acme\\Acme\Bar; > class Bacon extends Bar {} > ``` >=20 > Now what if we want the autoloader to be able to resolve this? Well, o= nce a class symbol is resolved with a container prefix, it would have to= also perform all its includes inside the scope of that container.=20 >=20 >=20 > ```php > function autoload($class_name) { > // Do autoloading shizzle. > } >=20 > spl_autoload_register(); >=20 > namespace Spam; > use Bacon_Acme\\Acme\Bar; > class Bacon extends Bar {} >=20 > // Meanwhile, behind the scenes, in the autoloader:=20 > container Bacon_acme { > autoload(Acme\Bar::class); > } > ``` >=20 > Now this mail is already quite long enough, so I'm gonna wrap it up he= re and do some more brainstorming. But I hope I may have inspired some o= f you. All in all, I think my approach might actually work, although I h= aven't even considered what the implementation would even look like. >=20 > Again, the main point I want to make is to just disregard composer and= the autoloader for now; those are just really fancy wrappers around imp= ort statements. Whatever solution we end up with would have to work inde= pendently of Composer and/or the autoloader. >=20 > Alwin >=20 I=E2=80=99m starting to think that maybe modules might be a bad idea; or= at least, class/module visibility. As an anecdote, I was looking to extract a protobuf encoding library fro= m a larger codebase and create a separate library for Larry=E2=80=99s Se= rde library. During the extraction I realized that many of the classes a= nd functions I was relying on actually used @internal classes/functions.= If =E2=80=9Cmodule=E2=80=9D visibility were a thing=E2=80=A6 would my i= mplementation have been possible? In other words, if visibility comes with modules; there really needs to = be some kind of escape hatch. Some way to say, =E2=80=9CI know what I=E2= =80=99m doing, so get out of my way.=E2=80=9D In Java, you can do this by creating a namespaced class in the target na= mespace. I=E2=80=99m not sure about other languages; but it=E2=80=99s so= mething to think about.=20 =E2=80=94 Rob --7974be9a3f774dc39f6117cf1e6ddb10 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable


On Mon, May 26, 2025, at 21:39, Alwin Garside wrote:
Hey all,

It took me a while, but I'm finally caught up with thi= s thread, and would like to give my 2 cents.

On= 25 May 2025, at 23:17, Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote:
>&nbs= p;
> On 25/05/2025 21:28, Larry Garfield wrote:
&= gt;> Even if we develop some way such that in Foo.php, loading the cl= ass \Beep\Boop\Narf pulls from /beep/boop/v1/Narf.php and loading it fro= m Bar.php pulls the same class from /beep/boop/v2/Narf.php, and does som= ething or other to keep the symbols separate... Narf itself is going to = load \Beep\Boop\Poink at some point.  So which one does it get?&nbs= p; Or rather, there's now two Narfs.  How do they know that the v1 = version of Narf should get the v1 version of Poink and the v2 version sh= ould get the v2 version.
=
> The prefixing, in my mind, has nothing to do with versions. Th= ere is no "v1" and "v2" directory, there are just two completely separat= e "vendor" directories, with the same layout we have right now.
> So it goes like this:
> 1. Some code in wp-plugins/AlicesCalendar/vendor/Beep/Boop= /Narf.php mentions a class called \Beep\Boop\Poink
> 2. The= Container mechanism has rewritten this to \__Container\AlicesCalendar\B= eep\Boop\Poink, but that isn't defined yet
> 3. The isolate= d autoloader stack (loaded from wp-plugins/AlicesCalendar/vendor/autoloa= d.php) is asked for the original name, \Beep\Boop\Poink
> 4= . It includes the file wp-plugins/AlicesCalendar/vendor/Beep/Boop/Poink.= php which contains the defintion of \Beep\Boop\Poink
> 5. T= he Container mechanism rewrites the class to \__Container\AlicesCalendar= \Beep\Boop\Poink and carries on
> When= code in wp-plugins/BobsDocs/vendor/Beep/Boop/Narf.php mentions \Beep\Bo= op\Poink, the same thing happens, but with a completely separate sandbox= : the rewritten class name is \__Container\BobsDocs\Beep\Boop\Poink, and= the autoloader was loaded from wp-plugins/BobsDocs/vendor/autoload.php<= /div>

In this thread I see a lot of talking about Com= poser and autoloaders. But in essence those are just tools we use to inc= lude files into our current PHP process, so for the sake of simplicity (= and compatibility), let's disregard all of that for a moment. Instead, p= lease bear with me while we do a little gedankenexperiment...
=
First, imagine one gigantic PHP file, `huge.php`, that co= ntains all the PHP code that is included from all the libraries you need= during a single PHP process lifecycle. That is in the crudest essence h= ow PHP's include system currently works: files get included, those files= declare symbols within the scope of the current process. If you were to= copy-paste all the code you need (disregarding the `declare()` statemen= ts) in one huge PHP file, you essentially get the same result.

So in our thought experiment we'll be doing just that. T= he only rule is that we copy all the code verbatim (again, disregarding = the `declare()` statements), because that's how PHP includes also work.<= /div>

```php
<?php

<= div>// ...

namespace Acme;
class Foo = {}

namespace Acme;
class Bar
{
    public readonly Foo $foo;
&nb= sp;   public function __construct()
  &nbs= p; {
        // For some re= ason, there is a string reference here. Don't ask.
  = ;      $fooClass =3D '\Acme\Foo';
&nb= sp;       $this->foo =3D new $fooClass(= );
    }
}

n= amespace Spam;
use Acme\Bar;
class Ham extends Bar {= }

namespace Spam;
use Acme\Bar;
=
class Bacon extends Bar {}

// ...
```

Now, the problem here is that if we copy-= paste two different versions of the same class with the same FQN into ou= r `huge.php` file they will try to declare the same symbols which will c= ause a conflict. Let's say our `Ham` depends on one version of `Acme\Bar= ` and our `Bacon` depends on another version of `Acme\Bar`:
```php
<?php

// ...

namespace Acme;
class Foo {}

namespace Acme;
class Bar {
 &n= bsp;  public readonly Foo $foo;
    public= function __construct()
    {
 &= nbsp;      // For some reason, there is a strin= g reference here. Don't ask.
     &nb= sp;  $fooClass =3D '\Acme\Foo';
    &= nbsp;   $this->foo =3D new $fooClass();
 &nb= sp;  }
}

namespace Spam;
use Acme\Bar;
class Ham extends Bar {}

=
namespace Acme;
class Foo {}  // Fatal error: Cannot= declare class Foo, because the name is already in use

namespace Acme;
class Bar {   // Fatal error= : Cannot declare class Bar, because the name is already in use
    public readonly Foo $foo;
  &nbs= p; public function __construct()
    {
        // For some reason, there i= s a string reference here. Don't ask.
    =     $fooClass =3D '\Acme\Foo';
  &nbs= p;     $this->foo =3D new $fooClass();
=     }
}

namespace Spam= ;
use Acme\Bar;
class Bacon extends Bar {}

// ...
```

So how do = we solve this in a way that we can copy-paste the code from both version= s of `Acme\Foo`, verbatim into `huge.php`?

Well= , one way is to break the single rule we have created: modify the code. = What if we just let the engine quietly rewrite the code? Well, then we q= uickly run into an issue. Any non-symbol references to classes are hard = to detect and rewrite, so this would break:

```= php
<?php

// ...

namespace Acme;
class Foo {}

= namespace Acme;
class Bar {
    publi= c readonly Foo $foo;
    public function __cons= truct()
    {
    = ;    // For some reason, there is a string reference here= . Don't ask.
        $fooCl= ass =3D '\Acme\Foo';
       = ; $this->foo =3D new $fooClass();
    }
}

namespace Spam;
use Acme\Bar;=
class Ham extends Bar {}

namespace S= pam\Bacon\Acme;        // Quietly rew= ritten
class Foo {}

namespace Spam\Ba= con\Acme;        // Quietly rewritten=
class Bar {
    public readonly Foo = $foo;
    public function __construct()
    {
      &n= bsp; // For some reason, there is a string reference here. Don't ask.
        $fooClass =3D '\Acme\= Foo';  // <=3D=3D Whoops, missed this one!!!
 &nb= sp;      $this->foo =3D new $fooClass();
    }
}

namespa= ce Spam;
use Spam\Bacon\Acme\Bar;     = ;     // Quietly rewritten
class Bacon ext= ends Bar {}

// ...
```

=
So let's just follow our rule for now. Now how do we include = Foo and Bar twice? Well, let's try Rowan's approach of "containerizing."= Let's take a very naive approach to what that syntax might look like. W= e simply copy-paste the code for our second version of `Acme\Bar` into t= he scope of a container. For the moment let's assume that the container = works like a "UnionFS" of sorts, where symbols declared inside the conta= iner override any symbols that may already exist outside the container:&= nbsp;

```php
<?php

<= /div>
// ...

namespace Acme;
clas= s Foo {}

namespace Acme;
class Bar {<= /div>
    public readonly Foo $foo;
 &= nbsp;  public function __construct()
    {=
        // For some reason= , there is a string reference here. Don't ask.
  &nb= sp;     $fooClass =3D '\Acme\Foo';
 &= nbsp;      $this->foo =3D new $fooClass();
    }
}

names= pace Spam;
use Acme\Bar;
class Ham extends Bar {}

container Bacon_Acme {
  &nbs= p; namespace Acme;
    class Foo {}
&= nbsp;   
    namespace Acme;
    class Bar {
    =     public readonly Foo $foo;
   = ;     public function __construct()
 =        {
    = ;        // For some reason, there is= a string reference here. Don't ask.
    &= nbsp;       $fooClass =3D '\Acme\Foo';
           = ; $this->foo =3D new $fooClass();
    &= nbsp;   }
    }
}

namespace Spam;
use Bacon_Acme\\Acme\Bar;
class Bacon extends Bar {}

// ...
```

That seems like it could work. As you ca= n see, I've decided to use double backspace (`\\`) to separate container= and namespace in this example. You may wonder how this would look in th= e real world, where not all code is copy-pasted into a single `huge.php`= . Of course, part of this depends on the autoloader implementation, but = let's start with a first step of abstraction by replacing the copy-paste= d code with includes:

```php

// ...

require_once '../vendor/acme/acme= /Foo.php';
require_once '../vendor/acme/acme/Bar.php';

namespace Spam;
use Acme\Bar;
clas= s Ham extends Bar {}

container Bacon_Acme {
    require_once '../lib/acme/acme/Foo.php';
=
    require_once '../lib/acme/acme/Bar.php';
}

namespace Spam;
use Bacon_Acme\\= Acme\Bar;
class Bacon extends Bar {}
```
<= br>
Now what if we want the autoloader to be able to resolve t= his? Well, once a class symbol is resolved with a container prefix, it w= ould have to also perform all its includes inside the scope of that cont= ainer. 


```php
fu= nction autoload($class_name) {
    // Do autolo= ading shizzle.
}

spl_autoload_registe= r();

namespace Spam;
use Bacon_Acme\\= Acme\Bar;
class Bacon extends Bar {}

=     // Meanwhile, behind the scenes, in the autoloader:&n= bsp;
    container Bacon_acme {
 = ;       autoload(Acme\Bar::class);
    }
```

Now this = mail is already quite long enough, so I'm gonna wrap it up here and do s= ome more brainstorming. But I hope I may have inspired some of you. All = in all, I think my approach might actually work, although I haven't even= considered what the implementation would even look like.

=
Again, the main point I want to make is to just disregard com= poser and the autoloader for now; those are just really fancy wrappers a= round import statements. Whatever solution we end up with would have to = work independently of Composer and/or the autoloader.

Alwin


I=E2=80= =99m starting to think that maybe modules might be a bad idea; or at lea= st, class/module visibility.

As an anecdote, I = was looking to extract a protobuf encoding library from a larger codebas= e and create a separate library for Larry=E2=80=99s Serde library. Durin= g the extraction I realized that many of the classes and functions I was= relying on actually used @internal classes/functions. If =E2=80=9Cmodul= e=E2=80=9D visibility were a thing=E2=80=A6 would my implementation have= been possible?

In other words, if visibility c= omes with modules; there really needs to be some kind of escape hatch. S= ome way to say, =E2=80=9CI know what I=E2=80=99m doing, so get out of my= way.=E2=80=9D

In Java, you can do this by crea= ting a namespaced class in the target namespace. I=E2=80=99m not sure ab= out other languages; but it=E2=80=99s something to think about. 

=E2=80=94 Rob
--7974be9a3f774dc39f6117cf1e6ddb10--