On Sun, Jun 30, 2024, at 22:28, Michael Morris wrote:
> So let's take another crack at this based on all the points raised in the 
> thread. This should also underline why I don't consider this an RFC - I am 
> iterating until we arrive at something that may be refinable into an RFC. And 
> I say we because without the aid of those in this conversation I would not 
> have arrived at what will follow.
> 
> Before I continue I would like to apologize for being somewhat irritable. 
> We're all here because we enjoy using this language and want to see it 
> improved and prevent bad changes. Opinions will differ on this and in the 
> heat of the moment of arguing a point things can get borderline.
> 
> 
> Returning to a point I made earlier, Composer isn't used on Wordpress.  I 
> went over to the Wordpress discussion list and read over why, because that 
> discussion provides clues to what kind of package management may be 
> adoptable. I think the largest point is that Wordpress should be usable 
> without ever resorting to using the command line. Yes, it does have a command 
> line tool - wp-cli - and it is powerful, but using it as an administrator of 
> a Wordpress site is not required.
> 
> The largest block to composer's inclusion in Wordpress is the inability to 
> run multiple versions of a module. Yes, it's a mess when this happens, but if 
> you're an end user, you just want your plugins to work.  If one plugin that 
> no one has updated in a year that you're using is consuming version 2 of a 
> package, you're gonna be annoyed at best if the module stops working when you 
> install a new plugin that is using version 3 of the same package and has a BC 
> break in it.  Composer can't resolve this easily.
> 
> There are WordPress plugins that use composer - I have a couple in the 
> website I'm working on. But they accomplish the inclusion of composer by 
> redistributing the packages, and using a utility called brianhenryie/strauss 
> to monkey type the entire included package into the plugin, changing the 
> namespace of the entire package to something different. The approach works, 
> but it's ugly.  In any event, the plugin that results from this carries a 
> copy of the code from packagist rather than sourcing the code from packagist.
> 
> 
> -- IMPORT --
> 
> The import statement is for bringing in packages.  It needs to be able to 
> deal with:
> 
> * Extensions - the existing and oldest of packages for PHP
> * PECL Extensions
> * Phar Packages
> * Composer Packages
> * PHP Modules - this is the new module system that has dominated the 
> conversation, but in this iteration it's going to be broken away from import 
> to some degree in this iteration.
> 
> Today we'll look just at composer.
> 
> Now import needs to load packages in a manner that allows different versions 
> to be run concurrently. A PHP application such as Wordpress should be 
> distributable without needing to use the command line. That is, if WordPress 
> leverages this in any way, they don't have to give up their famous 10 minute 
> quick install.
> 
> Some terms here to keep myself from getting lost (let alone anyone trying to 
> read this).
> 
> * APPLICATION - This is the overall application - WordPress, Drupal, 
> BobbysFirstFramework, etc. - that is doing the import. This code is on the 
> root scope.
> * ROOT SCOPE - This is where the global variables and the namespaces as we 
> know them exist.  Contrast this with
> * PACKAGE SCOPE - Each package brought in with import gets its own package 
> scope. This is a distinct behavior from Include/Require. I think each package 
> scope will need to be on its own request thread, but this is an 
> implementation detail I can't speak to with any authority. The goal is 
> whatever happens in a package stays in the package.  If two different 
> packages want to define /foo(), they can.
> 
> When a package is imported the parser will look for a `.php.mod` file at the 
> root of the package. Among other things, this file details what type of 
> package it is and where to mount it by default in the namespace of the ROOT 
> SCOPE. So, 
> 
>> GIVEN a package with this .php.mod file
>> 
>>> package MyModule
>> 
>> WHEN I issue this import in an application
>> 
>>> import "MyModule";
>> 
>> THEN I should be able to access a method in that module with this code
>> 
>>> \MyModule\foo();
> 
> Aliasing is an option - `import "MyModule" as BeerModule` will make the 
> methods accessible in the root with \BeerModule\foo();

