Modules, again.

php.internals

Michael Morris

1 year ago
It's been 9 months. Been researching, working on other projects, mulling over points raised the last time I brought this up. And at the moment I don't think PHP 8.5 is in its final weeks so this isn't a distraction for that. The previous discussion got seriously, seriously derailed and I got lost even though I started it. I'm not going to ask anyone to dig into the archives, let's just start afresh. -------------------------------------------------------------------------------- THE PROBLEM -------------------------------------------------------------------------------- PHP has no way of dealing with userland code trying to write to the same entry on the symbol tables. Namespaces are a workaround of this and combined with autoloaders they've yielded the package environments we currently have (usually composer), but at the end of the day if two code blocks want different versions of the same package invoked at the same time there's going to be a crash. This is seen most visibly in WordPress plugins that use composer, as they resort to monkey-typing the composer packages they consume to avoid such collisions. -------------------------------------------------------------------------------- PROPOSAL -------------------------------------------------------------------------------- Modules - blocks of PHP code with independent symbol tables and autoload queues. Instead of using any new keywords along with the backwards compatibility problems that creates three existing keywords will be used in a new way: "use", "require", and "yield" The first file that PHP loads will always be on the "main thread". To bring in code as a module the use require structure is used. The simplest possible version of this is as follows: PHP Code ----------------------------------------------------------------------- use require 'mymodule.php'; -------------------------------------------------------------------------------- The contents of 'mymodule.php' have two requirements. First, a namespace is *required* of a module. Second, yield statements are used to mark the functions classes and constants the module exports, and at least one such yield must be present. Hence the file may look something like this: PHP Code ----------------------------------------------------------------------- namespace MyModule; yield function sum(a, b) { return a + b; } -------------------------------------------------------------------------------- Returning to our caller, it could make use of this function as follows. PHP Code ----------------------------------------------------------------------- use require 'mymodule.php'; echo MyModule::sum(3, 4); -------------------------------------------------------------------------------- So far there is nothing here that couldn't have been done with a static class. The important difference though is in behavior. 1. The module does not affect or see variables on the main thread - including the superglobals. A module can only get to them if it receives them as an argument in some sort of setter yield function. 2. The module does not affect or see constants or functions established on the main thread or in other modules. It can see and autoload classes from the main thread if the module author opts into this (discussed below). The use case above is not typical - usually inclusions from modules are more targeted. PHP Code ----------------------------------------------------------------------- use sum require 'mymodule.php'; echo sum(3, 4); // 7 -------------------------------------------------------------------------------- Here the class is not created in the main thread, only the yielded function is. Aliases and multiple objects can be declared in the use just as is the case now. PHP Code ----------------------------------------------------------------------- use sum, difference as subtract require 'mymodule.php'; -------------------------------------------------------------------------------- And if desired the namespace of the module can be aliased on the fly using the wildcard operator PHP Code ----------------------------------------------------------------------- use * as AliasModule require 'mymodule.php'; AliasModule::sum(3,4); -------------------------------------------------------------------------------- Require continues to invoke autoloaders, even when used in the context of use require. The callback defined in spl_autoload_register will receive a second argument from this context, boolean true if the main thread is loading a module, and if a module is loading a module its namespace string will be sent. Hence PHP Code ----------------------------------------------------------------------- require('./vendor/composer/autoload.php') use require 'mymodule'; // Callback will receive args ('mymodule', true) class A; // Callback will receive args ('A', false) - the false case should preserve BC. -------------------------------------------------------------------------------- And in a module PHP Code ----------------------------------------------------------------------- namespace MyModule; /* * Mod Author can elect to use the global autoloader * by passing the string "global" instead of a callback */ spl_autoload_register('global'); use require 'othermodule'; // Callback will receive args ('othermodule', 'MyModule') class A; // Callback will receive args ('A', 'mymodule') -------------------------------------------------------------------------------- Autoload callbacks are currently required to directly load the file. For modules this *will not work* because the loading of a module file involves the setup of a new symbol table, autoload queue, and slightly different parsing rules (again - namespace is required, not optional, and at least one top level yield statement must be present). So when an autoloader is asked about a module it must return the absolute path to the module, or false if it can't resolve it (handing off to the next loader in the queue). PHP will then load the module as if the filepath had been placed in the use require statement in the first place. -------------------------------------------------------------------------------- CLOSING REMARKS -------------------------------------------------------------------------------- I want to take a moment to ruminate on what doors are opened by the above, but all that follows is NOT part of my proposal. The above gets what I feel is needed - a way to cleanly run disparate packages in an application whose authors refuse to update it to embrace composer (cough - WordPress - cough). But even for projects that do embrace composer and its package management the above makes the prospect of a large API change in a major version far less frightening. In the current setup all the extensions have to be kept current and more or less on the same page. The larger the extension authoring set becomes the less feasible this is - projects get abandoned, stagnate and if you have a site that needs such then finding a replacement or upgrading it personally can be a pain. The string that gets passed to the autoloader could be anything btw - rules for which versions the module might accept like 'mymodule@7.x' can work if the package manager is written to parse out such, but the rules for such, not to mention what the composer.json file or equivalent would need to look like is outside the scope of this proposal. It should be noted that Modules offer a much more black box behavior than the current namespaces and autoloaders can provide. The only part of a module the outside world can see is what it yields. If a class isn't yielded the outside world can't make an instance of it. This shielding of internal API's should be useful because no matter how big a note you make in the comments about how a class shouldn't be used by outside code sooner or later someone will do it and their code will break when you change the internal API. While it is their fault for doing such, it can be a concern especially if the use of such "internal" API's becomes commonplace (Drupal has several such instances of this) Finally, my previous writings on this have mused about possibly having module files possess vastly different parsing rules. I bring this up as a possibility but I won't dwell on it as it could easily become a thread derailing distraction. That said, it should be possible to use the autoload callback to signal to PHP that a block of code should be loaded using the module parsing rules if having them be different in any way is desired. That or a new require_module statement could be used to pull in code under the module parsing rules. As to why this might be desirable - I'm no expert on the engine but I'm going to guess that giving each module an independent symbol table will incur overhead, possibly significant overhead. One way to claw back performance is to fix bugs that have been unfixable for backwards compatibility reasons. There is no existing module code, so in one fell swoop this code can step away from those problems. If this is done though it has to be done right as the window closes once projects with modules start appearing.

