Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127433 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 AC72A1A00BC for ; Fri, 23 May 2025 01:28:10 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1747963562; bh=APPyEJ9rbtAvT2ujmoWr1QqpWJvW56pl8TIdO/aBGYU=; h=References:In-Reply-To:From:Date:Subject:To:From; b=TLBQq0hviUWHyfkaSUYXwTDlUZLj7VYi9vh9o16RLgMpldjskOyWqVn8K0edy1R7M 5JfsOrVhKKQVduTuEKyVyKhf9gxElRU5NDTa3JUKFp5XkUNaRiIglSVwVRftRMtOfi WmAG2/8feY5O7tMR78dkZqKju+EpPHzaDn7GqNTJHAdKeiNiiborTjkwI56TdhXtdM oyJdAlKQKPNqPoXgccTh9LniTD1rgnU6qfDz1IDilATnB0waJuplrGnbkOIqrO+2fx QgJRvwncuNBhcEIy32o6b3AIn17LVfxag+wgBN9naYLbuH9q+67qitlaB+9RgWD85O rq54YAIFaBdoQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 5B8DE1801E6 for ; Fri, 23 May 2025 01:25:58 +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=-1.2 required=5.0 tests=BAYES_40,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, 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 mail-qv1-f51.google.com (mail-qv1-f51.google.com [209.85.219.51]) (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, 23 May 2025 01:25:46 +0000 (UTC) Received: by mail-qv1-f51.google.com with SMTP id 6a1803df08f44-6f8c3db8709so5161506d6.0 for ; Thu, 22 May 2025 18:27:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1747963674; x=1748568474; darn=lists.php.net; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=pIbc9RbN7ksqezAHtlfLFWNNWb2p8c3QFUgRATCdzMA=; b=kqL0rNg0PmiTLPEdBd3/0OUTjm6utbaboIRMWrxJ+vEXrfk5lv37/z/IcKW6je2Tvd BpWJow7FadnqScMs8UlkPx0E08uu8LiXQzMOJd/VE7Edn9TiSDPBQ+2WbWZYl4h2lund pxdPHP1G2Z+R9p8DmbEv7QL0kZVPwfPkZUWt2olVuyKzvS/qJg9DHcXaiYtG5Mw3RwlE Y1OocLZjz47aH7qG0EoCaV1tYEpo/fWyx/A7OucPbdZVo7iSN7W209m5mdbNDua+niEY kyi9ZLyLId1ERJej7iAIKHVCDppE/Z7jY6ky11NOa6qHsI84qFcd5LTHFbCSp2y2oy9j 1OVA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747963674; x=1748568474; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=pIbc9RbN7ksqezAHtlfLFWNNWb2p8c3QFUgRATCdzMA=; b=oTo+GdIpMgGexZk1d1Hpgo2sUC8G44k2k9BE/nPXkUNZkG5R5LvYQDcfS3Q/VT4GMu LF2fp7fx349ZX9OKjHJCmMHkJO2lR3S+S9Z0jEc7D9uFekilbNiZ+K+OwtFwvaKJf32E GPJdmvqI1PGRi4O4dD08Q6/6GsYuDc7gfJB3CRtPpGlngvi0Rmg1lci0ApRwd9jnxMHe Le4LeBJfkdkoCCR9gRqEF54ntvPrgpME9cVVnbX38jmtqjConikn43KxyPDg0En9x1oR nx8GU9CdHUnHveoksVUyRK2GzdIEU/VlmmVGi9HpTlmeqc1J5bV46Z9LeSYV6QzngNJr c1WQ== X-Gm-Message-State: AOJu0YxDWK0NWtm7AV4yJ3Tk2G/mttWZiRCwe4m7tocfmpnxBUWF+4gi KmqsvMpGgDR3zc/ZDNttiKc75j2pBo9CTHGpAzhP0IgGYzDzMbuW2BFK2jKzmnj7ni3w3s4Nqv+ Ubagh3bGhwkCq4Et6DdLMxG11tSES9vtqsgj/ X-Gm-Gg: ASbGncsfIBDMOPTHEtUD1fzNOmw+m9j45e4V7zmn4StRQ7oGrL8MyoElx58P0zt8aWE UvDTa1BIMoszlx2yoSLe8HHAdvX5cZueG/ZD99uS8iRrxg+JUjCJ0lPXafqS1GDW3xv+TDAYuhP jEYHEt8WSCGUUxmqXDK8ds+qJDbfKs0PUIzCUarD4UqICK65u+U+tWn/+j2ifOxzg2bJ78CspU X-Google-Smtp-Source: AGHT+IGxDtmGW5AcYpC2HWgjE6+G+8YmMHC1HoQBcplmGiKmzPLQtGRAFiL5y5UCAEtLvoO5CCuvUQ3skW9r4IalgRI= X-Received: by 2002:a05:6214:21c2:b0:6d9:ac3:e730 with SMTP id 6a1803df08f44-6fa9359ba0amr24061066d6.5.1747963673667; Thu, 22 May 2025 18:27:53 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: <3ae9a6ea-f135-472b-b2bf-e6cd6ebad299@app.fastmail.com> <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> In-Reply-To: <354cb888-97c4-4f8c-a0da-359d1e63c0f9@rwec.co.uk> Date: Thu, 22 May 2025 21:27:41 -0400 X-Gm-Features: AX0GCFv9J3Zqt6PKC6bdSU_DigWIU_-RNZfNbvX5NqIPjggMHFA77741eAE8m0I Message-ID: Subject: Re: [PHP-DEV] Module or Class Visibility, Season 2 To: internals@lists.php.net Content-Type: multipart/alternative; boundary="000000000000d96ef80635c38145" From: tendoaki@gmail.com (Michael Morris) --000000000000d96ef80635c38145 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Thu, May 22, 2025 at 4:29=E2=80=AFPM Rowan Tommins [IMSoP] wrote: > On 22/05/2025 12:09, Michael Morris wrote: > > >> I've tried several times to explain why I think Linux containers are a >> good analogy; I'm not sure if you didn't understand, or just didn't agre= e, >> so I don't know what else I can say. >> > > I have no disagreement with that, but it's an implementation detail. I'm > not there yet - I'm just trying to describe what I think is needed from > outside the engine. > > > I think this is where we're not seeing eye to eye, and why we're getting > frustrated with each other, because I see it as far more fundamental than > details you have already gone into, like how autoloading will work. > > Perhaps a more realistic example will help, and also avoid the confusion > over "A, B, and D" from earler. > > Imagine a WordPress plugin, AlicesCalendar, which uses the Composer > packages monolog/monolog and google/apiclient. The google/apiclient packa= ge > also requires monolog/monolog. > > Another WordPress plugin, BobsDocs, also uses both monolog/monolog and > google/apiclient, but using different versions. > > > Inside those different places, there are lines of code like this: > > $logger =3D new \Monolog\Logger('alices-calendar'); // in AlicesCalendar > $logger =3D new \Monolog\Logger('bobs-docs'); // in BobsDocs > $logger =3D new \Monolog\Logger('google-api-php-client'); // in > google/apiclient > > We need to rewrite those lines so that they all refer to the correct > version of Monolog\Logger. > > > If every package/module/whatever rewrites the classes inside every other > package/module/whatever, we might start with this: > > $logger =3D new \AlicesCalendar\Monolog\Logger('alices-calendar'); // in > AlicesCalendar > $logger =3D new \BobsDocs\Monolog\Logger('bobs-docs'); // in BobsDocs > $logger =3D new \GoogleApiClient\Monolog\Logger('google-api-php-client');= // > in google/apiclient > > > That only works if we somehow know that AlicesCalendar and BobsDocs use > the same google/apiclient; if not, we need four copies: > > $logger =3D new \AlicesCalendar\Monolog\Logger('alices-calendar'); // in > AlicesCalendar > $logger =3D new > \AlicesCalendar\GoogleApiClient\Monolog\Logger('google-api-php-client'); = // > in google/apiclient when called from AlicesCalendar > > $logger =3D new \BobsDocs\Monolog\Logger('bobs-docs'); // in BobsDocs > $logger =3D new > \BobsDocs\GoogleApiClient\Monolog\Logger('google-api-php-client'); // in > google/apiclient when called from BobsDocs > > All of these are separate classes, which can't be used interchangeably, > and the names get longer and longer to isolate dependencies inside > dependencies. > > > But we don't actually need the Monolog\Logger used by AlicesCalendar to b= e > a different version from the one used by google/api-client. In fact, it > would be useful if they were the same, so we could pass around the object= s > interchangeably *inside* the plugin code. > > So what we want is some way of saying that AlicesCalendar and BobsDocs ar= e > special; they want to isolate code in a way that normal > modules/packages/whatever don't. Then we can have 2 copies of > Monolog\Logger, not 3 or 4: > > $logger =3D new \AlicesCalendar\Monolog\Logger('alices-calendar'); // in > AlicesCalendar > $logger =3D new \AlicesCalendar\Monolog\Logger('google-api-php-client'); = // > in google/apiclient when called from AlicesCalendar > > $logger =3D new \BobsDocs\Monolog\Logger('bobs-docs'); // in BobsDocs > $logger =3D new \BobsDocs\Monolog\Logger('google-api-php-client'); // in > google/apiclient when called from BobsDocs > > In this case, PHP doesn't need to know monolog/monolog even exists. It > just puts either "AlicesCalendar" or "BobsDocs" on any class name it sees= . > > > Before we can even think about *how* we'd implement the rewriting (or > shadowing, or whatever) we need some requirements of *what* we want to > rewrite. By suggesting an image of "containers" or "sandboxes" rather tha= n > "packages" or "modules", I was trying to define the requirement that > "AlicesCalendar and BobsDocs are special, in a way that monolog/monolog a= nd > google/apiclient are not". > This is worlds better, and I think I can work with this. First, let's revisit how autoloading works, if for no other reason than to test if *I* understand what's going on correctly. When PHP encounters a symbol it doesn't recognize, it triggers the autoload process. Autoloaders are closures registered with the engine using spl_autoload_register, and PHP queries them one at a time (I don't remember the order offhand). The autoloader function runs and PHP retests to see if it can resolve the symbol. If it can, code execution continues. If it can't the next autoloader is ran and if none are left a Fatal Error is thrown. Autoload closures get 1 argument - the fully qualified class name. They are expected to return void. I believe it would be best to leave the wild and wooly world of package management alone and just give the engine the ability to allow code in one area to use a different code even though it has the same label, at least on the surface. I think this is possible if the engine handles the symbol assignment in a different way from the existing include statements. The cleanest way to do that would be to have the autoloader return the file path to require and, optionally, what namespace to prefix onto all namespaces in the file. In summary, let the package manager resolve packages and give it better tools towards that end. Returning to your example and closing question, how do we know that AlicesCalendar and BobsDocs are special? Let the package manager tell us with this hook: spl_package_register( array[string] $packages):void To use composer the user has to run `require "/vendor/composer/autoload.php";` near the beginning of their application. So inside that file a package aware version of composer can call this to tell the engine what the package namespaces are - in your example ['AlicesCalendar', 'BobsDocs']. (Aside, if spl_package_register is called multiple times the arrays are merged). Now, PHP executes the application and enters the code of AlicesCalendar, it will be largely unchanged: ```php namepace AlicesCalendar; $logger =3D new Monolog\Logger('alices-calendar'); $api =3D new Google\ApiClient(); ``` But thanks to the spl_package_register hook the engine knows that when it sees a namespace that starts with or matches any string in the packages array that the code is part of a package. This will cause it to sent the autoload closure a second argument with that package namespace so that it can determine what to send back. So next it sees the Monolog\Logger symbol. Does AlicesCalendar\Monolog\Logger exists? No, so we invoke the autoloader callback with arguments ('AlicesCalendar\Monolog\Logger', 'AlicesCalendar'). The autologger checks its rules (way, way out of scope here) and determines that AlicesCalendar is using the latest Monolog\Logger. So it responds with ['file/path/to/latest/Monolog/Logger.php', ''], telling the engine what code to require and that there is no prefix for the namespaces appearing in that file ( "\" should also work). The engine aliases AlicesCalendar\Monolog\Logger to \Monolog\Logger so it doesn't have to pester the autoloader again for this symbol. The Google\ApiClient goes through the same process. As a result: ```php namepace AlicesCalendar; $logger =3D new Monolog\Logger('alices-calendar'); $api =3D new Google\ApiClient(); echo $logger::class // \Monolog\Logger echo $api::class // \Google\ApiClient ``` Now for the complexity - we reach BobsDocs ```php namespace BobsDocs; $logger =3D new Monolog\Logger('bobs-docs') $api =3D new Google\ApiClient(); ``` Bobs docs needs an older version of Monolog and is configured appropriately in its composer.json file, so when the engine calls the autoloader with ('BobsDocs\Monolog\Logger', 'BobsDocs') the autoloader returns ['file/path/to/older/Monolog/Logger.php', 'v1']. v1 is prefixed to the namespace declarations in Monolog\Logger and the file is included. The engine aliases BobsDocs\Monolog\Logger to \v1\Monolog\Logger. Keep in mind - namespace prefix is a decision left to the package manager. I'm sure a PSR will be made to establish best practice, but that's out of scope here. The Googl\ApiClient of BobDocs is again, up to the autoloader. Assuming it too is different (since it's using an older Monolog) we'd get something like this. ```php namespace BobsDocs; $logger =3D new Monolog\Logger('bobs-docs') $api =3D new Google\ApiClient(); echo $logger::class // \v1\Monolog\Logger echo $api::class // \v1\Google\ApiClient ``` Now later in the code if we make a new \Monolog\Logger the autoloader won't be invoked - the symbol was written when AlicesCalendar caused it to be created indirectly. This approach keeps package resolution out of the engine entirely, which I think is consistent with PHP's setup. We'd just be improving the tools the package manager / autoloader can leverage. Older code would still work since the new autoloader behavior is opt in. --000000000000d96ef80635c38145 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


On Thu, May 22,= 2025 at 4:29=E2=80=AFPM Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote:
=20 =20 =20
On 22/05/2025 12:09, Michael Morris wrote:
=20

I've tried several times to explain why I think Linux containers are a good analogy; I'm not sure if you didn'= ;t understand, or just didn't agree, so I don't know what = else I can say.

I have no disagreement with that, but it's an implementation detail. I'm not there yet - I'm just try= ing to describe what I think is needed from outside the engine.


I think this is where we're not seeing eye to eye, and why we= 9;re getting frustrated with each other, because I see it as far more fundamental than details you have already gone into, like how autoloading will work.

Perhaps a more realistic example will help, and also avoid the confusion over "A, B, and D" from earler.

Imagine a WordPress plugin, AlicesCalendar, which uses the Composer packages monolog/monolog and google/apiclient. The google/apiclient package also requires monolog/monolog.

Another WordPress plugin, BobsDocs, also uses both monolog/monolog and google/apiclient, but using different versions.


Inside those different places, there are lines of code like this:

$logger =3D new \Monolog\Logger('alices-calendar'); // in AlicesCalendar
$logger =3D new \Monolog\Logger('bobs-docs'); // in BobsDocs<= br> $logger =3D new \Monolog\Logger('google-api-php-client'); // = in google/apiclient

We need to rewrite those lines so that they all refer to the correct version of Monolog\Logger.


If every package/module/whatever rewrites the classes inside every other package/module/whatever, we might start with this:

$logger =3D new \AlicesCalendar\Monolog\Logger('alices-calendar&= #39;); // in AlicesCalendar
$logger =3D new \BobsDocs\Monolog\Logger('bobs-docs'); // in BobsDocs
$logger =3D new \GoogleApiClient\Monolog\Logger('google-api-php-client'); // = in google/apiclient


That only works if we somehow know that AlicesCalendar and BobsDocs use the same google/apiclient; if not, we need four copies:

$logger =3D new \AlicesCalendar\Monolog\Logger('alices-calendar&= #39;); // in AlicesCalendar
$logger =3D new \AlicesCalendar\GoogleApiClient\Monolog\Logger('google-api-php-cl= ient'); // in google/apiclient when called from AlicesCalendar

$logger =3D new \BobsDocs\Monolog\Logger('bobs-docs'); // in BobsDocs
$logger =3D new \BobsDocs\GoogleApiClient\Monolog\Logger('google-api-php-client&#= 39;); // in google/apiclient when called from BobsDocs

All of these are separate classes, which can't be used interchangeably, and the names get longer and longer to isolate dependencies inside dependencies.


But we don't actually need the Monolog\Logger used by AlicesCalendar to be a different version from the one used by google/api-client. In fact, it would be useful if they were the same, so we could pass around the objects interchangeably *inside* the plugin code.

So what we want is some way of saying that AlicesCalendar and BobsDocs are special; they want to isolate code in a way that normal modules/packages/whatever don't. Then we can have 2 copies of Monolog\Logger, not 3 or 4:

$logger =3D new \AlicesCalendar\Monolog\Logger('alices-calendar&= #39;); // in AlicesCalendar
$logger =3D new=C2=A0\AlicesCalendar\Monolog\Logger('google-api-php-client= 9;); // in google/apiclient when called from AlicesCalendar

$logger =3D new \BobsDocs\Monolog\Logger('bobs-docs'); // in BobsDocs
$logger =3D new \BobsDocs\Monolog\Logger('google-api-php-client&#= 39;); // in google/apiclient when called from BobsDocs

In this case, PHP doesn't need to know monolog/monolog even exists. It just puts either "AlicesCalendar" or "BobsD= ocs" on any class name it sees.


Before we can even think about *how* we'd implement the rewritin= g (or shadowing, or whatever) we need some requirements of *what* we want to rewrite. By suggesting an image of "containers" or "sandboxes" rather than "packages" or "modul= es", I was trying to define the requirement that "AlicesCalendar and BobsDocs are special, in a way that monolog/monolog and google/apiclient are not".

This is worlds better, and I th= ink I can work with this.=C2=A0=C2=A0

First, let&#= 39;s revisit how autoloading works, if for no other reason than to test if = *I* understand what's going on correctly.=C2=A0 When PHP encounters a s= ymbol it doesn't recognize, it triggers the autoload process.=C2=A0 Aut= oloaders are closures registered with the engine using spl_autoload_registe= r, and PHP queries them one at a time (I don't remember the order offha= nd). The autoloader function runs and PHP retests to see if it can resolve = the symbol. If it can, code execution continues. If it can't the next a= utoloader is ran and if none are left a Fatal Error is thrown. Autoload clo= sures get 1 argument - the fully qualified class name. They are expected to= return void.

I believe it would be best to leave = the wild and wooly world of package management alone and just give the engi= ne the ability to allow code in one area to use a different code even thoug= h it has the same label, at least on the surface.=C2=A0 I think this is pos= sible if the engine handles the symbol assignment in a different=C2=A0way f= rom the existing include statements.=C2=A0 The cleanest way to do that woul= d be to have the autoloader return the file path to require and, optionally= , what namespace to prefix onto all namespaces in the file.

<= /div>
In summary, let the package manager resolve packages and give it = better tools towards that end.

Returning to your e= xample and closing question, how do we know that AlicesCalendar and BobsDoc= s are special? Let the package manager tell us with this hook:
spl_package_register( array[string] $packages):void
=
To use composer the user has to run `require "/vendor/c= omposer/autoload.php";` near the beginning of their application. So in= side that file a package aware version of composer can call this to tell th= e engine what the package namespaces are - in your example ['AlicesCale= ndar', 'BobsDocs'].=C2=A0 (Aside, if spl_package_register is ca= lled multiple times the arrays are merged).

Now, P= HP executes the application and enters the code of AlicesCalendar, it will = be largely unchanged:

```php
namepace Al= icesCalendar;

$logger =3D new Monolog\Logger('= alices-calendar');
$api =3D new Google\ApiClient();
```

But thanks to the spl_package_register hook t= he engine knows=C2=A0that when it sees a namespace that starts with or matc= hes any string in the packages array that the code is part of a package. Th= is will cause it to sent the autoload closure a second argument with that p= ackage namespace so that it can determine what to send back.

=
So next it sees the Monolog\Logger symbol. Does=C2=A0AlicesCalen= dar\Monolog\Logger=C2=A0exists? No, so we invoke the autoloader callback wi= th arguments ('AlicesCalendar\Monolog\Logger', 'AlicesCalendar&= #39;).=C2=A0 The autologger checks its rules (way, way out of scope here) a= nd determines that AlicesCalendar=C2=A0is using the latest Monolog\Logger.= =C2=A0 So it responds with ['file/path/to/latest/Monolog/Logger.php'= ;, ''], telling the engine what code to require and that there is n= o prefix for the namespaces appearing in that file ( "\" should a= lso work). The engine aliases AlicesCalendar\Monolog\Logger=C2=A0to \Monolo= g\Logger so it doesn't have to pester the autoloader again for this sym= bol.
The Google\ApiClient goes through the same process. As = a result:

```php
namepace Ali= cesCalendar;

$logger =3D new Monolog\Logger('a= lices-calendar');
$api =3D new Google\ApiClient();
=
echo $logger::class // \Monolog\Logger
echo = $api::class // \Google\ApiClient
```

Now= for the complexity - we reach BobsDocs

```php
namespace BobsDocs;

$logger =3D new Monolog= \Logger('bobs-docs')
$api =3D new Google\ApiClient();
```

Bobs docs needs an older version of Mon= olog and is configured appropriately in its composer.json file, so when the= engine calls the autoloader with ('BobsDocs\Monolog\Logger', '= BobsDocs') the autoloader returns ['file/path/to/older/Monolog/Logg= er.php', 'v1']. v1 is prefixed to the namespace declarations in= Monolog\Logger and the file is included. The engine aliases BobsDocs\Monol= og\Logger to \v1\Monolog\Logger.

Keep in mind - na= mespace prefix is a=C2=A0decision left to the package manager. I'm sure= a PSR will be made to establish best practice, but that's out of scope= here.

The Googl\ApiClient of BobDocs is again, up= to the autoloader. Assuming it too is different (since it's using an o= lder Monolog) we'd get something like this.

```php
namespace BobsDocs;

$logger = =3D new Monolog\Logger('bobs-docs')
$api =3D new Google\A= piClient();

echo $logger::class // \v1\Monolog\Log= ger
echo $api::class //=C2=A0 \v1\Google\ApiClient
```<= /div>

Now later in the code if we make a new \Mono= log\Logger the autoloader won't be invoked - the symbol was written whe= n AlicesCalendar=C2=A0caused it to be created indirectly.

This approach keeps package resolution out of the engine entirely, = which I think is consistent with PHP's setup.=C2=A0 We'd just be im= proving the tools the package manager / autoloader can leverage. Older code= would still work since the new autoloader behavior is opt in.
<= /div> --000000000000d96ef80635c38145--