Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127287 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 06EB11A00BC for ; Sun, 4 May 2025 22:41:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1746398330; bh=gOByrivOXOIx3WsNltjkFBW+V8V4qhKBlBlAEJbfuKw=; h=References:In-Reply-To:From:Date:Subject:To:From; b=cJvjRGwJ4iQsLkP2q1/LHh52EqV4Gpidk2IyznBvfsENd55wxW+eXld+6JMw+NNt7 /DiqrKkh0SuAjnkaMa8FtTY+VR1FvwSOaapgvWn2n0t/S9kk972t7NKjvr6n8rptVs 6cWhCvZVZ11O/ldnuAivIYcN48ggiHYySDeRk8h75BxXGMxVKQJLQYKeZNZjZRMjIC ebN8VoKHCOb2oaiY7hqJZPHrx4E1hPM2apbVK4u6Kyp3G+wQpS4w7yETRpCA9Lmomn KvE+xcUrpbbPQzncLresaTDjsOua0tbDPc9GYCocTlTFtYvL+R1rwf8MGRMv7RndUI zcF1Y7Bb6JwMw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id E6D9218004F for ; Sun, 4 May 2025 22:38:47 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_50,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.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from mail-qk1-f172.google.com (mail-qk1-f172.google.com [209.85.222.172]) (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 ; Sun, 4 May 2025 22:38:47 +0000 (UTC) Received: by mail-qk1-f172.google.com with SMTP id af79cd13be357-7c7913bab2cso412331985a.0 for ; Sun, 04 May 2025 15:41:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1746398461; x=1747003261; 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=qyHVO85gGX8N1bR0LcSokpjqcDa7T/4pi0qTnsqiEtI=; b=gsML1RSNS7rso6/92N+u5z/yZUd2MS5/j1gDiK0X7aj2Wh+DP/wNJVbApbxtM48E+l qSn8G5T0gnW4EATUv5tdMh45iQ7Nr/zMy91V9THHKj/XGOKKAxj0TL4LQo3QO6nLE77D FOkaVehvF3U19XKxx6FkZjDYCSIwX6uULWR7/YapWL1H496H6VQRpDkN8bx7Ftl7B619 HuC7+3gvP4NQm6lhmJWFSiFsbzwo7cnYbt85do+ChjZDc24RIej1p5wgvIE/rZQhge1u IDVpybkTGSRb5kc/b9cSWwW0BhdKe0/lAQaELiZKVc8rNPg+Afu+LgovSRK5ZyRz2XNf 6xHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1746398461; x=1747003261; 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=qyHVO85gGX8N1bR0LcSokpjqcDa7T/4pi0qTnsqiEtI=; b=l7MK6TAnH0wWLzNEG3QSN8Sq0lzF4eRPorNIdr4YUMff9RGQut3PLrkDA3+mOebG4Y Ia4JqoPK99Vjc6IdhWy13/GnGgG6OUxkwhIVuA7ois0udXAU/dSoYmPTXLzDic5Dxh9O k9RRWJd1SSU7VolP2amWn+olJlZrofWraX79wkkt34N3aJtdiU+WFZ5hzCNu072ZGL4j Bzd42WZP+tRv3Tkg14RVyORkdzZ73I0fzCKxJHXvPJ7OW9RpIfBboiBujCUp47h5QbP6 gReLlzFNYysIchAWfZvTGZkkp3Yu4Ugn7SYsXgTLKGs4rd1lzYEVaQ0IG27NZ+X6G5fX o8ng== X-Gm-Message-State: AOJu0Ywkbvwt3nJMjUwgqFuljjf/u9IinVcgf/Hxo3oX9sG/E5f6Ub/7 AJCzdiiNSkPGunsxaTP4UFf/FIZway4nnmKHMkpgEa0BnvR4DU5zpKw1nPxnc3wTx13CvKGzbDR DvC72s4N/nylHNxRjKI8cxQgC/hWCYR8C X-Gm-Gg: ASbGncs17qt32i2Jye3qqBQU+nMARd6+hsNwR1A7m3I+ITOzLtt0b/TSNUIgIopBetO OxncNh6PflXK/R2vQz69DJhJpCS0ToxnABmSihLeJyJYoYpFq07dJBUaShyhINWGSjSpVTexpJL s4Z6Tj31NX6wH/SFfm/zNuDj7fvuUrv2Ib7on1xoJaFDwvMwJdpxcz X-Google-Smtp-Source: AGHT+IFyhvH7tgP6TizGcN3QF5hzAhVPsrD2jfbedaYIrAypRqH63ZDDAMj9f2p1m2c66s2/cPxAw/sia6R6Uz+XDNg= X-Received: by 2002:a05:620a:404d:b0:7c5:54d7:d744 with SMTP id af79cd13be357-7cae3a9cf07mr594064485a.23.1746398461383; Sun, 04 May 2025 15:41:01 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: <9cbbe803-5fd1-4011-b42e-033d4ede3fe9@app.fastmail.com> <15a33d20-47c1-443e-aafd-f39bd8f367c1@app.fastmail.com> In-Reply-To: <15a33d20-47c1-443e-aafd-f39bd8f367c1@app.fastmail.com> Date: Sun, 4 May 2025 18:40:50 -0400 X-Gm-Features: ATxdqUHI8lZdc0YhOE3nVc0ObonmZYH47LfG36lDV_xffJtabts_0nWO_vFB-ug Message-ID: Subject: Re: [PHP-DEV] Modules, again. To: PHP internals Content-Type: multipart/alternative; boundary="000000000000ed58bc06345713ca" From: tendoaki@gmail.com (Michael Morris) --000000000000ed58bc06345713ca Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Sun, May 4, 2025 at 5:38=E2=80=AFPM Larry Garfield wrote: > > > PHP Code > ----------------------------------------------------------------------- > > > > namespace MyModule; > > > > yield function sum(a, b) { return a + b; } > > > > > -------------------------------------------------------------------------= ------- > > Every module author changes their code. (Which is reasonable.) > Actually no. PHP Modules are only needed if the package author wants the functionality modules provide - specifically having an independent symbol table and autoload queue. Existing packages don't have to change in any way. If a module includes one their symbols will be written into that module's symbol table. If two modules include the same package they each write it onto their own respective symbol tables. > > > PHP Code > ----------------------------------------------------------------------- > > > > use require 'mymodule.php'; > > > > echo MyModule::sum(3, 4); > > > -------------------------------------------------------------------------= ------- > > Every caller changes their code, to use `use require` and to use the > pseudo-class syntax to call the function. > Again, if they want module functionality. No one *has* to do this - the new syntax was carefully chosen to let the existing syntax work as it always has. > > > PHP Code > ----------------------------------------------------------------------- > > > > use sum require 'mymodule.php'; > > > > echo sum(3, 4); // 7 > > > -------------------------------------------------------------------------= ------- > > Removes the pseudo-class syntax, but still has the "use sum require..." > change for the consumer. > > As described, the implication is that the millions of existing > > use Some\Class\Here; > > lines in code in the wild will... not work if the Some\Class package > becomes a module? This is left up to the autoloader. When you call that use statement from the main thread the autoloader receives args ("Some\Class\Here", false). When you call that use statement from a module the autoloader receives arguments ("Some\Class\Here", "RequestingModule"). If the autoloader isn't rewritten it will ignore the 2nd argument and serve out the same code for both module and main thread. HOWEVER, modules can define their own autoloaders and those will be executed independent of the autoloader on the main thread. Furthermore, the autoloader on the main thread will not be used unless the module opts into using it. > I think that's what's implied, but it's not clear. If that triggers > autoloading, how does the autoloader know how to find it? It needs to kn= ow > the module name, which as described is the file name of the module, not t= he > class. By convention the filename might be the module name, but that a convention of package management, autoloading, some future PSR-X that is far, far outside of scope here. The module's name is its namespace! For a module a namespace isn't just a run time string replacement, it has a very real effect. The namespace will be the 2nd argument of every autoload call made from the module or any file required/included in its scope. Existing packages don't need to be aware this is happening - but the module can have a custom autoloader that always loads version 2 of a popular package even if composer on the main thread is set to include version 3. > > Since the module name is the file name, that means if a module has 50 > internal classes, it must be in the same file. Hence, giant files. > I hope you can see why this isn't true now. The module entry file can simply yield out its public assets like so. namespace Vendor\Package; require 'autoloader.php'; yield { A, B, C } The above assuming that the autoloader knows where \Vendor\Package\A, \Vendor\Package\B and \Vendor\Package\C are located at. If an autoloader isn't used they'll have to be explicitly required before being yielded out. > > If those reads of your post are inaccurate, then please show with example= s > how they are inaccurate, because that is how I understood your proposal > text at face value. > > Ok, let's go over them in turn >> 1. Every module author to change their coding structure. No. Modules aren't replacing the current mechanism - just augmenting it when needed. The new syntax is only necessary when an independent symbol table is desired. Further, there's nothing stopping a package from having a standard require entry file that doesn't set up the symbol table and related protections, and one that does. >> 2. Every consumer of a package to change their coding structure That depends on the package maintainer. They can certainly rewrite the package such that consuming code must use module syntax to access it, but this isn't required of them. And as mentioned above they can get clever and provide multiple entry points to their code if they desire just as JavaScript package devs have been doing to make sure their packages work whether the consumer uses ESM import or CommonJS import. >> 3. Devs to abandon "it just works" autoloading and explicitly import packages. I don't know where you got that from - maybe the old thread from 9 months ago? The proposal I made last night not only takes autoloading into account but also discusses how autoload functions will receive a second argument moving forward that lets them make decisions about what code to supply to modules based on whatever logic they have which is well beyond the scope of this proposal. I will admit that while use doesn't really do anything and is handled at compile time, use - require evals at run time. The specified symbols come out of the module. The module can be located with an autoloader if desired, but it's not the same mechanism because module creation involves the creation of a new symbol table that will be used by the module file and EVERY file it requires and every file THEY require. This is why the autoloader is to return a string when handling module requests for classes because PHP should have the responsibility of attaching the code to the correct symbol table. The autoloader is told what module is about to receive the code. If the autoloader requires it's going to affect the symbol table it was declared in, which is ok if the module's own autoloader is handling the request - but if the global autoloader on the main thread does a require it's going to affect the symbol table of the main thread. >> 4. Abandoning 16 years of PSR-0/4 file convention in favor of "module = =3D file", which will almost certainly result in multi-thousand-line files even for clean, well-factored code. Again, I don't see this, at all. And I'm a little insulted you'd think that I'm dumb enough to propose something that outrageous. Modules simply have their own symbol table scope. They can import any package as they exist today without any modification into that scope. They can therefore pull a different version of the same package, which is desirable if they have a compatibility issue with the latest version of the package. Those autoloader are important, but out of scope. Another way to look at this is in JavaScript the ESM module convention was introduced without any package management ability and worked with raw absolute file paths. It would be a decade before import-maps were introduced and true integration with npm started and it's still not finished. I'm starting from the same place, use (some symbols) require and if PHP can't find the path it queries the autoloader and gives it a chance to return a path for PHP to use here. All existing code stays as is, and can even evolve as is when they don't need to worry with version mismatches. This syntax only really becomes useful when you want to "black box" the package from the rest of the application and allow the module to do what it needs without fear of affecting the rest of the application. Over time it might eclipse the old approach, but --000000000000ed58bc06345713ca Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


On Sun, May 4, = 2025 at 5:38=E2=80=AFPM Larry Garfield <larry@garfieldtech.com> wrote:

> PHP Code -------------------------------------------------------------= ----------
>
> namespace MyModule;
>
> yield function sum(a, b) { return a + b; }
>
> ----------------------------------------------------------------------= ----------

Every module author changes their code.=C2=A0 (Which is reasonable.)

Actually no. PHP Modules are only needed if t= he package author wants the functionality modules provide - specifically ha= ving an independent symbol table and autoload queue. Existing packages=C2= =A0don't have to change in any way.=C2=A0 If a module includes one thei= r symbols will be written into that module's symbol table. If two modul= es include the same package they each write it onto their own respective sy= mbol tables.
=C2=A0

> PHP Code -------------------------------------------------------------= ----------
>
> use require 'mymodule.php';
>
> echo MyModule::sum(3, 4);
> ----------------------------------------------------------------------= ----------

Every caller changes their code, to use `use require` and to use the pseudo= -class syntax to call the function.

Aga= in, if they want module functionality. No one *has* to do this - the new sy= ntax was carefully chosen to let the existing syntax work as it always has.=
=C2=A0

> PHP Code -------------------------------------------------------------= ----------
>
> use sum require 'mymodule.php';
>
> echo sum(3, 4); // 7
> ----------------------------------------------------------------------= ----------

Removes the pseudo-class syntax, but still has the "use sum require...= " change for the consumer.

As described, the implication is that the millions of existing

use Some\Class\Here;

lines in code in the wild will... not work if the Some\Class package become= s a module?=C2=A0

This is left up to the a= utoloader.=C2=A0 When you call that use statement from the main thread the = autoloader receives args ("Some\Class\Here", false). When you cal= l that use statement from a module the autoloader receives arguments ("= ;Some\Class\Here", "RequestingModule"). If the autoloader is= n't rewritten it will ignore the 2nd argument and serve out the same co= de for both module and main thread.

HOWEVER, modul= es can define their own autoloaders and those will be executed independent = of the autoloader on the main thread. Furthermore, the autoloader on the ma= in thread will not be used unless the module opts into using it.
= =C2=A0
I think that&= #39;s what's implied, but it's not clear.=C2=A0 If that triggers au= toloading, how does the autoloader know how to find it?=C2=A0 It needs to k= now the module name, which as described is the file name of the module, not= the class.=C2=A0

By convention the filenam= e might be the module name, but that a convention of package management, au= toloading, some future PSR-X that is far, far outside of scope here. The mo= dule's name is its namespace!=C2=A0 For a module a namespace isn't = just a run time string replacement, it has a very real effect. The namespac= e will be the 2nd argument of every autoload call made from the module or a= ny file required/included in its scope.=C2=A0 Existing packages don't n= eed to be aware this is happening - but the module can have a custom autolo= ader that always loads version 2 of a popular package even if composer on t= he main thread is set to include version 3.=C2=A0=C2=A0



Since the module name is the file name, that means if a module has 50 inter= nal classes, it must be in the same file.=C2=A0 Hence, giant files.

I hope you can see why this isn't true now= . The module entry file can simply yield out its public assets like so.

namespace Vendor\Package;
require 'auto= loader.php';
yield { A, B, C }

The a= bove assuming that the autoloader knows where \Vendor\Package\A, \Vendor\Pa= ckage\B and \Vendor\Package\C are located at.=C2=A0 If an autoloader isn= 9;t used they'll have to be explicitly required before being yielded ou= t.
=C2=A0

If those reads of your post are inaccurate, then please show with examples = how they are inaccurate, because that is how I understood your proposal tex= t at face value.


Ok, let's go o= ver them in turn

>> 1. Every module author t= o change their coding structure.

No. Modules aren&= #39;t replacing the current mechanism - just augmenting it when needed. The= new syntax is only necessary when an independent symbol table is desired.= =C2=A0 Further, there's nothing stopping a package from having a standa= rd require entry file that doesn't set up the symbol table and related = protections, and one that does.=C2=A0=C2=A0

>> 2. Every= consumer of a package to change their coding structure

That depends on the package maintainer.=C2=A0 They can certainly rewr= ite the package such that consuming code must use module syntax to access i= t, but this isn't required of them. And as mentioned above they can get= clever and provide multiple entry points to their code if they desire just= as JavaScript package devs have been doing to make sure their packages wor= k whether the consumer uses ESM import or CommonJS import.

&g= t;> 3. Devs to abandon "it just works" autoloading and explici= tly import packages.

I don't know where you go= t that from - maybe the old thread from 9 months ago?=C2=A0 The proposal I = made last night not only takes autoloading into account but also discusses = how autoload functions will receive a second argument moving forward that l= ets them make decisions about what code to supply to modules based on whate= ver logic they have which is well beyond the scope of this proposal.
<= div>
I will admit that while use doesn't really do anythi= ng and is handled at compile time, use - require evals at run time. The spe= cified symbols come out of the module. The module can be located with an au= toloader if desired, but it's not the same mechanism because module cre= ation involves the creation of a new symbol table that will be used by the = module file and EVERY file it requires and every file THEY require. This is= why the autoloader is to return a string when handling module requests for= classes because PHP should have the responsibility of attaching the code t= o the correct symbol table.=C2=A0 The autoloader is told what module is abo= ut to receive the code. If the autoloader requires it's going to affect= the symbol table it was declared in, which is ok if the module's own a= utoloader is handling the request - but if the global autoloader on the mai= n thread does a require it's going to affect the symbol table of the ma= in thread.

>> 4. Abandoning 16 years of PSR-0/4 file co= nvention in favor of "module =3D file", which will almost certain= ly result in multi-thousand-line files even for clean, well-factored code.<= br>
=C2=A0
Again, I don't see this, at all. And I&#= 39;m a little insulted you'd think that I'm dumb enough to propose = something that outrageous.=C2=A0 Modules simply have their own symbol table= scope.=C2=A0 They can import any package as they exist today without any m= odification into that scope. They can therefore pull a different version of= the same package, which is desirable if they have a compatibility issue wi= th the latest version of the package. Those autoloader are important, but o= ut of scope.

Another way to look at this is in Jav= aScript the ESM module convention was introduced without any package manage= ment ability and worked with raw absolute file paths.=C2=A0 It would be a d= ecade before import-maps were introduced and true integration with npm star= ted and it's still not finished.=C2=A0 I'm starting from the same p= lace, use (some symbols) require <path> and if PHP can't find the= path it queries the autoloader and gives it a chance to return a path for = PHP to use here.

All existing code stays as is, an= d can even evolve as is when they don't need to worry with version mism= atches. This syntax only really becomes useful when you want to "black= box" the package from the rest of the application and allow the modul= e to do what it needs without fear of affecting the rest of the application= . Over time it might eclipse the old approach, but=C2=A0

--000000000000ed58bc06345713ca--