Arvids Godjuks

1 year ago
On Sun, 4 May 2025 at 10:36, Michael Morris <tendoaki@gmail.com> wrote:
> It's been 9 months. Been researching, working on other projects, mulling > over > points raised the last time I brought this up. And at the moment I don't > think > PHP 8.5 is in its final weeks so this isn't a distraction for that. The > previous discussion got seriously, seriously derailed and I got lost even > though > I started it. I'm not going to ask anyone to dig into the archives, let's > just > start afresh. > > *snip* >
I've started reading, saw Wordpress... almost stopped right there, but decided *okay, this is too rash of me, read further*.... "My disappointment is immeasurable and my day is ruined".gif It ended exactly how my first judging brain wave thought it would go - this whole thing is about fixing WordPress by changing the language. Plain and simple. No.
-- Arvīds Godjuks +371 26 851 664 arvids.godjuks@gmail.com Telegram: @psihius https://t.me/psihius

Jochen Schultz

1 year ago

Arvids Godjuks

1 year ago
On Sun, 4 May 2025 at 14:57, Jochen Schultz <JochenSchultz@gmx.de> wrote:
> > > Am 04.05.25, 13:07 schrieb Arvids Godjuks <arvids.godjuks@gmail.com>: >> >> >> >> On Sun, 4 May 2025 at 10:36, Michael Morris <tendoaki@gmail.com> wrote: >> >>> It's been 9 months. Been researching, working on other projects, mulling >>> over >>> points raised the last time I brought this up. And at the moment I don't >>> think >>> PHP 8.5 is in its final weeks so this isn't a distraction for that. The >>> previous discussion got seriously, seriously derailed and I got lost >>> even though >>> I started it. I'm not going to ask anyone to dig into the archives, >>> let's just >>> start afresh. >>> >>> *snip* >>> >> >> I've started reading, saw Wordpress... almost stopped right there, but >> decided *okay, this is too rash of me, read further*.... >> >> "My disappointment is immeasurable and my day is ruined".gif >> It ended exactly how my first judging brain wave thought it would go - >> this whole thing is about fixing WordPress by changing the language. Plain >> and simple. >> >> No. >> -- >> >> >> *snip* >> >> I don't like wordpress either. It sure is the reason why PHP became a >> running gag in memes. >> >> But it exists. And if it goes away PHP might too. >> >> Yes, we hate it, it's a mess. But can we ignore it? >> >> I think it deserves a fair discussion. >> >> There might even be a "simple" solution to it. Maybe a version number >> that is auto-added to the namespace in composer? >> >> Maybe the wordpress community could fork composer and do it? >> Like what are we talking about? 10-20 lines of code? >> >> Best >> Jochen >> >> >>
Yes, we should ignore it when working on language design and trying to improve it. For whatever reason, they made a deliberate commercial decision not to do anything about it, and this is not even touching on events of recent times :) Two - I already have to deal with JavaScript at times and all it's ecosystem. I do not want another JavaScript in my PHP, thank you. For me, any concept for modules, packages, etc, etc that ignores autoloading is dead on arrival. Ideally it should be transparent to any consuming code and be done on the defining side, erroring out when attempting to load/access something internal to a namespaca/package/module. I'm also not a fan of ability to mix multiple package versions in one code base.
-- Arvīds Godjuks +371 26 851 664 arvids.godjuks@gmail.com Telegram: @psihius https://t.me/psihius

Deleu

