On Sun, May 4, 2025 at 5:38 PM Larry Garfield <la...@garfieldtech.com>
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 know
> the module name, which as described is the file name of the module, not the
> 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 examples
> 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 =
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
<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, 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

Reply via email to