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