1 year ago
Hi! On Sun, May 4, 2025 at 8:08 AM Arvids Godjuks <arvids.godjuks@gmail.com> wrote:
> > > On Sun, 4 May 2025 at 10:36, Michael Morris <tendoaki@gmail.com> wrote: > >> It's been 9 months. Been researching, working on other projects, mulling >> over >> points raised the last time I brought this up. And at the moment I don't >> think >> PHP 8.5 is in its final weeks so this isn't a distraction for that. The >> previous discussion got seriously, seriously derailed and I got lost even >> though >> I started it. I'm not going to ask anyone to dig into the archives, let's >> just >> start afresh. >> >> *snip* >> > > I've started reading, saw Wordpress... almost stopped right there, but > decided *okay, this is too rash of me, read further*.... > > "My disappointment is immeasurable and my day is ruined".gif > It ended exactly how my first judging brain wave thought it would go - > this whole thing is about fixing WordPress by changing the language. Plain > and simple. > > No. >
I think that is a very short sighted statement about the proposal. I have been using PHP for 15 years and I don't know, don't use nor don't care about the Wordpress ecosystem. However, the proposal has immeasurable merits to address many things that have been discussed several times on this list over the years. 1- Private classes. Instead of the currently being discussed nested class declaration with its own set of awkwardness, Modules would allow the declaration of classes that are not globally visible. Regardless of the state of Wordpress, **at first glance** this seems a superior proposal than Nested Class. 2- Friends classes. It has been discussed a couple of times the introduction of class friendship as a way to expose internals of a class only to a specific subset of known / expected classes. This has some foundation in the fact that you don't want to declare public attributes in a global class. With modules, you could declare a class that is not exported to the global scope and use public attributes knowing full well that only classes within your own module will be able to touch them. Again, major props to this proposal over Friends classes. 3- Class autoload vs Function autoload. There have been hundreds of hours spent on discussing function autoloading. I don't quite remember why that is such a hot topic and I also don't remember what it tries to accomplish, but it seems adjacent enough to consider that maybe this proposal could be a door on that realm as well. 4- Familiarity with the Typescript ecosystem. For better or worse, browsers run on Javascript for 3 decades and not even Wasm is fully changing that. This proposal is too familiar from Typescript's import / export system and would help bridge the gap for frontend engineers to better understand the flow of PHP code. ----------- I haven't read (or don't remember) the old discussion. From a superficial look of the proposal, it does seem rather awkward to not go full in with `import / export` keywords, even if it requires a soft reserved keyword. I'm not sure the syntax would require a full hard keyword reservation? Perhaps yield is good enough instead of export, but `use require` does seem rather odd. import ... require (as opposed to import ... from) would also work better-ish. In any case, this threads in bikeshedding space. I guess I'd rather have use .... require anyway than not have it at all, specially since IDEs often end up writing it for us anyway.
-- Marco Deleu

Michael Morris

1 year ago
On Sun, May 4, 2025 at 7:06 AM Arvids Godjuks <arvids.godjuks@gmail.com> wrote:
> > It ended exactly how my first judging brain wave thought it would go - > this whole thing is about fixing WordPress by changing the language. Plain > and simple. > >
That is a lie. Go back and read again - I discuss how this will improve Drupal, Symfony and other projects that embrace composer.

Arvids Godjuks

1 year ago
On Sun, 4 May 2025 at 17:31, Michael Morris <tendoaki@gmail.com> wrote:
> > > On Sun, May 4, 2025 at 7:06 AM Arvids Godjuks <arvids.godjuks@gmail.com> > wrote: > >> >> It ended exactly how my first judging brain wave thought it would go - >> this whole thing is about fixing WordPress by changing the language. Plain >> and simple. >> >> > That is a lie. Go back and read again - I discuss how this will improve > Drupal, Symfony and other projects that embrace composer. >
As Larry has already extensively covered, it seems that about 80% of the whole thing lives somewhere outside the emails you sent. That's just a bad proposal, either rushed or hasn't been worked on past 1st version of a draft. It is how it reads to me. Accounting for that (and I read through your correspondence) - there is nothing in the clarifications that didn't make me reconsider; if anything, it turned me off even more. I will repeat myself: I do not want a typescript/javascript/python/rails/whatever export/import type going into PHP. This is just going back to pre-autoloading times, where you needed to require/include files. Yes, the autoloading part is better, but then we have a whole mess of autoloader importing vs require/include at which point this whole thing is a ball of spaghetti that makes no sense and I am confused to a point I cannot make heads or tails of this whole thing.
-- Arvīds Godjuks +371 26 851 664 arvids.godjuks@gmail.com Telegram: @psihius https://t.me/psihius

Larry Garfield

1 year ago
On Sun, May 4, 2025, at 2:34 AM, Michael Morris wrote:
> It's been 9 months. Been researching, working on other projects, > mulling over > points raised the last time I brought this up. And at the moment I > don't think > PHP 8.5 is in its final weeks so this isn't a distraction for that. > The > previous discussion got seriously, seriously derailed and I got lost > even though > I started it. I'm not going to ask anyone to dig into the archives, > let's just > start afresh.
This proposal requires: 1. Every module author to change their coding structure 2. Every consumer of a package to change their coding structure 3. Devs to abandon "it just works" autoloading and explicitly import packages. 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. As I stated in the previous thread, this is a total nonstarter. Python/Javascript style module definition is simply not compatible with PHP. It may have been had we gone that way in 2005, but we didn't, and that ship has long since sailed. Any module proposal worth considering can require only the first point above. Requiring work from a package author to module-ify their code is fine, though it shouldn't be onerous. But it needs to "just work" with the rest of the existing ecosystem. This proposal fails at that. Arnaud and I spent some time last year exploring a different approach to modules that requires only trivial changes by package authors, and none by consumers: https://github.com/Crell/php-rfcs/blob/master/modules/spec-brainstorm.md https://github.com/arnaud-lb/php-src/pull/10 In our case, the main target was performance by giving the opcache optimizer a larger chunk of code to work with, plus the potential for namespace-private symbols. Arnaud was able to make it mostly work, and there was significant potential performance to be had, but it also ran into some very complex issues very quickly. (Circular dependencies, modules split across multiple directories, testing, etc.) We largely haven't gotten back to it as the implementation was very complex and it wasn't clear if the benefit would be worth it, though I'd still like to revisit it if possible. --Larry Garfield

Michael Morris

1 year ago
On Sun, May 4, 2025 at 5:19 PM Larry Garfield <larry@garfieldtech.com> wrote:
> On Sun, May 4, 2025, at 2:34 AM, Michael Morris wrote: > > It's been 9 months. Been researching, working on other projects, > > mulling over > > points raised the last time I brought this up. And at the moment I > > don't think > > PHP 8.5 is in its final weeks so this isn't a distraction for that. > > The > > previous discussion got seriously, seriously derailed and I got lost > > even though > > I started it. I'm not going to ask anyone to dig into the archives, > > let's just > > start afresh. > > This proposal requires: > > 1. Every module author to change their coding structure > 2. Every consumer of a package to change their coding structure > 3. Devs to abandon "it just works" autoloading and explicitly import > packages. > 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. >
You're 4 for 4 on falsehoods. Care to actually read what I wrote because I know you're more intelligent and better than this and very disappointed.

Larry Garfield

1 year ago
On Sun, May 4, 2025, at 4:25 PM, Michael Morris wrote:
> On Sun, May 4, 2025 at 5:19 PM Larry Garfield <larry@garfieldtech.com> wrote: >> On Sun, May 4, 2025, at 2:34 AM, Michael Morris wrote: >> > It's been 9 months. Been researching, working on other projects, >> > mulling over >> > points raised the last time I brought this up. And at the moment I >> > don't think >> > PHP 8.5 is in its final weeks so this isn't a distraction for that. >> > The >> > previous discussion got seriously, seriously derailed and I got lost >> > even though >> > I started it. I'm not going to ask anyone to dig into the archives, >> > let's just >> > start afresh. >> >> This proposal requires: >> >> 1. Every module author to change their coding structure >> 2. Every consumer of a package to change their coding structure >> 3. Devs to abandon "it just works" autoloading and explicitly import packages. >> 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. > > You're 4 for 4 on falsehoods. Care to actually read what I wrote > because I know you're more intelligent and better than this and very > disappointed.
> PHP Code ----------------------------------------------------------------------- > > namespace MyModule; > > yield function sum(a, b) { return a + b; } > > --------------------------------------------------------------------------------
Every module author changes their code. (Which is reasonable.)
> 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.
> 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? 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. 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. 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. --Larry Garfield

Michael Morris

1 year ago
On Sun, May 4, 2025 at 5:38 PM Larry Garfield <larry@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

Larry Garfield

1 year ago
On Sun, May 4, 2025, at 5:40 PM, Michael Morris wrote:
> On Sun, May 4, 2025 at 5:38 PM Larry Garfield <larry@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.
I thought it self-evident that I was referring to "every package author that wants to expose their package as a module", not literally every owner of a package on Packagist. (Though presumably over time those two circles will increasingly overlap.) But let me make that explicit. And, as noted, requiring the author of a package to introduce new syntax in order to turn the package into a module is a reasonable expectation. I have no issue with that.
>> > 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.
If my library is structured as a module, with yield and stuff, then how exactly would someone use it without using the `use require` syntax at least?
>> > 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.
I believe you are grossly misusing the term "thread" here. There is no separate execution thread involved, just scoped symbol tables. See note further down about the rest of the points raised here.
> 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.
Nothing in your initial post discussed any of this, or even implied 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.
From the proposal:
> use require 'mymodule.php'; > > echo MyModule::sum(3, 4);
How exactly does MyModule get defined? That is not at all clear.
>> 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 }
... Nothing in your initial proposal suggested that in the slightest. It did not even include yield with brackets to list multiple options.
> 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.
See above.
>>> 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.
It came from the examples in the original proposal at the top of this thread:
> use sum require 'mymodule.php'; > > echo sum(3, 4); // 7
So if I want to call sum(), I have to explicitly `use sum require` it. I cannot just let the autoloader sort it out. (Assuming we were talking about a class or function autoloading ever happens, of course.)
>>> 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.
The implication from your original post is that if I want to import 3 symbols from a module, I require a single file. That file has one or more symbols that are `yield` ed. Implication: If I want 3 symbols in a module together, they have to be in the same file. Your response here about having a fronting file that yields symbols it doesn't define, on the assumption that an autoloader will figure it out in the backend, was never stated nor implied anywhere in your proposal. Rather, it states that modules look like this:
> yield function sum(a, b) { return a + b; }
IE, like Javascript or Python libraries. That is the plain reading of your original post. And naturally means that whoever is authoring that file needs to make changes to it, to at minimum add the "yield" keyword. So before you get indignant and call me a liar ("You're 4 for 4 on falsehoods"), perhaps consider how someone you know is "more intelligent" than this can come away from your post understanding it so utterly differently than what you intended. From your follow up statements, it seems that what you are proposing is not "modules" in any sense used in any language I am familiar with. Rather, you're proposing a symbol table hack to get around PHP's inability to load two symbols with the same name, and using module terminology to inaccurately describe it. (And thread terminology as well.) That is, it seems your intent is that I can import arbitrary code from an arbitrary package and shunt it off to a separate symbol table. But it's not clear if you intend for the package author to have to do anything to enable that. Your answers seem contradictory. For a concrete example, I'll offer my AttributeUtils library: https://github.com/Crell/AttributeUtils/tree/master/src It has a whole bunch of interfaces, plus a few classes. We'll focus on a subset of them for the moment. Analyzer.php (this is the main class someone uses) AttributeParser.php (this is an internal class used by Analyzer, you should never use it directly) FromReflectionClass.php (an interface that other libraries implement to trigger certain functionality in Analyzer) Say I want to modularize this library, under your proposal. What would I, as the library author, do exactly? Anything? How can I mark AttributeParser as internal-only and thou-shalt-not-use-if-you're-not-me? (That's the most common value of module systems, and the one most people in PHP seem to think of, from what I've seen.) That library is used extensively by Serde (another of my libraries). If I modularize AttributeUtils, then what changes does Serde need to make? What would it get out of doing so? Code examples, please. --Larry Garfield