Just to challenge you a bit here. The language already has `use` for this. Does 
`use` stay or is it replaced with `import` and why not just change the meaning 
of `use` in a package context?

> 
> Unlike require/include import is sensitive to the namespace it is in for 
> mounting.  So
> 
>> namespace Trees;
>> import "MyModule";
>> 
>> MyModule\foo(); // works
>> 
>> \Trees\MyModule\foo(); // needed from another namespace.
> 
> That said, with aliasing an absolute namespace for the module can be assigned.
> 
>> namespace Trees;
>> import "MyModule" as \MyModule;
>> 
>> MyModule\foo(); // works if my understanding of existing namespace 
>> resolution rules is correct.
>> \MyModule\foo(); // also works.
> 
> Now, with that in place, let's crack a tougher nut - handling a composer 
> package. By default composer is designed to set up an autoloader, then 
> resolve symbol references as they come up. This works until you have two 
> packages that want the same symbol reference - which will most frequently 
> occur with incompatible versions of the same package.  So our puzzle here is 
> how to allow composer to do its thing without rewriting it.  We'll deal with 
> admittedly the hardest case first - importing a package whose maintainers 
> have taken no action to make it compatible with this new system.
>>  
>> import "composer://packagist.org/packages/twig/twig#v3.10.3" as 
>> TemplateEngine
> 
> The reason for that alias and not "Twig" is because the mounting point comes 
> before the internal namespace of the file. This is unavoidable with this 
> scheme
> 
> The URL there is "loader://package_url". PHP by default will know what the 
> composer loader is. It will look to see if the user has globally installed 
> composer already and use that, otherwise it will locally install composer for 
> the project, initialize it, download the package and have composer resolve 
> the package ending in setting up an autoloader that is only invoked within 
> that package.

Adding this after I just wrote the below book. It started out simple enough and 
it drives me crazy when people crash my proposal with a counter-proposal. So, 
by all means, take it with a grain of salt ... I kinda went overboard thinking 
about it.

I think composer and friends are a moot point. If we go a bespoke way for 
everything, we end up with a mess. What about creating "hooks" that things like 
composer can "register" an installer at? For example, we could define a 
"WELL_KNOWN/installers/composer/hooks.json" (I'm gonna steal a bunch of ideas 
from kubernetes from here on), where WELL_KNOWN is some engine-specific 
directory (like where the php.ini file is). Basically, any installer can 
register an installer by creating a directory in WELL_KNOWN/installers of which 
an installer might look like the following for composer:

WELL_KNOWN/
  installers/
    composer/
      hooks.json
      composer.phar
      cache

and hooks.json could have a schema of something like:

{
  "name": "composer",
  "version": "4.5",
  "executable": "./composer.phar",
  "scheme": "composer",
  "command": "install-package-module"
}

Then the engine can scan these directories and read each hooks.json. Then when 
it gets to your example above, it sees the scheme "composer" in the URL, looks 
for an installer with that name, and calls the executable with some arguments 
(the command, the "URL" aka the package, current directory, etc). So, it might 
call composer with something like:

/WELL_KNOWN/installers/composer/composer.phar install-package-module 
packagist.org/packages/twig/twig#v3.10.3 /app/public

The command is expected to dump the file to stdout -- which PHP then pipes to 
wherever it is supposed to go (as you mention below).

At this point, the user may not know a single thing about composer or how it 
works, or anything, really. As far as they are concerned, they said they wanted 
an import and they got one. However, there still exists a bunch of 
composer-specific scripts, etc. In this case, we make new WELL_KNOWN types. For 
example, we can have "script-runners" that can be registered so you can just do 
"php run composer test" and it will run `composer test` for you. If you want 
something shorter, you can just add `alias composer="php run composer"` to your 
shell and bob's your uncle. 

This is basically how kubernetes handles networking, disks, etc. so that the 
entire thing is completely swappable and extendable. So, we know the idea is 
sound and "just works," we just need to customize it for php.

— Rob

Reply via email to