Michael Morris

1 year ago
Resetting and moving the proposal writeup to a github hosted markdown file here: https://github.com/michael-lloyd-morris/php-modules-rfc/blob/main/php-modules.md On Mon, May 5, 2025 at 12:44 AM Larry Garfield <larry@garfieldtech.com> wrote:
> > > So before you get indignant and call me a liar ("You're 4 for 4 on > falsehoods"), perhaps consider how someone you know is "more intelligent" > than this can come away from your post understanding it so utterly > differently than what you intended. >
I'm sorry for doing that.
> > From your follow up statements, it seems that what you are proposing is > not "modules" in any sense used in any language I am familiar with. > Rather, you're proposing a symbol table hack to get around PHP's inability > to load two symbols with the same name, and using module terminology to > inaccurately describe it. (And thread terminology as well.) That is, it > seems your intent is that I can import arbitrary code from an arbitrary > package and shunt it off to a separate symbol table. But it's not clear if > you intend for the package author to have to do anything to enable that. > Your answers seem contradictory. > > For a concrete example, I'll offer my AttributeUtils library: > https://github.com/Crell/AttributeUtils/tree/master/src > > It has a whole bunch of interfaces, plus a few classes. We'll focus on a > subset of them for the moment. > > Analyzer.php (this is the main class someone uses) > AttributeParser.php (this is an internal class used by Analyzer, you > should never use it directly) > FromReflectionClass.php (an interface that other libraries implement to > trigger certain functionality in Analyzer) > >
In answer to your question - assuming the module file is at the root of your src directory. <?php module \Crell\AnalyzerModule; export [ \Crell\Analyzer, \Crell\FromReflectionClass ]; ?>
> Say I want to modularize this library, under your proposal. What would I, > as the library author, do exactly? Anything? How can I mark > AttributeParser as internal-only and thou-shalt-not-use-if-you're-not-me? > (That's the most common value of module systems, and the one most people in > PHP seem to think of, from what I've seen.) > > That library is used extensively by Serde (another of my libraries). If I > modularize AttributeUtils, then what changes does Serde need to make?
On that end <?php require_module <path_to_Crell\AnalyzerModule>; $analyzer = new \Crell\AnalyzerModule::Analyzer(); ?> If the autoloader gets updated to identify and properly load modules (outside the scope of this RFC) the require_module statement can be dropped. If a use statement is desired it looks like this <?php use \Crell\AnalyzerModule::Analyzer; // This is legal for modules, not for static classes. $analyzer = new Analyzer(); ?>
> What would it get out of doing so? >
Nothing much - it just wouldn't be able to see as deeply into the AttributeUtils code.

Larry Garfield

1 year ago
On Tue, May 6, 2025, at 1:33 AM, Michael Morris wrote:
> Resetting and moving the proposal writeup to a github hosted markdown > file here: > https://github.com/michael-lloyd-morris/php-modules-rfc/blob/main/php-modules.md
>> From your follow up statements, it seems that what you are proposing is not "modules" in any sense used in any language I am familiar with. Rather, you're proposing a symbol table hack to get around PHP's inability to load two symbols with the same name, and using module terminology to inaccurately describe it. (And thread terminology as well.) That is, it seems your intent is that I can import arbitrary code from an arbitrary package and shunt it off to a separate symbol table. But it's not clear if you intend for the package author to have to do anything to enable that. Your answers seem contradictory. >> >> For a concrete example, I'll offer my AttributeUtils library: https://github.com/Crell/AttributeUtils/tree/master/src >> >> It has a whole bunch of interfaces, plus a few classes. We'll focus on a subset of them for the moment. >> >> Analyzer.php (this is the main class someone uses) >> AttributeParser.php (this is an internal class used by Analyzer, you should never use it directly) >> FromReflectionClass.php (an interface that other libraries implement to trigger certain functionality in Analyzer) >> > > > In answer to your question - assuming the module file is at the root of > your src directory. > > <?php > module \Crell\AnalyzerModule; > > export [ > \Crell\Analyzer, > \Crell\FromReflectionClass > ]; > ?> > >> Say I want to modularize this library, under your proposal. What would I, as the library author, do exactly? Anything? How can I mark AttributeParser as internal-only and thou-shalt-not-use-if-you're-not-me? (That's the most common value of module systems, and the one most people in PHP seem to think of, from what I've seen.) >> >> That library is used extensively by Serde (another of my libraries). If I modularize AttributeUtils, then what changes does Serde need to make? > > On that end > > <?php > require_module <path_to_Crell\AnalyzerModule>; > > $analyzer = new \Crell\AnalyzerModule::Analyzer(); > ?> > > If the autoloader gets updated to identify and properly load modules > (outside the scope of this RFC) the require_module statement can be > dropped. If a use statement is desired it looks like this > > <?php > use \Crell\AnalyzerModule::Analyzer; // This is legal for modules, not > for static classes. > > $analyzer = new Analyzer(); > ?> > > >> What would it get out of doing so? > > Nothing much - it just wouldn't be able to see as deeply into the > AttributeUtils code.
So it's not really giving private symbols. It's not even blocking access to anything, since it can still just be included or autoloaded. What you're proposing is really just an optional loading facade (the real kind of facade) that lets you pull random symbols into a separate symbol table, accessed via separate syntax. (Either a class-like AnalyzerModule::Analyzer, or use-require.) Putting aside for the moment the question of whether that is a good idea or not, it is definitely not "modules" in any typical sense. I really don't think calling it "modules" is doing your proposal any favors, as it's just leading to confusion. What you're describing is a name-mangling facade, or symbol table facade, or something like that. (I don't know that there is a standard name.) I strongly recommend you recast your terminology around that, rather than "modules." That would remove a lot of confusion and allow people to review and respond to the idea you're actually proposing, now what they think a "module" should be. --Larry Garfield

Michael Morris

1 year ago
On Wed, May 7, 2025 at 10:59 AM Larry Garfield <larry@garfieldtech.com> wrote:
> > So it's not really giving private symbols. It's not even blocking access > to anything, since it can still just be included or autoloaded. What > you're proposing is really just an optional loading facade (the real kind > of facade) that lets you pull random symbols into a separate symbol table, > accessed via separate syntax. (Either a class-like > AnalyzerModule::Analyzer, or use-require.) > > Putting aside for the moment the question of whether that is a good idea > or not, it is definitely not "modules" in any typical sense. I really > don't think calling it "modules" is doing your proposal any favors, as it's > just leading to confusion. What you're describing is a name-mangling > facade, or symbol table facade, or something like that. (I don't know that > there is a standard name.) > > I strongly recommend you recast your terminology around that, rather than > "modules." That would remove a lot of confusion and allow people to review > and respond to the idea you're actually proposing, now what they think a > "module" should be. > > --Larry Garfield >
No proposal that replaces or even significantly alters how require et al works will be taken seriously, much less gain traction. As long as a package has PHP files that require can parse then clever users can direct require the files of a package distributed this way. The only way to stop it is to truly make files meant to be used with this unreadable to require - and the simplest way to do that is to assume code first and drop support for <?php ?> in these files. I'm in favor of that since I believe the engine can parse the files faster because it should simplify the token parsing process quite a bit. But I don't think a majority, much less the required supermajority would be in favor of such a change, at least not without other compelling reasons. Rather than fight the co-existence with require I think it would be better to harness and work with it. This would also make the changeover easier. As for the point of whether this counts as a module - Ask 10 programmers what a module is and expect 12 different answers. I'm patterning this on the module pattern used in JavaScript. I'm sorry if that doesn't meet your definition of module. I am open to any other name - component? library? Calling it a module until a better name can be found will have to do. But I'm not going to insult my own proposal by calling it "name-mangling facade" Part of the problem is PHP deals with code on a per file basis, not on a per directory one. Speaking of facades, the PSR-4 loading recommendation of organizing classes puts a module/package facade onto PHP, but it in no way ever has worked that way. Modules and Packages traditionally do work on the directory level, but how do you affix such to a language that has never had it and prevent older mechanisms of the language, such as require, from doing an end run. This problem hasn't been solved for years for a reason - it's not simple. It is an easy one to give up on, as most have. I'm just too stupid to give up I suppose.

Stephen Reay

1 year ago
> On 5 May 2025, at 04:18, Larry Garfield <larry@garfieldtech.com> wrote: > > On Sun, May 4, 2025, at 2:34 AM, Michael Morris wrote: >> It's been 9 months. Been researching, working on other projects, >> mulling over >> points raised the last time I brought this up. And at the moment I >> don't think >> PHP 8.5 is in its final weeks so this isn't a distraction for that. >> The >> previous discussion got seriously, seriously derailed and I got lost >> even though >> I started it. I'm not going to ask anyone to dig into the archives, >> let's just >> start afresh. > > This proposal requires: > > 1. Every module author to change their coding structure > 2. Every consumer of a package to change their coding structure > 3. Devs to abandon "it just works" autoloading and explicitly import packages. > 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. > > As I stated in the previous thread, this is a total nonstarter. Python/Javascript style module definition is simply not compatible with PHP. It may have been had we gone that way in 2005, but we didn't, and that ship has long since sailed. > > Any module proposal worth considering can require only the first point above. Requiring work from a package author to module-ify their code is fine, though it shouldn't be onerous. But it needs to "just work" with the rest of the existing ecosystem. This proposal fails at that. > > Arnaud and I spent some time last year exploring a different approach to modules that requires only trivial changes by package authors, and none by consumers: > > https://github.com/Crell/php-rfcs/blob/master/modules/spec-brainstorm.md > https://github.com/arnaud-lb/php-src/pull/10 > > In our case, the main target was performance by giving the opcache optimizer a larger chunk of code to work with, plus the potential for namespace-private symbols. Arnaud was able to make it mostly work, and there was significant potential performance to be had, but it also ran into some very complex issues very quickly. (Circular dependencies, modules split across multiple directories, testing, etc.) We largely haven't gotten back to it as the implementation was very complex and it wasn't clear if the benefit would be worth it, though I'd still like to revisit it if possible. > > --Larry Garfield >
Just to add my $0.02 as a userland/library developer: I agree with Larry's points - it needs to be transparent for the *consumers*, and it absolutely needs to align with namespaces, without any expectation about how symbols relates to files. I personally think initially the goal should be a more minimal approach that simply introduces the concept of a module/package/whatever-term-you-like as something that exists, with an implicit relationship to a namespace. Future RFC's could build on that - even within the same release cycle - but initially agreeing on a basic way to define "this namespace is also a <insert chosen name for a collection of code>" provides a building block for both language/runtime internal enhancements and external tooling. Cheers Stephen

Edmond Dantes

1 year ago
Hello, Michael. Thank you very much for your work. It's very interesting. Let me look at this idea from an architectural perspective. Did I understand correctly from the text that: * Modules control which entities are public and which are not? * During import, we specify which entities we want to bind to which namespace? * Could this solution help with separating Internal and Public classes? In other words, the information about what is public and what is private is part of the module. And the information about how the module is integrated into the project lies outside the module. Could this task be reduced to using `use as`? For example: ```php use \MyClass as MyClass2; ``` This situation is very similar to creating aliases for namespaces, but the difference is that the alias is created not for the code being used, but for the code being loaded. Technically, this is quite possible to implement: 1. We specify that files at a certain path will be loaded into a different namespace. 2. During loading, all namespaces are inserted into the symbol table with the desired alias, as if a person had manually renamed all the modules. But there is one problem here that cannot be solved at all. `PHP` lacks an abstraction over symbols. It doesn’t have a dedicated `Class` or `Namespace` type — strings are used in code for that purpose. And that means any code that tries to load a class using a string constant will break. To solve this, `PHP` would need to introduce a new data type. That’s not a problem, but it does require an `RFC`. In addition, a new `PSR` for packages and autoloading would be needed. You have a great idea, but I think it requires some groundwork in these areas first. P.S. I consider WordPress the best example of what rapid programming can look like — and where it can lead. And I don’t hate it. :) Best Regards, Ed

Rowan Tommins [IMSoP]

1 year ago
On 04/05/2025 08:34, Michael Morris wrote:
> PHP has no way of dealing with userland code trying to write to the > same entry > on the symbol tables.  Namespaces are a workaround of this and > combined with > autoloaders they've yielded the package environments we currently have > (usually > composer), but at the end of the day if two code blocks want different > versions > of the same package invoked at the same time there's going to be a > crash. This > is seen most visibly in WordPress plugins that use composer, as they > resort to > monkey-typing the composer packages they consume to avoid such collisions.
I think there are two different problems being muddled here: 1) How can two *completely unrelated* libraries avoid *accidentally* declaring something with the same name? 2) How can two versions of *the same* library be used in different parts of the same running program, despite overlapping names? Namespaces are not a workaround to problem number 2, they are a solution to problem number 1: every library chooses a prefix, and avoids declaring symbols with prefixes used by other libraries. Problem number 2 is what you seem to be trying to address. In languages like EcmaScript/JavaScript/TypeScript, the solution (to both problems) is to say that declarations don't have a canonical name. They're values that you can pass around, and their name is whatever a local scope says it is. This has major implications on both the language and its ecosystem: - contracts between libraries need to be structural, because there is no name that all libraries agree on for an interface/protocol/whatever - package managers don't resolve the graph of dependencies to a consistent set, they install multiple copies of the same library, for use in different scopes - package authors are free to depend on mutually incompatible concrete implementations, rather than agreed or de facto standards and interfaces NPM is notorious for creating a giant directory of tiny modules, because that's what the language design encourages. That's not how PHP works, and it never will be. In PHP (and other languages like Java, and C#), declarations have a single fully-qualified name. If you want two different versions of the same library, you will need to find a way to make their declarations use different fully-qualified names. Most applications do not have this problem. In fact, I would say that most applications would actively suffer from having multiple copies of the same library installed. The main exception, as you have pointed out, is plugin architectures like WordPress, where the plugin might want to "privately" use some library without impacting the host application and other plugins. For those, userland solutions already exist - a random search turned up this one, which lists some alternatives: https://github.com/BrianHenryIE/strauss?tab=readme-ov-file#alternatives I *do* think PHP would benefit from some native concept of "module" for other reasons, e.g. marking internal components, optimising across multiple files. I *do not* think that ES/JS/TS is a suitable model to look at, because it is starting from a fundamentally different concept of what a declaration is. I also *do not* think that allowing multiple versions of the same library should be a core requirement of any native module support; enabling userland to achieve it efficiently would be a nice to have.
-- Rowan Tommins [IMSoP]

Michael Morris

1 year ago
On Wed, May 7, 2025 at 3:24 PM Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote:
> > Problem number 2 is what you seem to be trying to address. >
The main thing, yes. This solution incidentally solves class privacy issues several unrelated and failed RFC's have addressed, including Nested Classes which is currently not doing so well in its vote.
> > NPM is notorious for creating a giant directory of tiny modules, because > that's what the language design encourages. > >
Indeed. Definitely the bathwater to be thrown out.
> > That's not how PHP works, and it never will be. In PHP (and other > languages like Java, and C#), declarations have a single fully-qualified > name. If you want two different versions of the same library, you will > need to find a way to make their declarations use different > fully-qualified names. >
Other libraries have the means to import into a namespace because their namespaces aren't just a quick and dirty string replacement. I've even proposed such a long, long time ago - allowing namespace to be the second argument of require, only to have that shot down as "require isn't a function." or some such.
> The main exception, as you have pointed out, is plugin architectures > like WordPress, where the plugin might want to "privately" use some > library without impacting the host application and other plugins.
It's not just WordPress. Drupal and other CMS's have extensions, but their reliance on composer means abandoned plugins that need an old library can block updating the whole system. The larger and more popular the application is the more likely this becomes. And like it or not WordPress haters on the board, for as much as WordPress sucks (and I will agree, it sucks hard) it still have a 50% - 80% market share of PHP websites depending on who you ask. A lot of this stems from the fact that end users just want their site to work - they don't give a rat's ass about dependencies and the like - and needing to drop a plugin to upgrade a site will put a bad taste in their mouth at best.
> For > those, userland solutions already exist - a random search turned up this > one, which lists some alternatives: > https://github.com/BrianHenryIE/strauss?tab=readme-ov-file#alternatives > >
I've seen Strauss in action. It's called monkey-typing. Other languages support this sort of monkey typing to some degree - PHP can only do it by doing an expensive and error prone programmatic keyword search of the source files and transforming them. The end result has to be shipped with the module, severing its dependencies from getting security updates. I *do* think PHP would benefit from some native concept of "module" for
> other reasons, e.g. marking internal components, optimising across > multiple files. I *do not* think that ES/JS/TS is a suitable model to > look at, because it is starting from a fundamentally different concept > of what a declaration is. >
So what do you suggest then? Go's model? Rusts? Gradle or Maven from Java? Shooting down stuff is trivial, finding solutions is the real work.

Rowan Tommins [IMSoP]

1 year ago
On 7 May 2025 21:51:29 BST, Michael Morris <tendoaki@gmail.com> wrote:
>On Wed, May 7, 2025 at 3:24 PM Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> >wrote: > >Other libraries have the means to import into a namespace because their >namespaces aren't just a quick and dirty string replacement. I've even >proposed such a long, long time ago - allowing namespace to be the second >argument of require, only to have that shot down as "require isn't a >function." or some such.
I've no idea what you're saying here, sorry. I never mentioned autoloading in my message, because it's largely irrelevant. What's relevant is how you identify something that's been declared (e.g. a class).
>> The main exception, as you have pointed out, is plugin architectures >> like WordPress, where the plugin might want to "privately" use some >> library without impacting the host application and other plugins. > > >It's not just WordPress.
That is why I said "plugin architectures like WordPress". I am absolutely aware that it is an example, not something unique.
>I've seen Strauss in action. It's called monkey-typing. Other languages >support this sort of monkey typing to some degree - PHP can only do it by >doing an expensive and error prone programmatic keyword search of the >source files and transforming them. The end result has to be shipped with >the module, severing its dependencies from getting security updates.
So, you don't like the first example I happened to pick in a 10-second search... More interestingly: Firstly, can PHP do something to make the rewriting easier? I've no idea, but if so, let's talk about it. Secondly, can the rewriting be done at install time rather than in advance? I see no reason why not; seems like something to tinker with in a Composer plugin, off-topic on this list.
>I *do* think PHP would benefit from some native concept of "module" for >> other reasons, e.g. marking internal components, optimising across >> multiple files. I *do not* think that ES/JS/TS is a suitable model to >> look at, because it is starting from a fundamentally different concept >> of what a declaration is. >> > >So what do you suggest then? Go's model? Rusts? Gradle or Maven from >Java? Shooting down stuff is trivial, finding solutions is the real work.
I suggest starting with what we already have: build modules on top of namespaces, not as something new and orthogonal; and leave package *management* (version resolution, installation, etc) to userlan, while recognising that Composer is overwhelmingly the most popular implementation. That probably means that rewriting namespace prefixes remains the only way to isolate "private" versions of a dependency, and I am totally fine with that. Rowan Tommins [IMSoP]

Larry Garfield

1 year ago
On Wed, May 7, 2025, at 3:51 PM, Michael Morris wrote:
>> The main exception, as you have pointed out, is plugin architectures >> like WordPress, where the plugin might want to "privately" use some >> library without impacting the host application and other plugins. > > It's not just WordPress. Drupal and other CMS's have extensions, but > their reliance on composer means abandoned plugins that need an old > library can block updating the whole system. The larger and more > popular the application is the more likely this becomes. And like it > or not WordPress haters on the board, for as much as WordPress sucks > (and I will agree, it sucks hard) it still have a 50% - 80% market > share of PHP websites depending on who you ask. A lot of this stems > from the fact that end users just want their site to work - they don't > give a rat's ass about dependencies and the like - and needing to drop > a plugin to upgrade a site will put a bad taste in their mouth at best.
If we're going to cite WordPress as a design factor, then we should also recognize that WordPress has virtually no involvement in PHP Internals, and its version requirement history means that even if PHP 8.5 were to ship with the perfect multi-version-loading system, it would be around 10 years before WordPress even supported that PHP version. As a practical matter, it's just not a meaningful factor.
>> I *do* think PHP would benefit from some native concept of "module" for >> other reasons, e.g. marking internal components, optimising across >> multiple files. I *do not* think that ES/JS/TS is a suitable model to >> look at, because it is starting from a fundamentally different concept >> of what a declaration is. > > So what do you suggest then? Go's model? Rusts? Gradle or Maven from > Java? Shooting down stuff is trivial, finding solutions is the real > work.
Quoting from my first post in this thread:
> Arnaud and I spent some time last year exploring a different approach > to modules that requires only trivial changes by package authors, and > none by consumers: > > https://github.com/Crell/php-rfcs/blob/master/modules/spec-brainstorm.md > https://github.com/arnaud-lb/php-src/pull/10 > > In our case, the main target was performance by giving the opcache > optimizer a larger chunk of code to work with, plus the potential for > namespace-private symbols. Arnaud was able to make it mostly work, and > there was significant potential performance to be had, but it also ran > into some very complex issues very quickly. (Circular dependencies, > modules split across multiple directories, testing, etc.) We largely > haven't gotten back to it as the implementation was very complex and it > wasn't clear if the benefit would be worth it, though I'd still like to > revisit it if possible.
I don't believe that is a perfect proposal, but it is a different approach that Arnaud's experimentation shows has the potential for as much as a 20% performance improvement in some situations. That's nothing to sneeze at. There's still challenges with it, both implementation and DX, but let's not pretend that aping Python is the only option that exists. I believe that qualifies as "real work." --Larry Garfield